@fudrouter/fsrouter 0.6.61 → 0.6.63

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 (557) hide show
  1. package/bin/copyJs.js +13 -9
  2. package/open-sse/AGENTS.md +39 -0
  3. package/open-sse/config/anthropicHeaders.js +87 -0
  4. package/open-sse/config/appConstants.js +238 -0
  5. package/open-sse/config/audioRegistry.js +506 -0
  6. package/open-sse/config/cliFingerprints.js +304 -0
  7. package/open-sse/config/codexInstructions.js +119 -0
  8. package/open-sse/config/codexQuotaScopes.js +42 -0
  9. package/open-sse/config/constants.js +4 -0
  10. package/open-sse/config/contextEditing.js +68 -0
  11. package/open-sse/config/credentialLoader.js +83 -0
  12. package/open-sse/config/defaultThinkingSignature.js +12 -0
  13. package/open-sse/config/embeddingRegistry.js +264 -0
  14. package/open-sse/config/errorConfig.js +96 -0
  15. package/open-sse/config/freeModelCatalog.data.js +121 -0
  16. package/open-sse/config/freeModelCatalog.js +90 -0
  17. package/open-sse/config/freeTierCatalog.js +81 -0
  18. package/open-sse/config/googleTtsLanguages.js +62 -0
  19. package/open-sse/config/imageParamDefs.js +360 -0
  20. package/open-sse/config/imageRegistry.js +493 -0
  21. package/open-sse/config/kiroConstants.js +268 -0
  22. package/open-sse/config/mediaConfig.js +27 -0
  23. package/open-sse/config/mediaServiceKinds.js +71 -0
  24. package/open-sse/config/models.js +13 -0
  25. package/open-sse/config/moderationRegistry.js +71 -0
  26. package/open-sse/config/musicRegistry.js +99 -0
  27. package/open-sse/config/ocrRegistry.js +61 -0
  28. package/open-sse/config/ollamaModels.js +19 -0
  29. package/open-sse/config/providerErrorRules.js +114 -0
  30. package/open-sse/config/providerFieldStrips.js +35 -0
  31. package/open-sse/config/providerHeaderProfiles.js +191 -0
  32. package/open-sse/config/providerModels.js +106 -0
  33. package/open-sse/config/providerRegistry.js +16 -0
  34. package/open-sse/config/providers.js +19 -0
  35. package/open-sse/config/registryUtils.js +106 -0
  36. package/open-sse/config/rerankRegistry.js +177 -0
  37. package/open-sse/config/runtimeConfig.js +96 -0
  38. package/open-sse/config/runway.js +39 -0
  39. package/open-sse/config/searchRegistry.js +153 -0
  40. package/open-sse/config/toolCloaking.js +199 -0
  41. package/open-sse/config/ttsModels.js +129 -0
  42. package/open-sse/config/videoRegistry.js +207 -0
  43. package/open-sse/executors/adapta-web.js +435 -0
  44. package/open-sse/executors/antigravity.js +534 -0
  45. package/open-sse/executors/antigravityUpstreamError.js +13 -0
  46. package/open-sse/executors/auggie.js +437 -0
  47. package/open-sse/executors/azure-openai.js +32 -0
  48. package/open-sse/executors/azure.js +57 -0
  49. package/open-sse/executors/base.js +181 -0
  50. package/open-sse/executors/bedrock.js +633 -0
  51. package/open-sse/executors/blackbox-web.js +578 -0
  52. package/open-sse/executors/chatgpt-web.js +2042 -0
  53. package/open-sse/executors/chipotle.js +357 -0
  54. package/open-sse/executors/claude-web-with-auto-refresh.js +73 -0
  55. package/open-sse/executors/claude-web.js +586 -0
  56. package/open-sse/executors/cliproxyapi.js +296 -0
  57. package/open-sse/executors/cloudflare-ai.js +46 -0
  58. package/open-sse/executors/codebuddy-cn.js +40 -0
  59. package/open-sse/executors/codex.js +1324 -0
  60. package/open-sse/executors/commandCode.js +558 -0
  61. package/open-sse/executors/commandcode.js +88 -0
  62. package/open-sse/executors/copilot-m365-connection.js +176 -0
  63. package/open-sse/executors/copilot-m365-frames.js +192 -0
  64. package/open-sse/executors/copilot-m365-web.js +299 -0
  65. package/open-sse/executors/copilot-web.js +580 -0
  66. package/open-sse/executors/cursor.js +795 -0
  67. package/open-sse/executors/deepseek-web-with-auto-refresh.js +150 -0
  68. package/open-sse/executors/deepseek-web.js +875 -0
  69. package/open-sse/executors/default.js +430 -0
  70. package/open-sse/executors/devin-cli.js +351 -0
  71. package/open-sse/executors/doubao-web.js +550 -0
  72. package/open-sse/executors/duckduckgo-web.js +629 -0
  73. package/open-sse/executors/forceResponsesUpstream.js +33 -0
  74. package/open-sse/executors/gemini-business.js +320 -0
  75. package/open-sse/executors/gemini-cli.js +89 -0
  76. package/open-sse/executors/gemini-web.js +297 -0
  77. package/open-sse/executors/github.js +379 -0
  78. package/open-sse/executors/gitlab.js +569 -0
  79. package/open-sse/executors/glm.js +361 -0
  80. package/open-sse/executors/grok-cli.js +119 -0
  81. package/open-sse/executors/grok-web.js +345 -0
  82. package/open-sse/executors/huggingchat.js +467 -0
  83. package/open-sse/executors/iflow.js +108 -0
  84. package/open-sse/executors/index.js +78 -0
  85. package/open-sse/executors/inner-ai.js +527 -0
  86. package/open-sse/executors/kimchi.js +123 -0
  87. package/open-sse/executors/kimi-web.js +325 -0
  88. package/open-sse/executors/kimi.js +97 -0
  89. package/open-sse/executors/kiro.js +503 -0
  90. package/open-sse/executors/kiroThinking.js +44 -0
  91. package/open-sse/executors/lmarena.js +395 -0
  92. package/open-sse/executors/mimo-free.js +167 -0
  93. package/open-sse/executors/mimocode.js +392 -0
  94. package/open-sse/executors/ninerouter.js +155 -0
  95. package/open-sse/executors/nlpcloud.js +438 -0
  96. package/open-sse/executors/ollama-local.js +14 -0
  97. package/open-sse/executors/opencode-go.js +41 -0
  98. package/open-sse/executors/opencode.js +32 -0
  99. package/open-sse/executors/perplexity-web.js +581 -0
  100. package/open-sse/executors/poe-web.js +133 -0
  101. package/open-sse/executors/pollinations.js +95 -0
  102. package/open-sse/executors/puter.js +31 -0
  103. package/open-sse/executors/qoder.js +448 -0
  104. package/open-sse/executors/qwen-web.js +372 -0
  105. package/open-sse/executors/qwen.js +129 -0
  106. package/open-sse/executors/t3-chat-web.js +417 -0
  107. package/open-sse/executors/theoldllm.js +290 -0
  108. package/open-sse/executors/trae.js +418 -0
  109. package/open-sse/executors/v0-vercel-web.js +141 -0
  110. package/open-sse/executors/venice-web.js +142 -0
  111. package/open-sse/executors/vertex.js +131 -0
  112. package/open-sse/executors/vertexMedia.js +236 -0
  113. package/open-sse/executors/windsurf.js +560 -0
  114. package/open-sse/executors/xai.js +46 -0
  115. package/open-sse/executors/xiaomi-tokenplan.js +19 -0
  116. package/open-sse/executors/yuanbao-web.js +385 -0
  117. package/open-sse/executors/zed-hosted.js +281 -0
  118. package/open-sse/executors/zenmux-free.js +247 -0
  119. package/open-sse/handlers/audioSpeech.js +759 -0
  120. package/open-sse/handlers/audioTranscription.js +395 -0
  121. package/open-sse/handlers/audioTranslation.js +181 -0
  122. package/open-sse/handlers/chatCore/nonStreamingHandler.js +300 -0
  123. package/open-sse/handlers/chatCore/requestDetail.js +102 -0
  124. package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
  125. package/open-sse/handlers/chatCore/streamingHandler.js +95 -0
  126. package/open-sse/handlers/chatCore.js +404 -0
  127. package/open-sse/handlers/embeddingProviders/_base.js +4 -0
  128. package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
  129. package/open-sse/handlers/embeddingProviders/index.js +23 -0
  130. package/open-sse/handlers/embeddingProviders/openai.js +39 -0
  131. package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
  132. package/open-sse/handlers/embeddingsCore.js +126 -0
  133. package/open-sse/handlers/fetch/index.js +237 -0
  134. package/open-sse/handlers/imageGenerationCore.js +230 -0
  135. package/open-sse/handlers/imageProviders/_base.js +31 -0
  136. package/open-sse/handlers/imageProviders/antigravity.js +73 -0
  137. package/open-sse/handlers/imageProviders/blackForestLabs.js +63 -0
  138. package/open-sse/handlers/imageProviders/cloudflareAi.js +207 -0
  139. package/open-sse/handlers/imageProviders/codex.js +217 -0
  140. package/open-sse/handlers/imageProviders/comfyui.js +25 -0
  141. package/open-sse/handlers/imageProviders/falAi.js +62 -0
  142. package/open-sse/handlers/imageProviders/gemini.js +40 -0
  143. package/open-sse/handlers/imageProviders/huggingface.js +38 -0
  144. package/open-sse/handlers/imageProviders/index.js +44 -0
  145. package/open-sse/handlers/imageProviders/leonardo.js +435 -0
  146. package/open-sse/handlers/imageProviders/nanobanana.js +79 -0
  147. package/open-sse/handlers/imageProviders/openai.js +61 -0
  148. package/open-sse/handlers/imageProviders/runwayml.js +72 -0
  149. package/open-sse/handlers/imageProviders/sdwebui.js +36 -0
  150. package/open-sse/handlers/imageProviders/stabilityAi.js +59 -0
  151. package/open-sse/handlers/imageProviders/weavy.js +222 -0
  152. package/open-sse/handlers/moderations.js +92 -0
  153. package/open-sse/handlers/ocr.js +89 -0
  154. package/open-sse/handlers/rerank.js +223 -0
  155. package/open-sse/handlers/responseSanitizer/reasoning.js +144 -0
  156. package/open-sse/handlers/responsesHandler.js +99 -0
  157. package/open-sse/handlers/search/callers.js +371 -0
  158. package/open-sse/handlers/search/chatSearch.js +409 -0
  159. package/open-sse/handlers/search/index.js +201 -0
  160. package/open-sse/handlers/search/normalizers.js +223 -0
  161. package/open-sse/handlers/search.js +1056 -0
  162. package/open-sse/handlers/sseParser.js +469 -0
  163. package/open-sse/handlers/sttCore.js +193 -0
  164. package/open-sse/handlers/ttsCore.js +74 -0
  165. package/open-sse/handlers/ttsProviders/_base.js +39 -0
  166. package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
  167. package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
  168. package/open-sse/handlers/ttsProviders/gemini.js +117 -0
  169. package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
  170. package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
  171. package/open-sse/handlers/ttsProviders/index.js +50 -0
  172. package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
  173. package/open-sse/handlers/ttsProviders/minimax.js +59 -0
  174. package/open-sse/handlers/ttsProviders/openai.js +30 -0
  175. package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
  176. package/open-sse/handlers/usageExtractor.js +93 -0
  177. package/open-sse/handlers/videoGenerationCore.js +119 -0
  178. package/open-sse/handlers/videoProviders/index.js +22 -0
  179. package/open-sse/handlers/videoProviders/leonardo.js +511 -0
  180. package/open-sse/handlers/videoProviders/weavy.js +198 -0
  181. package/open-sse/handlers/webFetch.js +200 -0
  182. package/open-sse/index.js +79 -0
  183. package/open-sse/mcp-server/audit.js +283 -0
  184. package/open-sse/mcp-server/catalog.js +184 -0
  185. package/open-sse/mcp-server/descriptionCompressor.js +175 -0
  186. package/open-sse/mcp-server/httpAuthContext.js +32 -0
  187. package/open-sse/mcp-server/httpTransport.js +229 -0
  188. package/open-sse/mcp-server/index.js +33 -0
  189. package/open-sse/mcp-server/mcpCallerIdentity.js +21 -0
  190. package/open-sse/mcp-server/runtimeHeartbeat.js +110 -0
  191. package/open-sse/mcp-server/schemas/a2a.js +153 -0
  192. package/open-sse/mcp-server/schemas/audit.js +17 -0
  193. package/open-sse/mcp-server/schemas/index.js +166 -0
  194. package/open-sse/mcp-server/schemas/pickFastestModel.js +90 -0
  195. package/open-sse/mcp-server/schemas/toolDefinition.js +0 -0
  196. package/open-sse/mcp-server/schemas/toolSearch.js +32 -0
  197. package/open-sse/mcp-server/schemas/tools.js +1219 -0
  198. package/open-sse/mcp-server/scopeEnforcement.js +80 -0
  199. package/open-sse/mcp-server/server.js +1168 -0
  200. package/open-sse/mcp-server/toolCardinality.js +78 -0
  201. package/open-sse/mcp-server/toolSearch/catalog.js +66 -0
  202. package/open-sse/mcp-server/toolSearch/handler.js +20 -0
  203. package/open-sse/mcp-server/toolSearch/index.js +10 -0
  204. package/open-sse/mcp-server/toolSearch/register.js +21 -0
  205. package/open-sse/mcp-server/toolSearch/search.js +55 -0
  206. package/open-sse/mcp-server/toolSearch/signature.js +67 -0
  207. package/open-sse/mcp-server/tools/advancedTools.js +893 -0
  208. package/open-sse/mcp-server/tools/agentSkillTools.js +69 -0
  209. package/open-sse/mcp-server/tools/compressionTools.js +309 -0
  210. package/open-sse/mcp-server/tools/gamificationTools.js +134 -0
  211. package/open-sse/mcp-server/tools/githubSkillTools.js +102 -0
  212. package/open-sse/mcp-server/tools/memoryTools.js +118 -0
  213. package/open-sse/mcp-server/tools/notionTools.js +101 -0
  214. package/open-sse/mcp-server/tools/obsidianTools.js +307 -0
  215. package/open-sse/mcp-server/tools/pickFastestModel.js +223 -0
  216. package/open-sse/mcp-server/tools/pluginTools.js +181 -0
  217. package/open-sse/mcp-server/tools/poolTools.js +139 -0
  218. package/open-sse/mcp-server/tools/skillTools.js +113 -0
  219. package/open-sse/package.json +10 -0
  220. package/open-sse/providers/REGISTRY_TEMPLATE.js +98 -0
  221. package/open-sse/providers/capabilities.js +293 -0
  222. package/open-sse/providers/index.js +51 -0
  223. package/open-sse/providers/models/helpers.js +20 -0
  224. package/open-sse/providers/models/namePatterns.js +33 -0
  225. package/open-sse/providers/models/schema.js +40 -0
  226. package/open-sse/providers/pricing.js +308 -0
  227. package/open-sse/providers/registry/alicode-intl.js +30 -0
  228. package/open-sse/providers/registry/alicode.js +31 -0
  229. package/open-sse/providers/registry/anthropic.js +37 -0
  230. package/open-sse/providers/registry/antigravity.js +81 -0
  231. package/open-sse/providers/registry/assemblyai.js +38 -0
  232. package/open-sse/providers/registry/aws-polly.js +45 -0
  233. package/open-sse/providers/registry/azure.js +21 -0
  234. package/open-sse/providers/registry/black-forest-labs.js +32 -0
  235. package/open-sse/providers/registry/blackbox.js +41 -0
  236. package/open-sse/providers/registry/brave-search.js +35 -0
  237. package/open-sse/providers/registry/byteplus.js +35 -0
  238. package/open-sse/providers/registry/cartesia.js +36 -0
  239. package/open-sse/providers/registry/cerebras.js +31 -0
  240. package/open-sse/providers/registry/chutes.js +24 -0
  241. package/open-sse/providers/registry/claude.js +87 -0
  242. package/open-sse/providers/registry/cline.js +51 -0
  243. package/open-sse/providers/registry/clinepass.js +57 -0
  244. package/open-sse/providers/registry/cloudflare-ai.js +55 -0
  245. package/open-sse/providers/registry/codebuddy-cn.js +77 -0
  246. package/open-sse/providers/registry/codex.js +100 -0
  247. package/open-sse/providers/registry/cohere.js +27 -0
  248. package/open-sse/providers/registry/comfyui.js +20 -0
  249. package/open-sse/providers/registry/commandcode.js +43 -0
  250. package/open-sse/providers/registry/coqui.js +30 -0
  251. package/open-sse/providers/registry/cursor.js +58 -0
  252. package/open-sse/providers/registry/deepgram.js +33 -0
  253. package/open-sse/providers/registry/deepseek.js +51 -0
  254. package/open-sse/providers/registry/digitalocean.js +92 -0
  255. package/open-sse/providers/registry/edge-tts.js +24 -0
  256. package/open-sse/providers/registry/elevenlabs.js +35 -0
  257. package/open-sse/providers/registry/exa.js +50 -0
  258. package/open-sse/providers/registry/fal-ai.js +34 -0
  259. package/open-sse/providers/registry/featherless.js +34 -0
  260. package/open-sse/providers/registry/firecrawl.js +34 -0
  261. package/open-sse/providers/registry/fireworks.js +29 -0
  262. package/open-sse/providers/registry/gemini-cli.js +58 -0
  263. package/open-sse/providers/registry/gemini.js +83 -0
  264. package/open-sse/providers/registry/github.js +88 -0
  265. package/open-sse/providers/registry/gitlab.js +32 -0
  266. package/open-sse/providers/registry/glm-cn.js +35 -0
  267. package/open-sse/providers/registry/glm.js +58 -0
  268. package/open-sse/providers/registry/google-pse.js +35 -0
  269. package/open-sse/providers/registry/google-tts.js +24 -0
  270. package/open-sse/providers/registry/grok-cli.js +86 -0
  271. package/open-sse/providers/registry/grok-web.js +39 -0
  272. package/open-sse/providers/registry/groq.js +37 -0
  273. package/open-sse/providers/registry/huggingface.js +34 -0
  274. package/open-sse/providers/registry/hyperbolic.js +35 -0
  275. package/open-sse/providers/registry/ibm-bob.js +27 -0
  276. package/open-sse/providers/registry/iflow.js +52 -0
  277. package/open-sse/providers/registry/index.js +210 -0
  278. package/open-sse/providers/registry/inworld.js +36 -0
  279. package/open-sse/providers/registry/jina-ai.js +42 -0
  280. package/open-sse/providers/registry/jina-reader.js +34 -0
  281. package/open-sse/providers/registry/kilocode.js +51 -0
  282. package/open-sse/providers/registry/kimchi.js +49 -0
  283. package/open-sse/providers/registry/kimi-coding.js +65 -0
  284. package/open-sse/providers/registry/kimi.js +56 -0
  285. package/open-sse/providers/registry/kiro.js +115 -0
  286. package/open-sse/providers/registry/linkup.js +34 -0
  287. package/open-sse/providers/registry/local-device.js +24 -0
  288. package/open-sse/providers/registry/mimo-free.js +24 -0
  289. package/open-sse/providers/registry/minimax-cn.js +76 -0
  290. package/open-sse/providers/registry/minimax.js +83 -0
  291. package/open-sse/providers/registry/mistral.js +34 -0
  292. package/open-sse/providers/registry/mmf.js +19 -0
  293. package/open-sse/providers/registry/nanobanana.js +35 -0
  294. package/open-sse/providers/registry/nebius.js +27 -0
  295. package/open-sse/providers/registry/nvidia.js +43 -0
  296. package/open-sse/providers/registry/ollama-local.js +19 -0
  297. package/open-sse/providers/registry/ollama.js +36 -0
  298. package/open-sse/providers/registry/openai.js +84 -0
  299. package/open-sse/providers/registry/opencode-go.js +41 -0
  300. package/open-sse/providers/registry/opencode.js +25 -0
  301. package/open-sse/providers/registry/openrouter.js +60 -0
  302. package/open-sse/providers/registry/perplexity-agent.js +49 -0
  303. package/open-sse/providers/registry/perplexity-web.js +33 -0
  304. package/open-sse/providers/registry/perplexity.js +37 -0
  305. package/open-sse/providers/registry/playht.js +36 -0
  306. package/open-sse/providers/registry/qoder.js +54 -0
  307. package/open-sse/providers/registry/qwen.js +33 -0
  308. package/open-sse/providers/registry/recraft.js +24 -0
  309. package/open-sse/providers/registry/runwayml.js +30 -0
  310. package/open-sse/providers/registry/sdwebui.js +20 -0
  311. package/open-sse/providers/registry/searchapi.js +35 -0
  312. package/open-sse/providers/registry/searxng.js +35 -0
  313. package/open-sse/providers/registry/serper.js +35 -0
  314. package/open-sse/providers/registry/siliconflow.js +39 -0
  315. package/open-sse/providers/registry/stability-ai.js +31 -0
  316. package/open-sse/providers/registry/tavily.js +50 -0
  317. package/open-sse/providers/registry/together.js +31 -0
  318. package/open-sse/providers/registry/topaz.js +19 -0
  319. package/open-sse/providers/registry/tortoise.js +30 -0
  320. package/open-sse/providers/registry/venice.js +56 -0
  321. package/open-sse/providers/registry/vercel-ai-gateway.js +41 -0
  322. package/open-sse/providers/registry/vertex-partner.js +29 -0
  323. package/open-sse/providers/registry/vertex.js +32 -0
  324. package/open-sse/providers/registry/volcengine-ark.js +35 -0
  325. package/open-sse/providers/registry/voyage-ai.js +30 -0
  326. package/open-sse/providers/registry/xai.js +45 -0
  327. package/open-sse/providers/registry/xiaomi-mimo.js +46 -0
  328. package/open-sse/providers/registry/xiaomi-tokenplan.js +63 -0
  329. package/open-sse/providers/registry/youcom.js +35 -0
  330. package/open-sse/providers/schema.js +76 -0
  331. package/open-sse/providers/shared.js +74 -0
  332. package/open-sse/providers/thinkingLevels.js +48 -0
  333. package/open-sse/rtk/applyFilter.js +15 -0
  334. package/open-sse/rtk/autodetect.js +111 -0
  335. package/open-sse/rtk/caveman.js +100 -0
  336. package/open-sse/rtk/cavemanPrompts.js +78 -0
  337. package/open-sse/rtk/constants.js +55 -0
  338. package/open-sse/rtk/filters/buildOutput.js +127 -0
  339. package/open-sse/rtk/filters/dedupLog.js +44 -0
  340. package/open-sse/rtk/filters/find.js +50 -0
  341. package/open-sse/rtk/filters/gitDiff.js +92 -0
  342. package/open-sse/rtk/filters/gitLog.js +99 -0
  343. package/open-sse/rtk/filters/gitStatus.js +117 -0
  344. package/open-sse/rtk/filters/grep.js +48 -0
  345. package/open-sse/rtk/filters/ls.js +79 -0
  346. package/open-sse/rtk/filters/readNumbered.js +27 -0
  347. package/open-sse/rtk/filters/searchList.js +52 -0
  348. package/open-sse/rtk/filters/smartTruncate.js +15 -0
  349. package/open-sse/rtk/filters/tree.js +32 -0
  350. package/open-sse/rtk/headroom.js +215 -0
  351. package/open-sse/rtk/index.js +155 -0
  352. package/open-sse/rtk/paleo.js +47 -0
  353. package/open-sse/rtk/ponytail.js +9 -0
  354. package/open-sse/rtk/ponytailPrompt.js +52 -0
  355. package/open-sse/rtk/ponytailPrompts.js +76 -0
  356. package/open-sse/rtk/pxpipe.js +104 -0
  357. package/open-sse/rtk/registry.js +38 -0
  358. package/open-sse/rtk/systemInject.js +98 -0
  359. package/open-sse/services/accountFallback.js +323 -0
  360. package/open-sse/services/antigravityHeaders.js +91 -0
  361. package/open-sse/services/antigravityVersion.js +154 -0
  362. package/open-sse/services/clinepassModels.js +63 -0
  363. package/open-sse/services/codexQuotaFetcher.js +312 -0
  364. package/open-sse/services/codexVerbosity.js +30 -0
  365. package/open-sse/services/combo/context.js +12 -0
  366. package/open-sse/services/combo/fingerprintExpansion.js +51 -0
  367. package/open-sse/services/combo/headroomRanking.js +28 -0
  368. package/open-sse/services/combo/quotaExhaustionCutoff.js +87 -0
  369. package/open-sse/services/combo/quotaScoring.js +226 -0
  370. package/open-sse/services/combo/quotaShareConcurrency.js +25 -0
  371. package/open-sse/services/combo/quotaShareInflight.js +50 -0
  372. package/open-sse/services/combo/quotaShareStrategy.js +130 -0
  373. package/open-sse/services/combo/resolveAutoStrategy.js +231 -0
  374. package/open-sse/services/combo/responseValidation.js +140 -0
  375. package/open-sse/services/combo/runtimeUnits.js +195 -0
  376. package/open-sse/services/combo/sessionStickiness.js +128 -0
  377. package/open-sse/services/combo/shadowRouting.js +131 -0
  378. package/open-sse/services/combo/targetExhaustion.js +75 -0
  379. package/open-sse/services/combo/targetSorters.js +126 -0
  380. package/open-sse/services/combo/targetTimeoutRunner.js +65 -0
  381. package/open-sse/services/combo/validateQuality.js +271 -0
  382. package/open-sse/services/combo.js +571 -0
  383. package/open-sse/services/comboAgentMiddleware.js +91 -0
  384. package/open-sse/services/comboConfig.js +153 -0
  385. package/open-sse/services/comboManifestMetrics.js +10 -0
  386. package/open-sse/services/comboMetrics.js +278 -0
  387. package/open-sse/services/compact.js +71 -0
  388. package/open-sse/services/copilotModels.js +155 -0
  389. package/open-sse/services/emergencyFallback.js +109 -0
  390. package/open-sse/services/errorClassifier.js +190 -0
  391. package/open-sse/services/evalRouting.js +201 -0
  392. package/open-sse/services/freeWebSearch.js +4 -0
  393. package/open-sse/services/fusion.js +223 -0
  394. package/open-sse/services/intentClassifier.js +631 -0
  395. package/open-sse/services/kimchiModels.js +176 -0
  396. package/open-sse/services/kiroModels.js +332 -0
  397. package/open-sse/services/model.js +136 -0
  398. package/open-sse/services/modelCapabilities.js +5 -0
  399. package/open-sse/services/modelDeprecation.js +101 -0
  400. package/open-sse/services/modelFamilyFallback.js +166 -0
  401. package/open-sse/services/oauthCredentialManager.js +156 -0
  402. package/open-sse/services/pipeline.js +107 -0
  403. package/open-sse/services/projectId.js +306 -0
  404. package/open-sse/services/provider.js +169 -0
  405. package/open-sse/services/providerCooldownTracker.js +94 -0
  406. package/open-sse/services/providerDefaultRateLimit.js +11 -0
  407. package/open-sse/services/proxyAutoSelector.js +24 -0
  408. package/open-sse/services/qoderModels.js +214 -0
  409. package/open-sse/services/quotaFetchThrottle.js +103 -0
  410. package/open-sse/services/quotaMonitor.js +310 -0
  411. package/open-sse/services/quotaPreflight.js +266 -0
  412. package/open-sse/services/rateLimitManager/headers.js +56 -0
  413. package/open-sse/services/rateLimitManager.js +616 -0
  414. package/open-sse/services/rateLimitSemaphore.js +112 -0
  415. package/open-sse/services/responsesInputSanitizer.js +99 -0
  416. package/open-sse/services/slidingWindowLimiter.js +44 -0
  417. package/open-sse/services/streamRecovery.js +355 -0
  418. package/open-sse/services/taskAwareRouter.js +227 -0
  419. package/open-sse/services/taskAwareRouting.js +332 -0
  420. package/open-sse/services/thinkingBudget.js +247 -0
  421. package/open-sse/services/tokenRefresh/dedup.js +31 -0
  422. package/open-sse/services/tokenRefresh/providers.js +624 -0
  423. package/open-sse/services/tokenRefresh.js +254 -0
  424. package/open-sse/services/usage/claude.js +147 -0
  425. package/open-sse/services/usage/codebuddy-cn.js +138 -0
  426. package/open-sse/services/usage/codex.js +267 -0
  427. package/open-sse/services/usage/github.js +100 -0
  428. package/open-sse/services/usage/google.js +241 -0
  429. package/open-sse/services/usage/grok-cli.js +274 -0
  430. package/open-sse/services/usage/kiro.js +188 -0
  431. package/open-sse/services/usage/minimax.js +234 -0
  432. package/open-sse/services/usage/misc.js +288 -0
  433. package/open-sse/services/usage/shared.js +70 -0
  434. package/open-sse/services/usage.js +62 -0
  435. package/open-sse/services/usageDb.js +2 -0
  436. package/open-sse/services/webSearchRouting.js +25 -0
  437. package/open-sse/services/wildcardRouter.js +59 -0
  438. package/open-sse/shared/clineAuth.js +37 -0
  439. package/open-sse/shared/machineId.js +19 -0
  440. package/open-sse/shared/qoder/constants.js +64 -0
  441. package/open-sse/shared/qoder/cosy.js +175 -0
  442. package/open-sse/shared/qoder/encoding.js +55 -0
  443. package/open-sse/transformer/responsesTransformer.js +439 -0
  444. package/open-sse/transformer/streamToJsonConverter.js +103 -0
  445. package/open-sse/translator/concerns/chunk.js +11 -0
  446. package/open-sse/translator/concerns/finishReason.js +63 -0
  447. package/open-sse/translator/concerns/image.js +124 -0
  448. package/open-sse/translator/concerns/json.js +5 -0
  449. package/open-sse/translator/concerns/message.js +7 -0
  450. package/open-sse/translator/concerns/modality.js +155 -0
  451. package/open-sse/translator/concerns/paramSupport.js +75 -0
  452. package/open-sse/translator/concerns/prefetch.js +96 -0
  453. package/open-sse/translator/concerns/reasoning.js +24 -0
  454. package/open-sse/translator/concerns/thinking.js +53 -0
  455. package/open-sse/translator/concerns/thinkingUnified.js +329 -0
  456. package/open-sse/translator/concerns/toolCall.js +153 -0
  457. package/open-sse/translator/concerns/usage.js +69 -0
  458. package/open-sse/translator/formats/claude.js +368 -0
  459. package/open-sse/translator/formats/gemini.js +380 -0
  460. package/open-sse/translator/formats/maxTokens.js +34 -0
  461. package/open-sse/translator/formats/openai.js +134 -0
  462. package/open-sse/translator/formats/responsesApi.js +141 -0
  463. package/open-sse/translator/formats.js +36 -0
  464. package/open-sse/translator/helpers/claudeHelper.js +216 -0
  465. package/open-sse/translator/helpers/geminiHelper.js +372 -0
  466. package/open-sse/translator/helpers/imageHelper.js +34 -0
  467. package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
  468. package/open-sse/translator/helpers/openaiHelper.js +130 -0
  469. package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
  470. package/open-sse/translator/helpers/toolCallHelper.js +148 -0
  471. package/open-sse/translator/index.js +252 -0
  472. package/open-sse/translator/request/antigravity-to-openai.js +242 -0
  473. package/open-sse/translator/request/claude-to-kiro.js +478 -0
  474. package/open-sse/translator/request/claude-to-openai.js +263 -0
  475. package/open-sse/translator/request/gemini-to-openai.js +152 -0
  476. package/open-sse/translator/request/openai-responses.js +385 -0
  477. package/open-sse/translator/request/openai-to-claude.js +383 -0
  478. package/open-sse/translator/request/openai-to-commandcode.js +176 -0
  479. package/open-sse/translator/request/openai-to-cursor.js +185 -0
  480. package/open-sse/translator/request/openai-to-gemini.js +442 -0
  481. package/open-sse/translator/request/openai-to-kiro.js +607 -0
  482. package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
  483. package/open-sse/translator/request/openai-to-ollama.js +195 -0
  484. package/open-sse/translator/request/openai-to-vertex.js +42 -0
  485. package/open-sse/translator/response/claude-to-openai.js +195 -0
  486. package/open-sse/translator/response/commandcode-to-openai.js +188 -0
  487. package/open-sse/translator/response/cursor-to-openai.js +30 -0
  488. package/open-sse/translator/response/gemini-to-openai.js +147 -0
  489. package/open-sse/translator/response/kiro-to-claude.js +261 -0
  490. package/open-sse/translator/response/kiro-to-openai.js +160 -0
  491. package/open-sse/translator/response/ollama-to-openai.js +134 -0
  492. package/open-sse/translator/response/openai-responses.js +535 -0
  493. package/open-sse/translator/response/openai-to-antigravity.js +123 -0
  494. package/open-sse/translator/response/openai-to-claude.js +265 -0
  495. package/open-sse/translator/schema/blocks.js +43 -0
  496. package/open-sse/translator/schema/defaults.js +7 -0
  497. package/open-sse/translator/schema/finishReasons.js +27 -0
  498. package/open-sse/translator/schema/index.js +8 -0
  499. package/open-sse/translator/schema/roles.js +16 -0
  500. package/open-sse/utils/agentGoalPolicy.js +85 -0
  501. package/open-sse/utils/aiSdkCompat.js +75 -0
  502. package/open-sse/utils/bypassHandler.js +298 -0
  503. package/open-sse/utils/cacheControlPolicy.js +189 -0
  504. package/open-sse/utils/claudeCloaking.js +155 -0
  505. package/open-sse/utils/claudeHeaderCache.js +70 -0
  506. package/open-sse/utils/claudeSignature.js +41 -0
  507. package/open-sse/utils/clientDetector.js +63 -0
  508. package/open-sse/utils/codebuddySanitizer.js +96 -0
  509. package/open-sse/utils/composerToolCalls.js +158 -0
  510. package/open-sse/utils/cors.js +13 -0
  511. package/open-sse/utils/cursorChecksum.js +149 -0
  512. package/open-sse/utils/cursorImages.js +224 -0
  513. package/open-sse/utils/cursorProtobuf.js +904 -0
  514. package/open-sse/utils/cursorVersionDetector.js +55 -0
  515. package/open-sse/utils/debugLog.js +14 -0
  516. package/open-sse/utils/diagnostics.js +143 -0
  517. package/open-sse/utils/error.js +164 -0
  518. package/open-sse/utils/estimateSize.js +33 -0
  519. package/open-sse/utils/finishReason.js +32 -0
  520. package/open-sse/utils/flattenToolHistory.js +68 -0
  521. package/open-sse/utils/headers.js +36 -0
  522. package/open-sse/utils/heapPressure.js +35 -0
  523. package/open-sse/utils/jsonToSse.js +125 -0
  524. package/open-sse/utils/keepaliveThreshold.js +40 -0
  525. package/open-sse/utils/logger.js +104 -0
  526. package/open-sse/utils/noThinkingAlias.js +74 -0
  527. package/open-sse/utils/number.js +7 -0
  528. package/open-sse/utils/ollamaTransform.js +85 -0
  529. package/open-sse/utils/opencodeHeaders.js +47 -0
  530. package/open-sse/utils/progressTracker.js +81 -0
  531. package/open-sse/utils/providerRequestLogging.js +219 -0
  532. package/open-sse/utils/proxyFetch.js +369 -0
  533. package/open-sse/utils/reasoningContentInjector.js +79 -0
  534. package/open-sse/utils/reasoningFields.js +70 -0
  535. package/open-sse/utils/requestLogger.js +260 -0
  536. package/open-sse/utils/responsesInputNormalization.js +92 -0
  537. package/open-sse/utils/responsesStatePolicy.js +52 -0
  538. package/open-sse/utils/responsesStreamHelpers.js +49 -0
  539. package/open-sse/utils/sessionManager.js +240 -0
  540. package/open-sse/utils/sleep.js +6 -0
  541. package/open-sse/utils/sse.js +14 -0
  542. package/open-sse/utils/sseConstants.js +23 -0
  543. package/open-sse/utils/sseHeartbeat.js +104 -0
  544. package/open-sse/utils/stream.js +517 -0
  545. package/open-sse/utils/streamFailureFinalization.js +134 -0
  546. package/open-sse/utils/streamHandler.js +250 -0
  547. package/open-sse/utils/streamHelpers.js +137 -0
  548. package/open-sse/utils/textualToolCall.js +104 -0
  549. package/open-sse/utils/thinkCloseMarker.js +72 -0
  550. package/open-sse/utils/thinkTagParser.js +146 -0
  551. package/open-sse/utils/toolCallArguments.js +12 -0
  552. package/open-sse/utils/toolDeduper.js +49 -0
  553. package/open-sse/utils/toolSources.js +31 -0
  554. package/open-sse/utils/upstreamResponseHeaders.js +25 -0
  555. package/open-sse/utils/urlSanitize.js +15 -0
  556. package/open-sse/utils/usageTracking.js +347 -0
  557. package/package.json +1 -1
