@fudrouter/fsrouter 0.6.72 → 0.6.74

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 (314) hide show
  1. package/dist/lib/headroom/detect.js +97 -7
  2. package/dist/lib/headroom/process.js +136 -4
  3. package/dist/open-sse/config/appConstants.js +23 -79
  4. package/dist/open-sse/config/cliFingerprints.js +1 -1
  5. package/dist/open-sse/config/errorConfig.js +0 -11
  6. package/dist/open-sse/config/grokCli.js +10 -0
  7. package/dist/open-sse/config/kiroConstants.js +99 -46
  8. package/dist/open-sse/config/runtimeConfig.js +2 -0
  9. package/dist/open-sse/config/ttsModels.js +2 -0
  10. package/dist/open-sse/executors/antigravity.js +250 -164
  11. package/dist/open-sse/executors/base.js +16 -11
  12. package/dist/open-sse/executors/codex.js +269 -1108
  13. package/dist/open-sse/executors/commandCode.js +1 -1
  14. package/dist/open-sse/executors/commandcode.js +10 -4
  15. package/dist/open-sse/executors/cursor.js +96 -203
  16. package/dist/open-sse/executors/default.js +146 -244
  17. package/dist/open-sse/executors/github.js +128 -73
  18. package/dist/open-sse/executors/grok-cli.js +527 -94
  19. package/dist/open-sse/executors/grok-web.js +5 -7
  20. package/dist/open-sse/executors/index.js +12 -0
  21. package/dist/open-sse/executors/kiro.js +150 -61
  22. package/dist/open-sse/executors/opencode-go.js +12 -4
  23. package/dist/open-sse/executors/opencode.js +1 -1
  24. package/dist/open-sse/executors/perplexity-web.js +20 -96
  25. package/dist/open-sse/executors/qoder.js +28 -18
  26. package/dist/open-sse/executors/vertex.js +53 -7
  27. package/dist/open-sse/executors/xiaomi-tokenplan.js +6 -5
  28. package/dist/open-sse/handlers/chatCore/nonStreamingHandler.js +97 -85
  29. package/dist/open-sse/handlers/chatCore/requestDetail.js +32 -8
  30. package/dist/open-sse/handlers/chatCore/sseToJsonHandler.js +13 -7
  31. package/dist/open-sse/handlers/chatCore/streamingHandler.js +74 -25
  32. package/dist/open-sse/handlers/chatCore.js +35 -41
  33. package/dist/open-sse/handlers/embeddingProviders/index.js +1 -0
  34. package/dist/open-sse/handlers/embeddingProviders/openai.js +8 -16
  35. package/dist/open-sse/handlers/imageProviders/blackForestLabs.js +2 -21
  36. package/dist/open-sse/handlers/imageProviders/cloudflareAi.js +2 -30
  37. package/dist/open-sse/handlers/imageProviders/codex.js +2 -20
  38. package/dist/open-sse/handlers/imageProviders/comfyui.js +5 -18
  39. package/dist/open-sse/handlers/imageProviders/falAi.js +2 -22
  40. package/dist/open-sse/handlers/imageProviders/gemini.js +2 -16
  41. package/dist/open-sse/handlers/imageProviders/huggingface.js +2 -17
  42. package/dist/open-sse/handlers/imageProviders/index.js +3 -4
  43. package/dist/open-sse/handlers/imageProviders/leonardo.js +1 -1
  44. package/dist/open-sse/handlers/imageProviders/nanobanana.js +4 -23
  45. package/dist/open-sse/handlers/imageProviders/openai.js +15 -43
  46. package/dist/open-sse/handlers/imageProviders/runwayml.js +2 -26
  47. package/dist/open-sse/handlers/imageProviders/sdwebui.js +4 -20
  48. package/dist/open-sse/handlers/imageProviders/stabilityAi.js +2 -26
  49. package/dist/open-sse/handlers/imageProviders/weavy.js +1 -1
  50. package/dist/open-sse/handlers/search/chatSearch.js +60 -14
  51. package/dist/open-sse/handlers/ttsProviders/gemini.js +14 -3
  52. package/dist/open-sse/handlers/ttsProviders/index.js +4 -2
  53. package/dist/open-sse/handlers/ttsProviders/openai.js +4 -1
  54. package/dist/open-sse/handlers/ttsProviders/openrouter.js +7 -4
  55. package/dist/open-sse/handlers/videoCore.js +166 -0
  56. package/dist/open-sse/handlers/videoProviders/weavy.js +1 -1
  57. package/dist/open-sse/mcp-server/catalog.js +1 -1
  58. package/dist/open-sse/mcp-server/server.js +2 -2
  59. package/dist/open-sse/mcp-server/tools/advancedTools.js +2 -2
  60. package/dist/open-sse/mcp-server/tools/agentSkillTools.js +1 -1
  61. package/dist/open-sse/mcp-server/tools/compressionTools.js +4 -4
  62. package/dist/open-sse/mcp-server/tools/gamificationTools.js +10 -10
  63. package/dist/open-sse/mcp-server/tools/githubSkillTools.js +1 -1
  64. package/dist/open-sse/mcp-server/tools/memoryTools.js +3 -3
  65. package/dist/open-sse/mcp-server/tools/notionTools.js +2 -2
  66. package/dist/open-sse/mcp-server/tools/obsidianTools.js +2 -2
  67. package/dist/open-sse/mcp-server/tools/pickFastestModel.js +1 -1
  68. package/dist/open-sse/mcp-server/tools/pluginTools.js +4 -4
  69. package/dist/open-sse/mcp-server/tools/skillTools.js +2 -2
  70. package/dist/open-sse/providers/capabilities.js +24 -4
  71. package/dist/open-sse/providers/registry/alicode-intl.js +1 -1
  72. package/dist/open-sse/providers/registry/anthropic.js +1 -8
  73. package/dist/open-sse/providers/registry/codex.js +0 -9
  74. package/dist/open-sse/providers/registry/cohere.js +0 -2
  75. package/dist/open-sse/providers/registry/gemini.js +0 -2
  76. package/dist/open-sse/providers/registry/github.js +9 -6
  77. package/dist/open-sse/providers/registry/grok-cli.js +28 -18
  78. package/dist/open-sse/providers/registry/index.js +74 -80
  79. package/dist/open-sse/providers/registry/kiro.js +12 -0
  80. package/dist/open-sse/providers/registry/mistral.js +0 -3
  81. package/dist/open-sse/providers/registry/openai.js +0 -3
  82. package/dist/open-sse/providers/registry/perplexity.js +0 -2
  83. package/dist/open-sse/providers/registry/xai.js +5 -3
  84. package/dist/open-sse/rtk/autodetect.js +10 -2
  85. package/dist/open-sse/rtk/caveman.js +2 -93
  86. package/dist/open-sse/rtk/cavemanPrompts.js +34 -2
  87. package/dist/open-sse/rtk/constants.js +7 -2
  88. package/dist/open-sse/rtk/headroom.js +133 -0
  89. package/dist/open-sse/rtk/ponytail.js +1 -1
  90. package/dist/open-sse/rtk/registry.js +2 -0
  91. package/dist/open-sse/services/accountFallback.js +0 -108
  92. package/dist/open-sse/services/combo/quotaExhaustionCutoff.js +2 -2
  93. package/dist/open-sse/services/combo/quotaShareStrategy.js +1 -1
  94. package/dist/open-sse/services/combo/resolveAutoStrategy.js +1 -1
  95. package/dist/open-sse/services/combo/sessionStickiness.js +1 -1
  96. package/dist/open-sse/services/combo/targetSorters.js +2 -2
  97. package/dist/open-sse/services/combo/validateQuality.js +1 -1
  98. package/dist/open-sse/services/comboConfig.js +1 -1
  99. package/dist/open-sse/services/emergencyFallback.js +1 -1
  100. package/dist/open-sse/services/grokCliModels.js +127 -0
  101. package/dist/open-sse/services/model.js +7 -1
  102. package/dist/open-sse/services/provider.js +1 -1
  103. package/dist/open-sse/services/proxyAutoSelector.js +1 -1
  104. package/dist/open-sse/services/tokenRefresh/providers.js +3 -3
  105. package/dist/open-sse/services/usage/codex.js +148 -214
  106. package/dist/open-sse/services/usage/grok-cli.js +61 -7
  107. package/dist/open-sse/services/usage/misc.js +0 -19
  108. package/dist/open-sse/services/usage.js +0 -2
  109. package/dist/open-sse/translator/concerns/paramSupport.js +2 -2
  110. package/dist/open-sse/translator/concerns/thinkingUnified.js +6 -0
  111. package/dist/open-sse/translator/index.js +97 -59
  112. package/dist/open-sse/translator/request/claude-to-kiro.js +67 -40
  113. package/dist/open-sse/translator/request/openai-responses.js +1 -0
  114. package/dist/open-sse/translator/request/openai-to-commandcode.js +0 -4
  115. package/dist/open-sse/translator/request/openai-to-kiro.js +49 -20
  116. package/dist/open-sse/translator/response/commandcode-to-openai.js +0 -4
  117. package/dist/open-sse/utils/bypassHandler.js +18 -3
  118. package/dist/open-sse/utils/claudeCloaking.js +15 -5
  119. package/dist/open-sse/utils/cursorImages.js +1 -1
  120. package/dist/open-sse/utils/error.js +0 -17
  121. package/dist/open-sse/utils/keepaliveThreshold.js +4 -4
  122. package/dist/open-sse/utils/kiroSessionReplay.js +125 -0
  123. package/dist/open-sse/utils/noThinkingAlias.js +1 -1
  124. package/dist/open-sse/utils/proxyFetch.js +0 -1
  125. package/dist/open-sse/utils/reasoningContentInjector.js +4 -7
  126. package/dist/open-sse/utils/responsesStatePolicy.js +1 -1
  127. package/dist/open-sse/utils/responsesStreamHelpers.js +1 -0
  128. package/dist/open-sse/utils/sessionManager.js +49 -22
  129. package/dist/open-sse/utils/stream.js +54 -77
  130. package/dist/open-sse/utils/streamHandler.js +19 -15
  131. package/dist/open-sse/utils/streamHelpers.js +2 -17
  132. package/dist/open-sse/utils/usageTracking.js +103 -12
  133. package/dist/routes/headroom/extras/route.js +58 -0
  134. package/dist/routes/headroom/restart/route.js +33 -0
  135. package/open-sse/config/appConstants.js +23 -79
  136. package/open-sse/config/errorConfig.js +0 -11
  137. package/open-sse/config/grokCli.js +10 -0
  138. package/open-sse/config/kiroConstants.js +99 -46
  139. package/open-sse/config/runtimeConfig.js +2 -0
  140. package/open-sse/config/ttsModels.js +2 -0
  141. package/open-sse/executors/antigravity.js +250 -164
  142. package/open-sse/executors/base.js +16 -11
  143. package/open-sse/executors/codex.js +269 -1108
  144. package/open-sse/executors/commandcode.js +10 -4
  145. package/open-sse/executors/cursor.js +96 -203
  146. package/open-sse/executors/default.js +146 -244
  147. package/open-sse/executors/github.js +128 -73
  148. package/open-sse/executors/grok-cli.js +527 -94
  149. package/open-sse/executors/grok-web.js +5 -7
  150. package/open-sse/executors/index.js +12 -0
  151. package/open-sse/executors/kiro.js +150 -61
  152. package/open-sse/executors/opencode-go.js +12 -4
  153. package/open-sse/executors/opencode.js +1 -1
  154. package/open-sse/executors/perplexity-web.js +20 -96
  155. package/open-sse/executors/qoder.js +28 -18
  156. package/open-sse/executors/vertex.js +53 -7
  157. package/open-sse/executors/xiaomi-tokenplan.js +6 -5
  158. package/open-sse/handlers/chatCore/nonStreamingHandler.js +97 -85
  159. package/open-sse/handlers/chatCore/requestDetail.js +32 -8
  160. package/open-sse/handlers/chatCore/sseToJsonHandler.js +13 -7
  161. package/open-sse/handlers/chatCore/streamingHandler.js +74 -25
  162. package/open-sse/handlers/chatCore.js +35 -41
  163. package/open-sse/handlers/embeddingProviders/index.js +1 -0
  164. package/open-sse/handlers/embeddingProviders/openai.js +8 -16
  165. package/open-sse/handlers/imageProviders/blackForestLabs.js +2 -21
  166. package/open-sse/handlers/imageProviders/cloudflareAi.js +2 -30
  167. package/open-sse/handlers/imageProviders/codex.js +2 -20
  168. package/open-sse/handlers/imageProviders/comfyui.js +5 -18
  169. package/open-sse/handlers/imageProviders/falAi.js +2 -22
  170. package/open-sse/handlers/imageProviders/gemini.js +2 -16
  171. package/open-sse/handlers/imageProviders/huggingface.js +2 -17
  172. package/open-sse/handlers/imageProviders/index.js +3 -4
  173. package/open-sse/handlers/imageProviders/leonardo.js +1 -1
  174. package/open-sse/handlers/imageProviders/nanobanana.js +4 -23
  175. package/open-sse/handlers/imageProviders/openai.js +15 -43
  176. package/open-sse/handlers/imageProviders/runwayml.js +2 -26
  177. package/open-sse/handlers/imageProviders/sdwebui.js +4 -20
  178. package/open-sse/handlers/imageProviders/stabilityAi.js +2 -26
  179. package/open-sse/handlers/imageProviders/weavy.js +1 -1
  180. package/open-sse/handlers/search/chatSearch.js +60 -14
  181. package/open-sse/handlers/ttsProviders/gemini.js +14 -3
  182. package/open-sse/handlers/ttsProviders/index.js +4 -2
  183. package/open-sse/handlers/ttsProviders/openai.js +4 -1
  184. package/open-sse/handlers/ttsProviders/openrouter.js +7 -4
  185. package/open-sse/handlers/videoCore.js +166 -0
  186. package/open-sse/handlers/videoProviders/weavy.js +1 -1
  187. package/open-sse/mcp-server/audit.js +1 -1
  188. package/open-sse/mcp-server/catalog.js +2 -2
  189. package/open-sse/mcp-server/descriptionCompressor.js +1 -1
  190. package/open-sse/mcp-server/mcpCallerIdentity.js +1 -1
  191. package/open-sse/mcp-server/schemas/tools.js +1 -1
  192. package/open-sse/mcp-server/server.js +4 -4
  193. package/open-sse/mcp-server/tools/advancedTools.js +5 -5
  194. package/open-sse/mcp-server/tools/agentSkillTools.js +1 -1
  195. package/open-sse/mcp-server/tools/compressionTools.js +4 -4
  196. package/open-sse/mcp-server/tools/gamificationTools.js +10 -10
  197. package/open-sse/mcp-server/tools/githubSkillTools.js +1 -1
  198. package/open-sse/mcp-server/tools/memoryTools.js +3 -3
  199. package/open-sse/mcp-server/tools/notionTools.js +2 -2
  200. package/open-sse/mcp-server/tools/obsidianTools.js +2 -2
  201. package/open-sse/mcp-server/tools/pickFastestModel.js +3 -3
  202. package/open-sse/mcp-server/tools/pluginTools.js +4 -4
  203. package/open-sse/mcp-server/tools/skillTools.js +2 -2
  204. package/open-sse/providers/capabilities.js +24 -4
  205. package/open-sse/providers/registry/alicode-intl.js +1 -1
  206. package/open-sse/providers/registry/anthropic.js +1 -8
  207. package/open-sse/providers/registry/codex.js +0 -9
  208. package/open-sse/providers/registry/cohere.js +0 -2
  209. package/open-sse/providers/registry/gemini.js +0 -2
  210. package/open-sse/providers/registry/github.js +9 -6
  211. package/open-sse/providers/registry/grok-cli.js +28 -18
  212. package/open-sse/providers/registry/index.js +74 -80
  213. package/open-sse/providers/registry/kiro.js +12 -0
  214. package/open-sse/providers/registry/mistral.js +0 -3
  215. package/open-sse/providers/registry/openai.js +0 -3
  216. package/open-sse/providers/registry/perplexity.js +0 -2
  217. package/open-sse/providers/registry/xai.js +5 -3
  218. package/open-sse/rtk/autodetect.js +10 -2
  219. package/open-sse/rtk/caveman.js +2 -93
  220. package/open-sse/rtk/cavemanPrompts.js +34 -2
  221. package/open-sse/rtk/constants.js +7 -2
  222. package/open-sse/rtk/headroom.js +133 -0
  223. package/open-sse/rtk/ponytail.js +1 -1
  224. package/open-sse/rtk/registry.js +2 -0
  225. package/open-sse/services/accountFallback.js +0 -108
  226. package/open-sse/services/combo/quotaExhaustionCutoff.js +2 -2
  227. package/open-sse/services/combo/quotaShareStrategy.js +1 -1
  228. package/open-sse/services/combo/resolveAutoStrategy.js +1 -1
  229. package/open-sse/services/combo/sessionStickiness.js +1 -1
  230. package/open-sse/services/combo/shadowRouting.js +1 -1
  231. package/open-sse/services/combo/targetSorters.js +4 -4
  232. package/open-sse/services/combo/validateQuality.js +1 -1
  233. package/open-sse/services/evalRouting.js +1 -1
  234. package/open-sse/services/grokCliModels.js +127 -0
  235. package/open-sse/services/model.js +7 -1
  236. package/open-sse/services/modelCapabilities.js +1 -1
  237. package/open-sse/services/modelFamilyFallback.js +1 -1
  238. package/open-sse/services/provider.js +1 -1
  239. package/open-sse/services/providerCooldownTracker.js +1 -1
  240. package/open-sse/services/rateLimitManager.js +1 -1
  241. package/open-sse/services/tokenRefresh/providers.js +3 -3
  242. package/open-sse/services/usage/codex.js +148 -214
  243. package/open-sse/services/usage/grok-cli.js +61 -7
  244. package/open-sse/services/usage/misc.js +0 -19
  245. package/open-sse/services/usage.js +0 -2
  246. package/open-sse/translator/concerns/paramSupport.js +2 -2
  247. package/open-sse/translator/concerns/thinkingUnified.js +6 -0
  248. package/open-sse/translator/index.js +97 -59
  249. package/open-sse/translator/request/claude-to-kiro.js +67 -40
  250. package/open-sse/translator/request/openai-responses.js +1 -0
  251. package/open-sse/translator/request/openai-to-commandcode.js +0 -4
  252. package/open-sse/translator/request/openai-to-kiro.js +49 -20
  253. package/open-sse/translator/response/commandcode-to-openai.js +0 -4
  254. package/open-sse/utils/bypassHandler.js +18 -3
  255. package/open-sse/utils/claudeCloaking.js +15 -5
  256. package/open-sse/utils/error.js +0 -17
  257. package/open-sse/utils/kiroSessionReplay.js +125 -0
  258. package/open-sse/utils/logger.js +1 -1
  259. package/open-sse/utils/proxyFetch.js +0 -1
  260. package/open-sse/utils/reasoningContentInjector.js +4 -7
  261. package/open-sse/utils/responsesStreamHelpers.js +1 -0
  262. package/open-sse/utils/sessionManager.js +49 -22
  263. package/open-sse/utils/stream.js +54 -77
  264. package/open-sse/utils/streamHandler.js +19 -15
  265. package/open-sse/utils/streamHelpers.js +2 -17
  266. package/open-sse/utils/usageTracking.js +103 -12
  267. package/package.json +4 -7
  268. package/public/assets/EndpointPageClient.C2WMTYy0.js +6 -0
  269. package/public/assets/{Loading.Bj-WGl4k.js → Loading.BHPtQ9vj.js} +1 -1
  270. package/public/assets/{NoAuthProxyCard.qAv57b-0.js → NoAuthProxyCard.BDcXaWvM.js} +1 -1
  271. package/public/assets/index.Be3gbrBl.js +89 -0
  272. package/public/assets/index.COMgQZYj.css +1 -0
  273. package/public/assets/{page.CbkENBd5.js → page.-lpNgYAw.js} +1 -1
  274. package/public/assets/{page.CssmrzTT.js → page.11hpNKmq.js} +1 -1
  275. package/public/assets/{page.C72FTTJr.js → page.B-LxsnqU.js} +1 -1
  276. package/public/assets/{page.sOFOrxqq.js → page.B-dzeIUC.js} +1 -1
  277. package/public/assets/{page.CsxWmPoI.js → page.B-nBR75u.js} +1 -1
  278. package/public/assets/{page.Dx_GRRdT.js → page.B3QqvLep.js} +1 -1
  279. package/public/assets/{page.DPyOkPVo.js → page.B8xdkCWA.js} +1 -1
  280. package/public/assets/{page.DUI8kqOY.js → page.BFb7yUd8.js} +1 -1
  281. package/public/assets/page.BbPPdoas.js +1 -0
  282. package/public/assets/page.BdS20a1O.js +1 -0
  283. package/public/assets/{page.DibhF7f-.js → page.BfmI9KYe.js} +1 -1
  284. package/public/assets/{page.DX4Nno6M.js → page.BgGJovOg.js} +1 -1
  285. package/public/assets/{page.BnmTC5jG.js → page.C0gBvR4O.js} +1 -1
  286. package/public/assets/{page.D5gj98Oe.js → page.C6bYizL7.js} +1 -1
  287. package/public/assets/{page.vla5Mzki.js → page.C8sLEMa6.js} +1 -1
  288. package/public/assets/{page.CXd_R9yU.js → page.CCNsZBXp.js} +1 -1
  289. package/public/assets/{page.CQlVGoXg.js → page.CF_YSLwP.js} +1 -1
  290. package/public/assets/{page.BbO1pspY.js → page.CPTtRDxw.js} +1 -1
  291. package/public/assets/{page.D1oKew1j.js → page.CdMmlUEm.js} +1 -1
  292. package/public/assets/{page.DxMDTbSx.js → page.Cged1bOZ.js} +1 -1
  293. package/public/assets/{page.Cj5h09Zo.js → page.Ch647dVk.js} +1 -1
  294. package/public/assets/{page.DVIHRbxB.js → page.CpvXDkQF.js} +1 -1
  295. package/public/assets/{page.i4KDuWcM.js → page.CrBl7f8N.js} +1 -1
  296. package/public/assets/{page.BSLJ4tx5.js → page.CrP7niA2.js} +1 -1
  297. package/public/assets/{page.CnTop8Zt.js → page.CvUFZzXr.js} +1 -1
  298. package/public/assets/{page.O2ddJYoT.js → page.CyUqJPmx.js} +1 -1
  299. package/public/assets/{page.DBhu4Arl.js → page.D8Rp8fFR.js} +1 -1
  300. package/public/assets/{page.CPZav1_c.js → page.Df9qtrdG.js} +1 -1
  301. package/public/assets/{page.Qkkv8roc.js → page.DiL4Gwqe.js} +1 -1
  302. package/public/assets/{page.BdmJ3yY3.js → page.Dl1apZE2.js} +1 -1
  303. package/public/assets/{page.1qjqvrwX.js → page.HV0w1d5C.js} +1 -1
  304. package/public/assets/{page.ChX1NqmC.js → page.N1x4YL8_.js} +1 -1
  305. package/public/assets/{page.Dh5f0XJC.js → page.P2Ny5veR.js} +1 -1
  306. package/public/assets/{page.CSs3-gdt.js → page.fOOCr18J.js} +1 -1
  307. package/public/assets/{page.Ir7KvKcF.js → page.sUIPWIiF.js} +1 -1
  308. package/public/assets/{page.CzlQTyAL.js → page.tE7PpWYW.js} +1 -1
  309. package/public/assets/{page.DzZLqI5Q.js → page.uziaGZsT.js} +1 -1
  310. package/public/index.html +2 -2
  311. package/public/assets/EndpointPageClient.LyNNvZ4z.js +0 -6
  312. package/public/assets/index.BuPfZebu.css +0 -1
  313. package/public/assets/index.CShjRV_B.js +0 -89
  314. package/public/assets/page.DD_czjjP.js +0 -1
