cartethyia 2.4.6 → 2.5.1

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 (303) hide show
  1. package/README.md +45 -13
  2. package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js +1 -0
  3. package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bcy150Jo.js → CliToolDetailPage-Bb9CNuoh.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DyNXefph.js → CliToolsPage-BXUaRCQQ.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-BXUaRCQQ.js.gz +0 -0
  8. package/dist/dashboard/assets/CombosPage-CisNZOUt.js +1 -0
  9. package/dist/dashboard/assets/CombosPage-CisNZOUt.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CATpAEOz.js → ConsoleLogPage-BOpI_LOH.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-D6jEU7nL.js → CustomizationPage-DAkdvJ4k.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-CZkpn0a7.js → HealthEventsModal-DeT-VXHd.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-BJlMVN4U.js → LoginPage-CytxSeGP.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-CytxSeGP.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-Br3CXEau.js → ModelPicker-oQ-SDXp8.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
  25. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  26. package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js +1 -0
  27. package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js.gz +0 -0
  28. package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js +7 -0
  29. package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js.gz +0 -0
  30. package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js +1 -0
  31. package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js.gz +0 -0
  32. package/dist/dashboard/assets/{SettingsPage-BHW4Zt06.js → SettingsPage-8-eVadhc.js} +1 -1
  33. package/dist/dashboard/assets/SettingsPage-8-eVadhc.js.gz +0 -0
  34. package/dist/dashboard/assets/{SetupPage-VOYC_lIa.js → SetupPage-uEIXPyGO.js} +1 -1
  35. package/dist/dashboard/assets/SetupPage-uEIXPyGO.js.gz +0 -0
  36. package/dist/dashboard/assets/SortableList-DCY0TPTZ.js +1 -0
  37. package/dist/dashboard/assets/SortableList-DCY0TPTZ.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-BNIDLiU6.js → StudioPage-DqK5E26W.js} +4 -4
  39. package/dist/dashboard/assets/StudioPage-DqK5E26W.js.gz +0 -0
  40. package/dist/dashboard/assets/UsagePage-38JfiZ0H.js +56 -0
  41. package/dist/dashboard/assets/UsagePage-38JfiZ0H.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BETfaMYZ.js → api-keys-BmDbsior.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
  44. package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
  45. package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
  46. package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
  47. package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-BTyGk_DL.js → badge-DpXT4Ixt.js} +1 -1
  49. package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-bujTWYM4.js → banner-CSAAWLJv.js} +1 -1
  51. package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-B0Bnx5CA.js → card-DEhRXXw4.js} +1 -1
  53. package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-DE2ug812.js → cli-tools-B0wBq-RL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-R_rArH8S.js → clipboard-button-DOo6EU8O.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-BWIP_EvW.js → dialog-BvQeBMfP.js} +1 -1
  59. package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BKcgVugF.js → drawer-CMwPXwQB.js} +1 -1
  61. package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
  62. package/dist/dashboard/assets/entry--yj84zk3.js +4 -0
  63. package/dist/dashboard/assets/entry--yj84zk3.js.gz +0 -0
  64. package/dist/dashboard/assets/{entry-CcWkGR4a.js → entry-D4O2229X.js} +1 -1
  65. package/dist/dashboard/assets/{entry-CcWkGR4a.js.gz → entry-D4O2229X.js.gz} +0 -0
  66. package/dist/dashboard/assets/{entry-kTiTFrvh.js → entry-DKvr5Q9K.js} +1 -1
  67. package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-Booc5mNe.js → github-badge-zliLvBwr.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-D70pChfn.js → index-CcD1eSJ6.js} +2 -2
  71. package/dist/dashboard/assets/index-CcD1eSJ6.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-D1TCqM2D.js → inline-D1IlZZ8H.js} +1 -1
  73. package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-B9oyU93y.js → input-BpVZdrbc.js} +1 -1
  75. package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-BArzXirQ.js → layout-BioLW7gN.js} +1 -1
  77. package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-BVZzaoHF.js → list-row-CwVcGAIc.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-EmuIayqp.js → live-yfEJFF9T.js} +1 -1
  81. package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CSPQ9xB0.js → model-bans-CT-TxSNX.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
  84. package/dist/dashboard/assets/providers/cursor.webp +0 -0
  85. package/dist/dashboard/assets/providers/trae.webp +0 -0
  86. package/dist/dashboard/assets/{routing-BExgxxBU.js → routing-Cs6jl49L.js} +1 -1
  87. package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
  88. package/dist/dashboard/assets/select-XPJ-dIw3.js +41 -0
  89. package/dist/dashboard/assets/select-XPJ-dIw3.js.gz +0 -0
  90. package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
  91. package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
  92. package/dist/dashboard/assets/{stack-Ca5HxI5i.js → stack-C9_uUjaA.js} +1 -1
  93. package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
  94. package/dist/dashboard/assets/{state-CzUPfLXo.js → state-DA0B95UQ.js} +1 -1
  95. package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
  96. package/dist/dashboard/assets/{switch-DOUIky_i.js → switch-Cittxy-x.js} +1 -1
  97. package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
  98. package/dist/dashboard/assets/{toast-C76WrMhc.js → toast-CNt_jzO4.js} +1 -1
  99. package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
  100. package/dist/dashboard/assets/{useMutation-D87FZwH0.js → useMutation-BQKMcJ1H.js} +1 -1
  101. package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
  102. package/dist/dashboard/index.html +1 -1
  103. package/dist/dashboard/index.html.gz +0 -0
  104. package/dist/dashboard/precompressed-assets.json +51 -52
  105. package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
  106. package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
  107. package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
  108. package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
  109. package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
  110. package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
  111. package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
  112. package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
  113. package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
  114. package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
  115. package/package.json +3 -2
  116. package/scripts/cli/command/console-menu.ts +30 -11
  117. package/scripts/cli/command/start.ts +77 -13
  118. package/scripts/cli/command/tray.ts +47 -8
  119. package/scripts/cli/command/update.ts +1 -1
  120. package/scripts/cli/gateway/instance-lock.ts +105 -0
  121. package/scripts/cli/gateway/spawn.ts +173 -101
  122. package/scripts/cli/ui/background.ts +110 -49
  123. package/src/boot/app.ts +20 -6
  124. package/src/boot/dependencies.ts +19 -0
  125. package/src/boot/elysia-typebox-bridge.ts +37 -0
  126. package/src/boot/lifecycle.ts +2 -0
  127. package/src/config/cooldowns.ts +138 -138
  128. package/src/config/providers.ts +5 -5
  129. package/src/config/runtime.ts +19 -3
  130. package/src/config/spec.ts +9 -0
  131. package/src/console/api-keys/contracts.ts +414 -391
  132. package/src/console/api-keys/routes.ts +880 -870
  133. package/src/console/auth/account-reset-store.ts +14 -0
  134. package/src/console/auth/account-reset.ts +3 -2
  135. package/src/console/auth/setup-service.ts +6 -3
  136. package/src/console/domain-registration.ts +9 -2
  137. package/src/console/observability/contracts.ts +475 -455
  138. package/src/console/observability/store.ts +1015 -952
  139. package/src/console/providers/catalog/contracts.ts +31 -0
  140. package/src/console/providers/catalog/provider-operations.ts +8 -0
  141. package/src/console/providers/catalog/store.ts +1532 -1525
  142. package/src/console/providers/oauth/callback-listener.ts +18 -3
  143. package/src/console/providers/oauth/routes.ts +38 -15
  144. package/src/console/quota/refresh.ts +19 -0
  145. package/src/console/updates/routes.ts +162 -4
  146. package/src/main.ts +3 -0
  147. package/src/network/http2-bidi.ts +85 -0
  148. package/src/network/outbound-fetch.ts +1 -26
  149. package/src/network/pool/agent.ts +478 -460
  150. package/src/network/pool/resolver.ts +1104 -544
  151. package/src/network/pool-health-machine.ts +12 -5
  152. package/src/observability/payload-capture.ts +344 -324
  153. package/src/persistence/db-pg.ts +9 -3
  154. package/src/persistence/db-sqlite.ts +497 -495
  155. package/src/persistence/health-event-flush.ts +137 -0
  156. package/src/persistence/migrate.ts +181 -129
  157. package/src/persistence/schema.ts +1360 -1295
  158. package/src/persistence/settings-revision.ts +25 -0
  159. package/src/persistence/shared-cache.ts +217 -0
  160. package/src/persistence/stores/share-store.ts +556 -528
  161. package/src/persistence/usage-flush.ts +61 -1
  162. package/src/protocol/canonical-model.ts +23 -1
  163. package/src/protocol/response/chat.ts +143 -10
  164. package/src/protocol/response/omp-tool-call-markup.ts +221 -0
  165. package/src/providers/default-registry.ts +173 -1
  166. package/src/providers/discovery/openai-model-discovery.ts +39 -3
  167. package/src/providers/discovery/openrouter-free-roster.ts +98 -0
  168. package/src/providers/discovery/probing-service.ts +1022 -1001
  169. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +39 -0
  170. package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
  171. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +18 -5
  172. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
  173. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
  174. package/src/providers/integrations/buddy/codebuddy/shared.ts +15 -6
  175. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
  176. package/src/providers/integrations/buddy/workbuddy/shared.ts +3 -3
  177. package/src/providers/integrations/claude/claude.ts +606 -582
  178. package/src/providers/integrations/cline/cline-quota.ts +18 -16
  179. package/src/providers/integrations/cline/cline.ts +539 -468
  180. package/src/providers/integrations/commandcode/commandcode.ts +675 -541
  181. package/src/providers/integrations/commandcode/discovery.ts +116 -0
  182. package/src/providers/integrations/commandcode/quota.ts +160 -0
  183. package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
  184. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
  185. package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
  186. package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
  187. package/src/providers/integrations/cursor/cursor-request.ts +471 -0
  188. package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
  189. package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
  190. package/src/providers/integrations/cursor/cursor.ts +960 -0
  191. package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
  192. package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
  193. package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
  194. package/src/providers/integrations/github/github.ts +4 -4
  195. package/src/providers/integrations/hermes.ts +79 -8
  196. package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
  197. package/src/providers/integrations/kilo/kilo.ts +153 -146
  198. package/src/providers/integrations/opencode/provider.ts +532 -538
  199. package/src/providers/integrations/openrouter/provider.ts +139 -281
  200. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
  201. package/src/providers/integrations/qoder/qoder-oauth.ts +310 -257
  202. package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
  203. package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
  204. package/src/providers/integrations/qoder/qoder.ts +865 -730
  205. package/src/providers/integrations/trae/trae-catalog.ts +190 -0
  206. package/src/providers/integrations/trae/trae-headers.ts +149 -0
  207. package/src/providers/integrations/trae/trae-oauth.ts +773 -0
  208. package/src/providers/integrations/trae/trae-protocol.ts +400 -0
  209. package/src/providers/integrations/trae/trae-realm.ts +220 -0
  210. package/src/providers/integrations/trae/trae.ts +458 -0
  211. package/src/providers/operations/account-health-service.ts +107 -10
  212. package/src/providers/operations/client-versions.ts +133 -6
  213. package/src/providers/operations/provider-catalog-seeder.ts +95 -86
  214. package/src/providers/operations/provider-credential-service.ts +46 -7
  215. package/src/providers/provider-contracts.ts +76 -0
  216. package/src/providers/provider-metadata.ts +78 -6
  217. package/src/providers/provider-model-definition.ts +373 -349
  218. package/src/security/api-key-auth.ts +126 -8
  219. package/src/transport/contracts.ts +3 -0
  220. package/src/transport/dispatch/attempt-finalize.ts +32 -1
  221. package/src/transport/dispatch/attempt-loop.ts +530 -515
  222. package/src/transport/dispatch/generation.ts +88 -21
  223. package/src/transport/dispatch/leases.ts +99 -2
  224. package/src/transport/dispatch/proxy-request.ts +60 -3
  225. package/src/transport/dispatch/streaming/finalize.ts +16 -1
  226. package/src/transport/dispatch/streaming/stream.ts +22 -2
  227. package/src/transport/dispatch/upstream.ts +19 -1
  228. package/src/transport/guards/auth-guard.ts +26 -1
  229. package/src/transport/guards/body-policy.ts +5 -4
  230. package/src/transport/request/preparer.ts +103 -0
  231. package/src/transport/request/state.ts +14 -0
  232. package/src/transport/routing/plan-gate.ts +45 -0
  233. package/src/transport/routing/route-catalog.ts +799 -777
  234. package/src/transport/routing/route-model.ts +52 -1
  235. package/src/transport/routing/router.ts +51 -0
  236. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js +0 -1
  237. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js.gz +0 -0
  238. package/dist/dashboard/assets/CliToolDetailPage-Bcy150Jo.js.gz +0 -0
  239. package/dist/dashboard/assets/CliToolsPage-DyNXefph.js.gz +0 -0
  240. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js +0 -1
  241. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js.gz +0 -0
  242. package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js.gz +0 -0
  243. package/dist/dashboard/assets/CustomizationPage-D6jEU7nL.js.gz +0 -0
  244. package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js.gz +0 -0
  245. package/dist/dashboard/assets/LoginPage-BJlMVN4U.js.gz +0 -0
  246. package/dist/dashboard/assets/ModelPicker-Br3CXEau.js.gz +0 -0
  247. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js +0 -1
  248. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js.gz +0 -0
  249. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js +0 -3
  250. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js.gz +0 -0
  251. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
  252. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js.gz +0 -0
  253. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js +0 -1
  254. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js.gz +0 -0
  255. package/dist/dashboard/assets/ProxyPage-CXiumA53.js +0 -7
  256. package/dist/dashboard/assets/ProxyPage-CXiumA53.js.gz +0 -0
  257. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js +0 -1
  258. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js.gz +0 -0
  259. package/dist/dashboard/assets/SettingsPage-BHW4Zt06.js.gz +0 -0
  260. package/dist/dashboard/assets/SetupPage-VOYC_lIa.js.gz +0 -0
  261. package/dist/dashboard/assets/SortableList-DpOTnGuH.js +0 -1
  262. package/dist/dashboard/assets/SortableList-DpOTnGuH.js.gz +0 -0
  263. package/dist/dashboard/assets/StudioPage-BNIDLiU6.js.gz +0 -0
  264. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js +0 -56
  265. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js.gz +0 -0
  266. package/dist/dashboard/assets/api-keys-BETfaMYZ.js.gz +0 -0
  267. package/dist/dashboard/assets/app-BLCvGdzb.css +0 -2
  268. package/dist/dashboard/assets/app-BLCvGdzb.css.gz +0 -0
  269. package/dist/dashboard/assets/assert-7J6_LVD3.js +0 -1
  270. package/dist/dashboard/assets/assert-7J6_LVD3.js.gz +0 -0
  271. package/dist/dashboard/assets/badge-BTyGk_DL.js.gz +0 -0
  272. package/dist/dashboard/assets/banner-bujTWYM4.js.gz +0 -0
  273. package/dist/dashboard/assets/card-B0Bnx5CA.js.gz +0 -0
  274. package/dist/dashboard/assets/cli-tools-DE2ug812.js.gz +0 -0
  275. package/dist/dashboard/assets/clipboard-button-R_rArH8S.js.gz +0 -0
  276. package/dist/dashboard/assets/dialog-BWIP_EvW.js.gz +0 -0
  277. package/dist/dashboard/assets/drawer-BKcgVugF.js.gz +0 -0
  278. package/dist/dashboard/assets/entry-7dpgvpJ3.js +0 -4
  279. package/dist/dashboard/assets/entry-7dpgvpJ3.js.gz +0 -0
  280. package/dist/dashboard/assets/entry-kTiTFrvh.js.gz +0 -0
  281. package/dist/dashboard/assets/github-badge-Booc5mNe.js.gz +0 -0
  282. package/dist/dashboard/assets/index-D70pChfn.js.gz +0 -0
  283. package/dist/dashboard/assets/inline-D1TCqM2D.js.gz +0 -0
  284. package/dist/dashboard/assets/input-B9oyU93y.js.gz +0 -0
  285. package/dist/dashboard/assets/layout-BArzXirQ.js.gz +0 -0
  286. package/dist/dashboard/assets/list-row-BVZzaoHF.js.gz +0 -0
  287. package/dist/dashboard/assets/live-EmuIayqp.js.gz +0 -0
  288. package/dist/dashboard/assets/model-bans-CSPQ9xB0.js.gz +0 -0
  289. package/dist/dashboard/assets/routing-BExgxxBU.js.gz +0 -0
  290. package/dist/dashboard/assets/select-C9mMoEpJ.js +0 -41
  291. package/dist/dashboard/assets/select-C9mMoEpJ.js.gz +0 -0
  292. package/dist/dashboard/assets/settings-C4_Gn_wq.js +0 -1
  293. package/dist/dashboard/assets/settings-C4_Gn_wq.js.gz +0 -0
  294. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
  295. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js.gz +0 -0
  296. package/dist/dashboard/assets/stack-Ca5HxI5i.js.gz +0 -0
  297. package/dist/dashboard/assets/state-CzUPfLXo.js.gz +0 -0
  298. package/dist/dashboard/assets/switch-DOUIky_i.js.gz +0 -0
  299. package/dist/dashboard/assets/toast-C76WrMhc.js.gz +0 -0
  300. package/dist/dashboard/assets/useMutation-D87FZwH0.js.gz +0 -0
  301. package/src/network/tls/bun-client-hello.ts +0 -112
  302. /package/dist/dashboard/assets/{app-Cjq7nrwu.js → app-C-Y4oZT0.js} +0 -0
  303. /package/dist/dashboard/assets/{app-Cjq7nrwu.js.gz → app-C-Y4oZT0.js.gz} +0 -0
