@fudrouter/fsrouter 0.6.64 → 0.6.66

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 (556) hide show
  1. package/open-sse/AGENTS.md +39 -0
  2. package/open-sse/config/anthropicHeaders.js +87 -0
  3. package/open-sse/config/appConstants.js +238 -0
  4. package/open-sse/config/audioRegistry.js +506 -0
  5. package/open-sse/config/cliFingerprints.js +304 -0
  6. package/open-sse/config/codexInstructions.js +119 -0
  7. package/open-sse/config/codexQuotaScopes.js +42 -0
  8. package/open-sse/config/constants.js +4 -0
  9. package/open-sse/config/contextEditing.js +68 -0
  10. package/open-sse/config/credentialLoader.js +83 -0
  11. package/open-sse/config/defaultThinkingSignature.js +12 -0
  12. package/open-sse/config/embeddingRegistry.js +264 -0
  13. package/open-sse/config/errorConfig.js +96 -0
  14. package/open-sse/config/freeModelCatalog.data.js +121 -0
  15. package/open-sse/config/freeModelCatalog.js +90 -0
  16. package/open-sse/config/freeTierCatalog.js +81 -0
  17. package/open-sse/config/googleTtsLanguages.js +62 -0
  18. package/open-sse/config/imageParamDefs.js +360 -0
  19. package/open-sse/config/imageRegistry.js +493 -0
  20. package/open-sse/config/kiroConstants.js +268 -0
  21. package/open-sse/config/mediaConfig.js +27 -0
  22. package/open-sse/config/mediaServiceKinds.js +71 -0
  23. package/open-sse/config/models.js +13 -0
  24. package/open-sse/config/moderationRegistry.js +71 -0
  25. package/open-sse/config/musicRegistry.js +99 -0
  26. package/open-sse/config/ocrRegistry.js +61 -0
  27. package/open-sse/config/ollamaModels.js +19 -0
  28. package/open-sse/config/providerErrorRules.js +114 -0
  29. package/open-sse/config/providerFieldStrips.js +35 -0
  30. package/open-sse/config/providerHeaderProfiles.js +191 -0
  31. package/open-sse/config/providerModels.js +106 -0
  32. package/open-sse/config/providerRegistry.js +16 -0
  33. package/open-sse/config/providers.js +19 -0
  34. package/open-sse/config/registryUtils.js +106 -0
  35. package/open-sse/config/rerankRegistry.js +177 -0
  36. package/open-sse/config/runtimeConfig.js +96 -0
  37. package/open-sse/config/runway.js +39 -0
  38. package/open-sse/config/searchRegistry.js +153 -0
  39. package/open-sse/config/toolCloaking.js +199 -0
  40. package/open-sse/config/ttsModels.js +129 -0
  41. package/open-sse/config/videoRegistry.js +207 -0
  42. package/open-sse/executors/adapta-web.js +435 -0
  43. package/open-sse/executors/antigravity.js +534 -0
  44. package/open-sse/executors/antigravityUpstreamError.js +13 -0
  45. package/open-sse/executors/auggie.js +437 -0
  46. package/open-sse/executors/azure-openai.js +32 -0
  47. package/open-sse/executors/azure.js +57 -0
  48. package/open-sse/executors/base.js +181 -0
  49. package/open-sse/executors/bedrock.js +633 -0
  50. package/open-sse/executors/blackbox-web.js +578 -0
  51. package/open-sse/executors/chatgpt-web.js +2042 -0
  52. package/open-sse/executors/chipotle.js +357 -0
  53. package/open-sse/executors/claude-web-with-auto-refresh.js +73 -0
  54. package/open-sse/executors/claude-web.js +586 -0
  55. package/open-sse/executors/cliproxyapi.js +296 -0
  56. package/open-sse/executors/cloudflare-ai.js +46 -0
  57. package/open-sse/executors/codebuddy-cn.js +40 -0
  58. package/open-sse/executors/codex.js +1324 -0
  59. package/open-sse/executors/commandCode.js +558 -0
  60. package/open-sse/executors/commandcode.js +88 -0
  61. package/open-sse/executors/copilot-m365-connection.js +176 -0
  62. package/open-sse/executors/copilot-m365-frames.js +192 -0
  63. package/open-sse/executors/copilot-m365-web.js +299 -0
  64. package/open-sse/executors/copilot-web.js +580 -0
  65. package/open-sse/executors/cursor.js +795 -0
  66. package/open-sse/executors/deepseek-web-with-auto-refresh.js +150 -0
  67. package/open-sse/executors/deepseek-web.js +875 -0
  68. package/open-sse/executors/default.js +430 -0
  69. package/open-sse/executors/devin-cli.js +351 -0
  70. package/open-sse/executors/doubao-web.js +550 -0
  71. package/open-sse/executors/duckduckgo-web.js +629 -0
  72. package/open-sse/executors/forceResponsesUpstream.js +33 -0
  73. package/open-sse/executors/gemini-business.js +320 -0
  74. package/open-sse/executors/gemini-cli.js +89 -0
  75. package/open-sse/executors/gemini-web.js +297 -0
  76. package/open-sse/executors/github.js +379 -0
  77. package/open-sse/executors/gitlab.js +569 -0
  78. package/open-sse/executors/glm.js +361 -0
  79. package/open-sse/executors/grok-cli.js +119 -0
  80. package/open-sse/executors/grok-web.js +345 -0
  81. package/open-sse/executors/huggingchat.js +467 -0
  82. package/open-sse/executors/iflow.js +108 -0
  83. package/open-sse/executors/index.js +78 -0
  84. package/open-sse/executors/inner-ai.js +527 -0
  85. package/open-sse/executors/kimchi.js +123 -0
  86. package/open-sse/executors/kimi-web.js +325 -0
  87. package/open-sse/executors/kimi.js +97 -0
  88. package/open-sse/executors/kiro.js +503 -0
  89. package/open-sse/executors/kiroThinking.js +44 -0
  90. package/open-sse/executors/lmarena.js +395 -0
  91. package/open-sse/executors/mimo-free.js +167 -0
  92. package/open-sse/executors/mimocode.js +392 -0
  93. package/open-sse/executors/ninerouter.js +155 -0
  94. package/open-sse/executors/nlpcloud.js +438 -0
  95. package/open-sse/executors/ollama-local.js +14 -0
  96. package/open-sse/executors/opencode-go.js +41 -0
  97. package/open-sse/executors/opencode.js +32 -0
  98. package/open-sse/executors/perplexity-web.js +581 -0
  99. package/open-sse/executors/poe-web.js +133 -0
  100. package/open-sse/executors/pollinations.js +95 -0
  101. package/open-sse/executors/puter.js +31 -0
  102. package/open-sse/executors/qoder.js +448 -0
  103. package/open-sse/executors/qwen-web.js +372 -0
  104. package/open-sse/executors/qwen.js +129 -0
  105. package/open-sse/executors/t3-chat-web.js +417 -0
  106. package/open-sse/executors/theoldllm.js +290 -0
  107. package/open-sse/executors/trae.js +418 -0
  108. package/open-sse/executors/v0-vercel-web.js +141 -0
  109. package/open-sse/executors/venice-web.js +142 -0
  110. package/open-sse/executors/vertex.js +131 -0
  111. package/open-sse/executors/vertexMedia.js +236 -0
  112. package/open-sse/executors/windsurf.js +560 -0
  113. package/open-sse/executors/xai.js +46 -0
  114. package/open-sse/executors/xiaomi-tokenplan.js +19 -0
  115. package/open-sse/executors/yuanbao-web.js +385 -0
  116. package/open-sse/executors/zed-hosted.js +281 -0
  117. package/open-sse/executors/zenmux-free.js +247 -0
  118. package/open-sse/handlers/audioSpeech.js +759 -0
  119. package/open-sse/handlers/audioTranscription.js +395 -0
  120. package/open-sse/handlers/audioTranslation.js +181 -0
  121. package/open-sse/handlers/chatCore/nonStreamingHandler.js +300 -0
  122. package/open-sse/handlers/chatCore/requestDetail.js +102 -0
  123. package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
  124. package/open-sse/handlers/chatCore/streamingHandler.js +95 -0
  125. package/open-sse/handlers/chatCore.js +404 -0
  126. package/open-sse/handlers/embeddingProviders/_base.js +4 -0
  127. package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
  128. package/open-sse/handlers/embeddingProviders/index.js +23 -0
  129. package/open-sse/handlers/embeddingProviders/openai.js +39 -0
  130. package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
  131. package/open-sse/handlers/embeddingsCore.js +126 -0
  132. package/open-sse/handlers/fetch/index.js +237 -0
  133. package/open-sse/handlers/imageGenerationCore.js +230 -0
  134. package/open-sse/handlers/imageProviders/_base.js +31 -0
  135. package/open-sse/handlers/imageProviders/antigravity.js +73 -0
  136. package/open-sse/handlers/imageProviders/blackForestLabs.js +63 -0
  137. package/open-sse/handlers/imageProviders/cloudflareAi.js +207 -0
  138. package/open-sse/handlers/imageProviders/codex.js +217 -0
  139. package/open-sse/handlers/imageProviders/comfyui.js +25 -0
  140. package/open-sse/handlers/imageProviders/falAi.js +62 -0
  141. package/open-sse/handlers/imageProviders/gemini.js +40 -0
  142. package/open-sse/handlers/imageProviders/huggingface.js +38 -0
  143. package/open-sse/handlers/imageProviders/index.js +44 -0
  144. package/open-sse/handlers/imageProviders/leonardo.js +435 -0
  145. package/open-sse/handlers/imageProviders/nanobanana.js +79 -0
  146. package/open-sse/handlers/imageProviders/openai.js +61 -0
  147. package/open-sse/handlers/imageProviders/runwayml.js +72 -0
  148. package/open-sse/handlers/imageProviders/sdwebui.js +36 -0
  149. package/open-sse/handlers/imageProviders/stabilityAi.js +59 -0
  150. package/open-sse/handlers/imageProviders/weavy.js +222 -0
  151. package/open-sse/handlers/moderations.js +92 -0
  152. package/open-sse/handlers/ocr.js +89 -0
  153. package/open-sse/handlers/rerank.js +223 -0
  154. package/open-sse/handlers/responseSanitizer/reasoning.js +144 -0
  155. package/open-sse/handlers/responsesHandler.js +99 -0
  156. package/open-sse/handlers/search/callers.js +371 -0
  157. package/open-sse/handlers/search/chatSearch.js +409 -0
  158. package/open-sse/handlers/search/index.js +201 -0
  159. package/open-sse/handlers/search/normalizers.js +223 -0
  160. package/open-sse/handlers/search.js +1056 -0
  161. package/open-sse/handlers/sseParser.js +469 -0
  162. package/open-sse/handlers/sttCore.js +193 -0
  163. package/open-sse/handlers/ttsCore.js +74 -0
  164. package/open-sse/handlers/ttsProviders/_base.js +39 -0
  165. package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
  166. package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
  167. package/open-sse/handlers/ttsProviders/gemini.js +117 -0
  168. package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
  169. package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
  170. package/open-sse/handlers/ttsProviders/index.js +50 -0
  171. package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
  172. package/open-sse/handlers/ttsProviders/minimax.js +59 -0
  173. package/open-sse/handlers/ttsProviders/openai.js +30 -0
  174. package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
  175. package/open-sse/handlers/usageExtractor.js +93 -0
  176. package/open-sse/handlers/videoGenerationCore.js +119 -0
  177. package/open-sse/handlers/videoProviders/index.js +22 -0
  178. package/open-sse/handlers/videoProviders/leonardo.js +511 -0
  179. package/open-sse/handlers/videoProviders/weavy.js +198 -0
  180. package/open-sse/handlers/webFetch.js +200 -0
  181. package/open-sse/index.js +79 -0
  182. package/open-sse/mcp-server/audit.js +283 -0
  183. package/open-sse/mcp-server/catalog.js +184 -0
  184. package/open-sse/mcp-server/descriptionCompressor.js +175 -0
  185. package/open-sse/mcp-server/httpAuthContext.js +32 -0
  186. package/open-sse/mcp-server/httpTransport.js +229 -0
  187. package/open-sse/mcp-server/index.js +33 -0
  188. package/open-sse/mcp-server/mcpCallerIdentity.js +21 -0
  189. package/open-sse/mcp-server/runtimeHeartbeat.js +110 -0
  190. package/open-sse/mcp-server/schemas/a2a.js +153 -0
  191. package/open-sse/mcp-server/schemas/audit.js +17 -0
  192. package/open-sse/mcp-server/schemas/index.js +166 -0
  193. package/open-sse/mcp-server/schemas/pickFastestModel.js +90 -0
  194. package/open-sse/mcp-server/schemas/toolDefinition.js +0 -0
  195. package/open-sse/mcp-server/schemas/toolSearch.js +32 -0
  196. package/open-sse/mcp-server/schemas/tools.js +1219 -0
  197. package/open-sse/mcp-server/scopeEnforcement.js +80 -0
  198. package/open-sse/mcp-server/server.js +1168 -0
  199. package/open-sse/mcp-server/toolCardinality.js +78 -0
  200. package/open-sse/mcp-server/toolSearch/catalog.js +66 -0
  201. package/open-sse/mcp-server/toolSearch/handler.js +20 -0
  202. package/open-sse/mcp-server/toolSearch/index.js +10 -0
  203. package/open-sse/mcp-server/toolSearch/register.js +21 -0
  204. package/open-sse/mcp-server/toolSearch/search.js +55 -0
  205. package/open-sse/mcp-server/toolSearch/signature.js +67 -0
  206. package/open-sse/mcp-server/tools/advancedTools.js +893 -0
  207. package/open-sse/mcp-server/tools/agentSkillTools.js +69 -0
  208. package/open-sse/mcp-server/tools/compressionTools.js +309 -0
  209. package/open-sse/mcp-server/tools/gamificationTools.js +134 -0
  210. package/open-sse/mcp-server/tools/githubSkillTools.js +102 -0
  211. package/open-sse/mcp-server/tools/memoryTools.js +118 -0
  212. package/open-sse/mcp-server/tools/notionTools.js +101 -0
  213. package/open-sse/mcp-server/tools/obsidianTools.js +307 -0
  214. package/open-sse/mcp-server/tools/pickFastestModel.js +223 -0
  215. package/open-sse/mcp-server/tools/pluginTools.js +181 -0
  216. package/open-sse/mcp-server/tools/poolTools.js +139 -0
  217. package/open-sse/mcp-server/tools/skillTools.js +113 -0
  218. package/open-sse/package.json +10 -0
  219. package/open-sse/providers/REGISTRY_TEMPLATE.js +98 -0
  220. package/open-sse/providers/capabilities.js +293 -0
  221. package/open-sse/providers/index.js +51 -0
  222. package/open-sse/providers/models/helpers.js +20 -0
  223. package/open-sse/providers/models/namePatterns.js +33 -0
  224. package/open-sse/providers/models/schema.js +40 -0
  225. package/open-sse/providers/pricing.js +308 -0
  226. package/open-sse/providers/registry/alicode-intl.js +30 -0
  227. package/open-sse/providers/registry/alicode.js +31 -0
  228. package/open-sse/providers/registry/anthropic.js +37 -0
  229. package/open-sse/providers/registry/antigravity.js +81 -0
  230. package/open-sse/providers/registry/assemblyai.js +38 -0
  231. package/open-sse/providers/registry/aws-polly.js +45 -0
  232. package/open-sse/providers/registry/azure.js +21 -0
  233. package/open-sse/providers/registry/black-forest-labs.js +32 -0
  234. package/open-sse/providers/registry/blackbox.js +41 -0
  235. package/open-sse/providers/registry/brave-search.js +35 -0
  236. package/open-sse/providers/registry/byteplus.js +35 -0
  237. package/open-sse/providers/registry/cartesia.js +36 -0
  238. package/open-sse/providers/registry/cerebras.js +31 -0
  239. package/open-sse/providers/registry/chutes.js +24 -0
  240. package/open-sse/providers/registry/claude.js +87 -0
  241. package/open-sse/providers/registry/cline.js +51 -0
  242. package/open-sse/providers/registry/clinepass.js +57 -0
  243. package/open-sse/providers/registry/cloudflare-ai.js +55 -0
  244. package/open-sse/providers/registry/codebuddy-cn.js +77 -0
  245. package/open-sse/providers/registry/codex.js +100 -0
  246. package/open-sse/providers/registry/cohere.js +27 -0
  247. package/open-sse/providers/registry/comfyui.js +20 -0
  248. package/open-sse/providers/registry/commandcode.js +43 -0
  249. package/open-sse/providers/registry/coqui.js +30 -0
  250. package/open-sse/providers/registry/cursor.js +58 -0
  251. package/open-sse/providers/registry/deepgram.js +33 -0
  252. package/open-sse/providers/registry/deepseek.js +51 -0
  253. package/open-sse/providers/registry/digitalocean.js +92 -0
  254. package/open-sse/providers/registry/edge-tts.js +24 -0
  255. package/open-sse/providers/registry/elevenlabs.js +35 -0
  256. package/open-sse/providers/registry/exa.js +50 -0
  257. package/open-sse/providers/registry/fal-ai.js +34 -0
  258. package/open-sse/providers/registry/featherless.js +34 -0
  259. package/open-sse/providers/registry/firecrawl.js +34 -0
  260. package/open-sse/providers/registry/fireworks.js +29 -0
  261. package/open-sse/providers/registry/gemini-cli.js +58 -0
  262. package/open-sse/providers/registry/gemini.js +83 -0
  263. package/open-sse/providers/registry/github.js +88 -0
  264. package/open-sse/providers/registry/gitlab.js +32 -0
  265. package/open-sse/providers/registry/glm-cn.js +35 -0
  266. package/open-sse/providers/registry/glm.js +58 -0
  267. package/open-sse/providers/registry/google-pse.js +35 -0
  268. package/open-sse/providers/registry/google-tts.js +24 -0
  269. package/open-sse/providers/registry/grok-cli.js +86 -0
  270. package/open-sse/providers/registry/grok-web.js +39 -0
  271. package/open-sse/providers/registry/groq.js +37 -0
  272. package/open-sse/providers/registry/huggingface.js +34 -0
  273. package/open-sse/providers/registry/hyperbolic.js +35 -0
  274. package/open-sse/providers/registry/ibm-bob.js +27 -0
  275. package/open-sse/providers/registry/iflow.js +52 -0
  276. package/open-sse/providers/registry/index.js +210 -0
  277. package/open-sse/providers/registry/inworld.js +36 -0
  278. package/open-sse/providers/registry/jina-ai.js +42 -0
  279. package/open-sse/providers/registry/jina-reader.js +34 -0
  280. package/open-sse/providers/registry/kilocode.js +51 -0
  281. package/open-sse/providers/registry/kimchi.js +49 -0
  282. package/open-sse/providers/registry/kimi-coding.js +65 -0
  283. package/open-sse/providers/registry/kimi.js +56 -0
  284. package/open-sse/providers/registry/kiro.js +115 -0
  285. package/open-sse/providers/registry/linkup.js +34 -0
  286. package/open-sse/providers/registry/local-device.js +24 -0
  287. package/open-sse/providers/registry/mimo-free.js +24 -0
  288. package/open-sse/providers/registry/minimax-cn.js +76 -0
  289. package/open-sse/providers/registry/minimax.js +83 -0
  290. package/open-sse/providers/registry/mistral.js +34 -0
  291. package/open-sse/providers/registry/mmf.js +19 -0
  292. package/open-sse/providers/registry/nanobanana.js +35 -0
  293. package/open-sse/providers/registry/nebius.js +27 -0
  294. package/open-sse/providers/registry/nvidia.js +43 -0
  295. package/open-sse/providers/registry/ollama-local.js +19 -0
  296. package/open-sse/providers/registry/ollama.js +36 -0
  297. package/open-sse/providers/registry/openai.js +84 -0
  298. package/open-sse/providers/registry/opencode-go.js +41 -0
  299. package/open-sse/providers/registry/opencode.js +25 -0
  300. package/open-sse/providers/registry/openrouter.js +60 -0
  301. package/open-sse/providers/registry/perplexity-agent.js +49 -0
  302. package/open-sse/providers/registry/perplexity-web.js +33 -0
  303. package/open-sse/providers/registry/perplexity.js +37 -0
  304. package/open-sse/providers/registry/playht.js +36 -0
  305. package/open-sse/providers/registry/qoder.js +54 -0
  306. package/open-sse/providers/registry/qwen.js +33 -0
  307. package/open-sse/providers/registry/recraft.js +24 -0
  308. package/open-sse/providers/registry/runwayml.js +30 -0
  309. package/open-sse/providers/registry/sdwebui.js +20 -0
  310. package/open-sse/providers/registry/searchapi.js +35 -0
  311. package/open-sse/providers/registry/searxng.js +35 -0
  312. package/open-sse/providers/registry/serper.js +35 -0
  313. package/open-sse/providers/registry/siliconflow.js +39 -0
  314. package/open-sse/providers/registry/stability-ai.js +31 -0
  315. package/open-sse/providers/registry/tavily.js +50 -0
  316. package/open-sse/providers/registry/together.js +31 -0
  317. package/open-sse/providers/registry/topaz.js +19 -0
  318. package/open-sse/providers/registry/tortoise.js +30 -0
  319. package/open-sse/providers/registry/venice.js +56 -0
  320. package/open-sse/providers/registry/vercel-ai-gateway.js +41 -0
  321. package/open-sse/providers/registry/vertex-partner.js +29 -0
  322. package/open-sse/providers/registry/vertex.js +32 -0
  323. package/open-sse/providers/registry/volcengine-ark.js +35 -0
  324. package/open-sse/providers/registry/voyage-ai.js +30 -0
  325. package/open-sse/providers/registry/xai.js +45 -0
  326. package/open-sse/providers/registry/xiaomi-mimo.js +46 -0
  327. package/open-sse/providers/registry/xiaomi-tokenplan.js +63 -0
  328. package/open-sse/providers/registry/youcom.js +35 -0
  329. package/open-sse/providers/schema.js +76 -0
  330. package/open-sse/providers/shared.js +74 -0
  331. package/open-sse/providers/thinkingLevels.js +48 -0
  332. package/open-sse/rtk/applyFilter.js +15 -0
  333. package/open-sse/rtk/autodetect.js +111 -0
  334. package/open-sse/rtk/caveman.js +100 -0
  335. package/open-sse/rtk/cavemanPrompts.js +78 -0
  336. package/open-sse/rtk/constants.js +55 -0
  337. package/open-sse/rtk/filters/buildOutput.js +127 -0
  338. package/open-sse/rtk/filters/dedupLog.js +44 -0
  339. package/open-sse/rtk/filters/find.js +50 -0
  340. package/open-sse/rtk/filters/gitDiff.js +92 -0
  341. package/open-sse/rtk/filters/gitLog.js +99 -0
  342. package/open-sse/rtk/filters/gitStatus.js +117 -0
  343. package/open-sse/rtk/filters/grep.js +48 -0
  344. package/open-sse/rtk/filters/ls.js +79 -0
  345. package/open-sse/rtk/filters/readNumbered.js +27 -0
  346. package/open-sse/rtk/filters/searchList.js +52 -0
  347. package/open-sse/rtk/filters/smartTruncate.js +15 -0
  348. package/open-sse/rtk/filters/tree.js +32 -0
  349. package/open-sse/rtk/headroom.js +215 -0
  350. package/open-sse/rtk/index.js +155 -0
  351. package/open-sse/rtk/paleo.js +47 -0
  352. package/open-sse/rtk/ponytail.js +9 -0
  353. package/open-sse/rtk/ponytailPrompt.js +52 -0
  354. package/open-sse/rtk/ponytailPrompts.js +76 -0
  355. package/open-sse/rtk/pxpipe.js +104 -0
  356. package/open-sse/rtk/registry.js +38 -0
  357. package/open-sse/rtk/systemInject.js +98 -0
  358. package/open-sse/services/accountFallback.js +323 -0
  359. package/open-sse/services/antigravityHeaders.js +91 -0
  360. package/open-sse/services/antigravityVersion.js +154 -0
  361. package/open-sse/services/clinepassModels.js +63 -0
  362. package/open-sse/services/codexQuotaFetcher.js +312 -0
  363. package/open-sse/services/codexVerbosity.js +30 -0
  364. package/open-sse/services/combo/context.js +12 -0
  365. package/open-sse/services/combo/fingerprintExpansion.js +51 -0
  366. package/open-sse/services/combo/headroomRanking.js +28 -0
  367. package/open-sse/services/combo/quotaExhaustionCutoff.js +87 -0
  368. package/open-sse/services/combo/quotaScoring.js +226 -0
  369. package/open-sse/services/combo/quotaShareConcurrency.js +25 -0
  370. package/open-sse/services/combo/quotaShareInflight.js +50 -0
  371. package/open-sse/services/combo/quotaShareStrategy.js +130 -0
  372. package/open-sse/services/combo/resolveAutoStrategy.js +231 -0
  373. package/open-sse/services/combo/responseValidation.js +140 -0
  374. package/open-sse/services/combo/runtimeUnits.js +195 -0
  375. package/open-sse/services/combo/sessionStickiness.js +128 -0
  376. package/open-sse/services/combo/shadowRouting.js +131 -0
  377. package/open-sse/services/combo/targetExhaustion.js +75 -0
  378. package/open-sse/services/combo/targetSorters.js +126 -0
  379. package/open-sse/services/combo/targetTimeoutRunner.js +65 -0
  380. package/open-sse/services/combo/validateQuality.js +271 -0
  381. package/open-sse/services/combo.js +571 -0
  382. package/open-sse/services/comboAgentMiddleware.js +91 -0
  383. package/open-sse/services/comboConfig.js +153 -0
  384. package/open-sse/services/comboManifestMetrics.js +10 -0
  385. package/open-sse/services/comboMetrics.js +278 -0
  386. package/open-sse/services/compact.js +71 -0
  387. package/open-sse/services/copilotModels.js +155 -0
  388. package/open-sse/services/emergencyFallback.js +109 -0
  389. package/open-sse/services/errorClassifier.js +190 -0
  390. package/open-sse/services/evalRouting.js +201 -0
  391. package/open-sse/services/freeWebSearch.js +4 -0
  392. package/open-sse/services/fusion.js +223 -0
  393. package/open-sse/services/intentClassifier.js +631 -0
  394. package/open-sse/services/kimchiModels.js +176 -0
  395. package/open-sse/services/kiroModels.js +332 -0
  396. package/open-sse/services/model.js +136 -0
  397. package/open-sse/services/modelCapabilities.js +5 -0
  398. package/open-sse/services/modelDeprecation.js +101 -0
  399. package/open-sse/services/modelFamilyFallback.js +166 -0
  400. package/open-sse/services/oauthCredentialManager.js +156 -0
  401. package/open-sse/services/pipeline.js +107 -0
  402. package/open-sse/services/projectId.js +306 -0
  403. package/open-sse/services/provider.js +169 -0
  404. package/open-sse/services/providerCooldownTracker.js +94 -0
  405. package/open-sse/services/providerDefaultRateLimit.js +11 -0
  406. package/open-sse/services/proxyAutoSelector.js +24 -0
  407. package/open-sse/services/qoderModels.js +214 -0
  408. package/open-sse/services/quotaFetchThrottle.js +103 -0
  409. package/open-sse/services/quotaMonitor.js +310 -0
  410. package/open-sse/services/quotaPreflight.js +266 -0
  411. package/open-sse/services/rateLimitManager/headers.js +56 -0
  412. package/open-sse/services/rateLimitManager.js +616 -0
  413. package/open-sse/services/rateLimitSemaphore.js +112 -0
  414. package/open-sse/services/responsesInputSanitizer.js +99 -0
  415. package/open-sse/services/slidingWindowLimiter.js +44 -0
  416. package/open-sse/services/streamRecovery.js +355 -0
  417. package/open-sse/services/taskAwareRouter.js +227 -0
  418. package/open-sse/services/taskAwareRouting.js +332 -0
  419. package/open-sse/services/thinkingBudget.js +247 -0
  420. package/open-sse/services/tokenRefresh/dedup.js +31 -0
  421. package/open-sse/services/tokenRefresh/providers.js +624 -0
  422. package/open-sse/services/tokenRefresh.js +254 -0
  423. package/open-sse/services/usage/claude.js +147 -0
  424. package/open-sse/services/usage/codebuddy-cn.js +138 -0
  425. package/open-sse/services/usage/codex.js +267 -0
  426. package/open-sse/services/usage/github.js +100 -0
  427. package/open-sse/services/usage/google.js +241 -0
  428. package/open-sse/services/usage/grok-cli.js +274 -0
  429. package/open-sse/services/usage/kiro.js +188 -0
  430. package/open-sse/services/usage/minimax.js +234 -0
  431. package/open-sse/services/usage/misc.js +288 -0
  432. package/open-sse/services/usage/shared.js +70 -0
  433. package/open-sse/services/usage.js +62 -0
  434. package/open-sse/services/usageDb.js +2 -0
  435. package/open-sse/services/webSearchRouting.js +25 -0
  436. package/open-sse/services/wildcardRouter.js +59 -0
  437. package/open-sse/shared/clineAuth.js +37 -0
  438. package/open-sse/shared/machineId.js +19 -0
  439. package/open-sse/shared/qoder/constants.js +64 -0
  440. package/open-sse/shared/qoder/cosy.js +175 -0
  441. package/open-sse/shared/qoder/encoding.js +55 -0
  442. package/open-sse/transformer/responsesTransformer.js +439 -0
  443. package/open-sse/transformer/streamToJsonConverter.js +103 -0
  444. package/open-sse/translator/concerns/chunk.js +11 -0
  445. package/open-sse/translator/concerns/finishReason.js +63 -0
  446. package/open-sse/translator/concerns/image.js +124 -0
  447. package/open-sse/translator/concerns/json.js +5 -0
  448. package/open-sse/translator/concerns/message.js +7 -0
  449. package/open-sse/translator/concerns/modality.js +155 -0
  450. package/open-sse/translator/concerns/paramSupport.js +75 -0
  451. package/open-sse/translator/concerns/prefetch.js +96 -0
  452. package/open-sse/translator/concerns/reasoning.js +24 -0
  453. package/open-sse/translator/concerns/thinking.js +53 -0
  454. package/open-sse/translator/concerns/thinkingUnified.js +329 -0
  455. package/open-sse/translator/concerns/toolCall.js +153 -0
  456. package/open-sse/translator/concerns/usage.js +69 -0
  457. package/open-sse/translator/formats/claude.js +368 -0
  458. package/open-sse/translator/formats/gemini.js +380 -0
  459. package/open-sse/translator/formats/maxTokens.js +34 -0
  460. package/open-sse/translator/formats/openai.js +134 -0
  461. package/open-sse/translator/formats/responsesApi.js +141 -0
  462. package/open-sse/translator/formats.js +36 -0
  463. package/open-sse/translator/helpers/claudeHelper.js +216 -0
  464. package/open-sse/translator/helpers/geminiHelper.js +372 -0
  465. package/open-sse/translator/helpers/imageHelper.js +34 -0
  466. package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
  467. package/open-sse/translator/helpers/openaiHelper.js +130 -0
  468. package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
  469. package/open-sse/translator/helpers/toolCallHelper.js +148 -0
  470. package/open-sse/translator/index.js +252 -0
  471. package/open-sse/translator/request/antigravity-to-openai.js +242 -0
  472. package/open-sse/translator/request/claude-to-kiro.js +478 -0
  473. package/open-sse/translator/request/claude-to-openai.js +263 -0
  474. package/open-sse/translator/request/gemini-to-openai.js +152 -0
  475. package/open-sse/translator/request/openai-responses.js +385 -0
  476. package/open-sse/translator/request/openai-to-claude.js +383 -0
  477. package/open-sse/translator/request/openai-to-commandcode.js +176 -0
  478. package/open-sse/translator/request/openai-to-cursor.js +185 -0
  479. package/open-sse/translator/request/openai-to-gemini.js +442 -0
  480. package/open-sse/translator/request/openai-to-kiro.js +607 -0
  481. package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
  482. package/open-sse/translator/request/openai-to-ollama.js +195 -0
  483. package/open-sse/translator/request/openai-to-vertex.js +42 -0
  484. package/open-sse/translator/response/claude-to-openai.js +195 -0
  485. package/open-sse/translator/response/commandcode-to-openai.js +188 -0
  486. package/open-sse/translator/response/cursor-to-openai.js +30 -0
  487. package/open-sse/translator/response/gemini-to-openai.js +147 -0
  488. package/open-sse/translator/response/kiro-to-claude.js +261 -0
  489. package/open-sse/translator/response/kiro-to-openai.js +160 -0
  490. package/open-sse/translator/response/ollama-to-openai.js +134 -0
  491. package/open-sse/translator/response/openai-responses.js +535 -0
  492. package/open-sse/translator/response/openai-to-antigravity.js +123 -0
  493. package/open-sse/translator/response/openai-to-claude.js +265 -0
  494. package/open-sse/translator/schema/blocks.js +43 -0
  495. package/open-sse/translator/schema/defaults.js +7 -0
  496. package/open-sse/translator/schema/finishReasons.js +27 -0
  497. package/open-sse/translator/schema/index.js +8 -0
  498. package/open-sse/translator/schema/roles.js +16 -0
  499. package/open-sse/utils/agentGoalPolicy.js +85 -0
  500. package/open-sse/utils/aiSdkCompat.js +75 -0
  501. package/open-sse/utils/bypassHandler.js +298 -0
  502. package/open-sse/utils/cacheControlPolicy.js +189 -0
  503. package/open-sse/utils/claudeCloaking.js +155 -0
  504. package/open-sse/utils/claudeHeaderCache.js +70 -0
  505. package/open-sse/utils/claudeSignature.js +41 -0
  506. package/open-sse/utils/clientDetector.js +63 -0
  507. package/open-sse/utils/codebuddySanitizer.js +96 -0
  508. package/open-sse/utils/composerToolCalls.js +158 -0
  509. package/open-sse/utils/cors.js +13 -0
  510. package/open-sse/utils/cursorChecksum.js +149 -0
  511. package/open-sse/utils/cursorImages.js +224 -0
  512. package/open-sse/utils/cursorProtobuf.js +904 -0
  513. package/open-sse/utils/cursorVersionDetector.js +55 -0
  514. package/open-sse/utils/debugLog.js +14 -0
  515. package/open-sse/utils/diagnostics.js +143 -0
  516. package/open-sse/utils/error.js +164 -0
  517. package/open-sse/utils/estimateSize.js +33 -0
  518. package/open-sse/utils/finishReason.js +32 -0
  519. package/open-sse/utils/flattenToolHistory.js +68 -0
  520. package/open-sse/utils/headers.js +36 -0
  521. package/open-sse/utils/heapPressure.js +35 -0
  522. package/open-sse/utils/jsonToSse.js +125 -0
  523. package/open-sse/utils/keepaliveThreshold.js +40 -0
  524. package/open-sse/utils/logger.js +104 -0
  525. package/open-sse/utils/noThinkingAlias.js +74 -0
  526. package/open-sse/utils/number.js +7 -0
  527. package/open-sse/utils/ollamaTransform.js +85 -0
  528. package/open-sse/utils/opencodeHeaders.js +47 -0
  529. package/open-sse/utils/progressTracker.js +81 -0
  530. package/open-sse/utils/providerRequestLogging.js +219 -0
  531. package/open-sse/utils/proxyFetch.js +369 -0
  532. package/open-sse/utils/reasoningContentInjector.js +79 -0
  533. package/open-sse/utils/reasoningFields.js +70 -0
  534. package/open-sse/utils/requestLogger.js +260 -0
  535. package/open-sse/utils/responsesInputNormalization.js +92 -0
  536. package/open-sse/utils/responsesStatePolicy.js +52 -0
  537. package/open-sse/utils/responsesStreamHelpers.js +49 -0
  538. package/open-sse/utils/sessionManager.js +240 -0
  539. package/open-sse/utils/sleep.js +6 -0
  540. package/open-sse/utils/sse.js +14 -0
  541. package/open-sse/utils/sseConstants.js +23 -0
  542. package/open-sse/utils/sseHeartbeat.js +104 -0
  543. package/open-sse/utils/stream.js +517 -0
  544. package/open-sse/utils/streamFailureFinalization.js +134 -0
  545. package/open-sse/utils/streamHandler.js +250 -0
  546. package/open-sse/utils/streamHelpers.js +137 -0
  547. package/open-sse/utils/textualToolCall.js +104 -0
  548. package/open-sse/utils/thinkCloseMarker.js +72 -0
  549. package/open-sse/utils/thinkTagParser.js +146 -0
  550. package/open-sse/utils/toolCallArguments.js +12 -0
  551. package/open-sse/utils/toolDeduper.js +49 -0
  552. package/open-sse/utils/toolSources.js +31 -0
  553. package/open-sse/utils/upstreamResponseHeaders.js +25 -0
  554. package/open-sse/utils/urlSanitize.js +15 -0
  555. package/open-sse/utils/usageTracking.js +347 -0
  556. package/package.json +1 -1