@@ -1,5 +1,7 @@
1
1
  import { BaseExecutor } from "./base.js";
2
2
  import { PROVIDERS } from "../config/providers.js";
3
+ import { SSE_DONE, SSE_HEADERS_NO_BUFFER } from "../utils/sseConstants.js";
4
+ import { sseChunk } from "../utils/sse.js";
3
5
 
4
6
  const GROK_CHAT_API = PROVIDERS["grok-web"].baseUrl;
5
7
  const GROK_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
@@ -130,10 +132,6 @@ async function* extractContent(eventStream, isThinkingModel, signal) {
130
132
  yield { done: true, fingerprint, responseId };
131
133
  }
132
134
 
133
- function sseChunk(data) {
134
- return `data: ${JSON.stringify(data)}\n\n`;
135
- }
136
-
137
135
  function buildStreamingResponse(eventStream, model, cid, created, isThinkingModel, signal) {
138
136
  const encoder = new TextEncoder();
139
137
  return new ReadableStream({
@@ -175,13 +173,13 @@ function buildStreamingResponse(eventStream, model, cid, created, isThinkingMode
175
173
  id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: fp || null,
176
174
  choices: [{ index: 0, delta: {}, finish_reason: "stop", logprobs: null }],
177
175
  })));
178
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
176
+ controller.enqueue(encoder.encode(SSE_DONE));
179
177
  } catch (err) {
180
178
  controller.enqueue(encoder.encode(sseChunk({
181
179
  id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: null,
182
180
  choices: [{ index: 0, delta: { content: `[Stream error: ${err.message || String(err)}]` }, finish_reason: "stop", logprobs: null }],
183
181
  })));
184
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
182
+ controller.enqueue(encoder.encode(SSE_DONE));
185
183
  } finally {
186
184
  controller.close();
187
185
  }
