@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,119 +1,552 @@
1
+ import crypto from "node:crypto";
2
+ import { BaseExecutor } from "./base.js";
3
+ import { PROVIDERS } from "../config/providers.js";
4
+ import {
5
+ refreshProviderCredentials,
6
+ shouldRefreshCredentials,
7
+ } from "../services/oauthCredentialManager.js";
8
+ import { normalizeResponsesInput } from "../translator/formats/responsesApi.js";
9
+ import { getModelUpstreamId } from "../config/providerModels.js";
10
+ import {
11
+ GROK_CLI_CLIENT_IDENTIFIER,
12
+ GROK_CLI_VERSION,
13
+ supportsGrokCliReasoningEffort,
14
+ } from "../config/grokCli.js";
15
+ import { MEMORY_CONFIG } from "../config/runtimeConfig.js";
16
+ import { resolveSessionId } from "../utils/sessionManager.js";
17
+ import { getConsistentMachineId } from "../shared/machineId.js";
18
+
19
+ // Server-generated item id prefixes that /responses cannot resolve when store=false
20
+ const SERVER_ID_PATTERN = /^(rs|fc|resp|msg)_/;
21
+
22
+ // Hosted tool types executed server-side by Grok CLI backend
23
+ const HOSTED_TOOL_TYPES = new Set([
24
+ "web_search",
25
+ "x_search",
26
+ "web_search_preview",
27
+ "file_search",
28
+ "image_generation",
29
+ "code_interpreter",
30
+ "mcp",
31
+ "local_shell",
32
+ ]);
33
+
34
+ // Fields accepted by cli-chat-proxy Responses API (mirrors Codex allowlist + Grok extras)
35
+ const RESPONSES_API_ALLOWLIST = new Set([
36
+ "model",
37
+ "input",
38
+ "instructions",
39
+ "tools",
40
+ "tool_choice",
41
+ "stream",
42
+ "store",
43
+ "reasoning",
44
+ "include",
45
+ "temperature",
46
+ "top_p",
47
+ "max_output_tokens",
48
+ "parallel_tool_calls",
49
+ "text",
50
+ "metadata",
51
+ "prompt_cache_key",
52
+ ]);
53
+
54
+ const EFFORT_LEVELS = ["low", "medium", "high", "xhigh"];
55
+ const GROK_CLI_TURN_STORE_MAX = 5000;
56
+ const GROK_CLI_NATIVE_ITEM_ID = /^(?:rs|msg|fc)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
57
+ const GROK_CLI_FREEFORM_TOOL_PARAMETERS = {
58
+ type: "object",
59
+ properties: { input: { type: "string" } },
60
+ required: ["input"],
61
+ };
62
+
63
+ // Per-session last turn index so multi-turn headers never go backwards within this process
64
+ const sessionTurnStore = new Map();
65
+ let requestTurnStore = new WeakMap();
66
+
1
67
  /**
2
- * Grok CLI Executor uses xAI API (api.x.ai) with token from ~/.grok/auth.json.
3
- * Auth: reads OIDC token from ~/.grok/auth.json (set by `grok login`).
4
- * OpenAI-compatible format.
5
- *
6
- * NOTE: Requires xAI credits or Grok subscription.
7
- * Free Grok 4.5 is only available via the Grok CLI TUI, not via API.
68
+ * Count user turns in a Responses `input` array.
69
+ * Official CLI sets x-grok-turn-idx to the 1-based conversation turn ( user messages).
70
+ * HAR: first chat turn → "1".
8
71
  */
72
+ export function countGrokCliUserTurns(input) {
73
+ if (!Array.isArray(input)) return 1;
74
+ let n = 0;
75
+ for (const item of input) {
76
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
77
+ const type = typeof item.type === "string" ? item.type : "";
78
+ // Responses message items (type omitted or "message") with role user
79
+ if (item.role === "user" && (!type || type === "message")) n += 1;
80
+ }
81
+ return Math.max(1, n);
82
+ }
9
83
 