@@ -0,0 +1,893 @@
1
+ import { logToolCall } from "../audit.ts";
2
+ import { getMcpHttpAuthHeadersForInternalFetch } from "../httpAuthContext.ts";
3
+ import { normalizeQuotaResponse } from "../../../src/shared/contracts/quota.ts";
4
+ import { resolveOmniRouteBaseUrl } from "../../../src/shared/utils/resolveOmniRouteBaseUrl.ts";
5
+ import {
6
+ getComboModelProvider,
7
+ getComboModelString,
8
+ getComboStepTarget
9
+ } from '../../dist/lib/combos/steps.ts.js';
10
+ import { normalizeRoutingStrategy } from "../../../src/shared/constants/routingStrategies.ts";
11
+ const OMNIROUTE_BASE_URL = resolveOmniRouteBaseUrl();
12
+ const OMNIROUTE_API_KEY = process.env.OMNIROUTE_API_KEY || "";
13
+ async function apiFetch(path, options = {}) {
14
+ const url = `${OMNIROUTE_BASE_URL}${path}`;
15
+ const headers = {
16
+ "Content-Type": "application/json",
17
+ // Static env key is only a fallback; the per-caller MCP identity forwarded via
18
+ // withMcpHttpAuthContext must win over it (#5819).
19
+ ...OMNIROUTE_API_KEY ? { Authorization: `Bearer ${OMNIROUTE_API_KEY}` } : {},
20
+ ...getMcpHttpAuthHeadersForInternalFetch(),
21
+ ...options.headers || {}
22
+ };
23
+ const response = await fetch(url, { ...options, headers, signal: AbortSignal.timeout(3e4) });
24
+ if (!response.ok) {
25
+ const text = await response.text().catch(() => "Unknown error");
26
+ throw new Error(`API [${response.status}]: ${text}`);
27
+ }
28
+ return response.json();
29
+ }
30
+ function isRecord(value) {
31
+ return !!value && typeof value === "object" && !Array.isArray(value);
32
+ }
33
+ function toRecord(value) {
34
+ return isRecord(value) ? value : {};
35
+ }
36
+ function toArrayOfRecords(value) {
37
+ return Array.isArray(value) ? value.filter(isRecord) : [];
38
+ }
39
+ function toString(value, fallback = "") {
40
+ return typeof value === "string" ? value : fallback;
41
+ }
42
+ function toNumber(value, fallback = 0) {
43
+ const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim().length > 0 ? Number(value) : Number.NaN;
44
+ return Number.isFinite(parsed) ? parsed : fallback;
45
+ }
46
+ function toBoolean(value, fallback = false) {
47
+ return typeof value === "boolean" ? value : fallback;
48
+ }
49
+ function getComboModels(combo) {
50
+ const directModels = toArrayOfRecords(combo.models);
51
+ const nestedModels = toArrayOfRecords(toRecord(combo.data).models);
52
+ const sourceModels = directModels.length > 0 ? directModels : nestedModels;
53
+ return sourceModels.map((model) => ({
54
+ provider: getComboModelProvider(model) || (getComboModelString(model) ? "unknown" : "combo"),
55
+ model: getComboModelString(model) || getComboStepTarget(model) || "",
56
+ inputCostPer1M: toNumber(model.inputCostPer1M, 3)
57
+ }));
58
+ }
59
+ function normalizeCombosResponse(raw) {
60
+ if (Array.isArray(raw)) return raw.filter(isRecord);
61
+ const source = toRecord(raw);
62
+ return Array.isArray(source.combos) ? source.combos.filter(isRecord) : [];
63
+ }
64
+ let activeBudgetGuard = null;
65
+ const RESILIENCE_PROFILES = {
66
+ aggressive: {
67
+ requestQueue: {
68
+ requestsPerMinute: 180,
69
+ minTimeBetweenRequestsMs: 100,
70
+ concurrentRequests: 16
71
+ },
72
+ connectionCooldown: {
73
+ oauth: {
74
+ baseCooldownMs: 3e4,
75
+ useUpstreamRetryHints: false,
76
+ maxBackoffSteps: 4
77
+ },
78
+ apikey: {
79
+ baseCooldownMs: 2e3,
80
+ useUpstreamRetryHints: true,
81
+ maxBackoffSteps: 3
82
+ }
83
+ },
84
+ providerBreaker: {
85
+ oauth: {
86
+ failureThreshold: 2,
87
+ resetTimeoutMs: 3e4
88
+ },
89
+ apikey: {
90
+ failureThreshold: 3,
91
+ resetTimeoutMs: 15e3
92
+ }
93
+ }
94
+ },
95
+ balanced: {
96
+ requestQueue: {
97
+ requestsPerMinute: 100,
98
+ minTimeBetweenRequestsMs: 200,
99
+ concurrentRequests: 10
100
+ },
101
+ connectionCooldown: {
102
+ oauth: {
103
+ baseCooldownMs: 6e4,
104
+ useUpstreamRetryHints: false,
105
+ maxBackoffSteps: 8
106
+ },
107
+ apikey: {
108
+ baseCooldownMs: 3e3,
109
+ useUpstreamRetryHints: true,
110
+ maxBackoffSteps: 5
111
+ }
112
+ },
113
+ providerBreaker: {
114
+ oauth: {
115
+ failureThreshold: 3,
116
+ resetTimeoutMs: 6e4
117
+ },
118
+ apikey: {
119
+ failureThreshold: 5,
120
+ resetTimeoutMs: 3e4
121
+ }
122
+ }
123
+ },
124
+ conservative: {
125
+ requestQueue: {
126
+ requestsPerMinute: 60,
127
+ minTimeBetweenRequestsMs: 350,
128
+ concurrentRequests: 6
129
+ },
130
+ connectionCooldown: {
131
+ oauth: {
132
+ baseCooldownMs: 12e4,
133
+ useUpstreamRetryHints: false,
134
+ maxBackoffSteps: 10
135
+ },
136
+ apikey: {
137
+ baseCooldownMs: 3e4,
138
+ useUpstreamRetryHints: false,
139
+ maxBackoffSteps: 8
140
+ }
141
+ },
142
+ providerBreaker: {
143
+ oauth: {
144
+ failureThreshold: 8,
145
+ resetTimeoutMs: 12e4
146
+ },
147
+ apikey: {
148
+ failureThreshold: 8,
149
+ resetTimeoutMs: 6e4
150
+ }
151
+ }
152
+ }
153
+ };
154
+ const TASK_FITNESS = {
155
+ coding: { preferred: ["claude", "deepseek", "codex"], traits: ["fast", "code-optimized"] },
156
+ review: { preferred: ["claude", "gemini", "openai"], traits: ["analytical", "thorough"] },
157
+ planning: { preferred: ["gemini", "claude", "openai"], traits: ["reasoning", "structured"] },
158
+ analysis: { preferred: ["gemini", "claude"], traits: ["deep-reasoning", "large-context"] },
159
+ debugging: { preferred: ["claude", "deepseek", "codex"], traits: ["code-aware", "fast"] },
160
+ documentation: { preferred: ["gemini", "claude", "openai"], traits: ["clear", "structured"] }
161
+ };
162
+ async function handleSimulateRoute(args) {
163
+ const start = Date.now();
164
+ try {
165
+ const [combosRaw, healthRaw, quotaRaw] = await Promise.allSettled([
166
+ apiFetch("/api/combos"),
167
+ apiFetch("/api/monitoring/health"),
168
+ apiFetch("/api/usage/quota")
169
+ ]);
170
+ const combos = combosRaw.status === "fulfilled" ? normalizeCombosResponse(combosRaw.value) : [];
171
+ const health = healthRaw.status === "fulfilled" ? toRecord(healthRaw.value) : {};
172
+ const quota = quotaRaw.status === "fulfilled" ? normalizeQuotaResponse(quotaRaw.value) : normalizeQuotaResponse({});
173
+ const targetCombo = args.combo ? combos.find(
174
+ (combo) => toString(combo.id) === args.combo || toString(combo.name) === args.combo
175
+ ) : combos.find((combo) => combo.enabled !== false);
176
+ if (!targetCombo) {
177
+ return {
178
+ content: [
179
+ { type: "text", text: JSON.stringify({ error: "No matching combo found" }) }
180
+ ],
181
+ isError: true
182
+ };
183
+ }
184
+ const models = getComboModels(targetCombo);
185
+ const breakers = toArrayOfRecords(health.circuitBreakers);
186
+ const providers = quota.providers;
187
+ const simulatedPath = models.map((model, idx) => {
188
+ const cb = breakers.find((breaker) => toString(breaker.provider) === model.provider);
189
+ const q = providers.find((providerEntry) => providerEntry.provider === model.provider);
190
+ const estimatedCost = args.promptTokenEstimate / 1e6 * model.inputCostPer1M;
191
+ return {
192
+ provider: model.provider,
193
+ model: model.model || args.model,
194
+ probability: idx === 0 ? 0.85 : 0.15 / Math.max(models.length - 1, 1),
195
+ estimatedCost: Math.round(estimatedCost * 1e4) / 1e4,
196
+ healthStatus: toString(cb?.state, "CLOSED"),
197
+ quotaAvailable: q?.percentRemaining ?? 100
198
+ };
199
+ });
200
+ const costs = simulatedPath.map((pathEntry) => pathEntry.estimatedCost);
201
+ const result = {
202
+ simulatedPath,
203
+ fallbackTree: {
204
+ primary: simulatedPath[0]?.provider || "unknown",
205
+ fallbacks: simulatedPath.slice(1).map((pathEntry) => pathEntry.provider),
206
+ worstCaseCost: Math.max(...costs, 0),
207
+ bestCaseCost: Math.min(...costs, 0)
208
+ }
209
+ };
210
+ await logToolCall("omniroute_simulate_route", args, result, Date.now() - start, true);
211
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
212
+ } catch (err) {
213
+ const msg = err instanceof Error ? err.message : String(err);
214
+ await logToolCall("omniroute_simulate_route", args, null, Date.now() - start, false, msg);
215
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
216
+ }
217
+ }
218
+ async function handleSetBudgetGuard(args) {
219
+ const start = Date.now();
220
+ try {
221
+ let spent = 0;
222
+ try {
223
+ const analytics = toRecord(await apiFetch("/api/usage/analytics?period=session"));
224
+ spent = toNumber(analytics.totalCost, 0);
225
+ } catch {
226
+ }
227
+ activeBudgetGuard = {
228
+ sessionId: `budget_${Date.now()}`,
229
+ maxCost: args.maxCost,
230
+ action: args.action,
231
+ degradeToTier: args.degradeToTier,
232
+ spent,
233
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
234
+ };
235
+ const remaining = Math.max(0, args.maxCost - spent);
236
+ const result = {
237
+ sessionId: activeBudgetGuard.sessionId,
238
+ budgetTotal: args.maxCost,
239
+ budgetSpent: Math.round(spent * 1e4) / 1e4,
240
+ budgetRemaining: Math.round(remaining * 1e4) / 1e4,
241
+ action: args.action,
242
+ status: remaining <= 0 ? "exceeded" : remaining < args.maxCost * 0.2 ? "warning" : "active"
243
+ };
244
+ await logToolCall(
245
+ "omniroute_set_budget_guard",
246
+ { maxCost: args.maxCost, action: args.action },
247
+ result,
248
+ Date.now() - start,
249
+ true
250
+ );
251
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
252
+ } catch (err) {
253
+ const msg = err instanceof Error ? err.message : String(err);
254
+ await logToolCall("omniroute_set_budget_guard", args, null, Date.now() - start, false, msg);
255
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
256
+ }
257
+ }
258
+ async function handleSetRoutingStrategy(args) {
259
+ const start = Date.now();
260
+ try {
261
+ const combos = normalizeCombosResponse(await apiFetch("/api/combos"));
262
+ const combo = combos.find(
263
+ (comboEntry) => toString(comboEntry.id) === args.comboId || toString(comboEntry.name) === args.comboId
264
+ );
265
+ if (!combo) {
266
+ const msg = `Combo '${args.comboId}' not found`;
267
+ await logToolCall(
268
+ "omniroute_set_routing_strategy",
269
+ args,
270
+ null,
271
+ Date.now() - start,
272
+ false,
273
+ msg
274
+ );
275
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
276
+ }
277
+ const comboId = toString(combo.id);
278
+ if (!comboId) {
279
+ const msg = "Matched combo has no id";
280
+ await logToolCall(
281
+ "omniroute_set_routing_strategy",
282
+ args,
283
+ null,
284
+ Date.now() - start,
285
+ false,
286
+ msg
287
+ );
288
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
289
+ }
290
+ const comboData = toRecord(combo.data);
291
+ const currentConfig = toRecord(
292
+ Object.keys(toRecord(combo.config)).length > 0 ? combo.config : comboData.config
293
+ );
294
+ const normalizedStrategy = normalizeRoutingStrategy(args.strategy);
295
+ let nextConfig = void 0;
296
+ if (normalizedStrategy === "auto" && args.autoRoutingStrategy) {
297
+ const currentAutoConfig = toRecord(currentConfig.auto);
298
+ nextConfig = {
299
+ ...currentConfig,
300
+ auto: {
301
+ ...currentAutoConfig,
302
+ routerStrategy: args.autoRoutingStrategy
303
+ }
304
+ };
305
+ }
306
+ const payload = { strategy: normalizedStrategy };
307
+ if (nextConfig && Object.keys(nextConfig).length > 0) {
308
+ payload.config = nextConfig;
309
+ }
310
+ const updatedCombo = toRecord(
311
+ await apiFetch(`/api/combos/${encodeURIComponent(comboId)}`, {
312
+ method: "PUT",
313
+ body: JSON.stringify(payload)
314
+ })
315
+ );
316
+ const updatedConfig = toRecord(updatedCombo.config);
317
+ const resolvedAutoStrategy = toString(toRecord(updatedConfig.auto).routerStrategy) || (normalizedStrategy === "auto" ? args.autoRoutingStrategy ?? "rules" : "");
318
+ const result = {
319
+ success: true,
320
+ combo: {
321
+ id: toString(updatedCombo.id, comboId),
322
+ name: toString(updatedCombo.name, toString(combo.name, comboId)),
323
+ strategy: toString(updatedCombo.strategy, normalizedStrategy),
324
+ autoRoutingStrategy: toString(updatedCombo.strategy, normalizedStrategy) === "auto" ? resolvedAutoStrategy : null
325
+ }
326
+ };
327
+ await logToolCall("omniroute_set_routing_strategy", args, result, Date.now() - start, true);
328
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
329
+ } catch (err) {
330
+ const msg = err instanceof Error ? err.message : String(err);
331
+ await logToolCall("omniroute_set_routing_strategy", args, null, Date.now() - start, false, msg);
332
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
333
+ }
334
+ }
335
+ async function handleSetResilienceProfile(args) {
336
+ const start = Date.now();
337
+ try {
338
+ const settings = RESILIENCE_PROFILES[args.profile];
339
+ if (!settings) {
340
+ return {
341
+ content: [{ type: "text", text: `Error: Invalid profile "${args.profile}"` }],
342
+ isError: true
343
+ };
344
+ }
345
+ await apiFetch("/api/resilience", {
346
+ method: "PATCH",
347
+ body: JSON.stringify(settings)
348
+ });
349
+ const result = { applied: true, profile: args.profile, settings };
350
+ await logToolCall("omniroute_set_resilience_profile", args, result, Date.now() - start, true);
351
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
352
+ } catch (err) {
353
+ const msg = err instanceof Error ? err.message : String(err);
354
+ await logToolCall(
355
+ "omniroute_set_resilience_profile",
356
+ args,
357
+ null,
358
+ Date.now() - start,
359
+ false,
360
+ msg
361
+ );
362
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
363
+ }
364
+ }
365
+ async function handleTestCombo(args) {
366
+ const start = Date.now();
367
+ try {
368
+ const combos = normalizeCombosResponse(await apiFetch("/api/combos"));
369
+ const combo = combos.find(
370
+ (comboEntry) => toString(comboEntry.id) === args.comboId || toString(comboEntry.name) === args.comboId
371
+ );
372
+ if (!combo) {
373
+ return {
374
+ content: [
375
+ {
376
+ type: "text",
377
+ text: JSON.stringify({ error: `Combo "${args.comboId}" not found` })
378
+ }
379
+ ],
380
+ isError: true
381
+ };
382
+ }
383
+ const models = getComboModels(combo);
384
+ const prompt = (args.testPrompt || "Say hello").slice(0, 200);
385
+ const results = await Promise.allSettled(
386
+ models.map(async (model) => {
387
+ const providerStart = Date.now();
388
+ try {
389
+ const resp = toRecord(
390
+ await apiFetch("/v1/chat/completions", {
391
+ method: "POST",
392
+ body: JSON.stringify({
393
+ model: model.model || "auto",
394
+ messages: [{ role: "user", content: prompt }],
395
+ max_tokens: 50,
396
+ stream: false,
397
+ "x-provider": model.provider
398
+ })
399
+ })
400
+ );
401
+ const usage = toRecord(resp.usage);
402
+ return {
403
+ provider: model.provider,
404
+ model: model.model || toString(resp.model, "unknown"),
405
+ success: true,
406
+ latencyMs: Date.now() - providerStart,
407
+ cost: toNumber(resp.cost, 0),
408
+ tokenCount: toNumber(usage.prompt_tokens, 0) + toNumber(usage.completion_tokens, 0)
409
+ };
410
+ } catch (err) {
411
+ return {
412
+ provider: model.provider,
413
+ model: model.model || "unknown",
414
+ success: false,
415
+ latencyMs: Date.now() - providerStart,
416
+ cost: 0,
417
+ tokenCount: 0,
418
+ error: err instanceof Error ? err.message : String(err)
419
+ };
420
+ }
421
+ })
422
+ );
423
+ const providerResults = results.map(
424
+ (r) => r.status === "fulfilled" ? r.value : {
425
+ provider: "unknown",
426
+ model: "unknown",
427
+ success: false,
428
+ latencyMs: 0,
429
+ cost: 0,
430
+ tokenCount: 0,
431
+ error: "Promise rejected"
432
+ }
433
+ );
434
+ const successful = providerResults.filter((r) => r.success);
435
+ const fastest = successful.sort((a, b) => a.latencyMs - b.latencyMs)[0];
436
+ const cheapest = successful.sort((a, b) => a.cost - b.cost)[0];
437
+ const result = {
438
+ results: providerResults,
439
+ summary: {
440
+ totalProviders: providerResults.length,
441
+ successful: successful.length,
442
+ fastestProvider: fastest?.provider || "none",
443
+ cheapestProvider: cheapest?.provider || "none"
444
+ }
445
+ };
446
+ await logToolCall(
447
+ "omniroute_test_combo",
448
+ { comboId: args.comboId },
449
+ result.summary,
450
+ Date.now() - start,
451
+ true
452
+ );
453
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
454
+ } catch (err) {
455
+ const msg = err instanceof Error ? err.message : String(err);
456
+ await logToolCall("omniroute_test_combo", args, null, Date.now() - start, false, msg);
457
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
458
+ }
459
+ }
460
+ async function handleGetProviderMetrics(args) {
461
+ const start = Date.now();
462
+ try {
463
+ const [healthRaw, quotaRaw, analyticsRaw] = await Promise.allSettled([
464
+ apiFetch("/api/monitoring/health"),
465
+ apiFetch(`/api/usage/quota?provider=${encodeURIComponent(args.provider)}`),
466
+ apiFetch(`/api/usage/analytics?period=session&provider=${encodeURIComponent(args.provider)}`)
467
+ ]);
468
+ const health = healthRaw.status === "fulfilled" ? toRecord(healthRaw.value) : {};
469
+ const quota = quotaRaw.status === "fulfilled" ? normalizeQuotaResponse(quotaRaw.value, { provider: args.provider }) : normalizeQuotaResponse({});
470
+ const analytics = analyticsRaw.status === "fulfilled" ? toRecord(analyticsRaw.value) : {};
471
+ const cb = toArrayOfRecords(health.circuitBreakers).find(
472
+ (breaker) => toString(breaker.provider) === args.provider
473
+ );
474
+ const providerQuota = quota.providers.find((p) => p.provider === args.provider) || null;
475
+ const result = {
476
+ provider: args.provider,
477
+ successRate: toNumber(analytics.successRate, 1),
478
+ requestCount: toNumber(analytics.requestCount, 0),
479
+ avgLatencyMs: toNumber(analytics.avgLatencyMs, 0),
480
+ p50LatencyMs: toNumber(analytics.p50LatencyMs, 0),
481
+ p95LatencyMs: toNumber(analytics.p95LatencyMs, 0),
482
+ p99LatencyMs: toNumber(analytics.p99LatencyMs, 0),
483
+ errorRate: toNumber(analytics.errorRate, 0),
484
+ lastError: toString(analytics.lastError) || null,
485
+ circuitBreakerState: toString(cb?.state, "CLOSED"),
486
+ quotaInfo: providerQuota ? {
487
+ used: providerQuota.quotaUsed,
488
+ total: providerQuota.quotaTotal,
489
+ resetAt: providerQuota.resetAt
490
+ } : { used: 0, total: null, resetAt: null }
491
+ };
492
+ await logToolCall("omniroute_get_provider_metrics", args, result, Date.now() - start, true);
493
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
494
+ } catch (err) {
495
+ const msg = err instanceof Error ? err.message : String(err);
496
+ await logToolCall("omniroute_get_provider_metrics", args, null, Date.now() - start, false, msg);
497
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
498
+ }
499
+ }
500
+ async function handleBestComboForTask(args) {
501
+ const start = Date.now();
502
+ try {
503
+ const fitness = TASK_FITNESS[args.taskType] || TASK_FITNESS.coding;
504
+ const combos = normalizeCombosResponse(await apiFetch("/api/combos"));
505
+ const enabledCombos = combos.filter((combo) => combo.enabled !== false);
506
+ if (enabledCombos.length === 0) {
507
+ return {
508
+ content: [
509
+ { type: "text", text: JSON.stringify({ error: "No enabled combos available" }) }
510
+ ],
511
+ isError: true
512
+ };
513
+ }
514
+ const scored = enabledCombos.map((combo) => {
515
+ const models = getComboModels(combo);
516
+ let score = 0;
517
+ for (const model of models) {
518
+ const prefIdx = fitness.preferred.indexOf(model.provider);
519
+ if (prefIdx >= 0) score += (fitness.preferred.length - prefIdx) * 10;
520
+ }
521
+ const name = toString(combo.name).toLowerCase();
522
+ for (const trait of fitness.traits) {
523
+ if (name.includes(trait)) score += 5;
524
+ }
525
+ const isFree = name.includes("free") || models.every((model) => model.provider.toLowerCase().includes("free"));
526
+ return { combo, score, isFree };
527
+ });
528
+ scored.sort((a, b) => b.score - a.score);
529
+ const best = scored[0];
530
+ const alternatives = scored.slice(1, 4).map((s) => ({
531
+ id: s.combo.id,
532
+ name: s.combo.name,
533
+ tradeoff: s.isFree ? "free but may have limits" : s.score < best.score * 0.5 ? "cheaper but slower" : "similar quality, different providers"
534
+ }));
535
+ const freeAlt = scored.find((s) => s.isFree && s !== best);
536
+ const result = {
537
+ recommendedCombo: {
538
+ id: best.combo.id,
539
+ name: best.combo.name,
540
+ reason: `Best match for "${args.taskType}": preferred providers (${fitness.preferred.slice(0, 3).join(", ")})`
541
+ },
542
+ alternatives,
543
+ freeAlternative: freeAlt ? { id: freeAlt.combo.id, name: freeAlt.combo.name } : null
544
+ };
545
+ await logToolCall(
546
+ "omniroute_best_combo_for_task",
547
+ args,
548
+ result.recommendedCombo,
549
+ Date.now() - start,
550
+ true
551
+ );
552
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
553
+ } catch (err) {
554
+ const msg = err instanceof Error ? err.message : String(err);
555
+ await logToolCall("omniroute_best_combo_for_task", args, null, Date.now() - start, false, msg);
556
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
557
+ }
558
+ }
559
+ async function handleExplainRoute(args) {
560
+ const start = Date.now();
561
+ try {
562
+ let decision = null;
563
+ try {
564
+ decision = toRecord(
565
+ await apiFetch(`/api/routing/decisions/${encodeURIComponent(args.requestId)}`)
566
+ );
567
+ } catch {
568
+ }
569
+ const result = decision ? {
570
+ requestId: args.requestId,
571
+ decision: {
572
+ comboUsed: decision.comboUsed || "default",
573
+ providerSelected: decision.providerSelected || "unknown",
574
+ modelUsed: decision.modelUsed || "unknown",
575
+ score: decision.score || 0,
576
+ factors: decision.factors || [
577
+ { name: "health", value: 1, weight: 0.3, contribution: 0.3 },
578
+ { name: "quota", value: 1, weight: 0.25, contribution: 0.25 },
579
+ { name: "cost", value: 0.8, weight: 0.2, contribution: 0.16 },
580
+ { name: "latency", value: 0.9, weight: 0.15, contribution: 0.135 },
581
+ { name: "task_fit", value: 0.7, weight: 0.1, contribution: 0.07 }
582
+ ],
583
+ fallbacksTriggered: decision.fallbacksTriggered || [],
584
+ costActual: decision.costActual || 0,
585
+ latencyActual: decision.latencyActual || 0
586
+ }
587
+ } : {
588
+ requestId: args.requestId,
589
+ decision: {
590
+ comboUsed: "unknown",
591
+ providerSelected: "unknown",
592
+ modelUsed: "unknown",
593
+ score: 0,
594
+ factors: [],
595
+ fallbacksTriggered: [],
596
+ costActual: 0,
597
+ latencyActual: 0
598
+ },
599
+ note: "Routing decision not found. The /api/routing/decisions endpoint may not be implemented yet, or the requestId is invalid."
600
+ };
601
+ await logToolCall(
602
+ "omniroute_explain_route",
603
+ args,
604
+ { requestId: args.requestId },
605
+ Date.now() - start,
606
+ true
607
+ );
608
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
609
+ } catch (err) {
610
+ const msg = err instanceof Error ? err.message : String(err);
611
+ await logToolCall("omniroute_explain_route", args, null, Date.now() - start, false, msg);
612
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
613
+ }
614
+ }
615
+ async function handleSyncPricing(args) {
616
+ const start = Date.now();
617
+ try {
618
+ const result = toRecord(
619
+ await apiFetch("/api/pricing/sync", {
620
+ method: "POST",
621
+ body: JSON.stringify({
622
+ sources: args.sources,
623
+ dryRun: args.dryRun ?? false
624
+ })
625
+ })
626
+ );
627
+ await logToolCall("omniroute_sync_pricing", args, result, Date.now() - start, true);
628
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
629
+ } catch (err) {
630
+ const msg = err instanceof Error ? err.message : String(err);
631
+ await logToolCall("omniroute_sync_pricing", args, null, Date.now() - start, false, msg);
632
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
633
+ }
634
+ }
635
+ async function handleGetSessionSnapshot() {
636
+ const start = Date.now();
637
+ try {
638
+ const analytics = toRecord(
639
+ await apiFetch("/api/usage/analytics?period=session").catch(() => ({}))
640
+ );
641
+ const tokenCount = toRecord(analytics.tokenCount);
642
+ const byModel = toArrayOfRecords(analytics.byModel);
643
+ const byProvider = toArrayOfRecords(analytics.byProvider);
644
+ const result = {
645
+ sessionStart: toString(analytics.sessionStart, (/* @__PURE__ */ new Date()).toISOString()),
646
+ duration: toString(analytics.duration, "unknown"),
647
+ requestCount: toNumber(analytics.requestCount, 0),
648
+ costTotal: toNumber(analytics.totalCost, 0),
649
+ tokenCount: {
650
+ prompt: toNumber(tokenCount.prompt, 0),
651
+ completion: toNumber(tokenCount.completion, 0)
652
+ },
653
+ topModels: byModel.slice(0, 5).map((model) => ({
654
+ model: toString(model.model, "unknown"),
655
+ count: toNumber(model.requests, 0)
656
+ })),
657
+ topProviders: byProvider.slice(0, 5).map((provider) => ({
658
+ provider: toString(provider.name, "unknown"),
659
+ count: toNumber(provider.requests, 0)
660
+ })),
661
+ errors: toNumber(analytics.errorCount, 0),
662
+ fallbacks: toNumber(analytics.fallbackCount, 0),
663
+ budgetGuard: activeBudgetGuard ? {
664
+ active: true,
665
+ remaining: Math.max(0, activeBudgetGuard.maxCost - activeBudgetGuard.spent),
666
+ action: activeBudgetGuard.action
667
+ } : null
668
+ };
669
+ await logToolCall(
670
+ "omniroute_get_session_snapshot",
671
+ {},
672
+ { requestCount: result.requestCount },
673
+ Date.now() - start,
674
+ true
675
+ );
676
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
677
+ } catch (err) {
678
+ const msg = err instanceof Error ? err.message : String(err);
679
+ await logToolCall("omniroute_get_session_snapshot", {}, null, Date.now() - start, false, msg);
680
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
681
+ }
682
+ }
683
+ async function handleDbHealthCheck(args) {
684
+ const start = Date.now();
685
+ const autoRepair = args.autoRepair === true;
686
+ try {
687
+ const { runManagedDbHealthCheck } = await import('../../dist/lib/db/core.ts.js');
688
+ const result = runManagedDbHealthCheck({ autoRepair });
689
+ await logToolCall(
690
+ "omniroute_db_health_check",
691
+ args,
692
+ {
693
+ isHealthy: toBoolean(result.isHealthy, false),
694
+ repairedCount: toNumber(result.repairedCount, 0)
695
+ },
696
+ Date.now() - start,
697
+ true
698
+ );
699
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
700
+ } catch (err) {
701
+ const msg = err instanceof Error ? err.message : String(err);
702
+ await logToolCall("omniroute_db_health_check", args, null, Date.now() - start, false, msg);
703
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
704
+ }
705
+ }
706
+ async function handleCacheStats() {
707
+ const start = Date.now();
708
+ try {
709
+ const raw = toRecord(await apiFetch("/api/cache"));
710
+ const semanticCache = toRecord(raw.semanticCache);
711
+ const promptCache = raw.promptCache ? toRecord(raw.promptCache) : null;
712
+ const idempotency = toRecord(raw.idempotency);
713
+ const config = raw.config ? toRecord(raw.config) : null;
714
+ const result = {
715
+ semanticCache: {
716
+ memoryEntries: toNumber(semanticCache.memoryEntries, 0),
717
+ dbEntries: toNumber(semanticCache.dbEntries, 0),
718
+ hits: toNumber(semanticCache.hits, 0),
719
+ misses: toNumber(semanticCache.misses, 0),
720
+ hitRate: toString(semanticCache.hitRate, "0%"),
721
+ tokensSaved: toNumber(semanticCache.tokensSaved, 0)
722
+ },
723
+ promptCache: promptCache ? {
724
+ totalRequests: toNumber(promptCache.totalRequests, 0),
725
+ requestsWithCacheControl: toNumber(promptCache.requestsWithCacheControl, 0),
726
+ totalInputTokens: toNumber(promptCache.totalInputTokens, 0),
727
+ totalCachedTokens: toNumber(promptCache.totalCachedTokens, 0),
728
+ totalCacheCreationTokens: toNumber(promptCache.totalCacheCreationTokens, 0),
729
+ tokensSaved: toNumber(promptCache.tokensSaved, 0),
730
+ estimatedCostSaved: toNumber(promptCache.estimatedCostSaved, 0)
731
+ } : null,
732
+ idempotency: {
733
+ activeKeys: toNumber(idempotency.activeKeys, 0),
734
+ windowMs: toNumber(idempotency.windowMs, 0)
735
+ },
736
+ config: config ? {
737
+ semanticCacheEnabled: toBoolean(config.semanticCacheEnabled, true)
738
+ } : void 0
739
+ };
740
+ await logToolCall("omniroute_cache_stats", {}, result, Date.now() - start, true);
741
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
742
+ } catch (err) {
743
+ const msg = err instanceof Error ? err.message : String(err);
744
+ await logToolCall("omniroute_cache_stats", {}, null, Date.now() - start, false, msg);
745
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
746
+ }
747
+ }
748
+ async function handleCacheFlush(args) {
749
+ const start = Date.now();
750
+ try {
751
+ const params = new URLSearchParams();
752
+ let scope = "all";
753
+ if (args.signature) {
754
+ params.set("signature", args.signature);
755
+ scope = "signature";
756
+ } else if (args.model) {
757
+ params.set("model", args.model);
758
+ scope = "model";
759
+ }
760
+ const query = params.toString();
761
+ const path = query ? `/api/cache?${query}` : "/api/cache";
762
+ const raw = toRecord(
763
+ await apiFetch(path, {
764
+ method: "DELETE"
765
+ })
766
+ );
767
+ const result = {
768
+ ok: toBoolean(raw.ok, true),
769
+ invalidated: toNumber(raw.invalidated ?? raw.cleared, 0),
770
+ scope
771
+ };
772
+ await logToolCall("omniroute_cache_flush", args, result, Date.now() - start, true);
773
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
774
+ } catch (err) {
775
+ const msg = err instanceof Error ? err.message : String(err);
776
+ await logToolCall("omniroute_cache_flush", args, null, Date.now() - start, false, msg);
777
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
778
+ }
779
+ }
780
+ async function handleOneproxyFetch(args = {}) {
781
+ const start = Date.now();
782
+ try {
783
+ const params = new URLSearchParams();
784
+ if (args.protocol) params.set("protocol", args.protocol);
785
+ if (args.countryCode) params.set("countryCode", args.countryCode);
786
+ if (args.minQuality) params.set("minQuality", String(args.minQuality));
787
+ if (args.limit) params.set("limit", String(args.limit));
788
+ const query = params.toString();
789
+ const path = query ? `/api/settings/oneproxy?${query}` : "/api/settings/oneproxy";
790
+ const raw = toRecord(await apiFetch(path));
791
+ const items = toArrayOfRecords(raw.items).map((r) => ({
792
+ id: toString(r.id, ""),
793
+ host: toString(r.host, ""),
794
+ port: toNumber(r.port, 0),
795
+ type: toString(r.type, "http"),
796
+ countryCode: typeof r.country_code === "string" ? r.country_code : null,
797
+ qualityScore: r.quality_score != null ? toNumber(r.quality_score) : null,
798
+ latencyMs: r.latency_ms != null ? toNumber(r.latency_ms) : null,
799
+ anonymity: typeof r.anonymity === "string" ? r.anonymity : null,
800
+ googleAccess: r.google_access === 1 || r.google_access === true,
801
+ status: toString(r.status, "active")
802
+ }));
803
+ const result = { items, total: toNumber(raw.total, items.length) };
804
+ await logToolCall("omniroute_oneproxy_fetch", args, result, Date.now() - start, true);
805
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
806
+ } catch (err) {
807
+ const msg = err instanceof Error ? err.message : String(err);
808
+ await logToolCall("omniroute_oneproxy_fetch", args, null, Date.now() - start, false, msg);
809
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
810
+ }
811
+ }
812
+ async function handleOneproxyRotate(args = {}) {
813
+ const start = Date.now();
814
+ try {
815
+ const body = {};
816
+ if (args.strategy) body.strategy = args.strategy;
817
+ const raw = toRecord(
818
+ await apiFetch("/api/settings/oneproxy/rotate", {
819
+ method: "POST",
820
+ body: JSON.stringify(body)
821
+ })
822
+ );
823
+ const result = {
824
+ id: toString(raw.id, ""),
825
+ host: toString(raw.host, ""),
826
+ port: toNumber(raw.port, 0),
827
+ type: toString(raw.type, "http"),
828
+ countryCode: typeof raw.country_code === "string" ? raw.country_code : null,
829
+ qualityScore: raw.quality_score != null ? toNumber(raw.quality_score) : null,
830
+ latencyMs: raw.latency_ms != null ? toNumber(raw.latency_ms) : null
831
+ };
832
+ await logToolCall("omniroute_oneproxy_rotate", args, result, Date.now() - start, true);
833
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
834
+ } catch (err) {
835
+ const msg = err instanceof Error ? err.message : String(err);
836
+ await logToolCall("omniroute_oneproxy_rotate", args, null, Date.now() - start, false, msg);
837
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
838
+ }
839
+ }
840
+ async function handleOneproxyStats(args = {}) {
841
+ const start = Date.now();
842
+ try {
843
+ const raw = toRecord(await apiFetch("/api/settings/oneproxy?action=stats"));
844
+ const statsRaw = toRecord(raw.stats);
845
+ const statusRaw = toRecord(raw.status);
846
+ const stats = {
847
+ total: toNumber(statsRaw.total, 0),
848
+ active: toNumber(statsRaw.active, 0),
849
+ avgQuality: statsRaw.avg_quality != null ? toNumber(statsRaw.avg_quality) : null,
850
+ lastValidated: typeof statsRaw.last_validated === "string" ? statsRaw.last_validated : null,
851
+ byProtocol: toArrayOfRecords(statsRaw.by_protocol || statsRaw.byProtocol).map((r) => ({
852
+ protocol: toString(r.protocol, ""),
853
+ count: toNumber(r.count, 0)
854
+ })),
855
+ byCountry: toArrayOfRecords(statsRaw.by_country || statsRaw.byCountry).map((r) => ({
856
+ countryCode: toString(r.countryCode || r.country_code, ""),
857
+ count: toNumber(r.count, 0)
858
+ }))
859
+ };
860
+ const status = {
861
+ lastSyncSuccess: toBoolean(statusRaw.last_sync_success, false),
862
+ lastSyncError: typeof statusRaw.last_sync_error === "string" ? statusRaw.last_sync_error : null,
863
+ lastSyncAt: typeof statusRaw.last_sync_at === "string" ? statusRaw.last_sync_at : null,
864
+ lastSyncCount: toNumber(statusRaw.last_sync_count, 0),
865
+ consecutiveFailures: toNumber(statusRaw.consecutive_failures, 0)
866
+ };
867
+ const result = { stats, status };
868
+ await logToolCall("omniroute_oneproxy_stats", args, result, Date.now() - start, true);
869
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
870
+ } catch (err) {
871
+ const msg = err instanceof Error ? err.message : String(err);
872
+ await logToolCall("omniroute_oneproxy_stats", args, null, Date.now() - start, false, msg);
873
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
874
+ }
875
+ }
876
+ export {
877
+ handleBestComboForTask,
878
+ handleCacheFlush,
879
+ handleCacheStats,
880
+ handleDbHealthCheck,
881
+ handleExplainRoute,
882
+ handleGetProviderMetrics,
883
+ handleGetSessionSnapshot,
884
+ handleOneproxyFetch,
885
+ handleOneproxyRotate,
886
+ handleOneproxyStats,
887
+ handleSetBudgetGuard,
888
+ handleSetResilienceProfile,
889
+ handleSetRoutingStrategy,
890
+ handleSimulateRoute,
891
+ handleSyncPricing,
892
+ handleTestCombo
893
+ };