@@ -0,0 +1,250 @@
1
+ // Stream handler with disconnect detection - shared for all providers
2
+ import { STREAM_STALL_TIMEOUT_MS } from "../config/runtimeConfig.js";
3
+ import { dbg, isDebugEnabled } from "./debugLog.js";
4
+
5
+ // Get HH:MM:SS timestamp
6
+ function getTimeString() {
7
+ return new Date().toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
8
+ }
9
+
10
+ /**
11
+ * Create stream controller with abort and disconnect detection
12
+ * @param {object} options
13
+ * @param {function} options.onDisconnect - Callback when client disconnects
14
+ * @param {object} options.log - Logger instance
15
+ * @param {string} options.provider - Provider name
16
+ * @param {string} options.model - Model name
17
+ */
18
+ export function createStreamController({ onDisconnect, onError, log, provider, model } = {}) {
19
+ const abortController = new AbortController();
20
+ const startTime = Date.now();
21
+ let disconnected = false;
22
+ let abortTimeout = null;
23
+
24
+ const logStream = (status) => {
25
+ const duration = Date.now() - startTime;
26
+ const p = provider?.toUpperCase() || "UNKNOWN";
27
+ console.log(`[${getTimeString()}] 🌊 [STREAM] ${p} | ${model || "unknown"} | ${duration}ms | ${status}`);
28
+ };
29
+
30
+ return {
31
+ signal: abortController.signal,
32
+ startTime,
33
+
34
+ isConnected: () => !disconnected,
35
+
36
+ // Call when client disconnects
37
+ handleDisconnect: (reason = "client_closed") => {
38
+ if (disconnected) return;
39
+ disconnected = true;
40
+
41
+ logStream(`disconnect: ${reason}`);
42
+ dbg("CTRL", `${provider}/${model} | disconnect=${reason} | dur=${Date.now() - startTime}ms`);
43
+
44
+ // Delay abort to allow cleanup
45
+ abortTimeout = setTimeout(() => {
46
+ abortController.abort();
47
+ }, 500);
48
+
49
+ onDisconnect?.({ reason, duration: Date.now() - startTime });
50
+ },
51
+
52
+ // Call when stream completes normally
53
+ handleComplete: () => {
54
+ if (disconnected) return;
55
+ disconnected = true;
56
+
57
+ logStream("complete");
58
+
59
+ if (abortTimeout) {
60
+ clearTimeout(abortTimeout);
61
+ abortTimeout = null;
62
+ }
63
+ },
64
+
65
+ // Call on error
66
+ handleError: (error) => {
67
+ if (disconnected) return;
68
+ disconnected = true;
69
+
70
+ if (abortTimeout) {
71
+ clearTimeout(abortTimeout);
72
+ abortTimeout = null;
73
+ }
74
+
75
+ if (error.name === "AbortError") {
76
+ logStream("aborted");
77
+ return;
78
+ }
79
+
80
+ logStream(`error: ${error.message}`);
81
+ onError?.(error);
82
+ },
83
+
84
+ abort: () => abortController.abort()
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Create transform stream with disconnect detection
90
+ * Wraps existing transform stream and adds abort capability.
91
+ *
92
+ * Stall detection lives in pipeWithDisconnect (tied to upstream byte
93
+ * activity), not here — output of the transform stream may be silent
94
+ * for long periods while raw bytes still flow (e.g. Kiro EventStream
95
+ * binary frames buffering, Claude reasoning streams).
96
+ */
97
+ export function createDisconnectAwareStream(transformStream, streamController, onAbortTerminal = null) {
98
+ const reader = transformStream.readable.getReader();
99
+ const writer = transformStream.writable.getWriter();
100
+ let terminalEmitted = false;
101
+
102
+ // Emit a synthesized terminal payload (e.g. Responses response.failed + [DONE]) once
103
+ const emitTerminal = (controller) => {
104
+ if (terminalEmitted || !onAbortTerminal) return;
105
+ terminalEmitted = true;
106
+ try {
107
+ const bytes = onAbortTerminal();
108
+ if (bytes) controller.enqueue(bytes);
109
+ } catch { /* best-effort terminal */ }
110
+ };
111
+
112
+ return new ReadableStream({
113
+ async pull(controller) {
114
+ if (!streamController.isConnected()) {
115
+ emitTerminal(controller);
116
+ controller.close();
117
+ return;
118
+ }
119
+
120
+ try {
121
+ const { done, value } = await reader.read();
122
+
123
+ if (done) {
124
+ streamController.handleComplete();
125
+ controller.close();
126
+ return;
127
+ }
128
+ controller.enqueue(value);
129
+ } catch (error) {
130
+ const wasConnected = streamController.isConnected();
131
+ streamController.handleError(error);
132
+ reader.cancel().catch(() => {});
133
+ writer.abort().catch(() => {});
134
+
135
+ // Treat network resets / socket hang up / abort as graceful close
136
+ const msg = error?.message || "";
137
+ const code = error?.code || error?.cause?.code || "";
138
+ const isNetworkClose =
139
+ error.name === "AbortError" ||
140
+ msg.includes("aborted") ||
141
+ msg.includes("socket hang up") ||
142
+ msg.includes("ECONNRESET") ||
143
+ msg.includes("ETIMEDOUT") ||
144
+ msg.includes("EPIPE") ||
145
+ code === "ECONNRESET" ||
146
+ code === "ETIMEDOUT" ||
147
+ code === "EPIPE" ||
148
+ code === "UND_ERR_SOCKET";
149
+
150
+ // Graceful close on network/abort, or when a structured terminal is available
151
+ // (Responses passthrough prefers response.failed + [DONE] over a raw transport error)
152
+ try {
153
+ if (!wasConnected || isNetworkClose || onAbortTerminal) {
154
+ emitTerminal(controller);
155
+ controller.close();
156
+ } else {
157
+ controller.error(error);
158
+ }
159
+ } catch (e) { /* already closed or cancelled */ }
160
+ }
161
+ },
162
+
163
+ cancel(reason) {
164
+ streamController.handleDisconnect(reason || "cancelled");
165
+ reader.cancel();
166
+ writer.abort();
167
+ }
168
+ });
169
+ }
170
+
171
+ /**
172
+ * Pipe provider response through transform with disconnect detection.
173
+ *
174
+ * Stall watchdog tracks raw upstream byte activity, not transform output.
175
+ * Reasoning models (Claude thinking via Kiro, etc.) can produce zero SSE
176
+ * output for long stretches while partial EventStream frames keep arriving.
177
+ * Measuring stall on the transform output caused false stalls and the
178
+ * "failed to pipe response" error in Next.
179
+ *
180
+ * Any upstream chunk resets the timer. If no bytes arrive for
181
+ * STREAM_STALL_TIMEOUT_MS, abort the underlying fetch via the controller.
182
+ *
183
+ * @param {Response} providerResponse - Response from provider
184
+ * @param {TransformStream} transformStream - Transform stream for SSE
185
+ * @param {object} streamController - Stream controller from createStreamController
186
+ */
187
+ export function pipeWithDisconnect(providerResponse, transformStream, streamController, onAbortTerminal = null) {
188
+ let stallTimer = null;
189
+ let chunkCount = 0;
190
+ let totalBytes = 0;
191
+ let lastChunkAt = Date.now();
192
+ const t0 = Date.now();
193
+ const tag = "STREAM";
194
+ const clearStall = () => {
195
+ if (stallTimer) { clearTimeout(stallTimer); stallTimer = null; }
196
+ };
197
+ const armStall = () => {
198
+ clearStall();
199
+ stallTimer = setTimeout(() => {
200
+ stallTimer = null;
201
+ dbg(tag, `STALL TIMEOUT ${STREAM_STALL_TIMEOUT_MS}ms | chunks=${chunkCount} | bytes=${totalBytes} | sinceLast=${Date.now() - lastChunkAt}ms`);
202
+ streamController.handleError?.(new Error("stream stall timeout"));
203
+ streamController.abort?.();
204
+ }, STREAM_STALL_TIMEOUT_MS);
205
+ };
206
+
207
+ // Wrap controller so every termination path clears the stall timer.
208
+ // Without this, abort/cancel/downstream-error paths leave the timer armed
209
+ // and a stale abort could fire after the request has already ended.
210
+ const wrappedController = {
211
+ signal: streamController.signal,
212
+ startTime: streamController.startTime,
213
+ isConnected: () => streamController.isConnected(),
214
+ handleComplete: () => { dbg(tag, `complete | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); streamController.handleComplete(); },
215
+ handleError: (e) => { dbg(tag, `error: ${e?.message} | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); streamController.handleError(e); },
216
+ handleDisconnect: (r) => { dbg(tag, `disconnect: ${r} | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); streamController.handleDisconnect(r); },
217
+ abort: () => { clearStall(); streamController.abort(); }
218
+ };
219
+
220
+ armStall();
221
+ dbg(tag, `pipe start | stallTimeout=${STREAM_STALL_TIMEOUT_MS}ms`);
222
+
223
+ const upstreamTap = new TransformStream({
224
+ transform(chunk, controller) {
225
+ chunkCount++;
226
+ const sz = chunk?.byteLength || chunk?.length || 0;
227
+ totalBytes += sz;
228
+ const now = Date.now();
229
+ const gap = now - lastChunkAt;
230
+ lastChunkAt = now;
231
+ if (isDebugEnabled && (chunkCount <= 5 || chunkCount % 20 === 0 || gap > 5000)) {
232
+ dbg(tag, `chunk #${chunkCount} | size=${sz}B | gap=${gap}ms | total=${totalBytes}B`);
233
+ }
234
+ armStall();
235
+ controller.enqueue(chunk);
236
+ },
237
+ flush() { dbg(tag, `upstream EOF | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); }
238
+ });
239
+
240
+ const transformedBody = providerResponse.body
241
+ .pipeThrough(upstreamTap)
242
+ .pipeThrough(transformStream);
243
+
244
+ return createDisconnectAwareStream(
245
+ { readable: transformedBody, writable: { getWriter: () => ({ abort: () => Promise.resolve() }) } },
246
+ wrappedController,
247
+ onAbortTerminal
248
+ );
249
+ }
250
+
@@ -0,0 +1,137 @@
1
+ import { FORMATS } from "../translator/formats.js";
2
+
3
+ // Parse SSE data line
4
+ export function parseSSELine(line, format = null) {
5
+ if (!line) return null;
6
+
7
+ // NDJSON format (Ollama): raw JSON lines without "data:" prefix
8
+ if (format === FORMATS.OLLAMA) {
9
+ const trimmed = line.trim();
10
+ if (trimmed.startsWith("{")) {
11
+ try {
12
+ return JSON.parse(trimmed);
13
+ } catch (error) {
14
+ return null;
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+
20
+ // Handle raw JSON errors disguised as SSE
21
+ if (line.charCodeAt(0) === 123) { // '{' = 123
22
+ try {
23
+ const parsedJson = JSON.parse(line.trim());
24
+ // Check if it looks like an error payload
25
+ if (parsedJson && (parsedJson.error || parsedJson.code !== undefined || parsedJson.msg)) {
26
+ return { _isRawError: true, raw: parsedJson };
27
+ }
28
+ } catch (e) {
29
+ // Ignore parse errors, continue to normal SSE check
30
+ }
31
+ }
32
+
33
+ // Standard SSE format: "data: {...}"
34
+ if (line.charCodeAt(0) !== 100) return null; // 'd' = 100
35
+
36
+ const data = line.slice(5).trim();
37
+ if (data === "[DONE]") return { done: true };
38
+
39
+ try {
40
+ return JSON.parse(data);
41
+ } catch (error) {
42
+ if (data.length > 0 && data.length < 1000) {
43
+ console.log(`[WARN] Failed to parse SSE line (${data.length} chars): ${data.substring(0, 100)}...`);
44
+ }
45
+ return null;
46
+ }
47
+ }
48
+
49
+ // Check if chunk has valuable content (not empty)
50
+ export function hasValuableContent(chunk, format) {
51
+ // OpenAI format
52
+ if (format === FORMATS.OPENAI && chunk.choices?.[0]?.delta) {
53
+ const delta = chunk.choices[0].delta;
54
+ return !!(delta.content && delta.content !== "" ||
55
+ delta.reasoning_content && delta.reasoning_content !== "" ||
56
+ delta.tool_calls && delta.tool_calls.length > 0 ||
57
+ delta.refusal && delta.refusal !== "" ||
58
+ chunk.choices[0].finish_reason ||
59
+ delta.role ||
60
+ chunk.usage);
61
+ }
62
+
63
+ // Claude format
64
+ if (format === FORMATS.CLAUDE) {
65
+ const isContentBlockDelta = chunk.type === "content_block_delta";
66
+ const hasText = chunk.delta?.text && chunk.delta.text !== "";
67
+ const hasThinking = chunk.delta?.thinking && chunk.delta.thinking !== "";
68
+ const hasInputJson = chunk.delta?.partial_json && chunk.delta.partial_json !== "";
69
+
70
+ if (isContentBlockDelta && !hasText && !hasThinking && !hasInputJson) {
71
+ return false;
72
+ }
73
+ return true;
74
+ }
75
+
76
+ return true; // Other formats: keep all chunks
77
+ }
78
+
79
+ // Fix invalid id (generic or too short)
80
+ export function fixInvalidId(parsed) {
81
+ if (parsed.id && (parsed.id === "chat" || parsed.id === "completion" || parsed.id.length < 8)) {
82
+ const fallbackId = parsed.extend_fields?.requestId ||
83
+ parsed.extend_fields?.traceId ||
84
+ Date.now().toString(36);
85
+ parsed.id = `chatcmpl-${fallbackId}`;
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+
91
+ function cleanUsagePayload(payload) {
92
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
93
+ return payload;
94
+ }
95
+
96
+ let cleaned = payload;
97
+
98
+ if ("usage" in cleaned) {
99
+ if (cleaned.usage === null) {
100
+ const { usage, ...payloadWithoutUsage } = cleaned;
101
+ cleaned = payloadWithoutUsage;
102
+ } else if (typeof cleaned.usage === "object" && cleaned.usage.perf_metrics === null) {
103
+ const { perf_metrics, ...usageWithoutPerf } = cleaned.usage;
104
+ cleaned = { ...cleaned, usage: usageWithoutPerf };
105
+ }
106
+ }
107
+
108
+ if (cleaned.response && typeof cleaned.response === "object" && !Array.isArray(cleaned.response)) {
109
+ const cleanedResponse = cleanUsagePayload(cleaned.response);
110
+ if (cleanedResponse !== cleaned.response) {
111
+ cleaned = { ...cleaned, response: cleanedResponse };
112
+ }
113
+ }
114
+
115
+ return cleaned;
116
+ }
117
+
118
+ // Format output as SSE
119
+ export function formatSSE(data, sourceFormat) {
120
+ if (data === null || data === undefined) return "data: null\n\n";
121
+ if (data && data.done) return "data: [DONE]\n\n";
122
+
123
+ // OpenAI Responses API format
124
+ if (data && data.event && data.data) {
125
+ const cleanedEventData = cleanUsagePayload(data.data);
126
+ return `event: ${data.event}\ndata: ${JSON.stringify(cleanedEventData)}\n\n`;
127
+ }
128
+
129
+ data = cleanUsagePayload(data);
130
+
131
+ // Claude format
132
+ if (sourceFormat === FORMATS.CLAUDE && data && data.type) {
133
+ return `event: ${data.type}\ndata: ${JSON.stringify(data)}\n\n`;
134
+ }
135
+
136
+ return `data: ${JSON.stringify(data)}\n\n`;
137
+ }
@@ -0,0 +1,104 @@
1
+ function stripZeroWidth(value) {
2
+ if (typeof value === "string") {
3
+ return value.replace(/[\u200B-\u200D\uFEFF]/g, "");
4
+ }
5
+ if (Array.isArray(value)) {
6
+ return value.map((item) => stripZeroWidth(item));
7
+ }
8
+ if (value && typeof value === "object") {
9
+ return Object.fromEntries(
10
+ Object.entries(value).map(([key, item]) => [
11
+ key,
12
+ stripZeroWidth(item)
13
+ ])
14
+ );
15
+ }
16
+ return value;
17
+ }
18
+ function isValidToolCallHeaderPrefix(candidate) {
19
+ if (!candidate.startsWith("[Tool call:")) return false;
20
+ const bracketIndex = candidate.indexOf("]");
21
+ if (bracketIndex === -1) {
22
+ const namePart2 = candidate.slice("[Tool call:".length);
23
+ if (namePart2.includes("\n") || namePart2.includes("[")) return false;
24
+ return true;
25
+ }
26
+ const namePart = candidate.slice("[Tool call:".length, bracketIndex);
27
+ if (namePart.includes("\n") || namePart.trim().length === 0) return false;
28
+ const afterBracket = candidate.slice(bracketIndex + 1);
29
+ const leadingWhitespaceMatch = afterBracket.match(/^[\s\r\n]*/);
30
+ const leadingWhitespace = leadingWhitespaceMatch ? leadingWhitespaceMatch[0] : "";
31
+ const textAfterWhitespace = afterBracket.slice(leadingWhitespace.length);
32
+ if (textAfterWhitespace.length === 0) {
33
+ return true;
34
+ }
35
+ if (!leadingWhitespace.includes("\n")) {
36
+ return false;
37
+ }
38
+ const expectedText = "Arguments:";
39
+ if (expectedText.startsWith(textAfterWhitespace)) {
40
+ return true;
41
+ }
42
+ if (textAfterWhitespace.startsWith(expectedText)) {
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ function parseTextualToolCallCandidate(text) {
48
+ if (typeof text !== "string") return null;
49
+ const normalized = text.replace(/[\u200B-\u200D\uFEFF]/g, "");
50
+ const toolCallIndex = normalized.lastIndexOf("[Tool call:");
51
+ if (toolCallIndex < 0) {
52
+ const lastParen = normalized.lastIndexOf("(");
53
+ if (lastParen !== -1 && "(empty)[Tool call:".startsWith(normalized.slice(lastParen))) {
54
+ return { kind: "partial" };
55
+ }
56
+ const lastBracket = normalized.lastIndexOf("[");
57
+ if (lastBracket !== -1 && "[Tool call:".startsWith(normalized.slice(lastBracket))) {
58
+ return { kind: "partial" };
59
+ }
60
+ return null;
61
+ }
62
+ const candidate = normalized.slice(toolCallIndex);
63
+ if (!isValidToolCallHeaderPrefix(candidate)) {
64
+ return null;
65
+ }
66
+ const headerMatch = candidate.match(/^\[Tool call:\s*([^\]\n]+)\]\s*\nArguments:\s*/);
67
+ if (!headerMatch) return { kind: "partial" };
68
+ const name = headerMatch[1]?.trim();
69
+ const rawArgs = candidate.slice(headerMatch[0].length).trim();
70
+ if (!name || !rawArgs) return { kind: "partial" };
71
+ const decoders = [
72
+ (value) => value,
73
+ (value) => {
74
+ if (value.startsWith('"') && value.endsWith('"')) {
75
+ const decoded = JSON.parse(value);
76
+ return typeof decoded === "string" ? decoded : value;
77
+ }
78
+ return value;
79
+ }
80
+ ];
81
+ for (const decode of decoders) {
82
+ try {
83
+ const decoded = decode(rawArgs);
84
+ const parsed = JSON.parse(decoded);
85
+ return { kind: "complete", name, args: stripZeroWidth(parsed) };
86
+ } catch {
87
+ }
88
+ }
89
+ return { kind: "partial" };
90
+ }
91
+ function containsTextualToolCallMarker(text) {
92
+ if (typeof text !== "string") return false;
93
+ const normalized = text.replace(/[\u200B-\u200D\uFEFF]/g, "");
94
+ if (!normalized.includes("[Tool call:")) return false;
95
+ if (normalized.includes("Arguments:")) return true;
96
+ const trimmed = normalized.trim();
97
+ return trimmed.startsWith("[Tool call:") || trimmed.startsWith("(empty)[Tool call:");
98
+ }
99
+ export {
100
+ containsTextualToolCallMarker,
101
+ isValidToolCallHeaderPrefix,
102
+ parseTextualToolCallCandidate,
103
+ stripZeroWidth
104
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * `</think>` close-marker client policy.
3
+ *
4
+ * When OmniRoute translates a Claude-native streamed response to OpenAI Chat
5
+ * Completions shape (`claude-to-openai.ts`), it emits a single `</think>`
6
+ * close marker as `delta.content` so clients that scan content for the marker
7
+ * (Claude Code, Cursor) can split reasoning from the final answer — see #4633.
8
+ *
9
+ * Some OpenAI-compatible consumers do NOT parse that marker and render it
10
+ * verbatim, so a bare `</think>` leaks into the visible reply (#5245). OpenCode
11
+ * is one such client.
12
+ *
13
+ * Policy is conservative and opt-OUT by allowlist: the marker stays ON by
14
+ * default (preserving #4633 for Claude Code / Cursor and any unrecognized
15
+ * client), and is suppressed ONLY for known clients that render it literally.
16
+ * Detection is by inbound `User-Agent`.
17
+ *
18
+ * Clients that DO render the marker verbatim but are not in the UA allowlist
19
+ * (e.g. Cursor's reasoning_content-native OpenAI path — #5312 / #5245) can opt
20
+ * in explicitly with the request header `x-omniroute-thinking-marker: off`,
21
+ * which suppresses the marker regardless of User-Agent. `on` forces it kept
22
+ * (overriding the UA allowlist). The default (header absent) is byte-identical
23
+ * to the UA-only policy, so #4633 / #5123 are never regressed.
24
+ */
25
+
26
+ /** Header clients send to explicitly opt in/out of the `</think>` close marker. */
27
+ export const THINKING_MARKER_HEADER = "x-omniroute-thinking-marker";
28
+
29
+ // Lowercased User-Agent substrings of clients that render the textual
30
+ // `</think>` marker verbatim and therefore want it suppressed.
31
+ // - `opencode` (#5245): renders the marker as literal text.
32
+ // - `antigravity` (#1061): the Antigravity IDE client (UA
33
+ // `vscode/<v> (Antigravity/<v>)`) renders a bare `</think>` as the sole
34
+ // visible content on thinking-only turns, which trips its loop-detection.
35
+ const SUPPRESS_THINK_CLOSE_UA_MARKERS = ["opencode", "antigravity"];
36
+
37
+ /**
38
+ * Whether the streamed `</think>` close marker should be suppressed for the
39
+ * given inbound client. Returns false (emit the marker) for unknown clients and
40
+ * for Claude Code / Cursor, so #4633 is never regressed.
41
+ */
42
+ export function shouldSuppressThinkCloseMarker(userAgent) {
43
+ if (!userAgent || typeof userAgent !== "string") return false;
44
+ const ua = userAgent.toLowerCase();
45
+ return SUPPRESS_THINK_CLOSE_UA_MARKERS.some((marker) => ua.includes(marker));
46
+ }
47
+
48
+ /**
49
+ * Interpret the explicit `x-omniroute-thinking-marker` request header.
50
+ * Returns `true` (suppress the marker), `false` (force-keep the marker), or
51
+ * `null` when the header is absent/unrecognized (defer to the UA policy).
52
+ */
53
+ export function thinkingMarkerHeaderSignal(headerValue) {
54
+ if (typeof headerValue !== "string") return null;
55
+ const value = headerValue.trim().toLowerCase();
56
+ if (value === "off" || value === "false" || value === "0" || value === "suppress") return true;
57
+ if (value === "on" || value === "true" || value === "1" || value === "keep") return false;
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Resolve whether the streamed `</think>` close marker should be suppressed for
63
+ * this request. An explicit `x-omniroute-thinking-marker` header wins; absent
64
+ * that, the conservative User-Agent allowlist policy applies. With no header and
65
+ * an unrecognized UA the result is `false` (marker kept), so #4633 / #5123 stay
66
+ * byte-identical by default.
67
+ */
68
+ export function resolveSuppressThinkClose(opts) {
69
+ const headerSignal = thinkingMarkerHeaderSignal(opts.thinkingMarkerHeader);
70
+ if (headerSignal !== null) return headerSignal;
71
+ return shouldSuppressThinkCloseMarker(opts.userAgent);
72
+ }