10
- import { BaseExecutor } from "./base.js";
11
- import { PROVIDERS } from "../config/providers.js";
12
- import { readFileSync } from "fs";
13
- import { homedir } from "os";
14
- import { join } from "path";
15
-
16
- const XAI_API = "https://api.x.ai/v1/chat/completions";
17
- const AUTH_JSON_PATH = join(homedir(), ".grok", "auth.json");
18
-
19
- function readCliToken() {
20
- try {
21
- const raw = readFileSync(AUTH_JSON_PATH, "utf-8");
22
- const data = JSON.parse(raw);
23
- for (const [_scope, entry] of Object.entries(data)) {
24
- if (entry && typeof entry === "object" && entry.key) {
25
- return entry.key;
84
+ /**
85
+ * Resolve monotonic turn index for a session.
86
+ * Prefers user-message count from the payload (full history clients), but never
87
+ * decreases vs the last index observed for the same sessionId in this process.
88
+ */
89
+ export function resolveGrokCliTurnIdx(sessionId, input, requestKey = null) {
90
+ const fromInput = countGrokCliUserTurns(input);
91
+ if (!sessionId) return fromInput;
92
+
93
+ if (requestKey && requestTurnStore.has(requestKey)) {
94
+ return requestTurnStore.get(requestKey);
95
+ }
96
+
97
+ const now = Date.now();
98
+ const existing = sessionTurnStore.get(sessionId);
99
+ const prev = existing && now - existing.lastUsed <= MEMORY_CONFIG.sessionTtlMs
100
+ ? existing.turn
101
+ : 0;
102
+ if (existing) sessionTurnStore.delete(sessionId);
103
+
104
+ // A new delta-style request advances the turn; retries reuse requestKey.
105
+ const turn = prev > 0 ? Math.max(fromInput, prev + (requestKey ? 1 : 0)) : fromInput;
106
+ while (sessionTurnStore.size >= GROK_CLI_TURN_STORE_MAX) {
107
+ sessionTurnStore.delete(sessionTurnStore.keys().next().value);
108
+ }
109
+ sessionTurnStore.set(sessionId, { turn, lastUsed: now });
110
+ if (requestKey) requestTurnStore.set(requestKey, turn);
111
+ return turn;
112
+ }
113
+
114
+ /** Test helper — clear in-memory turn counters */
115
+ export function _resetGrokCliTurnStore() {
116
+ sessionTurnStore.clear();
117
+ requestTurnStore = new WeakMap();
118
+ }
119
+
120
+ export function _getGrokCliTurnStoreSize() {
121
+ return sessionTurnStore.size;
122
+ }
123
+
124
+ export function normalizeGrokCliEffort(value) {
125
+ const effort = typeof value === "string" ? value.trim().toLowerCase() : "";
126
+ if (effort === "max") return "xhigh";
127
+ if (EFFORT_LEVELS.includes(effort)) return effort;
128
+ return "high";
129
+ }
130
+
131
+ export { supportsGrokCliReasoningEffort } from "../config/grokCli.js";
132
+
133
+ export function resolveGrokCliSessionId(credentials, body) {
134
+ // ponytail: clients without stable thread metadata share one connection session;
135
+ // split further when their wire format exposes a durable conversation id.
136
+ const explicitSessionBody = {
137
+ prompt_cache_key: body?.prompt_cache_key,
138
+ session_id: body?.session_id,
139
+ conversation_id: body?.conversation_id,
140
+ metadata: body?.metadata,
141
+ };
142
+ return resolveSessionId({
143
+ headers: credentials?.rawHeaders,
144
+ body: explicitSessionBody,
145
+ connectionId: credentials?.connectionId || credentials?.id,
146
+ workspaceId: credentials?.providerSpecificData?.workspaceId,
147
+ scope: "grok-cli",
148
+ });
149
+ }
150
+
151
+ function stringifyGrokCliToolOutput(output) {
152
+ if (typeof output === "string") return output;
153
+ if (output === undefined) return "";
154
+ return JSON.stringify(output);
155
+ }
156
+
157
+ function isNativeGrokCliItemId(id) {
158
+ return typeof id === "string" && GROK_CLI_NATIVE_ITEM_ID.test(id);
159
+ }
160
+
161
+ function normalizeGrokCliInputItem(item) {
162
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
163
+ const { internal_chat_message_metadata_passthrough: _metadata, ...clean } = item;
164
+
165
+ if (item.type === "reasoning") {
166
+ if (!isNativeGrokCliItemId(item.id) || typeof item.encrypted_content !== "string") return null;
167
+ return clean;
168
+ }
169
+
170
+ if (item.type === "custom_tool_call") {
171
+ const callId = item.call_id || item.id;
172
+ const name = typeof item.name === "string" ? item.name.trim() : "";
173
+ if (!callId || !name) return null;
174
+ return {
175
+ type: "function_call",
176
+ call_id: callId,
177
+ name,
178
+ arguments: JSON.stringify({ input: stringifyGrokCliToolOutput(item.input ?? item.arguments) }),
179
+ };
180
+ }
181
+
182
+ if (item.type === "custom_tool_call_output" || item.type === "function_call_output") {
183
+ const callId = item.call_id || item.id;
184
+ if (!callId) return null;
185
+ return {
186
+ type: "function_call_output",
187
+ call_id: callId,
188
+ output: stringifyGrokCliToolOutput(item.output),
189
+ };
190
+ }
191
+
192
+ if (item.type === "function_call") {
193
+ const callId = item.call_id || item.id;
194
+ const name = typeof item.name === "string" ? item.name.trim() : "";
195
+ if (!callId || !name) return null;
196
+ return {
197
+ type: "function_call",
198
+ ...(isNativeGrokCliItemId(item.id) ? { id: item.id } : {}),
199
+ call_id: callId,
200
+ name,
201
+ arguments: typeof item.arguments === "string" ? item.arguments : JSON.stringify(item.arguments ?? {}),
202
+ ...(typeof item.status === "string" ? { status: item.status } : {}),
203
+ };
204
+ }
205
+
206
+ return clean;
207
+ }
208
+
209
+ export function normalizeGrokCliInput(body) {
210
+ if (!Array.isArray(body?.input)) return body;
211
+ const normalized = body.input.map(normalizeGrokCliInputItem).filter(Boolean);
212
+ const callIds = new Set(
213
+ normalized
214
+ .filter((item) => item?.type === "function_call" && item.call_id)
215
+ .map((item) => item.call_id)
216
+ );
217
+ body.input = normalized.filter(
218
+ (item) => item?.type !== "function_call_output" || callIds.has(item.call_id)
219
+ );
220
+ return body;
221
+ }
222
+
223
+ function stripStoredItemReferences(body) {
224
+ if (!Array.isArray(body.input)) return;
225
+ body.input = body.input.filter((item) => {
226
+ if (typeof item === "string" && SERVER_ID_PATTERN.test(item)) return false;
227
+ if (item && typeof item === "object" && !Array.isArray(item)) {
228
+ if (item.type === "item_reference") return false;
229
+ if (
230
+ typeof item.id === "string" &&
231
+ SERVER_ID_PATTERN.test(item.id) &&
232
+ !isNativeGrokCliItemId(item.id)
233
+ ) delete item.id;
234
+ }
235
+ return true;
236
+ });
237
+ }
238
+
239
+ /**
240
+ * Flatten Chat Completions tool shape → Responses flat format.
241
+ * Keep hosted tools (web_search / x_search) passthrough.
242
+ */
243
+ function normalizeGrokCliTools(body) {
244
+ if (!Array.isArray(body.tools) || body.tools.length === 0) {
245
+ delete body.tools;
246
+ delete body.tool_choice;
247
+ return;
248
+ }
249
+ const validNames = new Set();
250
+ const hostedTypes = new Set();
251
+ body.tools = body.tools.filter((tool) => {
252
+ if (!tool || typeof tool !== "object" || Array.isArray(tool)) return false;
253
+ const type = typeof tool.type === "string" ? tool.type : "";
254
+
255
+ if (type !== "function") {
256
+ // Hosted tools: { type: "web_search" } / { type: "x_search" }
257
+ if (HOSTED_TOOL_TYPES.has(type)) {
258
+ hostedTypes.add(type);
259
+ return true;
260
+ }
261
+ // Nested function shape without type
262
+ if (!type && tool.function) {
263
+ // fall through to function flatten below
264
+ } else if (!type || typeof tool.name === "string") {
265
+ // treat as bare function if name present
266
+ } else {
267
+ return false;
26
268
  }
27
269
  }
28
- return data.session_token || data.token || data.access_token || data.key || null;
29
- } catch {
30
- return null;
270
+
271
+ const isFunction =
272
+ type === "function" || type === "" || tool.function || typeof tool.name === "string";
273
+ if (!isFunction || HOSTED_TOOL_TYPES.has(type)) {
274
+ return HOSTED_TOOL_TYPES.has(type);
275
+ }
276
+
277
+ const fn =
278
+ tool.function && typeof tool.function === "object" && !Array.isArray(tool.function)
279
+ ? tool.function
280
+ : null;
281
+ const rawName =
282
+ typeof tool.name === "string" ? tool.name : typeof fn?.name === "string" ? fn.name : "";
283
+ const name = rawName.trim();
284
+ if (!name) return false;
285
+
286
+ const description =
287
+ typeof tool.description === "string"
288
+ ? tool.description
289
+ : typeof fn?.description === "string"
290
+ ? fn.description
291
+ : "";
292
+ const parameters = type === "custom"
293
+ ? GROK_CLI_FREEFORM_TOOL_PARAMETERS
294
+ : tool.parameters && typeof tool.parameters === "object" && !Array.isArray(tool.parameters)
295
+ ? tool.parameters
296
+ : fn?.parameters && typeof fn.parameters === "object" && !Array.isArray(fn.parameters)
297
+ ? fn.parameters
298
+ : { type: "object", properties: {} };
299
+
300
+ for (const k of Object.keys(tool)) delete tool[k];
301
+ tool.type = "function";
302
+ tool.name = name.slice(0, 128);
303
+ if (description) tool.description = description;
304
+ tool.parameters = parameters;
305
+ validNames.add(tool.name);
306
+ return true;
307
+ });
308
+
309
+ if (body.tools.length === 0) {
310
+ delete body.tools;
311
+ delete body.tool_choice;
312
+ return;
313
+ }
314
+
315
+ if (body.tool_choice && typeof body.tool_choice === "object" && !Array.isArray(body.tool_choice)) {
316
+ const choiceType = typeof body.tool_choice.type === "string" ? body.tool_choice.type : "";
317
+ if (choiceType === "function" || choiceType === "custom") {
318
+ const rawName = body.tool_choice.name ?? body.tool_choice.function?.name;
319
+ const name = typeof rawName === "string" ? rawName.trim().slice(0, 128) : "";
320
+ if (!name || !validNames.has(name)) delete body.tool_choice;
321
+ else body.tool_choice = { type: "function", name };
322
+ } else if (!hostedTypes.has(choiceType)) {
323
+ delete body.tool_choice;
324
+ }
325
+ }
326
+ }
327
+
328
+ function resolveEffortFromModel(modelId) {
329
+ if (!modelId || typeof modelId !== "string") return null;
330
+ for (const level of EFFORT_LEVELS) {
331
+ if (modelId.endsWith(`-${level}`)) return level;
31
332
  }
333
+ return null;
32
334
  }
33
335
 
336
+ /**
337
+ * Grok CLI Executor — OpenAI Responses API on cli-chat-proxy.grok.com
338
+ * Auth: OAuth device-code access token (xai-grok-cli).
339
+ */
34
340
  export class GrokCliExecutor extends BaseExecutor {
35
341
  constructor() {
36
342
  super("grok-cli", PROVIDERS["grok-cli"]);
343
+ this._currentSessionId = null;
344
+ this._currentReqId = null;
345
+ this._currentTurnIdx = 1;
346
+ this._agentId = null;
347
+ }
348
+
349
+ buildUrl() {
350
+ return this.config.baseUrl;
351
+ }
352
+
353
+ async refreshCredentials(credentials, log, proxyOptions = null) {
354
+ if (!credentials?.refreshToken) return null;
355
+ return refreshProviderCredentials("grok-cli", credentials, log, proxyOptions);
356
+ }
357
+
358
+ needsRefresh(credentials) {
359
+ return shouldRefreshCredentials("grok-cli", credentials);
37
360
  }
38
361
 
39
- async execute({ model, body, stream, credentials, signal, log }) {
40
- let token = credentials?.apiKey || readCliToken();
41
- if (!token || token === "cli-auth") {
42
- token = readCliToken();
362
+ buildHeaders(credentials, stream = true) {
363
+ const headers = super.buildHeaders(credentials, stream);
364
+
365
+ // Static fingerprint from registry
366
+ const staticHeaders = this.config.headers || {};
367
+ for (const [k, v] of Object.entries(staticHeaders)) {
368
+ if (v != null && headers[k] === undefined) headers[k] = v;
43
369
  }
44
- if (!token) {
45
- return {
46
- response: new Response(JSON.stringify({
47
- error: {
48
- message: "Grok CLI not authenticated. Run `grok login` on the server, or paste xAI API key.",
49
- type: "authentication_error",
50
- },
51
- }), { status: 401, headers: { "Content-Type": "application/json" } }),
52
- url: XAI_API,
53
- headers: {},
54
- transformedBody: body,
55
- };
370
+
371
+ headers["x-grok-client-identifier"] =
372
+ this.config.clientIdentifier || headers["x-grok-client-identifier"] || GROK_CLI_CLIENT_IDENTIFIER;
373
+ headers["x-grok-client-version"] =
374
+ this.config.clientVersion || headers["x-grok-client-version"] || GROK_CLI_VERSION;
375
+
376
+ const sessionId = this._currentSessionId || credentials?.connectionId || crypto.randomUUID();
377
+ const reqId = this._currentReqId || crypto.randomUUID();
378
+ headers["x-grok-session-id"] = sessionId;
379
+ // CLI uses the same id for conv + session on chat turns
380
+ headers["x-grok-conv-id"] = sessionId;
381
+ headers["x-grok-req-id"] = reqId;
382
+ headers["x-grok-turn-idx"] = String(this._currentTurnIdx || 1);
383
+
384
+ if (this._agentId) headers["x-grok-agent-id"] = this._agentId;
385
+
386
+ // Surface model override (CLI always sets this)
387
+ if (this._currentModel) headers["x-grok-model-override"] = this._currentModel;
388
+
389
+ // Identity: mapTokens stores email top-level AND in providerSpecificData;
390
+ // fall back either way so OAuth connections always fingerprint like the CLI.
391
+ const psd = credentials?.providerSpecificData || {};
392
+ const email = psd.email || credentials?.email;
393
+ const userId = psd.userId || credentials?.userId || credentials?.providerUserId;
394
+ if (email) headers["x-email"] = email;
395
+ if (userId) headers["x-userid"] = userId;
396
+
397
+ return headers;
398
+ }
399
+
400
+ parseError(response, bodyText) {
401
+ // 402 personal-team-blocked:spending-limit → surface as payment/quota for fallback
402
+ if (response.status === 402 && bodyText) {
403
+ try {
404
+ const json = JSON.parse(bodyText);
405
+ const code = json?.code || "";
406
+ const msg = json?.error || json?.message || bodyText;
407
+ return {
408
+ status: 402,
409
+ message: typeof msg === "string" ? msg : bodyText,
410
+ code: typeof code === "string" ? code : undefined,
411
+ };
412
+ } catch {
413
+ /* fall through */
414
+ }
56
415
  }
416
+ return super.parseError(response, bodyText);
417
+ }
57
418
 
58
- if (token.startsWith("Bearer ")) token = token.slice(7);
419
+ transformRequest(model, body, stream, credentials) {
420
+ // Session / request ids for headers — stable per client conversation when possible
421
+ const requestKey = body;
422
+ this._currentSessionId = resolveGrokCliSessionId(credentials, body);
423
+ this._currentReqId = crypto.randomUUID();
424
+ this._agentId =
425
+ credentials?.providerSpecificData?.deviceId ||
426
+ credentials?.providerSpecificData?.agentId ||
427
+ null;
59
428
 
60
- const headers = {
61
- "Content-Type": "application/json",
62
- "Authorization": `Bearer ${token}`,
63
- };
429
+ // Normalize Responses input
430
+ const normalized = normalizeResponsesInput(body.input);
431
+ if (normalized) body.input = normalized;
64
432
 
65
- // Map grok-4.5 to actual xAI model ID
66
- const MODEL_MAP = {
67
- "grok-4.5": "grok-4-0709",
68
- "grok-4": "grok-4-0709",
69
- "grok-3": "grok-3",
70
- };
433
+ // Chat Completions clients arrive with messages[] — translator should have
434
+ // converted already, but guard empty input.
435
+ if (!body.input || (Array.isArray(body.input) && body.input.length === 0)) {
436
+ if (Array.isArray(body.messages) && body.messages.length > 0) {
437
+ // Soft fallback: map messages → input messages (string content only)
438
+ body.input = body.messages.map((m) => ({
439
+ type: "message",
440
+ role: m.role || "user",
441
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content ?? ""),
442
+ }));
443
+ delete body.messages;
444
+ } else {
445
+ body.input = [{ type: "message", role: "user", content: "..." }];
446
+ }
447
+ }
448
+
449
+ // Keep role:"system" as-is — official grok-pager HAR sends system, not developer
450
+ // (Codex converts system→developer; Grok CLI does not).
451
+ normalizeGrokCliInput(body);
452
+ stripStoredItemReferences(body);
453
+ normalizeGrokCliTools(body);
454
+
455
+ // Turn index after input is finalized (user-message count, monotonic per session)
456
+ this._currentTurnIdx = resolveGrokCliTurnIdx(this._currentSessionId, body.input, requestKey);
71
457
 
72
- const upstreamModel = MODEL_MAP[model] || model;
73
- const upstreamBody = { ...body, model: upstreamModel, stream: stream !== false };
74
-
75
- log?.info?.("GROK-CLI", `→ ${model} (upstream: ${upstreamModel}) via api.x.ai`);
76
-
77
- let response;
78
- try {
79
- response = await fetch(XAI_API, {
80
- method: "POST",
81
- headers,
82
- body: JSON.stringify(upstreamBody),
83
- signal,
84
- });
85
- } catch (err) {
86
- log?.error?.("GROK-CLI", `Fetch failed: ${err.message}`);
87
- return {
88
- response: new Response(JSON.stringify({
89
- error: { message: `Grok connection failed: ${err.message}`, type: "upstream_error" },
90
- }), { status: 502, headers: { "Content-Type": "application/json" } }),
91
- url: XAI_API,
92
- headers,
93
- transformedBody: upstreamBody,
94
- };
458
+ body.stream = true;
459
+ body.store = false;
460
+
461
+ // Resolve upstream model id (strip effort suffix virtual models)
462
+ let modelEffort = resolveEffortFromModel(body.model || model);
463
+ let resolvedModel = body.model || model;
464
+ if (modelEffort) {
465
+ resolvedModel = resolvedModel.replace(new RegExp(`-${modelEffort}$`), "");
466
+ }
467
+ resolvedModel = getModelUpstreamId("gcli", resolvedModel) || resolvedModel;
468
+ // Also try provider id key
469
+ if (resolvedModel === (body.model || model)) {
470
+ resolvedModel = getModelUpstreamId("grok-cli", resolvedModel) || resolvedModel;
95
471
  }
472
+ body.model = resolvedModel;
473
+ this._currentModel = resolvedModel;
96
474
 
97
- if (!response.ok) {
98
- const status = response.status;
99
- const errBody = await response.text().catch(() => "");
100
- let errMsg;
101
- if (status === 401) errMsg = "Grok token expired. Run `grok login` to refresh, or paste a new xAI API key.";
102
- else if (status === 429) errMsg = "Grok rate limited. Wait and retry.";
103
- else if (errBody.includes("spending-limit")) errMsg = "No xAI credits. Add credits at https://console.x.ai or use Grok subscription.";
104
- else errMsg = `Grok returned HTTP ${status}: ${errBody.slice(0, 200)}`;
105
- log?.warn?.("GROK-CLI", errMsg);
106
- return {
107
- response: new Response(JSON.stringify({
108
- error: { message: errMsg, type: "upstream_error", code: `HTTP_${status}` },
109
- }), { status: status === 403 ? 402 : status, headers: { "Content-Type": "application/json" } }),
110
- url: XAI_API,
111
- headers,
112
- transformedBody: upstreamBody,
113
- };
475
+ // Reasoning effort priority: explicit > reasoning_effort > model suffix > default high.
476
+ // grok-build and Composer reject reasoningEffort but still accept summary/encrypted continuity.
477
+ const supportsReasoningEffort = supportsGrokCliReasoningEffort(resolvedModel);
478
+ if (!body.reasoning || typeof body.reasoning !== "object") {
479
+ body.reasoning = { summary: "concise" };
480
+ if (supportsReasoningEffort) {
481
+ body.reasoning.effort = normalizeGrokCliEffort(body.reasoning_effort || modelEffort);
482
+ }
483
+ } else {
484
+ if (supportsReasoningEffort) {
485
+ body.reasoning.effort = normalizeGrokCliEffort(
486
+ body.reasoning.effort || body.reasoning_effort || modelEffort,
487
+ );
488
+ } else {
489
+ delete body.reasoning.effort;
490
+ }
491
+ if (!body.reasoning.summary) body.reasoning.summary = "concise";
492
+ }
493
+ delete body.reasoning_effort;
494
+
495
+ // Encrypted reasoning for multi-turn continuity (CLI always requests this)
496
+ if (body.reasoning && body.reasoning.effort !== "none") {
497
+ const include = Array.isArray(body.include) ? body.include : [];
498
+ if (!include.includes("reasoning.encrypted_content")) {
499
+ include.push("reasoning.encrypted_content");
500
+ }
501
+ body.include = include;
502
+ }
503
+
504
+ // Drop Chat Completions leftovers that Responses rejects
505
+ delete body.messages;
506
+ delete body.max_tokens;
507
+ delete body.max_completion_tokens;
508
+ delete body.n;
509
+ delete body.seed;
510
+ delete body.logprobs;
511
+ delete body.top_logprobs;
512
+ delete body.frequency_penalty;
513
+ delete body.presence_penalty;
514
+ delete body.logit_bias;
515
+ delete body.user;
516
+ delete body.stream_options;
517
+ delete body.prompt_cache_retention;
518
+ delete body.safety_identifier;
519
+ delete body.previous_response_id; // store=false → cannot resolve
520
+
521
+ for (const k of Object.keys(body)) {
522
+ if (!RESPONSES_API_ALLOWLIST.has(k)) delete body[k];
114
523
  }
115
524
 
116
- // Pass through — xAI API is OpenAI-compatible
117
- return { response, url: XAI_API, headers, transformedBody: upstreamBody };
525
+ return body;
526
+ }
527
+
528
+ async execute(args) {
529
+ // Lazy-resolve stable agent id once per process if connection has none
530
+ if (!this._agentId && !args.credentials?.providerSpecificData?.deviceId) {
531
+ try {
532
+ const mid = await getConsistentMachineId("grok-cli-agent");
533
+ // Format as UUID-ish for header aesthetics
534
+ this._agentId = [
535
+ mid.slice(0, 8),
536
+ mid.slice(8, 12),
537
+ "5" + mid.slice(13, 16),
538
+ "a" + mid.slice(17, 20),
539
+ mid.slice(0, 12).padEnd(12, "0"),
540
+ ].join("-");
541
+ } catch {
542
+ this._agentId = crypto.randomUUID();
543
+ }
544
+ } else if (args.credentials?.providerSpecificData?.deviceId) {
545
+ this._agentId = args.credentials.providerSpecificData.deviceId;
546
+ }
547
+
548
+ return super.execute(args);
118
549
  }
119
550
  }
551
+
552
+ export default GrokCliExecutor;