@@ -333,7 +331,7 @@ export class GrokWebExecutor extends BaseExecutor {
333
331
  const sseStream = buildStreamingResponse(response.body, model, cid, created, isThinking, signal);
334
332
  finalResponse = new Response(sseStream, {
335
333
  status: 200,
336
- headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "X-Accel-Buffering": "no" },
334
+ headers: { ...SSE_HEADERS_NO_BUFFER },
337
335
  });
338
336
  } else {
339
337
  finalResponse = await buildNonStreamingResponse(response.body, model, cid, created, isThinking, signal);
@@ -5,6 +5,7 @@ import { GithubExecutor } from "./github.js";
5
5
  import { IFlowExecutor } from "./iflow.js";
6
6
  import { QoderExecutor } from "./qoder.js";
7
7
  import { KiroExecutor } from "./kiro.js";
8
+ import { KimchiExecutor } from "./kimchi.js";
8
9
  import { CodexExecutor } from "./codex.js";
9
10
  import { CursorExecutor } from "./cursor.js";
10
11
  import { VertexExecutor } from "./vertex.js";
@@ -17,6 +18,8 @@ import { PerplexityWebExecutor } from "./perplexity-web.js";
17
18
  import { OllamaLocalExecutor } from "./ollama-local.js";
18
19
  import { CommandCodeExecutor } from "./commandcode.js";
19
20
  import { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js";
21
+ import { MimoFreeExecutor } from "./mimo-free.js";
22
+ import { CodeBuddyExecutor } from "./codebuddy-cn.js";
20
23
  import { DefaultExecutor } from "./default.js";
21
24
 
22
25
  const executors = {
@@ -27,6 +30,7 @@ const executors = {
27
30
  iflow: new IFlowExecutor(),
28
31
  qoder: new QoderExecutor(),
29
32
  kiro: new KiroExecutor(),
33
+ kimchi: new KimchiExecutor(),
30
34
  codex: new CodexExecutor(),
31
35
  cursor: new CursorExecutor(),
32
36
  cu: new CursorExecutor(), // Alias for cursor
@@ -37,10 +41,15 @@ const executors = {
37
41
  "opencode-go": new OpenCodeGoExecutor(),
38
42
  "grok-web": new GrokWebExecutor(),
39
43
  "grok-cli": new GrokCliExecutor(),
44
+ gcli: new GrokCliExecutor(), // Alias
45
+ gb: new GrokCliExecutor(), // Alias (Grok Build)
40
46
  "perplexity-web": new PerplexityWebExecutor(),
41
47
  "ollama-local": new OllamaLocalExecutor(),
42
48
  commandcode: new CommandCodeExecutor(),
43
49
  "xiaomi-tokenplan": new XiaomiTokenplanExecutor(),
50
+ "mimo-free": new MimoFreeExecutor(),
51
+ mmf: new MimoFreeExecutor(), // Alias for mimo-free
52
+ "codebuddy-cn": new CodeBuddyExecutor(),
44
53
  };
45
54
 
46
55
  const defaultCache = new Map();
@@ -63,6 +72,7 @@ export { GithubExecutor } from "./github.js";
63
72
  export { IFlowExecutor } from "./iflow.js";
64
73
  export { QoderExecutor } from "./qoder.js";
65
74
  export { KiroExecutor } from "./kiro.js";
75
+ export { KimchiExecutor } from "./kimchi.js";
66
76
  export { CodexExecutor } from "./codex.js";
67
77
  export { CursorExecutor } from "./cursor.js";
68
78
  export { VertexExecutor } from "./vertex.js";
@@ -76,3 +86,5 @@ export { PerplexityWebExecutor } from "./perplexity-web.js";
76
86
  export { OllamaLocalExecutor } from "./ollama-local.js";
77
87
  export { CommandCodeExecutor } from "./commandcode.js";
78
88
  export { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js";
89
+ export { MimoFreeExecutor } from "./mimo-free.js";
90
+ export { CodeBuddyExecutor } from "./codebuddy-cn.js";
@@ -1,9 +1,10 @@
1
1
  import { BaseExecutor } from "./base.js";
2
2
  import { PROVIDERS } from "../config/providers.js";
3
+ import { resolveKiroModel } from "../config/kiroConstants.js";
3
4
  import { v4 as uuidv4 } from "uuid";
4
5
  import { refreshKiroToken } from "../services/tokenRefresh.js";
5
- import { proxyAwareFetch } from "../utils/proxyFetch.js";
6
- import { HTTP_STATUS, RETRY_CONFIG, DEFAULT_RETRY_CONFIG, resolveRetryEntry } from "../config/runtimeConfig.js";
6
+ import { SSE_DONE, SSE_HEADERS } from "../utils/sseConstants.js";
7
+ import { getCapabilitiesForModel } from "../providers/capabilities.js";
7
8
 
8
9
  /**
9
10
  * KiroExecutor - Executor for Kiro AI (AWS CodeWhisperer)
@@ -21,57 +22,99 @@ export class KiroExecutor extends BaseExecutor {
21
22
  "Amz-Sdk-Invocation-Id": uuidv4()
22
23
  };
23
24
 
24
- if (credentials.accessToken) {
25
+ // API-key auth: the key is stored as accessToken and sent as a bearer token
26
+ // exactly like an OAuth access token, but with an extra `tokentype: API_KEY`
27
+ // header so CodeWhisperer treats it as a long-lived API key rather than an
28
+ // OIDC/social access token. Mirrors the Kiro IDE headless-auth behavior.
29
+ // Enterprise / Microsoft Entra (external_idp) tokens are OAuth access tokens,
30
+ // but CodeWhisperer requires TokenType=EXTERNAL_IDP to bind them to profiles.
31
+ const authMethod = credentials?.providerSpecificData?.authMethod;
32
+ const isApiKey = authMethod === "api_key";
33
+ const isExternalIdp = authMethod === "external_idp";
34
+
35
+ const apiKey = credentials?.apiKey || (isApiKey ? credentials?.accessToken : null);
36
+ if (isApiKey && apiKey) {
37
+ headers["Authorization"] = `Bearer ${apiKey}`;
38
+ headers["tokentype"] = "API_KEY";
39
+ } else if (credentials.accessToken) {
25
40
  headers["Authorization"] = `Bearer ${credentials.accessToken}`;
41
+ if (isExternalIdp) {
42
+ headers["TokenType"] = "EXTERNAL_IDP";
43
+ }
26
44
  }
27
45
 
28
46
  return headers;
29
47
  }
30
48
 
49
+ /**
50
+ * Auth-aware endpoint ordering.
51
+ *
52
+ * API-key Kiro connections store a raw CodeWhisperer credential (validated
53
+ * against codewhisperer.us-east-1.amazonaws.com via ListAvailableProfiles).
54
+ * The Kiro IDE gateway (runtime.*.kiro.dev) expects Kiro OIDC/social tokens
55
+ * and rejects an `tokentype: API_KEY` token with 401/403 — which
56
+ * BaseExecutor.execute() returns immediately (only 429 / network errors fall
57
+ * through to the next host). So for api-key auth we must try the *.amazonaws.com
58
+ * CodeWhisperer hosts FIRST, mirroring the Kiro-Go reference fork which never
59
+ * routes api-key traffic through kiro.dev. External IdP enterprise tokens also
60
+ * use the CodeWhisperer surface, with the `TokenType: EXTERNAL_IDP` header.
61
+ * Other OAuth methods keep the default order (kiro.dev first) since their
62
+ * tokens are what that gateway accepts.
63
+ */
64
+ getOrderedBaseUrls(credentials) {
65
+ const baseUrls = this.getBaseUrls();
66
+ const authMethod = credentials?.providerSpecificData?.authMethod;
67
+ // IAM Identity Center (idc) tokens are AWS SSO access tokens — the same
68
+ // family as external_idp/api_key. The kiro.dev gateway rejects them with
69
+ // 403 "bearer token invalid", so they must hit the CodeWhisperer
70
+ // *.amazonaws.com surface, and in the region the token was minted in
71
+ // (the baseUrls are hardcoded us-east-1).
72
+ const isCodeWhispererSurface =
73
+ authMethod === "api_key" || authMethod === "external_idp" || authMethod === "idc";
74
+ if (!isCodeWhispererSurface) return baseUrls;
75
+
76
+ const region = (credentials?.providerSpecificData?.region || "us-east-1").trim();
77
+ const regionalize = (u) =>
78
+ region && region !== "us-east-1" && u.includes("amazonaws.com")
79
+ ? u.replace(/([a-z]+)\.[a-z0-9-]+\.amazonaws\.com/, `$1.${region}.amazonaws.com`)
80
+ : u;
81
+
82
+ const amazon = baseUrls.filter((u) => u.includes("amazonaws.com")).map(regionalize);
83
+ const others = baseUrls.filter((u) => !u.includes("amazonaws.com"));
84
+ return amazon.length > 0 ? [...amazon, ...others] : baseUrls;
85
+ }
86
+
87
+ buildUrl(model, stream, urlIndex = 0, credentials = null) {
88
+ const baseUrls = this.getOrderedBaseUrls(credentials);
89
+ return baseUrls[urlIndex] || baseUrls[0] || this.config.baseUrl;
90
+ }
91
+
31
92
  transformRequest(model, body, stream, credentials) {
32
93
  return body;
33
94
  }
34
95
 
35
96
  /**
36
- * Custom execute for Kiro - handles AWS EventStream binary response with retry support
97
+ * Kiro execute delegate to BaseExecutor for endpoint fallback + retry, then
98
+ * transform the binary AWS EventStream into OpenAI-shaped SSE on success.
99
+ *
100
+ * BaseExecutor.execute() walks config.baseUrls (runtime.us-east-1.kiro.dev →
101
+ * codewhisperer → q) advancing to the next host on 429 (shouldRetry) and on
102
+ * network/5xx errors, while tryRetry handles in-place retries per `retry: {429: 2}`.
103
+ * Note: api-key connections reorder these so the *.amazonaws.com hosts come
104
+ * first — see getOrderedBaseUrls/buildUrl above.
105
+ * Note: the baseUrls are alternate surfaces of one regional service, so rotation
106
+ * is edge-level failover — it does not grant fresh 429 quota. Per-account 429
107
+ * spreading is handled upstream by account rotation in sse/handlers/chat.js.
108
+ *
109
+ * Errors are returned untransformed so the upstream handler can read the body,
110
+ * classify the status, and trigger account fallback/cooldown.
37
111
  */
38
- async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
39
- const url = this.buildUrl(model, stream, 0);
40
- const transformedBody = this.transformRequest(model, body, stream, credentials);
41
-
42
- // Merge default retry config with provider-specific config
43
- const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
44
- let retryAttempts = 0;
45
-
46
- while (true) {
47
- const headers = this.buildHeaders(credentials, stream);
48
-
49
- const response = await proxyAwareFetch(url, {
50
- method: "POST",
51
- headers,
52
- body: JSON.stringify(transformedBody),
53
- signal
54
- }, proxyOptions);
55
-
56
- // Check if should retry based on status code
57
- const { attempts: maxRetries, delayMs } = resolveRetryEntry(retryConfig[response.status]);
58
- if (!response.ok && maxRetries > 0 && retryAttempts < maxRetries) {
59
- retryAttempts++;
60
- log?.debug?.("RETRY", `${response.status} retry ${retryAttempts}/${maxRetries} after ${delayMs / 1000}s`);
61
- await new Promise(resolve => setTimeout(resolve, delayMs));
62
- continue;
63
- }
64
-
65
- if (!response.ok) {
66
- return { response, url, headers, transformedBody };
67
- }
68
-
69
- // Success - transform and return
70
- // For Kiro, we need to transform the binary EventStream to SSE
71
- // Create a TransformStream to convert binary to SSE text
72
- const transformedResponse = this.transformEventStreamToSSE(response, model);
73
- return { response: transformedResponse, url, headers, transformedBody };
112
+ async execute(args) {
113
+ const result = await super.execute(args);
114
+ if (result?.response?.ok) {
115
+ result.response = this.transformEventStreamToSSE(result.response, args.model);
74
116
  }
117
+ return result;
75
118
  }
76
119
 
77
120
  /**
@@ -83,6 +126,8 @@ export class KiroExecutor extends BaseExecutor {
83
126
  let chunkIndex = 0;
84
127
  const responseId = `chatcmpl-${Date.now()}`;
85
128
  const created = Math.floor(Date.now() / 1000);
129
+ const capabilityModel = resolveKiroModel(model).upstream;
130
+ const contextWindow = getCapabilitiesForModel("kiro", capabilityModel).contextWindow || 200000;
86
131
  const state = {
87
132
  endDetected: false,
88
133
  finishEmitted: false,
@@ -90,11 +135,14 @@ export class KiroExecutor extends BaseExecutor {
90
135
  hasReasoningContent: false,
91
136
  reasoningChunkCount: 0,
92
137
  toolCallIndex: 0,
93
- seenToolIds: new Map()
138
+ seenToolIds: new Map(),
139
+ inThinking: false
94
140
  };
95
141
 
96
142
  const transformStream = new TransformStream({
97
143
  async transform(chunk, controller) {
144
+ // Track output so we can emit a keepalive if this frame yields no chunk.
145
+ const enqueueCountBefore = chunkIndex;
98
146
  // Append to buffer
99
147
  const newBuffer = new Uint8Array(buffer.length + chunk.length);
100
148
  newBuffer.set(buffer);
@@ -118,16 +166,45 @@ export class KiroExecutor extends BaseExecutor {
118
166
  if (!event) continue;
119
167
 
120
168
  const eventType = event.headers[":event-type"] || "";
121
-
169
+
122
170
  // Track total content length for token estimation
123
171
  if (!state.totalContentLength) state.totalContentLength = 0;
124
172
  if (!state.contextUsagePercentage) state.contextUsagePercentage = 0;
125
173
 
126
174
  // Handle assistantResponseEvent
127
175
  if (eventType === "assistantResponseEvent" && event.payload?.content) {
128
- const content = event.payload.content;
176
+ let content = event.payload.content;
177
+
178
+ // Kiro Claude models can leak <thinking> blocks into the content stream.
179
+ // We strip these literal tags to prevent duplication, as the reasoning
180
+ // is already routed correctly via reasoningContentEvent.
181
+ if (state.inThinking) {
182
+ if (content.includes("</thinking>")) {
183
+ state.inThinking = false;
184
+ const after = content.split("</thinking>").slice(1).join("</thinking>");
185
+ content = after.startsWith("\n") ? after.substring(1) : after;
186
+ } else {
187
+ content = ""; // Drop entirely while inside thinking block
188
+ }
189
+ } else if (content.includes("<thinking>")) {
190
+ state.inThinking = true;
191
+ if (content.includes("</thinking>")) {
192
+ state.inThinking = false;
193
+ const before = content.split("<thinking>")[0];
194
+ const after = content.split("</thinking>").slice(1).join("</thinking>");
195
+ content = before + (after.startsWith("\n") ? after.substring(1) : after);
196
+ } else {
197
+ content = content.split("<thinking>")[0];
198
+ }
199
+ }
200
+
201
+ if (!content && state.hasReasoningContent) {
202
+ // If we stripped everything, skip emitting an empty content chunk
203
+ continue;
204
+ }
205
+
129
206
  state.totalContentLength += content.length;
130
-
207
+
131
208
  const chunk = {
132
209
  id: responseId,
133
210
  object: "chat.completion.chunk",
@@ -314,13 +391,24 @@ export class KiroExecutor extends BaseExecutor {
314
391
  if (metrics && typeof metrics === 'object') {
315
392
  const inputTokens = metrics.inputTokens || 0;
316
393
  const outputTokens = metrics.outputTokens || 0;
317
-
394
+ // ponytail: Amazon Q upstream does not expose cache fields today,
395
+ // but pick up cache_read_input_tokens / cache_creation_input_tokens
396
+ // if the event shape grows them so cost tracking stays accurate.
397
+ const cachedTokens = metrics.cacheReadInputTokens || metrics.cache_read_input_tokens || 0;
398
+ const cacheCreationInputTokens = metrics.cacheCreationInputTokens || metrics.cache_creation_input_tokens || 0;
399
+
318
400
  if (inputTokens > 0 || outputTokens > 0) {
319
401
  state.usage = {
320
402
  prompt_tokens: inputTokens,
321
403
  completion_tokens: outputTokens,
322
404
  total_tokens: inputTokens + outputTokens
323
405
  };
406
+ // Kiro is Claude-backed: inputTokens EXCLUDES cache (Claude convention),
407
+ // not inclusive like OpenAI's cached_tokens. Emit cache_read_input_tokens
408
+ // (not cached_tokens) so canonicalizeUsage takes the Claude fold path and
409
+ // correctly adds cache back into prompt_tokens instead of undercharging.
410
+ if (cachedTokens > 0) state.usage.cache_read_input_tokens = cachedTokens;
411
+ if (cacheCreationInputTokens > 0) state.usage.cache_creation_input_tokens = cacheCreationInputTokens;
324
412
  }
325
413
  }
326
414
  }
@@ -328,27 +416,26 @@ export class KiroExecutor extends BaseExecutor {
328
416
  // Emit final chunk only after receiving BOTH meteringEvent AND contextUsageEvent
329
417
  if (state.hasMeteringEvent && state.hasContextUsage && !state.finishEmitted) {
330
418
  state.finishEmitted = true;
331
-
419
+
332
420
  // Estimate tokens if not available from events
333
421
  if (!state.usage) {
334
422
  // Estimate output tokens from content length
335
- const estimatedOutputTokens = state.totalContentLength > 0
423
+ const estimatedOutputTokens = state.totalContentLength > 0
336
424
  ? Math.max(1, Math.floor(state.totalContentLength / 4))
337
425
  : 0;
338
-
426
+
339
427
  // Estimate input tokens from contextUsagePercentage
340
- // Kiro models typically have 200k context window
341
428
  const estimatedInputTokens = state.contextUsagePercentage > 0
342
- ? Math.floor(state.contextUsagePercentage * 200000 / 100)
429
+ ? Math.floor(state.contextUsagePercentage * contextWindow / 100)
343
430
  : 0;
344
-
431
+
345
432
  state.usage = {
346
433
  prompt_tokens: estimatedInputTokens,
347
434
  completion_tokens: estimatedOutputTokens,
348
435
  total_tokens: estimatedInputTokens + estimatedOutputTokens
349
436
  };
350
437
  }
351
-
438
+
352
439
  const finishChunk = {
353
440
  id: responseId,
354
441
  object: "chat.completion.chunk",
@@ -360,12 +447,12 @@ export class KiroExecutor extends BaseExecutor {
360
447
  finish_reason: state.hasToolCalls ? "tool_calls" : "stop"
361
448
  }]
362
449
  };
363
-
450
+
364
451
  // Include usage in final chunk if available
365
452
  if (state.usage) {
366
453
  finishChunk.usage = state.usage;
367
454
  }
368
-
455
+
369
456
  controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(finishChunk)}\n\n`));
370
457
  }
371
458
  }
@@ -373,6 +460,12 @@ export class KiroExecutor extends BaseExecutor {
373
460
  if (iterations >= maxIterations) {
374
461
  console.warn("[Kiro] Max iterations reached in event parsing");
375
462
  }
463
+
464
+ // No client chunk produced this frame — emit an SSE comment keepalive
465
+ // so the stall watchdog sees upstream activity (ignored by parser/client).
466
+ if (chunkIndex === enqueueCountBefore && !state.finishEmitted) {
467
+ controller.enqueue(new TextEncoder().encode(": ka\n\n"));
468
+ }
376
469
  },
377
470
 
378
471
  flush(controller) {
@@ -394,24 +487,20 @@ export class KiroExecutor extends BaseExecutor {
394
487
  }
395
488
 
396
489
  // Send final done message
397
- controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
490
+ controller.enqueue(new TextEncoder().encode(SSE_DONE));
398
491
  }
399
492
  });
400
493
 
401
494
  // Pipe response body through transform stream
402
495
  if (!response.body) {
403
- return new Response("data: [DONE]\n\n", { status: response.status, headers: { "Content-Type": "text/event-stream" } });
496
+ return new Response(SSE_DONE, { status: response.status, headers: { "Content-Type": "text/event-stream" } });
404
497
  }
405
498
  const transformedStream = response.body.pipeThrough(transformStream);
406
499
 
407
500
  return new Response(transformedStream, {
408
501
  status: response.status,
409
502
  statusText: response.statusText,
410
- headers: {
411
- "Content-Type": "text/event-stream",
412
- "Cache-Control": "no-cache",
413
- "Connection": "keep-alive"
414
- }
503
+ headers: { ...SSE_HEADERS }
415
504
  });
416
505
  }
417
506
 
@@ -1,9 +1,17 @@
1
1
  import { BaseExecutor } from "./base.js";
2
2
  import { PROVIDERS } from "../config/providers.js";
3
3
  import { injectReasoningContent } from "../utils/reasoningContentInjector.js";
4
+ import { ANTHROPIC_API_VERSION } from "../providers/shared.js";
4
5
 
5
6
  // Models that use /zen/go/v1/messages (Anthropic/Claude format + x-api-key auth)
6
- const CLAUDE_FORMAT_MODELS = new Set(["minimax-m2.5", "minimax-m2.7"]);
7
+ const MESSAGES_FORMAT_MODELS = new Set([
8
+ "minimax-m3",
9
+ "minimax-m2.7",
10
+ "minimax-m2.5",
11
+ "qwen3.7-max",
12
+ "qwen3.7-plus",
13
+ "qwen3.6-plus",
14
+ ]);
7
15
 
8
16
  const BASE = "https://opencode.ai/zen/go/v1";
9
17
 
@@ -15,7 +23,7 @@ export class OpenCodeGoExecutor extends BaseExecutor {
15
23
  // buildUrl runs before buildHeaders in BaseExecutor.execute, cache model here
16
24
  buildUrl(model) {
17
25
  this._lastModel = model;
18
- return CLAUDE_FORMAT_MODELS.has(model)
26
+ return MESSAGES_FORMAT_MODELS.has(model)
19
27
  ? `${BASE}/messages`
20
28
  : `${BASE}/chat/completions`;
21
29
  }
@@ -24,9 +32,9 @@ export class OpenCodeGoExecutor extends BaseExecutor {
24
32
  const key = credentials?.apiKey || credentials?.accessToken;
25
33
  const headers = { "Content-Type": "application/json" };
26
34
 
27
- if (CLAUDE_FORMAT_MODELS.has(this._lastModel)) {
35
+ if (MESSAGES_FORMAT_MODELS.has(this._lastModel)) {
28
36
  headers["x-api-key"] = key;
29
- headers["anthropic-version"] = "2023-06-01";
37
+ headers["anthropic-version"] = ANTHROPIC_API_VERSION;
30
38
  } else {
31
39
  headers["Authorization"] = `Bearer ${key}`;
32
40
  }
@@ -15,7 +15,7 @@ export class OpenCodeExecutor extends BaseExecutor {
15
15
  }
16
16
 
17
17
  buildUrl(model) {
18
- const base = "https://opencode.ai";
18
+ const base = this.config.baseUrl;
19
19
  return MESSAGES_MODELS.has(model)
20
20
  ? `${base}/zen/v1/messages`
21
21
  : `${base}/zen/v1/chat/completions`;