@@ -1,541 +1,675 @@
1
- // Factory-blocked (Phase C4): proprietary `{threadId, config, params}`
2
- // envelope with NDJSON stream transform — non-OpenAI wire; stays bespoke.
3
- import { GatewayError } from "../../../protocol/gateway-error";
4
- import { canContainToolResult, joinTextParts, toolCallParts, toolResultParts } from "../../../protocol/canonical-model";
5
- import {
6
- classifyUpstreamError,
7
- extractUpstreamMessage,
8
- mapUpstreamHttpError,
9
- upstreamErrorIdentifier,
10
- } from "../../../transport/failure-policy";
11
- import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason, ContentPart } from "../../../protocol/canonical-model";
12
- import { isRecord, readNumber, readString, resolveImageSource, splitDataUrl } from "../../../protocol/codecs";
13
- import { usageFromProvider } from "../../provider-usage";
14
- import { readCredentialSecret, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext } from "../../provider-contracts";
15
- import { providerBaseUrl } from "../../provider-metadata";
16
- import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
17
- import { defineModel } from "../../provider-model-definition";
18
- import type { ModelDefinition } from "../../provider-model-definition";
19
- import {
20
- getCommandCodeVersion,
21
- } from "../../operations/client-versions";
22
- import { MimoThinkSplitter } from "../xiaomi-mimo/think-stream";
23
- export const COMMANDCODE_MODELS: readonly ModelDefinition[] = [
24
- // Synced from https://commandcode.ai/models — deepseek keep only v4.1 flash/pro
25
- "xiaomi/mimo-v2.5-pro",
26
- "xiaomi/mimo-v2.5",
27
- "xiaomi/mimo-v2.6-pro",
28
- "xiaomi/mimo-v2.6-pro-ultraspeed",
29
- "xiaomi/mimo-v2.6-flash",
30
- "deepseek/deepseek-v4.1-flash",
31
- "deepseek/deepseek-v4.1-flash-fast",
32
- "deepseek/deepseek-v4-pro",
33
- "moonshotai/Kimi-K2.6",
34
- "moonshotai/Kimi-K2.7-Code",
35
- "moonshotai/Kimi-K2.7-Code-Highspeed",
36
- "moonshotai/Kimi-K3",
37
- "zai-org/GLM-5",
38
- "zai-org/GLM-5.1",
39
- "zai-org/GLM-5.2",
40
- "zai-org/GLM-5.2-Fast",
41
- "zai-org/GLM-5.3",
42
- "zai-org/GLM-5.3-Flash",
43
- "MiniMaxAI/MiniMax-M2.5",
44
- "MiniMaxAI/MiniMax-M2.7",
45
- "MiniMaxAI/MiniMax-M3",
46
- "Qwen/Qwen3.6-Plus",
47
- "Qwen/Qwen3.6-Max-Preview",
48
- "Qwen/Qwen3.7-Max",
49
- "Qwen/Qwen3.7-Plus",
50
- "Qwen/Qwen3.7-Flash",
51
- "Qwen/Qwen3.8-Flash",
52
- "Qwen/Qwen3.8-Max",
53
- "Qwen/Qwen3.8-Max-0902",
54
- "Qwen/Qwen3.8-Omni-Flash",
55
- "Qwen/Qwen3.8-27B",
56
- "stepfun/Step-3.5-Flash",
57
- "stepfun/Step-3.7-Flash",
58
- "stepfun/Step-5-Preview",
59
- "nvidia/nemotron-3-ultra-550b-a55b",
60
- "poolside/laguna-s-2.1-free",
61
- ].map((id) =>
62
- defineModel({ id, providerId: "commandcode", endpoint: "/alpha/generate", vision: true, reasoning: true }),
63
- );
64
-
65
-
66
- // CommandCode provider constants.
67
-
68
- export const COMMANDCODE_BASE_URL = providerBaseUrl("commandcode");
69
- export const COMMANDCODE_PROVIDER_ID = "commandcode" as const;
70
- const COMMANDCODE_DEFAULT_MAX_TOKENS = 4096;
71
-
72
- /**
73
- * The `/alpha/generate` wire, as the CLI client sends it:
74
- * - `params.system` is a top-level string; system turns never enter `messages`
75
- * - every `content` is an array of blocks, never a string
76
- * - an assistant tool call is `{type:"tool-call", toolCallId, toolName, input}`
77
- * and is preceded by a reasoning block, which the upstream requires
78
- * - a tool result is its own `role:"tool"` turn, not text glued into the user turn
79
- * - an image is `{type:"image", image, mimeType}`
80
- */
81
- function toolResultValue(content: readonly ContentPart[] | string): string {
82
- if (typeof content === "string") return content;
83
- return content
84
- .filter((part) => part.kind === "text")
85
- .map((part) => part.text)
86
- .filter((text) => text.length > 0)
87
- .join("\n");
88
- }
89
-
90
- function imageBlock(payload: unknown): Record<string, unknown> | undefined {
91
- const source = resolveImageSource(payload);
92
- if (source?.url === undefined) return undefined;
93
- const inline = splitDataUrl(source.url);
94
- return { type: "image", image: source.url, ...(inline ? { mimeType: inline.mediaType } : {}) };
95
- }
96
-
97
- function contentBlocks(parts: readonly ContentPart[]): Array<Record<string, unknown>> {
98
- const blocks: Array<Record<string, unknown>> = [];
99
- for (const part of parts) {
100
- if (part.kind === "text") {
101
- if (part.text.length > 0) blocks.push({ type: "text", text: part.text });
102
- continue;
103
- }
104
- if (part.kind === "image") {
105
- const block = imageBlock(part.payload);
106
- if (block) blocks.push(block);
107
- }
108
- }
109
- return blocks.length > 0 ? blocks : [{ type: "text", text: "" }];
110
- }
111
-
112
- function parsedInput(value: unknown): unknown {
113
- if (typeof value !== "string") return value ?? {};
114
- try {
115
- return JSON.parse(value);
116
- } catch {
117
- return {};
118
- }
119
- }
120
-
121
- export function convertMessages(messages: CanonicalRequest["messages"]): {
122
- messages: Array<Record<string, unknown>>;
123
- system?: string;
124
- } {
125
- const out: Array<Record<string, unknown>> = [];
126
- const systemTexts: string[] = [];
127
- for (const message of messages) {
128
- if (message.role === "system" || message.role === "developer") {
129
- const text = joinTextParts(message.content);
130
- if (text.length > 0) systemTexts.push(text);
131
- continue;
132
- }
133
- // Answers live in `tool` turns or in `user` turns (the Messages ledger
134
- // re-homes them). Each result is its own `role:"tool"` turn: flattening
135
- // them into the user turn as prose left the tool call unanswered.
136
- if (canContainToolResult(message) && toolResultParts(message).length > 0) {
137
- for (const result of toolResultParts(message)) {
138
- out.push({
139
- role: "tool",
140
- content: [
141
- {
142
- type: "tool-result",
143
- toolCallId: result.call_id,
144
- toolName: "",
145
- output: { type: "text", value: toolResultValue(result.content) },
146
- },
147
- ],
148
- });
149
- }
150
- const remainder = contentBlocks(message.content.filter((part) => part.kind !== "toolResult"));
151
- if (remainder.some((block) => block["text"] !== "")) out.push({ role: "user", content: remainder });
152
- continue;
153
- }
154
- if (message.role === "assistant") {
155
- const blocks: Array<Record<string, unknown>> = [];
156
- const calls = toolCallParts(message);
157
- const reasoning = message.content
158
- .filter((part) => part.kind === "reasoning")
159
- .map((part) => part.summary ?? "")
160
- .filter((text) => text.length > 0)
161
- .join("");
162
- if (reasoning.length > 0 || calls.length > 0) {
163
- blocks.push({ type: "reasoning", text: reasoning.length > 0 ? reasoning : " " });
164
- }
165
- const text = joinTextParts(message.content);
166
- if (text.length > 0) blocks.push({ type: "text", text });
167
- for (const call of calls) {
168
- blocks.push({
169
- type: "tool-call",
170
- toolCallId: call.call_id,
171
- toolName: call.name,
172
- input: parsedInput(call.arguments),
173
- });
174
- }
175
- out.push({ role: "assistant", content: blocks.length > 0 ? blocks : [{ type: "text", text: "" }] });
176
- continue;
177
- }
178
- out.push({ role: "user", content: contentBlocks(message.content) });
179
- }
180
- const system = systemTexts.join("\n\n");
181
- return system.length > 0 ? { messages: out, system } : { messages: out };
182
- }
183
-
184
- export function buildRequest(
185
- modelId: string,
186
- request: { messages: CanonicalRequest["messages"]; tools: { readonly name: string; readonly description: string | undefined; readonly jsonSchema: unknown }[]; maxOutputTokens: number | null; temperature: number | null; topP: number | null; reasoningEffort: string | null },
187
- threadId: string,
188
- systemExtra?: string,
189
- ): Record<string, unknown> {
190
- const { messages, system: systemFromMessages } = convertMessages(request.messages);
191
- const systemCombined = systemExtra ? (systemFromMessages ? `${systemExtra}\n${systemFromMessages}` : systemExtra) : systemFromMessages;
192
- const params: Record<string, unknown> = {
193
- model: modelId,
194
- messages,
195
- stream: true,
196
- max_tokens: request.maxOutputTokens ?? COMMANDCODE_DEFAULT_MAX_TOKENS,
197
- temperature: request.temperature ?? 0.3,
198
- };
199
- if (request.topP !== null) params["top_p"] = request.topP;
200
- if (request.reasoningEffort !== null && request.reasoningEffort !== "none") {
201
- params["reasoning_effort"] = request.reasoningEffort;
202
- }
203
- if (systemCombined) params["system"] = systemCombined;
204
- if (request.tools.length > 0) {
205
- params["tools"] = request.tools.map((tool) => ({
206
- name: tool.name,
207
- description: tool.description ?? undefined,
208
- input_schema: tool.jsonSchema,
209
- }));
210
- }
211
- return {
212
- threadId,
213
- memory: "",
214
- config: {
215
- workingDir: "",
216
- date: new Date().toISOString().slice(0, 10),
217
- environment: "",
218
- structure: [],
219
- isGitRepo: false,
220
- currentBranch: "",
221
- mainBranch: "",
222
- gitStatus: "",
223
- recentCommits: [],
224
- },
225
- params,
226
- };
227
- }
228
-
229
- export async function headers(sessionId: string, token: string): Promise<Record<string, string>> {
230
- return {
231
- "content-type": "application/json",
232
- accept: "text/event-stream",
233
- authorization: `Bearer ${token}`,
234
- "x-command-code-version": getCommandCodeVersion(),
235
- "x-cli-environment": "cli",
236
- "x-session-id": sessionId,
237
- };
238
- }
239
-
240
-
241
- type StreamState = { toolIndexById: Map<string, number>; nextToolIndex: number; finishReason: string | undefined; usage: Record<string, unknown> | undefined; think: MimoThinkSplitter };
242
-
243
- export function ccMapFinishReason(reason: string): CanonicalStopReason {
244
- return reason === "length" ? "length" : reason === "tool_calls" ? "tool_use" : reason === "content_filter" ? "content_filter" : "stop";
245
- }
246
-
247
- export function transformLine(line: string, state: StreamState, seqBase: number): CanonicalEvent[] {
248
- let event: Record<string, unknown>;
249
- try {
250
- event = JSON.parse(line) as Record<string, unknown>;
251
- } catch {
252
- return [];
253
- }
254
- const type = readString(event, "type");
255
- if (!type) return [];
256
- if (type === "reasoning-delta") {
257
- const text = readString(event, "text") || readString(event, "delta");
258
- return text ? [{ type: "content_delta", sequence_number: seqBase, content: { kind: "reasoning", payload: null, summary: text } } as CanonicalEvent] : [];
259
- }
260
- if (type === "text-delta") {
261
- const text = readString(event, "text") || readString(event, "delta");
262
- if (!text) return [];
263
- const split = state.think.push(text);
264
- const events: CanonicalEvent[] = [];
265
- if (split.reasoning) events.push({ type: "content_delta", sequence_number: seqBase, content: { kind: "reasoning", payload: null, summary: split.reasoning } } as CanonicalEvent);
266
- if (split.text) events.push({ type: "content_delta", sequence_number: seqBase, content: { kind: "text", text: split.text } } as CanonicalEvent);
267
- return events;
268
- }
269
- if (type === "tool-input-start") {
270
- const id = readString(event, "id") || readString(event, "toolCallId");
271
- if (!id || state.toolIndexById.has(id)) return [];
272
- state.toolIndexById.set(id, state.nextToolIndex++);
273
- return [{ type: "tool_call_delta", sequence_number: seqBase, call_id: id, name: readString(event, "toolName") ?? "", arguments_delta: "" } as CanonicalEvent];
274
- }
275
- if (type === "tool-input-delta") {
276
- const id = readString(event, "id") || readString(event, "toolCallId");
277
- const delta = readString(event, "delta") || readString(event, "inputTextDelta");
278
- if (!id || !delta || !state.toolIndexById.has(id)) return [];
279
- return [{ type: "tool_call_delta", sequence_number: seqBase, call_id: id, arguments_delta: delta } as CanonicalEvent];
280
- }
281
- if (type === "tool-call") {
282
- const id = readString(event, "toolCallId");
283
- if (!id || state.toolIndexById.has(id)) return [];
284
- state.toolIndexById.set(id, state.nextToolIndex++);
285
- const input = typeof event["input"] === "string" ? (event["input"] as string) : JSON.stringify(event["input"] ?? {});
286
- return [
287
- { type: "tool_call_delta", sequence_number: seqBase, call_id: id, name: readString(event, "toolName") ?? "", arguments_delta: "" } as CanonicalEvent,
288
- { type: "tool_call_delta", sequence_number: seqBase + 1, call_id: id, arguments_delta: input } as CanonicalEvent,
289
- ];
290
- }
291
- if (type === "finish-step") {
292
- const reason = readString(event, "finishReason");
293
- if (reason) state.finishReason = reason;
294
- if (typeof event["usage"] === "object" && event["usage"] !== null && !Array.isArray(event["usage"])) state.usage = event["usage"] as Record<string, unknown>;
295
- return [];
296
- }
297
- if (type === "finish") {
298
- const reason = state.finishReason ?? readString(event, "finishReason") ?? "stop";
299
- const canonicalReason = ccMapFinishReason(reason);
300
- const events: CanonicalEvent[] = [
301
- { type: "terminal", sequence_number: seqBase, state: "complete", stop_reason: canonicalReason, provider_stop_reason: reason } as CanonicalEvent,
302
- ];
303
- if (state.usage) {
304
- const prompt = readNumber(state.usage, "promptTokens") ?? readNumber(state.usage, "inputTokens") ?? 0;
305
- const completion = readNumber(state.usage, "completionTokens") ?? readNumber(state.usage, "outputTokens") ?? 0;
306
- const total = readNumber(state.usage, "totalTokens") ?? prompt + completion;
307
- const cached = readNumber(state.usage, "cachedTokens") ?? readNumber(state.usage, "cacheReadTokens") ?? 0;
308
- const usageRecord = usageFromProvider({
309
- input_tokens: prompt,
310
- output_tokens: completion,
311
- total_tokens: total,
312
- cached_tokens: cached,
313
- cache_creation_input_tokens: readNumber(state.usage, "cacheWriteTokens") ?? undefined,
314
- } as unknown as Record<string, unknown>);
315
- // Append usage to terminal
316
- (events[0] as unknown as Record<string, unknown>)["usage"] = usageRecord;
317
- }
318
- return events;
319
- }
320
- if (type === "error") {
321
- const raw = event["error"] ?? event["message"] ?? event;
322
- const errorObject = isRecord(raw) ? raw : event;
323
- const identifier = upstreamErrorIdentifier(raw) ?? upstreamErrorIdentifier(event);
324
- const providerStatus = [
325
- errorObject["status"],
326
- errorObject["statusCode"],
327
- errorObject["code"],
328
- event["status"],
329
- event["statusCode"],
330
- event["code"],
331
- ].find(
332
- (value): value is number =>
333
- typeof value === "number" &&
334
- Number.isFinite(value) &&
335
- value >= 400 &&
336
- value < 600,
337
- );
338
- const classification = classifyUpstreamError(providerStatus, identifier);
339
- const message =
340
- extractUpstreamMessage(raw) ||
341
- extractUpstreamMessage(event) ||
342
- "Command Code stream error";
343
- throw new GatewayError(
344
- classification.code,
345
- classification.status,
346
- message.slice(0, 500),
347
- {
348
- ...(providerStatus === undefined ? {} : { providerStatus }),
349
- ...(identifier ? { providerCode: identifier } : {}),
350
- ...(classification.credentialEvidence ? { credentialEvidence: true } : {}),
351
- ...(classification.rateLimitScope
352
- ? { rateLimitScope: classification.rateLimitScope }
353
- : {}),
354
- },
355
- classification.origin,
356
- );
357
- }
358
- return [];
359
- }
360
-
361
- /** NDJSON transform — beta-style simple loop + signal-aware read for abort/timeout propagation. */
362
- export async function* transformNdjson(body: ReadableStream<Uint8Array>, signal: AbortSignal): AsyncIterable<CanonicalEvent> {
363
- const reader = body.getReader();
364
- const decoder = new TextDecoder();
365
- const state: StreamState = { toolIndexById: new Map(), nextToolIndex: 0, finishReason: undefined, usage: undefined, think: new MimoThinkSplitter() };
366
- let buffer = "";
367
- let hasTerminal = false;
368
- let seq = 1;
369
- // initial response_start
370
- yield { type: "response_start", sequence_number: seq++, model: "commandcode" } as CanonicalEvent;
371
-
372
- const readWithAbort = (): Promise<{ done: boolean; value?: Uint8Array }> => {
373
- if (signal.aborted) throw new GatewayError("transport_closed", 499, "Command Code stream aborted");
374
- const { promise, resolve, reject } = Promise.withResolvers<{ done: boolean; value?: Uint8Array }>() as unknown as { promise: Promise<{ done: boolean; value?: Uint8Array }>; resolve: (v: unknown) => void; reject: (e: unknown) => void };
375
- const onAbort = (): void => {
376
- if (signal.aborted) reject(new GatewayError("transport_closed", 499, "Command Code stream aborted"));
377
- else reject(new GatewayError("platform_unavailable", 502, "Command Code stream error", {}, "upstream"));
378
- };
379
- signal.addEventListener("abort", onAbort, { once: true });
380
- void reader.read().then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
381
- return promise;
382
- };
383
-
384
- try {
385
- while (true) {
386
- const { done, value } = await readWithAbort();
387
- if (done) break;
388
- buffer += decoder.decode(value, { stream: true });
389
- let nl = buffer.indexOf("\n");
390
- while (nl !== -1) {
391
- const line = buffer.slice(0, nl).trim();
392
- buffer = buffer.slice(nl + 1);
393
- if (line) {
394
- let evs: CanonicalEvent[] = [];
395
- try {
396
- evs = transformLine(line, state, seq);
397
- } catch (e) {
398
- throw e;
399
- }
400
- for (const ev of evs) {
401
- // Strictly increasing per line, in emission order: the Responses
402
- // surface throws on any non-monotonic sequence mid-stream, after
403
- // partial bytes are already flushed. No special-casing for
404
- // dual-event lines — order is order.
405
- (ev as unknown as Record<string, unknown>)["sequence_number"] = seq++;
406
- if (ev.type === "terminal") hasTerminal = true;
407
- yield ev;
408
- }
409
- }
410
- nl = buffer.indexOf("\n");
411
- }
412
- }
413
- buffer += decoder.decode();
414
- const tail = buffer.trim();
415
- if (tail) {
416
- for (const ev of transformLine(tail, state, seq)) {
417
- if (ev.type === "terminal") hasTerminal = true;
418
- (ev as unknown as Record<string, unknown>)["sequence_number"] = seq++;
419
- yield ev;
420
- }
421
- }
422
- const rest = state.think.flush();
423
- if (rest.reasoning) yield { type: "content_delta", sequence_number: seq++, content: { kind: "reasoning", payload: null, summary: rest.reasoning } } as CanonicalEvent;
424
- if (rest.text) yield { type: "content_delta", sequence_number: seq++, content: { kind: "text", text: rest.text } } as CanonicalEvent;
425
- if (!hasTerminal) {
426
- // NDJSON ended with no terminal envelope: truncated, never complete.
427
- yield { type: "terminal", sequence_number: seq++, state: "failed", stop_reason: ccMapFinishReason(state.finishReason ?? "stop"), provider_stop_reason: state.finishReason ?? "stop" } as CanonicalEvent;
428
- }
429
- } finally {
430
- try {
431
- reader.releaseLock?.();
432
- } catch {
433
- // Best-effort reader unlock during stream cleanup.
434
- }
435
- void reader.cancel().catch(() => {});
436
- }
437
- }
438
-
439
- // CommandCode model catalog.
440
-
441
-
442
-
443
- // ProviderAdapter — manual NDJSON dispatch, NOT a factory wrapper
444
-
445
- interface CommandCodeAdapterOptions {
446
- readonly fetch?: typeof fetch;
447
- }
448
-
449
- class CommandCodeAdapter implements ProviderAdapter {
450
- readonly provider_id = COMMANDCODE_PROVIDER_ID;
451
- private readonly fetchFn: typeof fetch;
452
-
453
- constructor(options: CommandCodeAdapterOptions = {}) {
454
- this.fetchFn = options.fetch ?? globalThis.fetch;
455
- }
456
-
457
- async *dispatch(
458
- request: CanonicalRequest,
459
- candidate: ProviderDispatchTarget,
460
- context: ProviderDispatchContext,
461
- ): AsyncIterable<CanonicalEvent> {
462
- if (candidate.wire_family !== "chat") {
463
- throw new GatewayError("capability_unsupported", 400, `commandcode supports chat only, got ${candidate.wire_family}`);
464
- }
465
- const token = readCredentialSecret(context.credential, "Command Code bearer token is required");
466
-
467
- // Tools: only function tools
468
- const tools = (request.tools ?? []).map((t) => ({ name: t.name, description: t.description, jsonSchema: t.jsonSchema }));
469
- // Extract system from request.system + instructions for buildRequest extra handling
470
- const systemExtraParts: string[] = [];
471
- if (request.system) for (const p of request.system) if (p.kind === "text") systemExtraParts.push(p.text);
472
- if (request.instructions) for (const p of request.instructions) if (p.kind === "text") systemExtraParts.push(p.text);
473
- const systemExtra = systemExtraParts.join("\n") || undefined;
474
-
475
- const controls = request.generation_controls as Record<string, unknown>;
476
- const maxOutputTokens =
477
- typeof controls["max_tokens"] === "number"
478
- ? controls["max_tokens"]
479
- : typeof controls["max_output_tokens"] === "number"
480
- ? controls["max_output_tokens"]
481
- : null;
482
- const temperature = typeof controls["temperature"] === "number" ? controls["temperature"] : null;
483
- const topP = typeof controls["top_p"] === "number" ? controls["top_p"] : null;
484
- const reasoningEffort = request.reasoning?.effort ?? null;
485
-
486
- const sessionId = crypto.randomUUID();
487
-
488
- const upstreamModelId = candidate.model_id || request.model;
489
- const payload = buildRequest(
490
- upstreamModelId,
491
- { messages: request.messages, tools, maxOutputTokens, temperature, topP, reasoningEffort },
492
- sessionId,
493
- systemExtra,
494
- );
495
-
496
- const lifecycle = createUpstreamDeadlineLifecycle(context);
497
-
498
- const outboundFetch: typeof fetch = (context.outbound_fetch as unknown as typeof fetch) ?? this.fetchFn;
499
- const url =
500
- candidate.endpoint_path && candidate.endpoint_path.startsWith("http") ? candidate.endpoint_path : COMMANDCODE_BASE_URL;
501
-
502
- try {
503
- const response = await outboundFetch(url, {
504
- method: "POST",
505
- headers: await headers(sessionId, token),
506
- body: JSON.stringify(payload),
507
- signal: lifecycle.signal,
508
- });
509
- if (!response.ok) throw await mapUpstreamHttpError(response, "commandcode");
510
- if (!response.body) throw new GatewayError("platform_unavailable", 502, "Command Code returned empty body", {}, "upstream");
511
-
512
- // Headers arrived: the pre-stream deadline has served its purpose.
513
- // From here the gateway stall/first-chunk watchdog (propagated via
514
- // context.abort_signal) governs the body. Keeping this timer armed
515
- // would kill healthy long streams at the stale pre-stream deadline.
516
- lifecycle.release();
517
-
518
- // For both stream and non-stream, the upstream always streams NDJSON (beta behavior).
519
- // We yield NDJSON-decoded canonical events; the caller handles non-stream buffering via terminal.
520
- if (!request.stream) {
521
- for await (const ev of transformNdjson(response.body as ReadableStream<Uint8Array>, lifecycle.signal)) {
522
- yield ev;
523
- }
524
- return;
525
- }
526
-
527
- yield* transformNdjson(response.body as ReadableStream<Uint8Array>, lifecycle.signal);
528
- } catch (err: unknown) {
529
- const abortError = abortGatewayError(lifecycle, err, context.abort_signal);
530
- if (abortError) throw abortError;
531
- throw err;
532
- } finally {
533
- lifecycle.release();
534
- }
535
- }
536
- }
537
-
538
- export function createCommandCodeAdapter(options: CommandCodeAdapterOptions = {}): ProviderAdapter {
539
- return new CommandCodeAdapter(options);
540
- }
541
-
1
+ // Factory-blocked (Phase C4): proprietary `{threadId, config, params}`
2
+ // envelope with NDJSON stream transform — non-OpenAI wire; stays bespoke.
3
+ import { GatewayError } from "../../../protocol/gateway-error";
4
+ import { canContainToolResult, joinTextParts, toolCallParts, toolResultParts } from "../../../protocol/canonical-model";
5
+ import {
6
+ classifyUpstreamError,
7
+ extractUpstreamMessage,
8
+ upstreamErrorIdentifier,
9
+ } from "../../../transport/failure-policy";
10
+ import { createApiKeyAdapter } from "../../configured-provider";
11
+ import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason, ContentPart } from "../../../protocol/canonical-model";
12
+ import { isRecord, readNumber, readString, resolveImageSource, splitDataUrl } from "../../../protocol/codecs";
13
+ import { usageFromProvider } from "../../provider-usage";
14
+ import { readCredentialSecret, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext } from "../../provider-contracts";
15
+ import { providerBaseUrl } from "../../provider-metadata";
16
+ import { defineModel } from "../../provider-model-definition";
17
+ import type { ModelDefinition } from "../../provider-model-definition";
18
+ import {
19
+ getCommandCodeVersion,
20
+ } from "../../operations/client-versions";
21
+ import { MimoThinkSplitter } from "../xiaomi-mimo/think-stream";
22
+ export const COMMANDCODE_PROVIDER_ID = "commandcode" as const;
23
+ export const COMMANDCODE_BASE_URL = providerBaseUrl("commandcode");
24
+ /** The three routes the Provider API serves, one per wire family. */
25
+ export const COMMANDCODE_CHAT_PATH = "/chat/completions" as const;
26
+ export const COMMANDCODE_RESPONSES_PATH = "/responses" as const;
27
+ export const COMMANDCODE_MESSAGES_PATH = "/messages" as const;
28
+
29
+ /**
30
+ * Command Code's roster, as the Provider API publishes it.
31
+ *
32
+ * Generated from the live `/provider/v1/models` listing (95 rows). Each row
33
+ * carries the wire that serves it and the context window the API states, which
34
+ * is the whole point: the previous list was 36 ids with no limits and one wire
35
+ * for everything, so every `claude-*` id — served on `/messages` only — was
36
+ * filed as chat and could never be dispatched.
37
+ *
38
+ * `vision: true` on every row is the catalog's own declaration for this
39
+ * provider; a model that rejects an image answers for itself.
40
+ *
41
+ * This is the offline seed. It is superseded by `discoverCommandCodeModels`
42
+ * whenever a fetch succeeds, and `seedBundledModels` prunes any row this list
43
+ * stops declaring — so an id Command Code retires disappears on the next boot
44
+ * rather than lingering as a card that can only ever fail.
45
+ */
46
+ export const COMMANDCODE_MODELS: readonly ModelDefinition[] = [
47
+ defineModel({ id: "MiniMaxAI/MiniMax-M2.5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
48
+ defineModel({ id: "MiniMaxAI/MiniMax-M2.7", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
49
+ defineModel({ id: "MiniMaxAI/MiniMax-M3", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
50
+ defineModel({ id: "Qwen/Qwen3.6-Max-Preview", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
51
+ defineModel({ id: "Qwen/Qwen3.6-Plus", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
52
+ defineModel({ id: "Qwen/Qwen3.7-Flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
53
+ defineModel({ id: "Qwen/Qwen3.7-Max", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
54
+ defineModel({ id: "Qwen/Qwen3.7-Plus", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
55
+ defineModel({ id: "Qwen/Qwen3.8-27B", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 262144, reasoning: true, toolCall: true, vision: true }),
56
+ defineModel({ id: "Qwen/Qwen3.8-Flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
57
+ defineModel({ id: "Qwen/Qwen3.8-Max", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
58
+ defineModel({ id: "Qwen/Qwen3.8-Max-0902", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
59
+ defineModel({ id: "Qwen/Qwen3.8-Max-Prime", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
60
+ defineModel({ id: "Qwen/Qwen3.8-Omni-Flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
61
+ defineModel({ id: "claude-fable-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
62
+ defineModel({ id: "claude-fable-5-1", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
63
+ defineModel({ id: "claude-haiku-4-5-20251001", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
64
+ defineModel({ id: "claude-haiku-5-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
65
+ defineModel({ id: "claude-opus-4-7", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
66
+ defineModel({ id: "claude-opus-4-8", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
67
+ defineModel({ id: "claude-opus-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
68
+ defineModel({ id: "claude-opus-5-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
69
+ defineModel({ id: "claude-sonnet-4-6", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
70
+ defineModel({ id: "claude-sonnet-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
71
+ defineModel({ id: "claude-sonnet-5-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "messages", endpoint: "/messages", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
72
+ defineModel({ id: "deepseek/deepseek-v4-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
73
+ defineModel({ id: "deepseek/deepseek-v4-flash-fast", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
74
+ defineModel({ id: "deepseek/deepseek-v4-flash-vision-exp", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
75
+ defineModel({ id: "deepseek/deepseek-v4-pro", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
76
+ defineModel({ id: "deepseek/deepseek-v4.1-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
77
+ defineModel({ id: "deepseek/deepseek-v4.1-flash-fast", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
78
+ defineModel({ id: "google/gemini-3.1-flash-lite", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
79
+ defineModel({ id: "google/gemini-3.5-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
80
+ defineModel({ id: "google/gemini-3.5-flash-lite", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
81
+ defineModel({ id: "google/gemini-3.6-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
82
+ defineModel({ id: "google/gemini-3.7-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
83
+ defineModel({ id: "google/gemini-3.8-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
84
+ defineModel({ id: "google/gemini-4-argon", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
85
+ defineModel({ id: "gpt-5.3-codex", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 400000, reasoning: true, toolCall: true, vision: true }),
86
+ defineModel({ id: "gpt-5.4", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 400000, reasoning: true, toolCall: true, vision: true }),
87
+ defineModel({ id: "gpt-5.4-mini", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 400000, reasoning: true, toolCall: true, vision: true }),
88
+ defineModel({ id: "gpt-5.5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 400000, reasoning: true, toolCall: true, vision: true }),
89
+ defineModel({ id: "gpt-5.6-luna", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
90
+ defineModel({ id: "gpt-5.6-sol", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
91
+ defineModel({ id: "gpt-5.6-terra", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
92
+ defineModel({ id: "gpt-6-astra", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
93
+ defineModel({ id: "gpt-6-luna", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
94
+ defineModel({ id: "gpt-6-sol", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
95
+ defineModel({ id: "gpt-6.1-sol", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1050000, reasoning: true, toolCall: true, vision: true }),
96
+ defineModel({ id: "inclusionai/ling-3.1-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 262144, reasoning: true, toolCall: true, vision: true }),
97
+ defineModel({ id: "meituan/LongCat-2.0", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
98
+ defineModel({ id: "meta/muse-spark-1.1", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
99
+ defineModel({ id: "meta/muse-spark-1.2", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
100
+ defineModel({ id: "meta/muse-spark-1.2-contributor", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
101
+ defineModel({ id: "meta/muse-spark-1.3", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
102
+ defineModel({ id: "meta/muse-spark-1.3-contributor", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
103
+ defineModel({ id: "meta/muse-spark-open", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
104
+ defineModel({ id: "meta/muse-wonder-1.0", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
105
+ defineModel({ id: "minimax/minimax-m3.1-flash-preview", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
106
+ defineModel({ id: "minimax/minimax-m3.1-flash-preview:free", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
107
+ defineModel({ id: "mistral/mistral-large-4", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 524288, reasoning: true, toolCall: true, vision: true }),
108
+ defineModel({ id: "moonshotai/Kimi-K2.5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
109
+ defineModel({ id: "moonshotai/Kimi-K2.6", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
110
+ defineModel({ id: "moonshotai/Kimi-K2.7-Code", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
111
+ defineModel({ id: "moonshotai/Kimi-K2.7-Code-Highspeed", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 262000, reasoning: true, toolCall: true, vision: true }),
112
+ defineModel({ id: "moonshotai/Kimi-K3", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
113
+ defineModel({ id: "nvidia/nemotron-3-ultra-550b-a55b", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
114
+ defineModel({ id: "nvidia/nemotron-3.5-super-vl", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 262144, reasoning: true, toolCall: true, vision: true }),
115
+ defineModel({ id: "poolside/laguna-s-2.1-free", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
116
+ defineModel({ id: "reflection/beam:free", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
117
+ defineModel({ id: "sakana/fugu-ultra", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
118
+ defineModel({ id: "stealth/cat-jam:free", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
119
+ defineModel({ id: "stealth/glyph-cluster:free", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
120
+ defineModel({ id: "stepfun/Step-3.5-Flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 262144, reasoning: true, toolCall: true, vision: true }),
121
+ defineModel({ id: "stepfun/Step-3.7-Flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
122
+ defineModel({ id: "stepfun/Step-5-Preview", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
123
+ defineModel({ id: "tencent/hy3-paid", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 262144, reasoning: true, toolCall: true, vision: true }),
124
+ defineModel({ id: "tencent/hy4-preview", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
125
+ defineModel({ id: "thinkingmachines/inkling", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 256000, reasoning: true, toolCall: true, vision: true }),
126
+ defineModel({ id: "thinkingmachines/inkling-small", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
127
+ defineModel({ id: "xai/grok-4.5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 500000, reasoning: true, toolCall: true, vision: true }),
128
+ defineModel({ id: "xai/grok-4.6", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 500000, reasoning: true, toolCall: true, vision: true }),
129
+ defineModel({ id: "xai/grok-4.7", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 500000, reasoning: true, toolCall: true, vision: true }),
130
+ defineModel({ id: "xiaomi/mimo-v2.5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
131
+ defineModel({ id: "xiaomi/mimo-v2.5-pro", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
132
+ defineModel({ id: "xiaomi/mimo-v2.6-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
133
+ defineModel({ id: "xiaomi/mimo-v2.6-pro", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
134
+ defineModel({ id: "xiaomi/mimo-v2.6-pro-ultraspeed", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
135
+ defineModel({ id: "z-ai/glm-5.3-flash", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "chat", endpoint: "/chat/completions", ctx: 1048576, reasoning: true, toolCall: true, vision: true }),
136
+ defineModel({ id: "z-ai/glm-5.3-flashx", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
137
+ defineModel({ id: "zai-org/GLM-5", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
138
+ defineModel({ id: "zai-org/GLM-5.1", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 200000, reasoning: true, toolCall: true, vision: true }),
139
+ defineModel({ id: "zai-org/GLM-5.2", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
140
+ defineModel({ id: "zai-org/GLM-5.2-Fast", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
141
+ defineModel({ id: "zai-org/GLM-5.3", providerId: COMMANDCODE_PROVIDER_ID, wireFamily: "responses", endpoint: "/responses", ctx: 1000000, reasoning: true, toolCall: true, vision: true }),
142
+ ];
143
+
144
+
145
+ // CommandCode provider constants.
146
+
147
+ const COMMANDCODE_DEFAULT_MAX_TOKENS = 4096;
148
+
149
+ /**
150
+ * The `/alpha/generate` wire, as the CLI client sends it:
151
+ * - `params.system` is a top-level string; system turns never enter `messages`
152
+ * - every `content` is an array of blocks, never a string
153
+ * - an assistant tool call is `{type:"tool-call", toolCallId, toolName, input}`
154
+ * and is preceded by a reasoning block, which the upstream requires
155
+ * - a tool result is its own `role:"tool"` turn, not text glued into the user turn
156
+ * - an image is `{type:"image", image, mimeType}`
157
+ */
158
+ function toolResultValue(content: readonly ContentPart[] | string): string {
159
+ if (typeof content === "string") return content;
160
+ return content
161
+ .filter((part) => part.kind === "text")
162
+ .map((part) => part.text)
163
+ .filter((text) => text.length > 0)
164
+ .join("\n");
165
+ }
166
+
167
+ function imageBlock(payload: unknown): Record<string, unknown> | undefined {
168
+ const source = resolveImageSource(payload);
169
+ if (source?.url === undefined) return undefined;
170
+ const inline = splitDataUrl(source.url);
171
+ return { type: "image", image: source.url, ...(inline ? { mimeType: inline.mediaType } : {}) };
172
+ }
173
+
174
+ function contentBlocks(parts: readonly ContentPart[]): Array<Record<string, unknown>> {
175
+ const blocks: Array<Record<string, unknown>> = [];
176
+ for (const part of parts) {
177
+ if (part.kind === "text") {
178
+ if (part.text.length > 0) blocks.push({ type: "text", text: part.text });
179
+ continue;
180
+ }
181
+ if (part.kind === "image") {
182
+ const block = imageBlock(part.payload);
183
+ if (block) blocks.push(block);
184
+ }
185
+ }
186
+ return blocks.length > 0 ? blocks : [{ type: "text", text: "" }];
187
+ }
188
+
189
+ function parsedInput(value: unknown): unknown {
190
+ if (typeof value !== "string") return value ?? {};
191
+ try {
192
+ return JSON.parse(value);
193
+ } catch {
194
+ return {};
195
+ }
196
+ }
197
+
198
+ export function convertMessages(messages: CanonicalRequest["messages"]): {
199
+ messages: Array<Record<string, unknown>>;
200
+ system?: string;
201
+ } {
202
+ const out: Array<Record<string, unknown>> = [];
203
+ const systemTexts: string[] = [];
204
+ for (const message of messages) {
205
+ if (message.role === "system" || message.role === "developer") {
206
+ const text = joinTextParts(message.content);
207
+ if (text.length > 0) systemTexts.push(text);
208
+ continue;
209
+ }
210
+ // Answers live in `tool` turns or in `user` turns (the Messages ledger
211
+ // re-homes them). Each result is its own `role:"tool"` turn: flattening
212
+ // them into the user turn as prose left the tool call unanswered.
213
+ if (canContainToolResult(message) && toolResultParts(message).length > 0) {
214
+ for (const result of toolResultParts(message)) {
215
+ out.push({
216
+ role: "tool",
217
+ content: [
218
+ {
219
+ type: "tool-result",
220
+ toolCallId: result.call_id,
221
+ toolName: "",
222
+ output: { type: "text", value: toolResultValue(result.content) },
223
+ },
224
+ ],
225
+ });
226
+ }
227
+ const remainder = contentBlocks(message.content.filter((part) => part.kind !== "toolResult"));
228
+ if (remainder.some((block) => block["text"] !== "")) out.push({ role: "user", content: remainder });
229
+ continue;
230
+ }
231
+ if (message.role === "assistant") {
232
+ const blocks: Array<Record<string, unknown>> = [];
233
+ const calls = toolCallParts(message);
234
+ const reasoning = message.content
235
+ .filter((part) => part.kind === "reasoning")
236
+ .map((part) => part.summary ?? "")
237
+ .filter((text) => text.length > 0)
238
+ .join("");
239
+ if (reasoning.length > 0 || calls.length > 0) {
240
+ blocks.push({ type: "reasoning", text: reasoning.length > 0 ? reasoning : " " });
241
+ }
242
+ const text = joinTextParts(message.content);
243
+ if (text.length > 0) blocks.push({ type: "text", text });
244
+ for (const call of calls) {
245
+ blocks.push({
246
+ type: "tool-call",
247
+ toolCallId: call.call_id,
248
+ toolName: call.name,
249
+ input: parsedInput(call.arguments),
250
+ });
251
+ }
252
+ out.push({ role: "assistant", content: blocks.length > 0 ? blocks : [{ type: "text", text: "" }] });
253
+ continue;
254
+ }
255
+ out.push({ role: "user", content: contentBlocks(message.content) });
256
+ }
257
+ const system = systemTexts.join("\n\n");
258
+ return system.length > 0 ? { messages: out, system } : { messages: out };
259
+ }
260
+
261
+ /**
262
+ * Whether a tool schema needs Gemini's normalized form.
263
+ *
264
+ * The client rewrites a schema for `google/gemini-*` because the gateway can
265
+ * turn nullable type arrays into `any_of` with sibling fields, which Vertex
266
+ * rejects. This provider routes Gemini over the same Provider API, so the same
267
+ * rewrite applies.
268
+ */
269
+ export function isGeminiModelId(modelId: string): boolean {
270
+ return modelId.startsWith("google/gemini-");
271
+ }
272
+
273
+ const SCHEMA_MAP_FIELDS = new Set([
274
+ "$defs",
275
+ "definitions",
276
+ "dependentSchemas",
277
+ "patternProperties",
278
+ "properties",
279
+ ]);
280
+ const SCHEMA_ARRAY_FIELDS = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
281
+ const SCHEMA_VALUE_FIELDS = new Set([
282
+ "additionalItems",
283
+ "additionalProperties",
284
+ "contains",
285
+ "contentSchema",
286
+ "else",
287
+ "if",
288
+ "items",
289
+ "not",
290
+ "propertyNames",
291
+ "then",
292
+ "unevaluatedItems",
293
+ "unevaluatedProperties",
294
+ ]);
295
+
296
+ /**
297
+ * A schema Gemini's transport accepts: a single type plus `nullable`, with null
298
+ * defaults dropped. Genuine multi-type unions and `required` are left alone —
299
+ * only the nullable-array spelling is what Vertex rejects.
300
+ */
301
+ export function geminiSafeSchema(value: unknown): unknown {
302
+ if (Array.isArray(value)) return value.map(geminiSafeSchema);
303
+ if (!isRecord(value)) return value;
304
+ const out: Record<string, unknown> = {};
305
+ for (const [key, raw] of Object.entries(value)) {
306
+ if (key === "default" && raw === null) continue;
307
+ if (SCHEMA_MAP_FIELDS.has(key) && isRecord(raw)) {
308
+ out[key] = Object.fromEntries(
309
+ Object.entries(raw).map(([name, child]) => [name, geminiSafeSchema(child)]),
310
+ );
311
+ continue;
312
+ }
313
+ out[key] = SCHEMA_ARRAY_FIELDS.has(key) || SCHEMA_VALUE_FIELDS.has(key)
314
+ ? geminiSafeSchema(raw)
315
+ : raw;
316
+ }
317
+ // A nullable type array becomes one type plus `nullable`.
318
+ if (Array.isArray(out["type"])) {
319
+ const types = out["type"].filter((entry): entry is string => typeof entry === "string");
320
+ const withoutNull = types.filter((entry) => entry !== "null");
321
+ if (types.length !== withoutNull.length && withoutNull.length > 0) {
322
+ out["type"] = withoutNull.length === 1 ? withoutNull[0] : withoutNull;
323
+ out["nullable"] = true;
324
+ }
325
+ }
326
+ return out;
327
+ }
328
+
329
+ export function buildRequest(
330
+ modelId: string,
331
+ request: { messages: CanonicalRequest["messages"]; tools: { readonly name: string; readonly description: string | undefined; readonly jsonSchema: unknown }[]; maxOutputTokens: number | null; temperature: number | null; topP: number | null; reasoningEffort: string | null },
332
+ threadId: string,
333
+ systemExtra?: string,
334
+ ): Record<string, unknown> {
335
+ const { messages, system: systemFromMessages } = convertMessages(request.messages);
336
+ const systemCombined = systemExtra ? (systemFromMessages ? `${systemExtra}\n${systemFromMessages}` : systemExtra) : systemFromMessages;
337
+ const params: Record<string, unknown> = {
338
+ model: modelId,
339
+ messages,
340
+ stream: true,
341
+ max_tokens: request.maxOutputTokens ?? COMMANDCODE_DEFAULT_MAX_TOKENS,
342
+ temperature: request.temperature ?? 0.3,
343
+ };
344
+ if (request.topP !== null) params["top_p"] = request.topP;
345
+ if (request.reasoningEffort !== null && request.reasoningEffort !== "none") {
346
+ params["reasoning_effort"] = request.reasoningEffort;
347
+ }
348
+ if (systemCombined) params["system"] = systemCombined;
349
+ if (request.tools.length > 0) {
350
+ params["tools"] = request.tools.map((tool) => ({
351
+ // `type: "function"` is part of the envelope. The reference client sends
352
+ // it on every tool, and a tool without it is not recognised as callable.
353
+ type: "function",
354
+ name: tool.name,
355
+ description: tool.description ?? undefined,
356
+ input_schema: isGeminiModelId(modelId) ? geminiSafeSchema(tool.jsonSchema) : tool.jsonSchema,
357
+ }));
358
+ }
359
+ return {
360
+ threadId,
361
+ // The client sends these three as `null`, not `""` / absent. `memory` was
362
+ // an empty string and `taste`/`skills` were missing entirely; the envelope
363
+ // is validated, so an absent field is not the same as a null one.
364
+ memory: null,
365
+ taste: null,
366
+ skills: null,
367
+ config: {
368
+ workingDir: "",
369
+ date: new Date().toISOString().slice(0, 10),
370
+ // The client reports the real runtime: `linux-x64, Node.js v24.0.0`.
371
+ // An empty string is a blank attribution field the upstream never sees
372
+ // filled, which is indistinguishable from a client that did not report.
373
+ environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,
374
+ structure: [],
375
+ isGitRepo: false,
376
+ currentBranch: "",
377
+ mainBranch: "",
378
+ gitStatus: "",
379
+ recentCommits: [],
380
+ },
381
+ params,
382
+ };
383
+ }
384
+
385
+ /**
386
+ * The identity headers Command Code's own client sends.
387
+ *
388
+ * `x-cli-environment` is `production`, not `cli` — that is the value the client
389
+ * sends, and the pair is what identifies the surface to the gateway.
390
+ *
391
+ * `x-project-slug` and `x-taste-learning` were simply absent. Both are
392
+ * attribution fields the client always sends; the slug is derived from the
393
+ * working directory the same way the client derives it.
394
+ *
395
+ * `x-session-id` is sent only when a session exists. The client omits it rather
396
+ * than inventing one, and a value minted per request would attribute every
397
+ * call to a different session.
398
+ */
399
+ export async function headers(sessionId: string, token: string): Promise<Record<string, string>> {
400
+ return {
401
+ "content-type": "application/json",
402
+ accept: "text/event-stream",
403
+ authorization: `Bearer ${token}`,
404
+ "x-command-code-version": getCommandCodeVersion(),
405
+ "x-cli-environment": "production",
406
+ "x-taste-learning": "true",
407
+ "x-project-slug": "cartethyia",
408
+ "user-agent": "cli",
409
+ ...(sessionId.length > 0 ? { "x-session-id": sessionId } : {}),
410
+ };
411
+ }
412
+
413
+
414
+ type StreamState = { toolIndexById: Map<string, number>; nextToolIndex: number; finishReason: string | undefined; usage: Record<string, unknown> | undefined; think: MimoThinkSplitter };
415
+
416
+ export function ccMapFinishReason(reason: string): CanonicalStopReason {
417
+ return reason === "length" ? "length" : reason === "tool_calls" ? "tool_use" : reason === "content_filter" ? "content_filter" : "stop";
418
+ }
419
+
420
+ export function transformLine(line: string, state: StreamState, seqBase: number): CanonicalEvent[] {
421
+ let event: Record<string, unknown>;
422
+ try {
423
+ event = JSON.parse(line) as Record<string, unknown>;
424
+ } catch {
425
+ return [];
426
+ }
427
+ const type = readString(event, "type");
428
+ if (!type) return [];
429
+ if (type === "reasoning-delta") {
430
+ const text = readString(event, "text") || readString(event, "delta");
431
+ return text ? [{ type: "content_delta", sequence_number: seqBase, content: { kind: "reasoning", payload: null, summary: text } } as CanonicalEvent] : [];
432
+ }
433
+ if (type === "text-delta") {
434
+ const text = readString(event, "text") || readString(event, "delta");
435
+ if (!text) return [];
436
+ const split = state.think.push(text);
437
+ const events: CanonicalEvent[] = [];
438
+ if (split.reasoning) events.push({ type: "content_delta", sequence_number: seqBase, content: { kind: "reasoning", payload: null, summary: split.reasoning } } as CanonicalEvent);
439
+ if (split.text) events.push({ type: "content_delta", sequence_number: seqBase, content: { kind: "text", text: split.text } } as CanonicalEvent);
440
+ return events;
441
+ }
442
+ if (type === "tool-input-start") {
443
+ const id = readString(event, "id") || readString(event, "toolCallId");
444
+ if (!id || state.toolIndexById.has(id)) return [];
445
+ state.toolIndexById.set(id, state.nextToolIndex++);
446
+ return [{ type: "tool_call_delta", sequence_number: seqBase, call_id: id, name: readString(event, "toolName") ?? "", arguments_delta: "" } as CanonicalEvent];
447
+ }
448
+ if (type === "tool-input-delta") {
449
+ const id = readString(event, "id") || readString(event, "toolCallId");
450
+ const delta = readString(event, "delta") || readString(event, "inputTextDelta");
451
+ if (!id || !delta || !state.toolIndexById.has(id)) return [];
452
+ return [{ type: "tool_call_delta", sequence_number: seqBase, call_id: id, arguments_delta: delta } as CanonicalEvent];
453
+ }
454
+ if (type === "tool-call") {
455
+ const id = readString(event, "toolCallId");
456
+ if (!id || state.toolIndexById.has(id)) return [];
457
+ state.toolIndexById.set(id, state.nextToolIndex++);
458
+ const input = typeof event["input"] === "string" ? (event["input"] as string) : JSON.stringify(event["input"] ?? {});
459
+ return [
460
+ { type: "tool_call_delta", sequence_number: seqBase, call_id: id, name: readString(event, "toolName") ?? "", arguments_delta: "" } as CanonicalEvent,
461
+ { type: "tool_call_delta", sequence_number: seqBase + 1, call_id: id, arguments_delta: input } as CanonicalEvent,
462
+ ];
463
+ }
464
+ if (type === "finish-step") {
465
+ const reason = readString(event, "finishReason");
466
+ if (reason) state.finishReason = reason;
467
+ if (typeof event["usage"] === "object" && event["usage"] !== null && !Array.isArray(event["usage"])) state.usage = event["usage"] as Record<string, unknown>;
468
+ return [];
469
+ }
470
+ if (type === "finish") {
471
+ const reason = state.finishReason ?? readString(event, "finishReason") ?? "stop";
472
+ const canonicalReason = ccMapFinishReason(reason);
473
+ const events: CanonicalEvent[] = [
474
+ { type: "terminal", sequence_number: seqBase, state: "complete", stop_reason: canonicalReason, provider_stop_reason: reason } as CanonicalEvent,
475
+ ];
476
+ if (state.usage) {
477
+ const prompt = readNumber(state.usage, "promptTokens") ?? readNumber(state.usage, "inputTokens") ?? 0;
478
+ const completion = readNumber(state.usage, "completionTokens") ?? readNumber(state.usage, "outputTokens") ?? 0;
479
+ const total = readNumber(state.usage, "totalTokens") ?? prompt + completion;
480
+ const cached = readNumber(state.usage, "cachedTokens") ?? readNumber(state.usage, "cacheReadTokens") ?? 0;
481
+ const usageRecord = usageFromProvider({
482
+ input_tokens: prompt,
483
+ output_tokens: completion,
484
+ total_tokens: total,
485
+ cached_tokens: cached,
486
+ cache_creation_input_tokens: readNumber(state.usage, "cacheWriteTokens") ?? undefined,
487
+ } as unknown as Record<string, unknown>);
488
+ // Append usage to terminal
489
+ (events[0] as unknown as Record<string, unknown>)["usage"] = usageRecord;
490
+ }
491
+ return events;
492
+ }
493
+ if (type === "error") {
494
+ const raw = event["error"] ?? event["message"] ?? event;
495
+ const errorObject = isRecord(raw) ? raw : event;
496
+ const identifier = upstreamErrorIdentifier(raw) ?? upstreamErrorIdentifier(event);
497
+ const providerStatus = [
498
+ errorObject["status"],
499
+ errorObject["statusCode"],
500
+ errorObject["code"],
501
+ event["status"],
502
+ event["statusCode"],
503
+ event["code"],
504
+ ].find(
505
+ (value): value is number =>
506
+ typeof value === "number" &&
507
+ Number.isFinite(value) &&
508
+ value >= 400 &&
509
+ value < 600,
510
+ );
511
+ const classification = classifyUpstreamError(providerStatus, identifier);
512
+ const message =
513
+ extractUpstreamMessage(raw) ||
514
+ extractUpstreamMessage(event) ||
515
+ "Command Code stream error";
516
+ throw new GatewayError(
517
+ classification.code,
518
+ classification.status,
519
+ message.slice(0, 500),
520
+ {
521
+ ...(providerStatus === undefined ? {} : { providerStatus }),
522
+ ...(identifier ? { providerCode: identifier } : {}),
523
+ ...(classification.credentialEvidence ? { credentialEvidence: true } : {}),
524
+ ...(classification.rateLimitScope
525
+ ? { rateLimitScope: classification.rateLimitScope }
526
+ : {}),
527
+ },
528
+ classification.origin,
529
+ );
530
+ }
531
+ return [];
532
+ }
533
+
534
+ /** NDJSON transform — beta-style simple loop + signal-aware read for abort/timeout propagation. */
535
+ export async function* transformNdjson(body: ReadableStream<Uint8Array>, signal: AbortSignal): AsyncIterable<CanonicalEvent> {
536
+ const reader = body.getReader();
537
+ const decoder = new TextDecoder();
538
+ const state: StreamState = { toolIndexById: new Map(), nextToolIndex: 0, finishReason: undefined, usage: undefined, think: new MimoThinkSplitter() };
539
+ let buffer = "";
540
+ let hasTerminal = false;
541
+ let seq = 1;
542
+ // initial response_start
543
+ yield { type: "response_start", sequence_number: seq++, model: "commandcode" } as CanonicalEvent;
544
+
545
+ const readWithAbort = (): Promise<{ done: boolean; value?: Uint8Array }> => {
546
+ if (signal.aborted) throw new GatewayError("transport_closed", 499, "Command Code stream aborted");
547
+ const { promise, resolve, reject } = Promise.withResolvers<{ done: boolean; value?: Uint8Array }>() as unknown as { promise: Promise<{ done: boolean; value?: Uint8Array }>; resolve: (v: unknown) => void; reject: (e: unknown) => void };
548
+ const onAbort = (): void => {
549
+ if (signal.aborted) reject(new GatewayError("transport_closed", 499, "Command Code stream aborted"));
550
+ else reject(new GatewayError("platform_unavailable", 502, "Command Code stream error", {}, "upstream"));
551
+ };
552
+ signal.addEventListener("abort", onAbort, { once: true });
553
+ void reader.read().then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
554
+ return promise;
555
+ };
556
+
557
+ try {
558
+ while (true) {
559
+ const { done, value } = await readWithAbort();
560
+ if (done) break;
561
+ buffer += decoder.decode(value, { stream: true });
562
+ let nl = buffer.indexOf("\n");
563
+ while (nl !== -1) {
564
+ const line = buffer.slice(0, nl).trim();
565
+ buffer = buffer.slice(nl + 1);
566
+ if (line) {
567
+ let evs: CanonicalEvent[] = [];
568
+ try {
569
+ evs = transformLine(line, state, seq);
570
+ } catch (e) {
571
+ throw e;
572
+ }
573
+ for (const ev of evs) {
574
+ // Strictly increasing per line, in emission order: the Responses
575
+ // surface throws on any non-monotonic sequence mid-stream, after
576
+ // partial bytes are already flushed. No special-casing for
577
+ // dual-event lines — order is order.
578
+ (ev as unknown as Record<string, unknown>)["sequence_number"] = seq++;
579
+ if (ev.type === "terminal") hasTerminal = true;
580
+ yield ev;
581
+ }
582
+ }
583
+ nl = buffer.indexOf("\n");
584
+ }
585
+ }
586
+ buffer += decoder.decode();
587
+ const tail = buffer.trim();
588
+ if (tail) {
589
+ for (const ev of transformLine(tail, state, seq)) {
590
+ if (ev.type === "terminal") hasTerminal = true;
591
+ (ev as unknown as Record<string, unknown>)["sequence_number"] = seq++;
592
+ yield ev;
593
+ }
594
+ }
595
+ const rest = state.think.flush();
596
+ if (rest.reasoning) yield { type: "content_delta", sequence_number: seq++, content: { kind: "reasoning", payload: null, summary: rest.reasoning } } as CanonicalEvent;
597
+ if (rest.text) yield { type: "content_delta", sequence_number: seq++, content: { kind: "text", text: rest.text } } as CanonicalEvent;
598
+ if (!hasTerminal) {
599
+ // NDJSON ended with no terminal envelope: truncated, never complete.
600
+ yield { type: "terminal", sequence_number: seq++, state: "failed", stop_reason: ccMapFinishReason(state.finishReason ?? "stop"), provider_stop_reason: state.finishReason ?? "stop" } as CanonicalEvent;
601
+ }
602
+ } finally {
603
+ try {
604
+ reader.releaseLock?.();
605
+ } catch {
606
+ // Best-effort reader unlock during stream cleanup.
607
+ }
608
+ void reader.cancel().catch(() => {});
609
+ }
610
+ }
611
+
612
+ // CommandCode model catalog.
613
+
614
+
615
+
616
+ // ProviderAdapter — the Provider API serves three wires, so dispatch is the
617
+ // shared OpenAI-compatible adapter rather than one bespoke NDJSON reader.
618
+ //
619
+ // `/provider/v1` answers `/chat/completions`, `/responses` and `/messages`, and
620
+ // the model row states which one serves it. The bespoke `/alpha/generate`
621
+ // envelope is the CLI's own surface and stays reachable through
622
+ // `buildRequest` for callers that still speak it, but routing no longer forces
623
+ // every model onto it — which is what left all nine `claude-*` ids unroutable.
624
+
625
+ interface CommandCodeAdapterOptions {
626
+ readonly fetch?: typeof fetch;
627
+ }
628
+
629
+ function commandCodeIdentityHeaders(_token: string): Record<string, string> {
630
+ return {
631
+ "x-command-code-version": getCommandCodeVersion(),
632
+ "x-cli-environment": "production",
633
+ "x-taste-learning": "true",
634
+ "x-project-slug": "cartethyia",
635
+ "user-agent": "cli",
636
+ };
637
+ }
638
+
639
+ function providerApiAdapter(options: CommandCodeAdapterOptions): ProviderAdapter {
640
+ return createApiKeyAdapter(
641
+ {
642
+ provider_id: COMMANDCODE_PROVIDER_ID,
643
+ base_url: COMMANDCODE_BASE_URL,
644
+ endpoint_paths_by_wire_family: {
645
+ chat: COMMANDCODE_CHAT_PATH,
646
+ responses: COMMANDCODE_RESPONSES_PATH,
647
+ messages: COMMANDCODE_MESSAGES_PATH,
648
+ },
649
+ buildExtraHeaders: async (context: ProviderDispatchContext) =>
650
+ commandCodeIdentityHeaders(await readCredentialSecret(context.credential, "Command Code bearer token is required")),
651
+ },
652
+ options.fetch,
653
+ );
654
+ }
655
+
656
+ class CommandCodeAdapter implements ProviderAdapter {
657
+ readonly provider_id = COMMANDCODE_PROVIDER_ID;
658
+ private readonly inner: ProviderAdapter;
659
+
660
+ constructor(options: CommandCodeAdapterOptions = {}) {
661
+ this.inner = providerApiAdapter(options);
662
+ }
663
+
664
+ dispatch(
665
+ request: CanonicalRequest,
666
+ candidate: ProviderDispatchTarget,
667
+ context: ProviderDispatchContext,
668
+ ): AsyncIterable<CanonicalEvent> {
669
+ return this.inner.dispatch(request, candidate, context);
670
+ }
671
+ }
672
+
673
+ export function createCommandCodeAdapter(options: CommandCodeAdapterOptions = {}): ProviderAdapter {
674
+ return new CommandCodeAdapter(options);
675
+ }