@farhanic017/octocode 3.5.0

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 (716) hide show
  1. package/.swarm_lessons.json +85 -0
  2. package/AGENTS.md +151 -0
  3. package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
  4. package/Dockerfile +18 -0
  5. package/README.md +15 -0
  6. package/bin/octocode +31 -0
  7. package/bunfig.toml +7 -0
  8. package/git +0 -0
  9. package/package.json +151 -0
  10. package/parsers-config.ts +1 -0
  11. package/script/bench-search.ts +115 -0
  12. package/script/bench-test-suite.ts +52 -0
  13. package/script/build.ts +243 -0
  14. package/script/generate.ts +14 -0
  15. package/script/httpapi-exercise.ts +1 -0
  16. package/script/octo-shim.mjs +34 -0
  17. package/script/postinstall.mjs +198 -0
  18. package/script/profile-test-files.ts +42 -0
  19. package/script/publish.ts +211 -0
  20. package/script/run-workspace-server +106 -0
  21. package/script/schema.ts +77 -0
  22. package/script/time.ts +6 -0
  23. package/script/trace-imports.ts +154 -0
  24. package/specs/effect/error-boundaries-plan.md +235 -0
  25. package/specs/effect/errors.md +207 -0
  26. package/specs/effect/facades.md +218 -0
  27. package/specs/effect/guide.md +247 -0
  28. package/specs/effect/instance-context.md +13 -0
  29. package/specs/effect/loose-ends.md +30 -0
  30. package/specs/effect/migration.md +62 -0
  31. package/specs/effect/routes.md +61 -0
  32. package/specs/effect/schema.md +88 -0
  33. package/specs/effect/server-package.md +58 -0
  34. package/specs/effect/todo.md +241 -0
  35. package/specs/effect/tools.md +88 -0
  36. package/specs/openapi-translation-cleanup.md +204 -0
  37. package/specs/tui-plugins.md +544 -0
  38. package/specs/v2/api.ts +67 -0
  39. package/specs/v2/message-shape.md +136 -0
  40. package/specs/v2/notifications.md +13 -0
  41. package/specs/v2/tui-command-shim.md +67 -0
  42. package/src/account/account.ts +459 -0
  43. package/src/account/repo.ts +170 -0
  44. package/src/account/schema.ts +99 -0
  45. package/src/account/url.ts +8 -0
  46. package/src/acp/agent.ts +95 -0
  47. package/src/acp/config-option.ts +203 -0
  48. package/src/acp/content.ts +250 -0
  49. package/src/acp/directory.ts +210 -0
  50. package/src/acp/error.ts +90 -0
  51. package/src/acp/event.ts +345 -0
  52. package/src/acp/permission.ts +145 -0
  53. package/src/acp/profile.ts +42 -0
  54. package/src/acp/service.ts +1062 -0
  55. package/src/acp/tool.ts +321 -0
  56. package/src/acp/usage.ts +239 -0
  57. package/src/agent/agent.ts +451 -0
  58. package/src/agent/generate.txt +75 -0
  59. package/src/agent/prompt/compaction.txt +30 -0
  60. package/src/agent/prompt/explore.txt +18 -0
  61. package/src/agent/prompt/summary.txt +11 -0
  62. package/src/agent/prompt/title.txt +50 -0
  63. package/src/agent/subagent-permissions.ts +35 -0
  64. package/src/audio.d.ts +14 -0
  65. package/src/auth/index.ts +96 -0
  66. package/src/background/job.ts +36 -0
  67. package/src/bus/global.ts +22 -0
  68. package/src/cli/bootstrap.ts +11 -0
  69. package/src/cli/cmd/account.ts +264 -0
  70. package/src/cli/cmd/acp.ts +76 -0
  71. package/src/cli/cmd/agent.ts +259 -0
  72. package/src/cli/cmd/attach.ts +97 -0
  73. package/src/cli/cmd/cmd.ts +7 -0
  74. package/src/cli/cmd/db.ts +62 -0
  75. package/src/cli/cmd/debug/agent.handler.ts +188 -0
  76. package/src/cli/cmd/debug/agent.ts +27 -0
  77. package/src/cli/cmd/debug/config.ts +14 -0
  78. package/src/cli/cmd/debug/file.ts +87 -0
  79. package/src/cli/cmd/debug/index.ts +87 -0
  80. package/src/cli/cmd/debug/lsp.ts +51 -0
  81. package/src/cli/cmd/debug/ripgrep.ts +99 -0
  82. package/src/cli/cmd/debug/scrap.ts +18 -0
  83. package/src/cli/cmd/debug/skill.ts +15 -0
  84. package/src/cli/cmd/debug/snapshot.ts +50 -0
  85. package/src/cli/cmd/debug/startup.ts +11 -0
  86. package/src/cli/cmd/debug/v2.ts +46 -0
  87. package/src/cli/cmd/export.ts +292 -0
  88. package/src/cli/cmd/generate.ts +54 -0
  89. package/src/cli/cmd/github.handler.ts +1594 -0
  90. package/src/cli/cmd/github.shared.ts +30 -0
  91. package/src/cli/cmd/github.ts +42 -0
  92. package/src/cli/cmd/import.ts +224 -0
  93. package/src/cli/cmd/mcp.ts +859 -0
  94. package/src/cli/cmd/models.ts +66 -0
  95. package/src/cli/cmd/plug.ts +230 -0
  96. package/src/cli/cmd/pr.ts +115 -0
  97. package/src/cli/cmd/prompt-display.ts +1 -0
  98. package/src/cli/cmd/providers.ts +550 -0
  99. package/src/cli/cmd/run/demo.ts +1274 -0
  100. package/src/cli/cmd/run/entry.body.ts +205 -0
  101. package/src/cli/cmd/run/footer.command.tsx +1088 -0
  102. package/src/cli/cmd/run/footer.menu.tsx +353 -0
  103. package/src/cli/cmd/run/footer.permission.tsx +482 -0
  104. package/src/cli/cmd/run/footer.prompt.tsx +1351 -0
  105. package/src/cli/cmd/run/footer.question.tsx +580 -0
  106. package/src/cli/cmd/run/footer.subagent.tsx +180 -0
  107. package/src/cli/cmd/run/footer.ts +1153 -0
  108. package/src/cli/cmd/run/footer.view.tsx +992 -0
  109. package/src/cli/cmd/run/footer.width.ts +27 -0
  110. package/src/cli/cmd/run/otel.ts +117 -0
  111. package/src/cli/cmd/run/permission.shared.ts +256 -0
  112. package/src/cli/cmd/run/prompt.editor.ts +157 -0
  113. package/src/cli/cmd/run/prompt.shared.ts +153 -0
  114. package/src/cli/cmd/run/question.shared.ts +340 -0
  115. package/src/cli/cmd/run/runtime.boot.ts +202 -0
  116. package/src/cli/cmd/run/runtime.lifecycle.ts +431 -0
  117. package/src/cli/cmd/run/runtime.queue.ts +349 -0
  118. package/src/cli/cmd/run/runtime.shared.ts +17 -0
  119. package/src/cli/cmd/run/runtime.stdin.ts +37 -0
  120. package/src/cli/cmd/run/runtime.ts +905 -0
  121. package/src/cli/cmd/run/scrollback.shared.ts +92 -0
  122. package/src/cli/cmd/run/scrollback.surface.ts +448 -0
  123. package/src/cli/cmd/run/scrollback.writer.tsx +353 -0
  124. package/src/cli/cmd/run/splash.ts +284 -0
  125. package/src/cli/cmd/run/stream.transport.ts +1465 -0
  126. package/src/cli/cmd/run/stream.ts +175 -0
  127. package/src/cli/cmd/run/subagent-data.ts +876 -0
  128. package/src/cli/cmd/run/theme.ts +690 -0
  129. package/src/cli/cmd/run/tool.ts +1489 -0
  130. package/src/cli/cmd/run/trace.ts +94 -0
  131. package/src/cli/cmd/run/turn-summary.ts +47 -0
  132. package/src/cli/cmd/run/types.ts +350 -0
  133. package/src/cli/cmd/run/variant.shared.ts +215 -0
  134. package/src/cli/cmd/run.ts +897 -0
  135. package/src/cli/cmd/serve.ts +24 -0
  136. package/src/cli/cmd/stats.ts +393 -0
  137. package/src/cli/cmd/tui.ts +264 -0
  138. package/src/cli/cmd/uninstall.ts +353 -0
  139. package/src/cli/cmd/upgrade.ts +74 -0
  140. package/src/cli/cmd/web.ts +84 -0
  141. package/src/cli/effect/prompt.ts +37 -0
  142. package/src/cli/effect-cmd.ts +96 -0
  143. package/src/cli/error.ts +118 -0
  144. package/src/cli/heap.ts +66 -0
  145. package/src/cli/logo.ts +1 -0
  146. package/src/cli/network.ts +64 -0
  147. package/src/cli/tui/layer.ts +7 -0
  148. package/src/cli/tui/photon_rs_bg-bq08arze.wasm +0 -0
  149. package/src/cli/tui/tree-sitter-3jzf13jk.wasm +0 -0
  150. package/src/cli/tui/tree-sitter-bash-hq5s6fxb.wasm +0 -0
  151. package/src/cli/tui/tree-sitter-powershell-ryb2ffqs.wasm +0 -0
  152. package/src/cli/tui/worker.ts +99 -0
  153. package/src/cli/ui.ts +144 -0
  154. package/src/cli/upgrade.ts +53 -0
  155. package/src/command/index.ts +189 -0
  156. package/src/command/template/initialize.txt +66 -0
  157. package/src/command/template/review.txt +101 -0
  158. package/src/command/terminal-ai-commands.ts +567 -0
  159. package/src/config/agent.ts +68 -0
  160. package/src/config/command.ts +45 -0
  161. package/src/config/config.ts +686 -0
  162. package/src/config/entry-name.ts +19 -0
  163. package/src/config/managed.ts +77 -0
  164. package/src/config/markdown.ts +36 -0
  165. package/src/config/parse.ts +79 -0
  166. package/src/config/paths.ts +47 -0
  167. package/src/config/plugin.ts +79 -0
  168. package/src/config/reference.ts +48 -0
  169. package/src/config/tui-cwd.ts +5 -0
  170. package/src/config/tui-host-attention.ts +21 -0
  171. package/src/config/tui-migrate.ts +156 -0
  172. package/src/config/tui.ts +294 -0
  173. package/src/config/variable.ts +91 -0
  174. package/src/control-plane/adapters/index.ts +41 -0
  175. package/src/control-plane/adapters/worktree.ts +96 -0
  176. package/src/control-plane/dev/README.md +19 -0
  177. package/src/control-plane/dev/debug-workspace-plugin.ts +73 -0
  178. package/src/control-plane/types.ts +59 -0
  179. package/src/control-plane/util.ts +39 -0
  180. package/src/control-plane/workspace-adapter-runtime.ts +51 -0
  181. package/src/control-plane/workspace-context.ts +26 -0
  182. package/src/control-plane/workspace.ts +1075 -0
  183. package/src/desktop/agent.ts +147 -0
  184. package/src/desktop/index.ts +247 -0
  185. package/src/desktop/platform.ts +97 -0
  186. package/src/desktop/stream.ts +64 -0
  187. package/src/desktop/vision.ts +52 -0
  188. package/src/desktop/websocket.ts +120 -0
  189. package/src/effect/app-runtime.ts +133 -0
  190. package/src/effect/bootstrap-runtime.ts +23 -0
  191. package/src/effect/bridge.ts +84 -0
  192. package/src/effect/config-service.ts +67 -0
  193. package/src/effect/instance-ref.ts +11 -0
  194. package/src/effect/instance-registry.ts +12 -0
  195. package/src/effect/instance-state.ts +72 -0
  196. package/src/effect/promise.ts +17 -0
  197. package/src/effect/run-service.ts +47 -0
  198. package/src/effect/runner.ts +217 -0
  199. package/src/effect/runtime-flags.ts +76 -0
  200. package/src/env/index.ts +40 -0
  201. package/src/event-v2-bridge.ts +76 -0
  202. package/src/format/formatter.ts +404 -0
  203. package/src/format/index.ts +212 -0
  204. package/src/git/index.ts +347 -0
  205. package/src/id/id.ts +80 -0
  206. package/src/ide/index.ts +70 -0
  207. package/src/image/image.ts +177 -0
  208. package/src/index.ts +208 -0
  209. package/src/installation/index.ts +350 -0
  210. package/src/lsp/client.ts +686 -0
  211. package/src/lsp/diagnostic.ts +29 -0
  212. package/src/lsp/language.ts +121 -0
  213. package/src/lsp/launch.ts +21 -0
  214. package/src/lsp/lsp.ts +517 -0
  215. package/src/lsp/server.ts +2116 -0
  216. package/src/markdown.d.ts +4 -0
  217. package/src/mcp/auth.ts +171 -0
  218. package/src/mcp/builtin.ts +92 -0
  219. package/src/mcp/index.ts +1000 -0
  220. package/src/mcp/oauth-callback.ts +232 -0
  221. package/src/mcp/oauth-provider.ts +217 -0
  222. package/src/node.ts +5 -0
  223. package/src/patch/index.ts +689 -0
  224. package/src/permission/arity.ts +163 -0
  225. package/src/permission/evaluate.ts +1 -0
  226. package/src/permission/index.ts +230 -0
  227. package/src/plugin/azure.ts +26 -0
  228. package/src/plugin/cloudflare.ts +76 -0
  229. package/src/plugin/digitalocean.ts +391 -0
  230. package/src/plugin/github-copilot/copilot.ts +417 -0
  231. package/src/plugin/github-copilot/models.ts +246 -0
  232. package/src/plugin/index.ts +320 -0
  233. package/src/plugin/install.ts +439 -0
  234. package/src/plugin/loader.ts +237 -0
  235. package/src/plugin/meta.ts +188 -0
  236. package/src/plugin/openai/README.md +31 -0
  237. package/src/plugin/openai/codex.ts +647 -0
  238. package/src/plugin/openai/ws-pool.ts +290 -0
  239. package/src/plugin/openai/ws.ts +381 -0
  240. package/src/plugin/shared.ts +323 -0
  241. package/src/plugin/tui/internal.ts +12 -0
  242. package/src/plugin/tui/runtime.ts +1174 -0
  243. package/src/plugin/xai.ts +742 -0
  244. package/src/project/bootstrap-service.ts +9 -0
  245. package/src/project/bootstrap.ts +80 -0
  246. package/src/project/instance-context.ts +24 -0
  247. package/src/project/instance-layer.ts +11 -0
  248. package/src/project/instance-runtime.ts +16 -0
  249. package/src/project/instance-store.ts +207 -0
  250. package/src/project/project.ts +520 -0
  251. package/src/project/vcs.ts +435 -0
  252. package/src/provider/auth.ts +230 -0
  253. package/src/provider/error.ts +188 -0
  254. package/src/provider/model-status.ts +8 -0
  255. package/src/provider/provider.ts +2037 -0
  256. package/src/provider/transform.ts +1367 -0
  257. package/src/pty-preparation.ts +30 -0
  258. package/src/question/index.ts +229 -0
  259. package/src/question/schema.ts +10 -0
  260. package/src/reference/reference.ts +239 -0
  261. package/src/reference/repository-cache.ts +320 -0
  262. package/src/server/auth.ts +48 -0
  263. package/src/server/cors.ts +34 -0
  264. package/src/server/event.ts +13 -0
  265. package/src/server/global-lifecycle.ts +37 -0
  266. package/src/server/init-projectors.ts +3 -0
  267. package/src/server/mdns.ts +60 -0
  268. package/src/server/projectors.ts +1 -0
  269. package/src/server/proxy-util.ts +48 -0
  270. package/src/server/routes/instance/httpapi/AGENTS.md +39 -0
  271. package/src/server/routes/instance/httpapi/api.ts +80 -0
  272. package/src/server/routes/instance/httpapi/errors.ts +193 -0
  273. package/src/server/routes/instance/httpapi/groups/config.ts +65 -0
  274. package/src/server/routes/instance/httpapi/groups/control-plane.ts +35 -0
  275. package/src/server/routes/instance/httpapi/groups/control.ts +76 -0
  276. package/src/server/routes/instance/httpapi/groups/event.ts +29 -0
  277. package/src/server/routes/instance/httpapi/groups/experimental.ts +260 -0
  278. package/src/server/routes/instance/httpapi/groups/file.ts +172 -0
  279. package/src/server/routes/instance/httpapi/groups/global.ts +138 -0
  280. package/src/server/routes/instance/httpapi/groups/instance.ts +206 -0
  281. package/src/server/routes/instance/httpapi/groups/mcp.ts +156 -0
  282. package/src/server/routes/instance/httpapi/groups/metadata.ts +18 -0
  283. package/src/server/routes/instance/httpapi/groups/permission.ts +61 -0
  284. package/src/server/routes/instance/httpapi/groups/project-copy.ts +88 -0
  285. package/src/server/routes/instance/httpapi/groups/project.ts +93 -0
  286. package/src/server/routes/instance/httpapi/groups/provider.ts +101 -0
  287. package/src/server/routes/instance/httpapi/groups/pty.ts +172 -0
  288. package/src/server/routes/instance/httpapi/groups/query.ts +12 -0
  289. package/src/server/routes/instance/httpapi/groups/question.ts +74 -0
  290. package/src/server/routes/instance/httpapi/groups/reference.ts +60 -0
  291. package/src/server/routes/instance/httpapi/groups/sync.ts +113 -0
  292. package/src/server/routes/instance/httpapi/groups/tui.ts +208 -0
  293. package/src/server/routes/instance/httpapi/groups/workspace.ts +141 -0
  294. package/src/server/routes/instance/httpapi/handlers/config.ts +34 -0
  295. package/src/server/routes/instance/httpapi/handlers/control-plane.ts +37 -0
  296. package/src/server/routes/instance/httpapi/handlers/control.ts +37 -0
  297. package/src/server/routes/instance/httpapi/handlers/event.ts +102 -0
  298. package/src/server/routes/instance/httpapi/handlers/experimental.ts +187 -0
  299. package/src/server/routes/instance/httpapi/handlers/file.ts +128 -0
  300. package/src/server/routes/instance/httpapi/handlers/global.ts +157 -0
  301. package/src/server/routes/instance/httpapi/handlers/instance.ts +110 -0
  302. package/src/server/routes/instance/httpapi/handlers/mcp.ts +111 -0
  303. package/src/server/routes/instance/httpapi/handlers/permission.ts +41 -0
  304. package/src/server/routes/instance/httpapi/handlers/project-copy.ts +157 -0
  305. package/src/server/routes/instance/httpapi/handlers/project.ts +63 -0
  306. package/src/server/routes/instance/httpapi/handlers/provider.ts +113 -0
  307. package/src/server/routes/instance/httpapi/handlers/pty.ts +258 -0
  308. package/src/server/routes/instance/httpapi/handlers/question.ts +54 -0
  309. package/src/server/routes/instance/httpapi/handlers/reference.ts +27 -0
  310. package/src/server/routes/instance/httpapi/handlers/sync.ts +95 -0
  311. package/src/server/routes/instance/httpapi/handlers/tui.ts +131 -0
  312. package/src/server/routes/instance/httpapi/handlers/workspace.ts +102 -0
  313. package/src/server/routes/instance/httpapi/lifecycle.ts +57 -0
  314. package/src/server/routes/instance/httpapi/middleware/authorization.ts +150 -0
  315. package/src/server/routes/instance/httpapi/middleware/compression.ts +64 -0
  316. package/src/server/routes/instance/httpapi/middleware/cors-vary.ts +29 -0
  317. package/src/server/routes/instance/httpapi/middleware/error.ts +36 -0
  318. package/src/server/routes/instance/httpapi/middleware/fence.ts +25 -0
  319. package/src/server/routes/instance/httpapi/middleware/instance-context.ts +43 -0
  320. package/src/server/routes/instance/httpapi/middleware/proxy.ts +108 -0
  321. package/src/server/routes/instance/httpapi/middleware/schema-error.ts +42 -0
  322. package/src/server/routes/instance/httpapi/middleware/workspace-routing.ts +250 -0
  323. package/src/server/routes/instance/httpapi/public.ts +535 -0
  324. package/src/server/routes/instance/httpapi/server.ts +281 -0
  325. package/src/server/routes/instance/httpapi/websocket-tracker.ts +57 -0
  326. package/src/server/server.ts +218 -0
  327. package/src/server/shared/fence.ts +68 -0
  328. package/src/server/shared/pty-ticket.ts +15 -0
  329. package/src/server/shared/public-ui.ts +12 -0
  330. package/src/server/shared/tui-control.ts +28 -0
  331. package/src/server/shared/ui.ts +108 -0
  332. package/src/server/shared/workspace-routing.ts +38 -0
  333. package/src/server/tui-event.ts +53 -0
  334. package/src/share/share-next.ts +379 -0
  335. package/src/shell/shell.ts +215 -0
  336. package/src/skill/discovery.ts +115 -0
  337. package/src/skill/index.ts +382 -0
  338. package/src/snapshot/index.ts +762 -0
  339. package/src/sql.d.ts +4 -0
  340. package/src/storage/schema.ts +5 -0
  341. package/src/storage/storage.ts +329 -0
  342. package/src/swarm/evolution.ts +304 -0
  343. package/src/swarm/index.ts +7 -0
  344. package/src/swarm/learning.ts +736 -0
  345. package/src/swarm/vision.ts +131 -0
  346. package/src/sync/README.md +179 -0
  347. package/src/sync/schema.ts +11 -0
  348. package/src/temporary.ts +33 -0
  349. package/src/tool/apply_patch.ts +313 -0
  350. package/src/tool/apply_patch.txt +33 -0
  351. package/src/tool/browser_click.ts +51 -0
  352. package/src/tool/browser_drag.ts +76 -0
  353. package/src/tool/browser_evaluate.ts +51 -0
  354. package/src/tool/browser_hover.ts +51 -0
  355. package/src/tool/browser_navigate.ts +45 -0
  356. package/src/tool/browser_screenshot.ts +66 -0
  357. package/src/tool/browser_select.ts +52 -0
  358. package/src/tool/browser_type.ts +52 -0
  359. package/src/tool/browser_wait.ts +60 -0
  360. package/src/tool/desktop_clipboard.ts +82 -0
  361. package/src/tool/desktop_control.ts +160 -0
  362. package/src/tool/desktop_record.ts +93 -0
  363. package/src/tool/desktop_replay.ts +99 -0
  364. package/src/tool/desktop_screen_record.ts +143 -0
  365. package/src/tool/desktop_window.ts +142 -0
  366. package/src/tool/desktop_workflow.ts +81 -0
  367. package/src/tool/edit.ts +737 -0
  368. package/src/tool/edit.txt +10 -0
  369. package/src/tool/external-directory.ts +49 -0
  370. package/src/tool/glob.ts +84 -0
  371. package/src/tool/glob.txt +6 -0
  372. package/src/tool/grep.ts +140 -0
  373. package/src/tool/grep.txt +8 -0
  374. package/src/tool/invalid.ts +21 -0
  375. package/src/tool/json-schema.ts +164 -0
  376. package/src/tool/lsp.ts +113 -0
  377. package/src/tool/lsp.txt +24 -0
  378. package/src/tool/mcp-websearch.ts +96 -0
  379. package/src/tool/open_app.ts +55 -0
  380. package/src/tool/open_terminal.ts +69 -0
  381. package/src/tool/plan-enter.txt +14 -0
  382. package/src/tool/plan-exit.txt +13 -0
  383. package/src/tool/plan.ts +79 -0
  384. package/src/tool/question.ts +44 -0
  385. package/src/tool/question.txt +10 -0
  386. package/src/tool/read.ts +392 -0
  387. package/src/tool/read.txt +14 -0
  388. package/src/tool/registry.ts +527 -0
  389. package/src/tool/schema.ts +14 -0
  390. package/src/tool/screenshot.ts +72 -0
  391. package/src/tool/shell/id.ts +19 -0
  392. package/src/tool/shell/prompt.ts +307 -0
  393. package/src/tool/shell/shell.txt +21 -0
  394. package/src/tool/shell.ts +669 -0
  395. package/src/tool/skill.ts +72 -0
  396. package/src/tool/skill.txt +5 -0
  397. package/src/tool/task.ts +339 -0
  398. package/src/tool/task.txt +19 -0
  399. package/src/tool/todo.ts +57 -0
  400. package/src/tool/todowrite.txt +44 -0
  401. package/src/tool/tool.ts +180 -0
  402. package/src/tool/truncate.ts +160 -0
  403. package/src/tool/truncation-dir.ts +4 -0
  404. package/src/tool/visual_qa.ts +77 -0
  405. package/src/tool/webfetch.ts +192 -0
  406. package/src/tool/webfetch.txt +13 -0
  407. package/src/tool/websearch.ts +143 -0
  408. package/src/tool/websearch.txt +14 -0
  409. package/src/tool/write.ts +104 -0
  410. package/src/tool/write.txt +8 -0
  411. package/src/util/archive.ts +17 -0
  412. package/src/util/bom.ts +27 -0
  413. package/src/util/data-url.ts +9 -0
  414. package/src/util/defer.ts +10 -0
  415. package/src/util/effect-http-client.ts +11 -0
  416. package/src/util/error.ts +1 -0
  417. package/src/util/filesystem.ts +251 -0
  418. package/src/util/iife.ts +3 -0
  419. package/src/util/lazy.ts +20 -0
  420. package/src/util/local-context.ts +25 -0
  421. package/src/util/locale.ts +2 -0
  422. package/src/util/media.ts +26 -0
  423. package/src/util/process.ts +177 -0
  424. package/src/util/proxy-env.ts +72 -0
  425. package/src/util/queue.ts +32 -0
  426. package/src/util/record.ts +1 -0
  427. package/src/util/repository.ts +232 -0
  428. package/src/util/rpc.ts +66 -0
  429. package/src/util/signal.ts +12 -0
  430. package/src/util/sys-monitor.ts +223 -0
  431. package/src/util/timeout.ts +13 -0
  432. package/src/util/wildcard.ts +59 -0
  433. package/src/worktree/index.ts +645 -0
  434. package/sst-env.d.ts +10 -0
  435. package/test/AGENTS.md +204 -0
  436. package/test/EFFECT_TEST_MIGRATION.md +169 -0
  437. package/test/account/repo.test.ts +353 -0
  438. package/test/account/service.test.ts +453 -0
  439. package/test/acp/config-option.test.ts +229 -0
  440. package/test/acp/content.test.ts +201 -0
  441. package/test/acp/directory.test.ts +186 -0
  442. package/test/acp/error.test.ts +67 -0
  443. package/test/acp/event.test.ts +743 -0
  444. package/test/acp/permission.test.ts +273 -0
  445. package/test/acp/tool.test.ts +210 -0
  446. package/test/acp/usage.test.ts +315 -0
  447. package/test/agent/agent.test.ts +711 -0
  448. package/test/agent/plan-mode-subagent-bypass.test.ts +213 -0
  449. package/test/agent/plugin-agent-regression.test.ts +62 -0
  450. package/test/auth/auth.test.ts +77 -0
  451. package/test/background/job.test.ts +243 -0
  452. package/test/cli/account.test.ts +30 -0
  453. package/test/cli/acp/acp-test-client.ts +97 -0
  454. package/test/cli/acp/config-options.test.ts +103 -0
  455. package/test/cli/acp/helpers.ts +96 -0
  456. package/test/cli/acp/initialize-auth.test.ts +61 -0
  457. package/test/cli/acp/lifecycle.test.ts +118 -0
  458. package/test/cli/acp/prompt-content.test.ts +97 -0
  459. package/test/cli/acp/skills.test.ts +38 -0
  460. package/test/cli/cmd/tui/attention.test.ts +484 -0
  461. package/test/cli/effect-cmd-instance-als.test.ts +39 -0
  462. package/test/cli/error.test.ts +95 -0
  463. package/test/cli/github-action.test.ts +199 -0
  464. package/test/cli/github-remote.test.ts +90 -0
  465. package/test/cli/help/__snapshots__/help-snapshots.test.ts.snap +631 -0
  466. package/test/cli/help/help-snapshots.test.ts +137 -0
  467. package/test/cli/import.test.ts +54 -0
  468. package/test/cli/mcp-add.test.ts +74 -0
  469. package/test/cli/plugin-auth-picker.test.ts +120 -0
  470. package/test/cli/run/entry.body.test.ts +536 -0
  471. package/test/cli/run/footer.menu.test.ts +43 -0
  472. package/test/cli/run/footer.view.test.tsx +1336 -0
  473. package/test/cli/run/footer.width.test.ts +35 -0
  474. package/test/cli/run/permission.shared.test.ts +144 -0
  475. package/test/cli/run/prompt.editor.test.ts +101 -0
  476. package/test/cli/run/prompt.shared.test.ts +101 -0
  477. package/test/cli/run/question.shared.test.ts +115 -0
  478. package/test/cli/run/run-process.test.ts +84 -0
  479. package/test/cli/run/runtime.boot.test.ts +283 -0
  480. package/test/cli/run/runtime.queue.test.ts +481 -0
  481. package/test/cli/run/runtime.stdin.test.ts +71 -0
  482. package/test/cli/run/runtime.test.ts +238 -0
  483. package/test/cli/run/scrollback.surface.test.ts +1065 -0
  484. package/test/cli/run/stream.test.ts +56 -0
  485. package/test/cli/run/stream.transport.test.ts +2363 -0
  486. package/test/cli/run/subagent-data.test.ts +547 -0
  487. package/test/cli/run/theme.test.ts +177 -0
  488. package/test/cli/run/variant.shared.test.ts +217 -0
  489. package/test/cli/serve/serve-process.test.ts +61 -0
  490. package/test/cli/smokes/read-only.test.ts +115 -0
  491. package/test/cli/tui/attach.test.ts +11 -0
  492. package/test/cli/tui/editor-context-zed.test.ts +379 -0
  493. package/test/cli/tui/editor-context.test.tsx +297 -0
  494. package/test/cli/tui/plugin-add.test.ts +110 -0
  495. package/test/cli/tui/plugin-install.test.ts +87 -0
  496. package/test/cli/tui/plugin-lifecycle.test.ts +224 -0
  497. package/test/cli/tui/plugin-loader-entrypoint.test.ts +485 -0
  498. package/test/cli/tui/plugin-loader-pure.test.ts +72 -0
  499. package/test/cli/tui/plugin-loader.test.ts +1332 -0
  500. package/test/cli/tui/plugin-toggle.test.ts +264 -0
  501. package/test/cli/tui/thread.test.ts +36 -0
  502. package/test/cli.test.ts +7 -0
  503. package/test/command/acp-slash-detect.test.ts +202 -0
  504. package/test/command/demo-slash.test.ts +213 -0
  505. package/test/command/slash-head.test.ts +232 -0
  506. package/test/command/slash-parsing.test.ts +193 -0
  507. package/test/command/terminal-ai-commands.test.ts +536 -0
  508. package/test/config/agent-color.test.ts +47 -0
  509. package/test/config/config.test.ts +1994 -0
  510. package/test/config/entry-name.test.ts +57 -0
  511. package/test/config/fixtures/empty-frontmatter.md +4 -0
  512. package/test/config/fixtures/frontmatter.md +28 -0
  513. package/test/config/fixtures/markdown-header.md +11 -0
  514. package/test/config/fixtures/no-frontmatter.md +1 -0
  515. package/test/config/fixtures/weird-model-id.md +13 -0
  516. package/test/config/lsp.test.ts +69 -0
  517. package/test/config/markdown.test.ts +228 -0
  518. package/test/config/plugin.test.ts +0 -0
  519. package/test/config/tui.test.ts +886 -0
  520. package/test/control-plane/adapters.test.ts +71 -0
  521. package/test/control-plane/workspace.test.ts +1704 -0
  522. package/test/effect/app-runtime-logger.test.ts +105 -0
  523. package/test/effect/config-service.test.ts +65 -0
  524. package/test/effect/instance-state.test.ts +391 -0
  525. package/test/effect/run-service.test.ts +89 -0
  526. package/test/effect/runner.test.ts +514 -0
  527. package/test/effect/runtime-flags.test.ts +373 -0
  528. package/test/fake/account.ts +9 -0
  529. package/test/fake/auth.ts +8 -0
  530. package/test/fake/npm.ts +8 -0
  531. package/test/fake/provider.ts +82 -0
  532. package/test/fake/skill.ts +8 -0
  533. package/test/filesystem/filesystem.test.ts +319 -0
  534. package/test/fixture/agent-plugin.constants.ts +6 -0
  535. package/test/fixture/agent-plugin.ts +12 -0
  536. package/test/fixture/config.ts +23 -0
  537. package/test/fixture/db.ts +11 -0
  538. package/test/fixture/fixture.test.ts +26 -0
  539. package/test/fixture/fixture.ts +224 -0
  540. package/test/fixture/flag.ts +20 -0
  541. package/test/fixture/flock-worker.ts +72 -0
  542. package/test/fixture/lsp/fake-lsp-server.js +249 -0
  543. package/test/fixture/plug-worker.ts +96 -0
  544. package/test/fixture/plugin-meta-worker.ts +19 -0
  545. package/test/fixture/plugin.ts +10 -0
  546. package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
  547. package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
  548. package/test/fixture/skills/cloudflare/SKILL.md +211 -0
  549. package/test/fixture/skills/index.json +6 -0
  550. package/test/fixture/tui-environment.tsx +32 -0
  551. package/test/fixture/tui-plugin.ts +357 -0
  552. package/test/fixture/tui-runtime.ts +56 -0
  553. package/test/fixture/tui-sdk.ts +82 -0
  554. package/test/fixture/workspace.ts +30 -0
  555. package/test/format/format.test.ts +228 -0
  556. package/test/git/git.test.ts +178 -0
  557. package/test/ide/ide.test.ts +82 -0
  558. package/test/image/fixtures/picture-5mb-base64.png +0 -0
  559. package/test/image/image.test.ts +123 -0
  560. package/test/installation/installation.test.ts +231 -0
  561. package/test/lib/cli-process.ts +459 -0
  562. package/test/lib/effect.ts +177 -0
  563. package/test/lib/filesystem.ts +10 -0
  564. package/test/lib/llm-server.ts +771 -0
  565. package/test/lib/snapshot.ts +73 -0
  566. package/test/lib/test-provider.ts +37 -0
  567. package/test/lib/websocket.ts +46 -0
  568. package/test/lsp/client.test.ts +493 -0
  569. package/test/lsp/index.test.ts +232 -0
  570. package/test/lsp/jdtls-root.test.ts +459 -0
  571. package/test/lsp/launch.test.ts +22 -0
  572. package/test/lsp/lifecycle.test.ts +160 -0
  573. package/test/mcp/auth.test.ts +78 -0
  574. package/test/mcp/builtin.test.ts +95 -0
  575. package/test/mcp/headers.test.ts +126 -0
  576. package/test/mcp/lifecycle.test.ts +999 -0
  577. package/test/mcp/oauth-auto-connect.test.ts +274 -0
  578. package/test/mcp/oauth-browser.test.ts +232 -0
  579. package/test/mcp/oauth-callback.test.ts +34 -0
  580. package/test/mcp/oauth-provider.test.ts +61 -0
  581. package/test/patch/patch.test.ts +383 -0
  582. package/test/permission/arity.test.ts +33 -0
  583. package/test/permission/next.test.ts +1176 -0
  584. package/test/permission-task.test.ts +318 -0
  585. package/test/plugin/auth-override.test.ts +105 -0
  586. package/test/plugin/cloudflare.test.ts +68 -0
  587. package/test/plugin/codex.test.ts +247 -0
  588. package/test/plugin/github-copilot-models.test.ts +332 -0
  589. package/test/plugin/install-concurrency.test.ts +140 -0
  590. package/test/plugin/install.test.ts +573 -0
  591. package/test/plugin/loader-shared.test.ts +1303 -0
  592. package/test/plugin/meta.test.ts +137 -0
  593. package/test/plugin/openai-rollout.test.ts +17 -0
  594. package/test/plugin/openai-ws.test.ts +877 -0
  595. package/test/plugin/shared.test.ts +88 -0
  596. package/test/plugin/trigger.test.ts +120 -0
  597. package/test/plugin/workspace-adapter.test.ts +137 -0
  598. package/test/plugin/xai.test.ts +634 -0
  599. package/test/preload.ts +99 -0
  600. package/test/project/instance-bootstrap.test.ts +110 -0
  601. package/test/project/instance.test.ts +245 -0
  602. package/test/project/migrate-global.test.ts +170 -0
  603. package/test/project/project-directory.test.ts +169 -0
  604. package/test/project/project.test.ts +818 -0
  605. package/test/project/vcs.test.ts +336 -0
  606. package/test/project/worktree-remove.test.ts +126 -0
  607. package/test/project/worktree.test.ts +320 -0
  608. package/test/provider/amazon-bedrock.test.ts +360 -0
  609. package/test/provider/cf-ai-gateway-e2e.test.ts +132 -0
  610. package/test/provider/digitalocean.test.ts +123 -0
  611. package/test/provider/gitlab-duo.test.ts +412 -0
  612. package/test/provider/header-timeout.test.ts +233 -0
  613. package/test/provider/model-status.test.ts +61 -0
  614. package/test/provider/provider.test.ts +1795 -0
  615. package/test/provider/transform.test.ts +3937 -0
  616. package/test/pty/pty-shell.test.ts +102 -0
  617. package/test/question/question.test.ts +465 -0
  618. package/test/reference/reference.test.ts +311 -0
  619. package/test/server/AGENTS.md +15 -0
  620. package/test/server/auth.test.ts +59 -0
  621. package/test/server/global-bus.ts +31 -0
  622. package/test/server/httpapi-authorization.test.ts +174 -0
  623. package/test/server/httpapi-compression.test.ts +154 -0
  624. package/test/server/httpapi-config.test.ts +113 -0
  625. package/test/server/httpapi-control-plane.test.ts +63 -0
  626. package/test/server/httpapi-cors-vary.test.ts +66 -0
  627. package/test/server/httpapi-cors.test.ts +122 -0
  628. package/test/server/httpapi-error-middleware.test.ts +96 -0
  629. package/test/server/httpapi-event.test.ts +97 -0
  630. package/test/server/httpapi-exercise/assertions.ts +64 -0
  631. package/test/server/httpapi-exercise/backend.ts +144 -0
  632. package/test/server/httpapi-exercise/dsl.ts +210 -0
  633. package/test/server/httpapi-exercise/environment.ts +40 -0
  634. package/test/server/httpapi-exercise/index.ts +1538 -0
  635. package/test/server/httpapi-exercise/report.ts +66 -0
  636. package/test/server/httpapi-exercise/routing.ts +96 -0
  637. package/test/server/httpapi-exercise/runner.ts +267 -0
  638. package/test/server/httpapi-exercise/runtime.ts +52 -0
  639. package/test/server/httpapi-exercise/types.ts +123 -0
  640. package/test/server/httpapi-experimental.test.ts +300 -0
  641. package/test/server/httpapi-file.test.ts +76 -0
  642. package/test/server/httpapi-global.test.ts +66 -0
  643. package/test/server/httpapi-instance-context.test.ts +347 -0
  644. package/test/server/httpapi-instance-route-auth.test.ts +84 -0
  645. package/test/server/httpapi-instance.test.ts +265 -0
  646. package/test/server/httpapi-layer.ts +33 -0
  647. package/test/server/httpapi-listen.test.ts +415 -0
  648. package/test/server/httpapi-mcp-oauth.test.ts +73 -0
  649. package/test/server/httpapi-mcp.test.ts +234 -0
  650. package/test/server/httpapi-mdns.test.ts +82 -0
  651. package/test/server/httpapi-promptasync-context.test.ts +222 -0
  652. package/test/server/httpapi-provider.test.ts +403 -0
  653. package/test/server/httpapi-pty.test.ts +275 -0
  654. package/test/server/httpapi-public-openapi.test.ts +297 -0
  655. package/test/server/httpapi-query-schema-drift.test.ts +330 -0
  656. package/test/server/httpapi-reference.test.ts +56 -0
  657. package/test/server/httpapi-schema-error-body.test.ts +165 -0
  658. package/test/server/httpapi-sdk.test.ts +909 -0
  659. package/test/server/httpapi-sync.test.ts +154 -0
  660. package/test/server/httpapi-ui.test.ts +456 -0
  661. package/test/server/httpapi-v2-location.test.ts +85 -0
  662. package/test/server/httpapi-workspace-routing.test.ts +554 -0
  663. package/test/server/httpapi-workspace.test.ts +515 -0
  664. package/test/server/project-copy.test.ts +101 -0
  665. package/test/server/project-init-git.test.ts +117 -0
  666. package/test/server/proxy-util.test.ts +113 -0
  667. package/test/server/sdk-error-shape.test.ts +84 -0
  668. package/test/server/sdk-v1-smoke.test.ts +60 -0
  669. package/test/server/workspace-proxy.test.ts +181 -0
  670. package/test/server/workspace-routing.test.ts +94 -0
  671. package/test/server/worktree-endpoint-repro.test.ts +307 -0
  672. package/test/share/share-next.test.ts +344 -0
  673. package/test/shell/shell.test.ts +99 -0
  674. package/test/skill/discovery.test.ts +139 -0
  675. package/test/skill/skill.test.ts +571 -0
  676. package/test/snapshot/snapshot.test.ts +1121 -0
  677. package/test/storage/storage.test.ts +296 -0
  678. package/test/storage/workspace-time-migration.test.ts +50 -0
  679. package/test/tool/__snapshots__/parameters.test.ts.snap +484 -0
  680. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  681. package/test/tool/apply_patch.test.ts +533 -0
  682. package/test/tool/browser.integration.test.ts +141 -0
  683. package/test/tool/desktop.integration.test.ts +129 -0
  684. package/test/tool/desktop.test.ts +85 -0
  685. package/test/tool/edit.test.ts +578 -0
  686. package/test/tool/external-directory.test.ts +167 -0
  687. package/test/tool/fixtures/large-image.png +0 -0
  688. package/test/tool/fixtures/models-api.json +117299 -0
  689. package/test/tool/glob.test.ts +188 -0
  690. package/test/tool/grep.test.ts +266 -0
  691. package/test/tool/lsp.test.ts +181 -0
  692. package/test/tool/parameters.test.ts +293 -0
  693. package/test/tool/question.test.ts +138 -0
  694. package/test/tool/read.test.ts +659 -0
  695. package/test/tool/registry.test.ts +539 -0
  696. package/test/tool/shell.test.ts +1256 -0
  697. package/test/tool/skill.test.ts +135 -0
  698. package/test/tool/task.test.ts +901 -0
  699. package/test/tool/tool-define.test.ts +153 -0
  700. package/test/tool/truncation.test.ts +266 -0
  701. package/test/tool/webfetch.test.ts +113 -0
  702. package/test/tool/websearch.test.ts +99 -0
  703. package/test/tool/write.test.ts +276 -0
  704. package/test/util/data-url.test.ts +14 -0
  705. package/test/util/error.test.ts +16 -0
  706. package/test/util/filesystem.test.ts +656 -0
  707. package/test/util/glob.test.ts +164 -0
  708. package/test/util/iife.test.ts +36 -0
  709. package/test/util/lazy.test.ts +50 -0
  710. package/test/util/log.test.ts +77 -0
  711. package/test/util/module.test.ts +59 -0
  712. package/test/util/process.test.ts +128 -0
  713. package/test/util/repository.test.ts +93 -0
  714. package/test/util/timeout.test.ts +21 -0
  715. package/test/util/wildcard.test.ts +90 -0
  716. package/tsconfig.json +16 -0
@@ -0,0 +1,1367 @@
1
+ import type { ModelMessage, ToolResultPart } from "ai"
2
+ import { mergeDeep, unique } from "remeda"
3
+ import type { JSONSchema7 } from "@ai-sdk/provider"
4
+ import type * as Provider from "./provider"
5
+ import type * as ModelsDev from "@octocode-ai/core/models-dev"
6
+ import { iife } from "@/util/iife"
7
+
8
+ type Modality = NonNullable<ModelsDev.Model["modalities"]>["input"][number]
9
+
10
+ function mimeToModality(mime: string): Modality | undefined {
11
+ if (mime.startsWith("image/")) return "image"
12
+ if (mime.startsWith("audio/")) return "audio"
13
+ if (mime.startsWith("video/")) return "video"
14
+ if (mime === "application/pdf") return "pdf"
15
+ return undefined
16
+ }
17
+
18
+ // Default max output tokens when model doesn't specify one
19
+ export const DEFAULT_OUTPUT_TOKEN_MAX = 32_000
20
+
21
+ // OpenAI Responses `include` value that returns the encrypted reasoning state
22
+ // needed for stateless multi-turn reasoning (store: false). Hoisted so every
23
+ // branch that requests it stays in lockstep.
24
+ const INCLUDE_ENCRYPTED_REASONING = ["reasoning.encrypted_content"] as const
25
+
26
+ export function sanitizeSurrogates(content: string) {
27
+ return content.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD")
28
+ }
29
+
30
+ // Maps npm package to the key the AI SDK expects for providerOptions
31
+ function sdkKey(npm: string): string | undefined {
32
+ switch (npm) {
33
+ case "@ai-sdk/github-copilot":
34
+ return "copilot"
35
+ case "@ai-sdk/azure":
36
+ return "azure"
37
+ case "@ai-sdk/openai":
38
+ return "openai"
39
+ case "@ai-sdk/amazon-bedrock/mantle":
40
+ return "openai"
41
+ case "@ai-sdk/amazon-bedrock":
42
+ return "bedrock"
43
+ case "@ai-sdk/anthropic":
44
+ case "@ai-sdk/google-vertex/anthropic":
45
+ return "anthropic"
46
+ case "@ai-sdk/google-vertex":
47
+ return "vertex"
48
+ case "@ai-sdk/google":
49
+ return "google"
50
+ case "@ai-sdk/gateway":
51
+ return "gateway"
52
+ case "@openrouter/ai-sdk-provider":
53
+ return "openrouter"
54
+ case "ai-gateway-provider":
55
+ // ai-gateway-provider/unified wraps createOpenAICompatible({ name: "Unified" }),
56
+ // and @ai-sdk/openai-compatible parses compatibleOptions from one of
57
+ // "openai-compatible" / "openaiCompatible" / "Unified" / "unified". The
58
+ // "openai-compatible" key emits a deprecation warning at runtime, so we
59
+ // pick the camelCase form the SDK now treats as canonical.
60
+ return "openaiCompatible"
61
+ }
62
+ return undefined
63
+ }
64
+
65
+ // Optimized message normalization with provider-specific transforms
66
+ function normalizeMessages(
67
+ msgs: ModelMessage[],
68
+ model: Provider.Model,
69
+ _options: Record<string, unknown>,
70
+ ): ModelMessage[] {
71
+ const sanitizeToolResultOutput = (content: ToolResultPart) => {
72
+ if (content.output.type === "text" || content.output.type === "error-text") {
73
+ content.output.value = sanitizeSurrogates(content.output.value)
74
+ }
75
+ if (content.output.type === "content") {
76
+ content.output.value = content.output.value.map((item) => {
77
+ if (item.type === "text") {
78
+ item.text = sanitizeSurrogates(item.text)
79
+ }
80
+ return item
81
+ })
82
+ }
83
+ return content
84
+ }
85
+
86
+ msgs = msgs.map((msg) => {
87
+ switch (msg.role) {
88
+ case "tool":
89
+ if (!Array.isArray(msg.content)) return msg
90
+ msg.content = msg.content.map((content) => {
91
+ if (content.type === "tool-result") {
92
+ return sanitizeToolResultOutput(content)
93
+ }
94
+ return content
95
+ })
96
+ return msg
97
+
98
+ case "system":
99
+ msg.content = sanitizeSurrogates(msg.content)
100
+ return msg
101
+
102
+ case "user":
103
+ if (typeof msg.content === "string") {
104
+ msg.content = sanitizeSurrogates(msg.content)
105
+ } else {
106
+ msg.content = msg.content.map((content) => {
107
+ if (content.type === "text") {
108
+ content.text = sanitizeSurrogates(content.text)
109
+ }
110
+ return content
111
+ })
112
+ }
113
+ return msg
114
+
115
+ case "assistant":
116
+ if (typeof msg.content === "string") {
117
+ msg.content = sanitizeSurrogates(msg.content)
118
+ } else {
119
+ msg.content = msg.content.map((content) => {
120
+ if (content.type === "text" || content.type === "reasoning") {
121
+ content.text = sanitizeSurrogates(content.text)
122
+ }
123
+ if (content.type === "tool-result") {
124
+ return sanitizeToolResultOutput(content)
125
+ }
126
+ return content
127
+ })
128
+ }
129
+ return msg
130
+ }
131
+ })
132
+
133
+ // Anthropic rejects messages with empty content - filter out empty string messages
134
+ // and remove empty text/reasoning parts from array content
135
+ if (model.api.npm === "@ai-sdk/anthropic") {
136
+ msgs = msgs
137
+ .map((msg) => {
138
+ if (typeof msg.content === "string") {
139
+ if (msg.content === "") return undefined
140
+ return msg
141
+ }
142
+ if (!Array.isArray(msg.content)) return msg
143
+ const filtered = msg.content.filter((part) => {
144
+ if (part.type === "text") {
145
+ return part.text !== ""
146
+ }
147
+ if (part.type === "reasoning") {
148
+ return (
149
+ part.text.trim().length > 0 ||
150
+ part.providerOptions?.anthropic?.signature != null ||
151
+ part.providerOptions?.anthropic?.redactedData != null
152
+ )
153
+ }
154
+ return true
155
+ })
156
+ if (filtered.length === 0) return undefined
157
+ return { ...msg, content: filtered }
158
+ })
159
+ .filter((msg): msg is ModelMessage => msg !== undefined && msg.content !== "")
160
+ }
161
+
162
+ // Bedrock specific transforms
163
+ if (model.api.npm === "@ai-sdk/amazon-bedrock") {
164
+ msgs = msgs
165
+ .map((msg) => {
166
+ if (typeof msg.content === "string") {
167
+ if (msg.content === "") return undefined
168
+ return msg
169
+ }
170
+ if (!Array.isArray(msg.content)) return msg
171
+ const filtered = msg.content.filter((part) => {
172
+ if (part.type === "text") {
173
+ return part.text !== ""
174
+ }
175
+ if (part.type === "reasoning") {
176
+ return (
177
+ part.text.trim().length > 0 ||
178
+ part.providerOptions?.bedrock?.signature != null ||
179
+ part.providerOptions?.bedrock?.redactedData != null
180
+ )
181
+ }
182
+ return true
183
+ })
184
+ if (filtered.length === 0) return undefined
185
+ return { ...msg, content: filtered }
186
+ })
187
+ .filter((msg): msg is ModelMessage => msg !== undefined && msg.content !== "")
188
+ }
189
+
190
+ if (model.api.id.includes("claude")) {
191
+ const scrub = (id: string) => id.replace(/[^a-zA-Z0-9_-]/g, "_")
192
+ msgs = msgs.map((msg) => {
193
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
194
+ return {
195
+ ...msg,
196
+ content: msg.content.map((part) => {
197
+ if (part.type === "tool-call" || part.type === "tool-result") {
198
+ return { ...part, toolCallId: scrub(part.toolCallId) }
199
+ }
200
+ return part
201
+ }),
202
+ }
203
+ }
204
+ if (msg.role === "tool" && Array.isArray(msg.content)) {
205
+ return {
206
+ ...msg,
207
+ content: msg.content.map((part) => {
208
+ if (part.type === "tool-result") {
209
+ return { ...part, toolCallId: scrub(part.toolCallId) }
210
+ }
211
+ return part
212
+ }),
213
+ }
214
+ }
215
+ return msg
216
+ })
217
+ }
218
+
219
+ if (
220
+ model.providerID === "mistral" ||
221
+ model.api.id.toLowerCase().includes("mistral") ||
222
+ model.api.id.toLocaleLowerCase().includes("devstral")
223
+ ) {
224
+ const scrub = (id: string) => {
225
+ return id
226
+ .replace(/[^a-zA-Z0-9]/g, "") // Remove non-alphanumeric characters
227
+ .substring(0, 9) // Take first 9 characters
228
+ .padEnd(9, "0") // Pad with zeros if less than 9 characters
229
+ }
230
+ const result: ModelMessage[] = []
231
+ for (let i = 0; i < msgs.length; i++) {
232
+ const msg = msgs[i]
233
+ const nextMsg = msgs[i + 1]
234
+
235
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
236
+ msg.content = msg.content.map((part) => {
237
+ if (part.type === "tool-call" || part.type === "tool-result") {
238
+ return { ...part, toolCallId: scrub(part.toolCallId) }
239
+ }
240
+ return part
241
+ })
242
+ }
243
+ if (msg.role === "tool" && Array.isArray(msg.content)) {
244
+ msg.content = msg.content.map((part) => {
245
+ if (part.type === "tool-result") {
246
+ return { ...part, toolCallId: scrub(part.toolCallId) }
247
+ }
248
+ return part
249
+ })
250
+ }
251
+ result.push(msg)
252
+
253
+ // Fix message sequence: tool messages cannot be followed by user messages
254
+ if (msg.role === "tool" && nextMsg?.role === "user") {
255
+ result.push({
256
+ role: "assistant",
257
+ content: [
258
+ {
259
+ type: "text",
260
+ text: "Done.",
261
+ },
262
+ ],
263
+ })
264
+ }
265
+ }
266
+ return result
267
+ }
268
+
269
+ // Deepseek requires all assistant messages to have reasoning on them
270
+ if (model.api.id.toLowerCase().includes("deepseek")) {
271
+ msgs = msgs.map((msg) => {
272
+ if (msg.role !== "assistant") return msg
273
+ if (Array.isArray(msg.content)) {
274
+ if (msg.content.some((part) => part.type === "reasoning")) return msg
275
+ return { ...msg, content: [...msg.content, { type: "reasoning", text: "" }] }
276
+ }
277
+ return {
278
+ ...msg,
279
+ content: [
280
+ ...(msg.content ? [{ type: "text" as const, text: msg.content }] : []),
281
+ { type: "reasoning" as const, text: "" },
282
+ ],
283
+ }
284
+ })
285
+ }
286
+
287
+ if (
288
+ typeof model.capabilities.interleaved === "object" &&
289
+ model.capabilities.interleaved.field &&
290
+ model.api.npm !== "@openrouter/ai-sdk-provider"
291
+ ) {
292
+ const field = model.capabilities.interleaved.field
293
+ return msgs.map((msg) => {
294
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
295
+ const reasoningParts = msg.content.filter((part: any) => part.type === "reasoning")
296
+ const reasoningText = reasoningParts.map((part: any) => part.text).join("")
297
+
298
+ // Filter out reasoning parts from content
299
+ const filteredContent = msg.content.filter((part: any) => part.type !== "reasoning")
300
+
301
+ // Include reasoning_content | reasoning_details directly on the message for all assistant messages.
302
+ // Always set the field even when empty — some providers (e.g. DeepSeek) may return empty
303
+ // reasoning_content which still needs to be sent back in subsequent requests.
304
+ return {
305
+ ...msg,
306
+ content: filteredContent,
307
+ providerOptions: {
308
+ ...msg.providerOptions,
309
+ openaiCompatible: {
310
+ ...msg.providerOptions?.openaiCompatible,
311
+ [field]: reasoningText,
312
+ },
313
+ },
314
+ }
315
+ }
316
+
317
+ return msg
318
+ })
319
+ }
320
+
321
+ return msgs
322
+ }
323
+
324
+ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
325
+ const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
326
+ const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
327
+
328
+ const providerOptions = {
329
+ anthropic: {
330
+ cacheControl: { type: "ephemeral" },
331
+ },
332
+ openrouter: {
333
+ cacheControl: { type: "ephemeral" },
334
+ },
335
+ bedrock: {
336
+ cachePoint: { type: "default" },
337
+ },
338
+ openaiCompatible: {
339
+ cache_control: { type: "ephemeral" },
340
+ },
341
+ copilot: {
342
+ copilot_cache_control: { type: "ephemeral" },
343
+ },
344
+ alibaba: {
345
+ cacheControl: { type: "ephemeral" },
346
+ },
347
+ }
348
+
349
+ for (const msg of unique([...system, ...final])) {
350
+ const useMessageLevelOptions =
351
+ model.providerID === "anthropic" ||
352
+ model.providerID.includes("bedrock") ||
353
+ model.api.npm === "@ai-sdk/amazon-bedrock"
354
+ const shouldUseContentOptions = !useMessageLevelOptions && Array.isArray(msg.content) && msg.content.length > 0
355
+
356
+ if (shouldUseContentOptions) {
357
+ const lastContent = msg.content[msg.content.length - 1]
358
+ if (
359
+ lastContent &&
360
+ typeof lastContent === "object" &&
361
+ lastContent.type !== "tool-approval-request" &&
362
+ lastContent.type !== "tool-approval-response"
363
+ ) {
364
+ lastContent.providerOptions = mergeDeep(lastContent.providerOptions ?? {}, providerOptions)
365
+ continue
366
+ }
367
+ }
368
+
369
+ msg.providerOptions = mergeDeep(msg.providerOptions ?? {}, providerOptions)
370
+ }
371
+
372
+ return msgs
373
+ }
374
+
375
+ function unsupportedParts(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
376
+ return msgs.map((msg) => {
377
+ if (msg.role !== "user" || !Array.isArray(msg.content)) return msg
378
+
379
+ const filtered = msg.content.map((part) => {
380
+ if (part.type !== "file" && part.type !== "image") return part
381
+
382
+ // Check for empty base64 image data
383
+ if (part.type === "image") {
384
+ const imageStr = String(part.image)
385
+ if (imageStr.startsWith("data:")) {
386
+ const match = imageStr.match(/^data:([^;]+);base64,(.*)$/)
387
+ if (match && (!match[2] || match[2].length === 0)) {
388
+ return {
389
+ type: "text" as const,
390
+ text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
391
+ }
392
+ }
393
+ }
394
+ }
395
+
396
+ const mime = part.type === "image" ? String(part.image).split(";")[0].replace("data:", "") : part.mediaType
397
+ const filename = part.type === "file" ? part.filename : undefined
398
+ const modality = mimeToModality(mime)
399
+ if (!modality) return part
400
+ if (model.capabilities.input[modality]) return part
401
+
402
+ const name = filename ? `"${filename}"` : modality
403
+ return {
404
+ type: "text" as const,
405
+ text: `ERROR: Cannot read ${name} (this model does not support ${modality} input). Inform the user.`,
406
+ }
407
+ })
408
+
409
+ return { ...msg, content: filtered }
410
+ })
411
+ }
412
+
413
+ export function message(msgs: ModelMessage[], model: Provider.Model, options: Record<string, unknown>) {
414
+ msgs = unsupportedParts(msgs, model)
415
+ msgs = normalizeMessages(msgs, model, options)
416
+ if (
417
+ (model.providerID === "anthropic" ||
418
+ model.providerID === "google-vertex-anthropic" ||
419
+ model.api.id.includes("anthropic") ||
420
+ model.api.id.includes("claude") ||
421
+ model.id.includes("anthropic") ||
422
+ model.id.includes("claude") ||
423
+ model.api.npm === "@ai-sdk/anthropic" ||
424
+ model.api.npm === "@ai-sdk/alibaba") &&
425
+ model.api.npm !== "@ai-sdk/gateway"
426
+ ) {
427
+ msgs = applyCaching(msgs, model)
428
+ }
429
+
430
+ // Remap providerOptions keys from stored providerID to expected SDK key
431
+ const key = sdkKey(model.api.npm)
432
+ if (key && key !== model.providerID) {
433
+ const remap = (opts: Record<string, any> | undefined) => {
434
+ if (!opts) return opts
435
+ if (!(model.providerID in opts)) return opts
436
+ const result = { ...opts }
437
+ result[key] = result[model.providerID]
438
+ delete result[model.providerID]
439
+ return result
440
+ }
441
+
442
+ msgs = msgs.map((msg) => {
443
+ if (!Array.isArray(msg.content)) return { ...msg, providerOptions: remap(msg.providerOptions) }
444
+ return {
445
+ ...msg,
446
+ providerOptions: remap(msg.providerOptions),
447
+ content: msg.content.map((part) => {
448
+ if (part.type === "tool-approval-request" || part.type === "tool-approval-response") {
449
+ return { ...part }
450
+ }
451
+ return { ...part, providerOptions: remap(part.providerOptions) }
452
+ }),
453
+ } as typeof msg
454
+ })
455
+ }
456
+
457
+ return msgs
458
+ }
459
+
460
+ export function temperature(model: Provider.Model) {
461
+ const id = model.id.toLowerCase()
462
+ if (id.includes("qwen")) return 0.55
463
+ if (id.includes("claude")) return undefined
464
+ if (id.includes("gemini")) return 1.0
465
+ if (id.includes("glm-4.6")) return 1.0
466
+ if (id.includes("glm-4.7")) return 1.0
467
+ if (id.includes("minimax-m2")) return 1.0
468
+ if (id.includes("kimi-k2")) {
469
+ // kimi-k2-thinking & kimi-k2.5 && kimi-k2p5 && kimi-k2-5
470
+ if (["thinking", "k2.", "k2p", "k2-5"].some((s) => id.includes(s))) {
471
+ return 1.0
472
+ }
473
+ return 0.6
474
+ }
475
+ return undefined
476
+ }
477
+
478
+ export function topP(model: Provider.Model) {
479
+ const id = model.id.toLowerCase()
480
+ if (id.includes("qwen")) return 1
481
+ if (["minimax-m2", "gemini", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) {
482
+ return 0.95
483
+ }
484
+ return undefined
485
+ }
486
+
487
+ export function topK(model: Provider.Model) {
488
+ const id = model.id.toLowerCase()
489
+ if (id.includes("minimax-m2")) {
490
+ if (["m2.", "m25", "m21"].some((s) => id.includes(s))) return 40
491
+ return 20
492
+ }
493
+ if (id.includes("gemini")) return 64
494
+ return undefined
495
+ }
496
+
497
+ const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"]
498
+ const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
499
+ const OPENAI_GPT5_1_EFFORTS = ["none", ...WIDELY_SUPPORTED_EFFORTS]
500
+ const OPENAI_GPT5_2_PLUS_EFFORTS = [...OPENAI_GPT5_1_EFFORTS, "xhigh"]
501
+ const OPENAI_GPT5_PRO_EFFORTS = ["high"]
502
+ const OPENAI_GPT5_PRO_2_PLUS_EFFORTS = ["medium", "high", "xhigh"]
503
+ const OPENAI_GPT5_CHAT_EFFORTS = ["medium"]
504
+ const OPENAI_GPT5_CODEX_XHIGH_EFFORTS = [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
505
+ const OPENAI_GPT5_CODEX_3_PLUS_EFFORTS = ["none", ...OPENAI_GPT5_CODEX_XHIGH_EFFORTS]
506
+
507
+ // OpenAI rolled out the `none` reasoning_effort tier on this date (Responses API).
508
+ // Models released before it 400 on `reasoning_effort: "none"`, so we only expose
509
+ // it as a variant for models new enough to accept it.
510
+ const OPENAI_NONE_EFFORT_RELEASE_DATE = "2025-11-13"
511
+
512
+ // OpenAI rolled out the `xhigh` reasoning_effort tier on this date. Same reasoning.
513
+ const OPENAI_XHIGH_EFFORT_RELEASE_DATE = "2025-12-04"
514
+
515
+ // Matches members of the gpt-5 family across the id formats we encounter:
516
+ // "gpt-5", "gpt-5-nano", "gpt-5.4", "openai/gpt-5.4-codex".
517
+ // Anchored to start-of-string or "/" so it doesn't false-match "gpt-50" or "gpt-5o".
518
+ const GPT5_FAMILY_RE = /(?:^|\/)gpt-5(?:[.-]|$)/
519
+ const GPT5_VERSION_RE = /(?:^|\/)gpt-5[.-](\d+)(?:[.-]|$)/
520
+ const GPT5_PRO_RE = /(?:^|\/)gpt-5[.-]?pro(?:[.-]|$)/
521
+ const GPT5_VERSIONED_PRO_RE = /(?:^|\/)gpt-5[.-]\d+[.-]pro(?:[.-]|$)/
522
+
523
+ function gpt5Version(apiId: string) {
524
+ return Number(GPT5_VERSION_RE.exec(apiId)?.[1]) || undefined
525
+ }
526
+
527
+ function versionedGpt5ReasoningEfforts(apiId: string) {
528
+ if (GPT5_VERSIONED_PRO_RE.test(apiId)) return OPENAI_GPT5_PRO_2_PLUS_EFFORTS
529
+ const version = gpt5Version(apiId)
530
+ if (version === undefined) return undefined
531
+ if (version === 1) return OPENAI_GPT5_1_EFFORTS
532
+ return OPENAI_GPT5_2_PLUS_EFFORTS
533
+ }
534
+
535
+ function gpt5CodexReasoningEfforts(apiId: string) {
536
+ if (!GPT5_FAMILY_RE.test(apiId) || !apiId.includes("codex")) return undefined
537
+ const version = gpt5Version(apiId)
538
+ if (version !== undefined && version >= 3) return OPENAI_GPT5_CODEX_3_PLUS_EFFORTS
539
+ if (apiId.includes("codex-max") || (version !== undefined && version >= 2)) return OPENAI_GPT5_CODEX_XHIGH_EFFORTS
540
+ return WIDELY_SUPPORTED_EFFORTS
541
+ }
542
+
543
+ function gpt5ChatReasoningEfforts(apiId: string) {
544
+ if (!GPT5_FAMILY_RE.test(apiId) || !apiId.includes("-chat")) return undefined
545
+ return gpt5Version(apiId) === undefined ? [] : OPENAI_GPT5_CHAT_EFFORTS
546
+ }
547
+
548
+ // Computes the reasoning_effort tiers an OpenAI (or OpenAI-compatible upstream
549
+ // routed through it, e.g. cf-ai-gateway) model exposes. Effort order: weakest
550
+ // to strongest.
551
+ function openaiReasoningEfforts(apiId: string, releaseDate: string) {
552
+ const id = apiId.toLowerCase()
553
+ if (id.includes("deep-research")) return ["medium"]
554
+ const chatEfforts = gpt5ChatReasoningEfforts(id)
555
+ if (chatEfforts) return chatEfforts
556
+ if (GPT5_PRO_RE.test(id)) return OPENAI_GPT5_PRO_EFFORTS
557
+ const codexEfforts = gpt5CodexReasoningEfforts(id)
558
+ if (codexEfforts) return codexEfforts
559
+ const versionedEfforts = versionedGpt5ReasoningEfforts(id)
560
+ // GPT-5.1 replaced GPT-5's `minimal` effort with `none`; GPT-5.2+
561
+ // additionally accepts `xhigh`. Model pages list the supported subset.
562
+ if (versionedEfforts) return versionedEfforts
563
+ const efforts = [...WIDELY_SUPPORTED_EFFORTS]
564
+ if (GPT5_FAMILY_RE.test(id)) efforts.unshift("minimal")
565
+ if (releaseDate >= OPENAI_NONE_EFFORT_RELEASE_DATE) efforts.unshift("none")
566
+ if (releaseDate >= OPENAI_XHIGH_EFFORT_RELEASE_DATE) efforts.push("xhigh")
567
+ return efforts
568
+ }
569
+
570
+ function openaiCompatibleReasoningEfforts(id: string) {
571
+ const apiId = id.toLowerCase()
572
+ const chatEfforts = gpt5ChatReasoningEfforts(apiId)
573
+ if (chatEfforts) return chatEfforts
574
+ if (GPT5_PRO_RE.test(apiId)) return OPENAI_GPT5_PRO_EFFORTS
575
+ return gpt5CodexReasoningEfforts(apiId) ?? versionedGpt5ReasoningEfforts(apiId) ?? OPENAI_EFFORTS
576
+ }
577
+
578
+ function anthropicOpus47OrLater(apiId: string) {
579
+ // Matches "opus-4.7" (Anthropic/Bedrock/Vertex) and "claude-4.7-opus" (SAP AI Core inverted).
580
+ // Greedy \d+ correctly extends to multi-digit majors (e.g. "claude-10.0-opus") for forward compatibility.
581
+ const version = /opus-(\d+)[.-](\d+)(?:[.@-]|$)|claude-(\d+)[.-](\d+)-opus(?:[.@-]|$)/i.exec(apiId)
582
+ if (!version) return false
583
+ const major = Number(version[1] ?? version[3])
584
+ const minor = Number(version[2] ?? version[4])
585
+ return major > 4 || (major === 4 && minor >= 7)
586
+ }
587
+
588
+ function anthropicAdaptiveEfforts(apiId: string): string[] | null {
589
+ if (anthropicOpus47OrLater(apiId)) {
590
+ return ["low", "medium", "high", "xhigh", "max"]
591
+ }
592
+ if (
593
+ ["opus-4-6", "opus-4.6", "4-6-opus", "4.6-opus", "sonnet-4-6", "sonnet-4.6", "4-6-sonnet", "4.6-sonnet"].some((v) =>
594
+ apiId.includes(v),
595
+ )
596
+ ) {
597
+ return ["low", "medium", "high", "max"]
598
+ }
599
+ return null
600
+ }
601
+
602
+ function googleThinkingLevelEfforts(apiId: string) {
603
+ const id = apiId.toLowerCase()
604
+ if (!id.includes("gemini-3")) return ["low", "high"]
605
+ if (id.includes("flash-image")) return ["minimal", "high"]
606
+ if (id.includes("pro-image")) return ["high"]
607
+ if (id.includes("flash")) return ["minimal", "low", "medium", "high"]
608
+ return ["low", "medium", "high"]
609
+ }
610
+
611
+ function googleThinkingBudgetMax(apiId: string) {
612
+ const id = apiId.toLowerCase()
613
+ if (id.includes("2.5") && id.includes("pro") && !id.includes("flash")) return 32_768
614
+ return 24_576
615
+ }
616
+
617
+ // SAP's Zod schema drops unknown top-level keys; reasoning controls survive
618
+ // only via `modelParams` (catchall), forwarded verbatim by the SAP SDKs.
619
+ function wrapInSapModelParams(variants: Record<string, Record<string, any>>): Record<string, Record<string, any>> {
620
+ return Object.fromEntries(Object.entries(variants).map(([k, v]) => [k, { modelParams: v }]))
621
+ }
622
+
623
+ function googleThinkingVariants(model: Provider.Model): Record<string, Record<string, any>> {
624
+ const id = model.api.id.toLowerCase()
625
+ if (id.includes("2.5")) {
626
+ return {
627
+ high: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } },
628
+ max: {
629
+ thinkingConfig: { includeThoughts: true, thinkingBudget: googleThinkingBudgetMax(id) },
630
+ },
631
+ }
632
+ }
633
+ return Object.fromEntries(
634
+ googleThinkingLevelEfforts(id).map((effort) => [
635
+ effort,
636
+ { thinkingConfig: { includeThoughts: true, thinkingLevel: effort } },
637
+ ]),
638
+ )
639
+ }
640
+
641
+ export function variants(model: Provider.Model): Record<string, Record<string, any>> {
642
+ if (!model.capabilities.reasoning) return {}
643
+
644
+ const id = model.id.toLowerCase()
645
+ const adaptiveOpus = anthropicOpus47OrLater(model.api.id)
646
+ const adaptiveEfforts = anthropicAdaptiveEfforts(model.api.id)
647
+ if (
648
+ id.includes("deepseek-chat") ||
649
+ id.includes("deepseek-reasoner") ||
650
+ id.includes("deepseek-r1") ||
651
+ id.includes("deepseek-v3") ||
652
+ id.includes("minimax") ||
653
+ id.includes("glm") ||
654
+ id.includes("kimi") ||
655
+ id.includes("k2p") ||
656
+ id.includes("qwen") ||
657
+ id.includes("big-pickle")
658
+ )
659
+ return {}
660
+
661
+ // see: https://docs.x.ai/docs/guides/reasoning#control-how-hard-the-model-thinks
662
+ if (id.includes("grok") && id.includes("grok-3-mini")) {
663
+ if (model.api.npm === "@openrouter/ai-sdk-provider") {
664
+ return {
665
+ low: { reasoning: { effort: "low" } },
666
+ high: { reasoning: { effort: "high" } },
667
+ }
668
+ }
669
+ return {
670
+ low: { reasoningEffort: "low" },
671
+ high: { reasoningEffort: "high" },
672
+ }
673
+ }
674
+ if (id.includes("grok")) return {}
675
+
676
+ switch (model.api.npm) {
677
+ case "@openrouter/ai-sdk-provider":
678
+ if (!id.includes("gpt") && !id.includes("gemini-3") && !id.includes("claude")) return {}
679
+ return Object.fromEntries(
680
+ (id.includes("gpt") ? openaiCompatibleReasoningEfforts(id) : OPENAI_EFFORTS).map((effort) => [
681
+ effort,
682
+ { reasoning: { effort } },
683
+ ]),
684
+ )
685
+
686
+ case "ai-gateway-provider": {
687
+ // Cloudflare AI Gateway routes every upstream through its OpenAI-compatible
688
+ // /v1/compat endpoint, so the body is always OAI-shaped. The gateway
689
+ // translates `reasoning_effort` to the upstream provider's native control
690
+ // (e.g. Anthropic thinking budgets) when needed. Variants therefore stay
691
+ // OAI-style for all upstreams, with an extended effort set for OpenAI
692
+ // models that support it.
693
+ if (model.api.id.startsWith("openai/")) {
694
+ const efforts = openaiReasoningEfforts(model.api.id, model.release_date)
695
+ return Object.fromEntries(efforts.map((effort) => [effort, { reasoningEffort: effort }]))
696
+ }
697
+ return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
698
+ }
699
+
700
+ case "@ai-sdk/gateway":
701
+ if (model.id.includes("anthropic")) {
702
+ if (adaptiveEfforts) {
703
+ return Object.fromEntries(
704
+ adaptiveEfforts.map((effort) => [
705
+ effort,
706
+ {
707
+ thinking: {
708
+ type: "adaptive",
709
+ // Opus 4.7+ flips the API default for `display` to "omitted", which
710
+ // returns empty thinking blocks. Force "summarized" so summaries
711
+ // survive (4.6/Sonnet 4.6 already default to "summarized").
712
+ ...(adaptiveOpus ? { display: "summarized" } : {}),
713
+ },
714
+ effort,
715
+ },
716
+ ]),
717
+ )
718
+ }
719
+ return {
720
+ high: {
721
+ thinking: {
722
+ type: "enabled",
723
+ budgetTokens: 16000,
724
+ },
725
+ },
726
+ max: {
727
+ thinking: {
728
+ type: "enabled",
729
+ budgetTokens: 31999,
730
+ },
731
+ },
732
+ }
733
+ }
734
+ if (model.id.includes("google")) {
735
+ if (id.includes("2.5")) {
736
+ return {
737
+ high: {
738
+ thinkingConfig: {
739
+ includeThoughts: true,
740
+ thinkingBudget: 16000,
741
+ },
742
+ },
743
+ max: {
744
+ thinkingConfig: {
745
+ includeThoughts: true,
746
+ thinkingBudget: googleThinkingBudgetMax(id),
747
+ },
748
+ },
749
+ }
750
+ }
751
+ return Object.fromEntries(
752
+ ["low", "high"].map((effort) => [
753
+ effort,
754
+ {
755
+ includeThoughts: true,
756
+ thinkingLevel: effort,
757
+ },
758
+ ]),
759
+ )
760
+ }
761
+ return Object.fromEntries(
762
+ openaiCompatibleReasoningEfforts(model.api.id).map((effort) => [effort, { reasoningEffort: effort }]),
763
+ )
764
+
765
+ case "@ai-sdk/github-copilot":
766
+ if (model.id.includes("gemini")) {
767
+ // currently github copilot only returns thinking
768
+ return {}
769
+ }
770
+ if (model.id.includes("claude")) {
771
+ return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
772
+ }
773
+ const copilotEfforts = iife(() => {
774
+ if (id.includes("5.1-codex-max") || id.includes("5.2") || id.includes("5.3"))
775
+ return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
776
+ const arr = [...WIDELY_SUPPORTED_EFFORTS]
777
+ if (id.includes("gpt-5") && model.release_date >= "2025-12-04") arr.push("xhigh")
778
+ return arr
779
+ })
780
+ return Object.fromEntries(
781
+ copilotEfforts.map((effort) => [
782
+ effort,
783
+ {
784
+ reasoningEffort: effort,
785
+ reasoningSummary: "auto",
786
+ include: INCLUDE_ENCRYPTED_REASONING,
787
+ },
788
+ ]),
789
+ )
790
+
791
+ case "@ai-sdk/cerebras":
792
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/cerebras
793
+ case "@ai-sdk/togetherai":
794
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/togetherai
795
+ case "@ai-sdk/xai":
796
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/xai
797
+ case "@ai-sdk/deepinfra":
798
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/deepinfra
799
+ case "venice-ai-sdk-provider":
800
+ // https://docs.venice.ai/overview/guides/reasoning-models#reasoning-effort
801
+ case "@ai-sdk/openai-compatible":
802
+ const efforts = [...WIDELY_SUPPORTED_EFFORTS]
803
+ if (model.api.id.toLowerCase().includes("deepseek-v4")) {
804
+ efforts.push("max")
805
+ }
806
+ return Object.fromEntries(efforts.map((effort) => [effort, { reasoningEffort: effort }]))
807
+
808
+ case "@ai-sdk/azure":
809
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure
810
+ if (id === "o1-mini") return {}
811
+ return Object.fromEntries(
812
+ openaiReasoningEfforts(id, model.release_date).map((effort) => [
813
+ effort,
814
+ {
815
+ reasoningEffort: effort,
816
+ reasoningSummary: "auto",
817
+ include: INCLUDE_ENCRYPTED_REASONING,
818
+ },
819
+ ]),
820
+ )
821
+ case "@ai-sdk/amazon-bedrock/mantle":
822
+ case "@ai-sdk/openai": {
823
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/openai
824
+ const efforts = openaiReasoningEfforts(model.api.id, model.release_date)
825
+ return Object.fromEntries(
826
+ efforts.map((effort) => [
827
+ effort,
828
+ {
829
+ reasoningEffort: effort,
830
+ reasoningSummary: "auto",
831
+ include: INCLUDE_ENCRYPTED_REASONING,
832
+ },
833
+ ]),
834
+ )
835
+ }
836
+
837
+ case "@ai-sdk/anthropic":
838
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/anthropic
839
+ case "@ai-sdk/google-vertex/anthropic":
840
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-vertex#anthropic-provider
841
+ if (adaptiveEfforts) {
842
+ let efforts = [...adaptiveEfforts]
843
+ if (model.providerID === "github-copilot") {
844
+ if (model.api.id.includes("opus-4.7")) {
845
+ efforts = ["medium"]
846
+ }
847
+ // Efforts currently supported are: low, medium, high
848
+ efforts = efforts.filter((v) => v !== "max" && v !== "xhigh")
849
+ }
850
+ return Object.fromEntries(
851
+ efforts.map((effort) => [
852
+ effort,
853
+ {
854
+ thinking: {
855
+ type: "adaptive",
856
+ ...(adaptiveOpus ? { display: "summarized" } : {}),
857
+ },
858
+ effort,
859
+ },
860
+ ]),
861
+ )
862
+ }
863
+
864
+ if (["opus-4-5", "opus-4.5"].some((v) => model.api.id.includes(v))) {
865
+ return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { effort }]))
866
+ }
867
+
868
+ return {
869
+ high: {
870
+ thinking: {
871
+ type: "enabled",
872
+ budgetTokens: Math.min(16_000, Math.floor(model.limit.output / 2 - 1)),
873
+ },
874
+ },
875
+ max: {
876
+ thinking: {
877
+ type: "enabled",
878
+ budgetTokens: Math.min(31_999, model.limit.output - 1),
879
+ },
880
+ },
881
+ }
882
+
883
+ case "@ai-sdk/amazon-bedrock":
884
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock
885
+ if (adaptiveEfforts) {
886
+ return Object.fromEntries(
887
+ adaptiveEfforts.map((effort) => [
888
+ effort,
889
+ {
890
+ reasoningConfig: {
891
+ type: "adaptive",
892
+ maxReasoningEffort: effort,
893
+ ...(adaptiveOpus ? { display: "summarized" } : {}),
894
+ },
895
+ },
896
+ ]),
897
+ )
898
+ }
899
+ // For Anthropic models on Bedrock, use reasoningConfig with budgetTokens
900
+ if (model.api.id.includes("anthropic")) {
901
+ return {
902
+ high: {
903
+ reasoningConfig: {
904
+ type: "enabled",
905
+ budgetTokens: 16000,
906
+ },
907
+ },
908
+ max: {
909
+ reasoningConfig: {
910
+ type: "enabled",
911
+ budgetTokens: 31999,
912
+ },
913
+ },
914
+ }
915
+ }
916
+
917
+ // For Amazon Nova models, use reasoningConfig with maxReasoningEffort
918
+ return Object.fromEntries(
919
+ WIDELY_SUPPORTED_EFFORTS.map((effort) => [
920
+ effort,
921
+ {
922
+ reasoningConfig: {
923
+ type: "enabled",
924
+ maxReasoningEffort: effort,
925
+ },
926
+ },
927
+ ]),
928
+ )
929
+
930
+ case "@ai-sdk/google-vertex":
931
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-vertex
932
+ case "@ai-sdk/google":
933
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai
934
+ return googleThinkingVariants(model)
935
+
936
+ case "@ai-sdk/mistral":
937
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/mistral
938
+ // https://docs.mistral.ai/capabilities/reasoning/adjustable
939
+ if (!model.capabilities.reasoning) return {}
940
+ // Only Mistral Small 4 and Medium 3.5 support reasoning
941
+ const MISTRAL_REASONING_IDS = [
942
+ "mistral-small-2603",
943
+ "mistral-small-latest",
944
+ "mistral-medium-3.5",
945
+ "mistral-medium-2604",
946
+ ]
947
+ const mistralId = model.api.id.toLowerCase()
948
+ if (!MISTRAL_REASONING_IDS.some((id) => mistralId.includes(id))) return {}
949
+ return {
950
+ high: { reasoningEffort: "high" },
951
+ }
952
+
953
+ case "@ai-sdk/cohere":
954
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/cohere
955
+ return {}
956
+
957
+ case "@ai-sdk/groq":
958
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/groq
959
+ const groqEffort = ["none", ...WIDELY_SUPPORTED_EFFORTS]
960
+ return Object.fromEntries(
961
+ groqEffort.map((effort) => [
962
+ effort,
963
+ {
964
+ reasoningEffort: effort,
965
+ },
966
+ ]),
967
+ )
968
+
969
+ case "@ai-sdk/perplexity":
970
+ // https://v5.ai-sdk.dev/providers/ai-sdk-providers/perplexity
971
+ return {}
972
+
973
+ case "@jerome-benoit/sap-ai-provider-v2": {
974
+ if (id.includes("anthropic")) {
975
+ if (adaptiveEfforts) {
976
+ // Bedrock adaptive splits `effort` out into `output_config` (vs Anthropic
977
+ // native which inlines it). Opus 4.7+ flipped `display` default to "omitted".
978
+ return wrapInSapModelParams(
979
+ Object.fromEntries(
980
+ adaptiveEfforts.map((effort) => [
981
+ effort,
982
+ {
983
+ thinking: { type: "adaptive", ...(adaptiveOpus ? { display: "summarized" } : {}) },
984
+ output_config: { effort },
985
+ },
986
+ ]),
987
+ ),
988
+ )
989
+ }
990
+ return wrapInSapModelParams({
991
+ high: { thinking: { type: "enabled", budget_tokens: 16000 } },
992
+ max: { thinking: { type: "enabled", budget_tokens: 31999 } },
993
+ })
994
+ }
995
+ if (id.includes("gemini") && id.includes("2.5")) {
996
+ return wrapInSapModelParams(googleThinkingVariants(model))
997
+ }
998
+ if (id.includes("gpt") || /\bo[1-9]/.test(id)) {
999
+ const efforts = openaiReasoningEfforts(id, model.release_date)
1000
+ return wrapInSapModelParams(Object.fromEntries(efforts.map((effort) => [effort, { reasoning_effort: effort }])))
1001
+ }
1002
+ return wrapInSapModelParams(
1003
+ Object.fromEntries(["low", "medium", "high"].map((effort) => [effort, { reasoning_effort: effort }])),
1004
+ )
1005
+ }
1006
+ }
1007
+ return {}
1008
+ }
1009
+
1010
+ export function options(input: {
1011
+ model: Provider.Model
1012
+ sessionID: string
1013
+ providerOptions?: Record<string, any>
1014
+ }): Record<string, any> {
1015
+ const result: Record<string, any> = {}
1016
+
1017
+ if (
1018
+ input.model.api.npm === "@ai-sdk/google-vertex/anthropic" ||
1019
+ (!input.model.api.id.includes("claude") && input.model.api.npm === "@ai-sdk/anthropic")
1020
+ ) {
1021
+ result["toolStreaming"] = false
1022
+ }
1023
+
1024
+ // openai and providers using openai package should set store to false by default.
1025
+ if (
1026
+ input.model.providerID === "openai" ||
1027
+ input.model.api.npm === "@ai-sdk/openai" ||
1028
+ input.model.api.npm === "@ai-sdk/github-copilot" ||
1029
+ input.model.api.npm === "@ai-sdk/amazon-bedrock/mantle"
1030
+ ) {
1031
+ result["store"] = false
1032
+ }
1033
+
1034
+ if (input.model.api.npm === "@ai-sdk/azure") {
1035
+ result["store"] = false
1036
+ result["promptCacheKey"] = input.sessionID
1037
+ }
1038
+
1039
+ if (input.model.api.npm === "@openrouter/ai-sdk-provider" || input.model.api.npm === "@llmgateway/ai-sdk-provider") {
1040
+ result["usage"] = {
1041
+ include: true,
1042
+ }
1043
+ if (input.model.api.id.includes("gemini-3")) {
1044
+ result["reasoning"] = { effort: "high" }
1045
+ }
1046
+ }
1047
+
1048
+ if (
1049
+ input.model.providerID === "baseten" ||
1050
+ (input.model.providerID === "octo" && ["kimi-k2-thinking", "glm-4.6"].includes(input.model.api.id))
1051
+ ) {
1052
+ result["chat_template_args"] = { enable_thinking: true }
1053
+ }
1054
+
1055
+ if (
1056
+ ["zai", "zhipuai"].some((id) => input.model.providerID.includes(id)) &&
1057
+ input.model.api.npm === "@ai-sdk/openai-compatible"
1058
+ ) {
1059
+ result["thinking"] = {
1060
+ type: "enabled",
1061
+ clear_thinking: false,
1062
+ }
1063
+ }
1064
+
1065
+ if (input.model.providerID === "openai" || input.providerOptions?.setCacheKey) {
1066
+ result["promptCacheKey"] = input.sessionID
1067
+ }
1068
+
1069
+ if (input.model.api.npm === "@ai-sdk/google" || input.model.api.npm === "@ai-sdk/google-vertex") {
1070
+ if (input.model.capabilities.reasoning) {
1071
+ result["thinkingConfig"] = {
1072
+ includeThoughts: true,
1073
+ }
1074
+ if (input.model.api.id.includes("gemini-3")) {
1075
+ result["thinkingConfig"]["thinkingLevel"] = "high"
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+ // Enable thinking by default for kimi models using anthropic SDK
1081
+ const modelId = input.model.api.id.toLowerCase()
1082
+ if (
1083
+ (input.model.api.npm === "@ai-sdk/anthropic" || input.model.api.npm === "@ai-sdk/google-vertex/anthropic") &&
1084
+ (modelId.includes("k2p") || modelId.includes("kimi-k2.") || modelId.includes("kimi-k2p"))
1085
+ ) {
1086
+ result["thinking"] = {
1087
+ type: "enabled",
1088
+ budgetTokens: Math.min(16_000, Math.floor(input.model.limit.output / 2 - 1)),
1089
+ }
1090
+ }
1091
+
1092
+ // Enable thinking for reasoning models on alibaba-cn (DashScope).
1093
+ // DashScope's OpenAI-compatible API requires `enable_thinking: true` in the request body
1094
+ // to return reasoning_content. Without it, models like kimi-k2.5, qwen-plus, qwen3, qwq,
1095
+ // deepseek-r1, etc. never output thinking/reasoning tokens.
1096
+ // Note: kimi-k2-thinking is excluded as it returns reasoning_content by default.
1097
+ if (
1098
+ input.model.providerID === "alibaba-cn" &&
1099
+ input.model.capabilities.reasoning &&
1100
+ input.model.api.npm === "@ai-sdk/openai-compatible" &&
1101
+ !modelId.includes("kimi-k2-thinking")
1102
+ ) {
1103
+ result["enable_thinking"] = true
1104
+ }
1105
+
1106
+ if (input.model.api.npm === "@ai-sdk/azure" && input.model.api.id.includes("gpt-5.5")) {
1107
+ result["reasoningSummary"] = "auto"
1108
+ return result
1109
+ }
1110
+
1111
+ if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) {
1112
+ if (!input.model.api.id.includes("gpt-5-pro")) {
1113
+ result["reasoningEffort"] = "medium"
1114
+ if (
1115
+ input.model.api.npm === "@ai-sdk/openai" ||
1116
+ input.model.api.npm === "@ai-sdk/azure" ||
1117
+ input.model.api.npm === "@ai-sdk/github-copilot" ||
1118
+ input.model.api.npm === "@ai-sdk/amazon-bedrock/mantle"
1119
+ ) {
1120
+ result["reasoningSummary"] = "auto"
1121
+ }
1122
+ if (input.model.api.npm === "@ai-sdk/openai" || input.model.api.npm === "@ai-sdk/amazon-bedrock/mantle") {
1123
+ result["include"] = INCLUDE_ENCRYPTED_REASONING
1124
+ }
1125
+ }
1126
+
1127
+ // Only set textVerbosity for non-chat gpt-5.x models
1128
+ // Chat models (e.g. gpt-5.2-chat-latest) only support "medium" verbosity
1129
+ if (
1130
+ input.model.api.id.includes("gpt-5.") &&
1131
+ !input.model.api.id.includes("codex") &&
1132
+ !input.model.api.id.includes("-chat") &&
1133
+ input.model.providerID !== "azure"
1134
+ ) {
1135
+ result["textVerbosity"] = "low"
1136
+ }
1137
+
1138
+ if (input.model.providerID.startsWith("octo")) {
1139
+ result["promptCacheKey"] = input.sessionID
1140
+ result["include"] = INCLUDE_ENCRYPTED_REASONING
1141
+ result["reasoningSummary"] = "auto"
1142
+ }
1143
+ }
1144
+
1145
+ if (input.model.providerID === "venice") {
1146
+ result["promptCacheKey"] = input.sessionID
1147
+ }
1148
+
1149
+ if (input.model.providerID === "openrouter") {
1150
+ result["prompt_cache_key"] = input.sessionID
1151
+ }
1152
+ if (input.model.api.npm === "@ai-sdk/gateway") {
1153
+ result["gateway"] = {
1154
+ caching: "auto",
1155
+ }
1156
+ }
1157
+
1158
+ return result
1159
+ }
1160
+
1161
+ export function smallOptions(model: Provider.Model) {
1162
+ const small = Object.values(model.variants ?? {})[0] ?? {}
1163
+ if (
1164
+ model.providerID === "openai" ||
1165
+ model.api.npm === "@ai-sdk/openai" ||
1166
+ model.api.npm === "@ai-sdk/github-copilot"
1167
+ ) {
1168
+ const base = { store: false }
1169
+ return mergeDeep(base, small)
1170
+ }
1171
+ if (model.providerID === "openrouter" || model.providerID === "llmgateway") {
1172
+ if (Object.keys(small).length === 0 && model.api.id.includes("google")) {
1173
+ return { reasoning: { enabled: false } }
1174
+ }
1175
+ }
1176
+
1177
+ if (model.providerID === "venice") {
1178
+ if (Object.keys(small).length > 0) return small
1179
+ return { veniceParameters: { disableThinking: true } }
1180
+ }
1181
+
1182
+ return small
1183
+ }
1184
+
1185
+ // Maps model ID prefix to provider slug used in providerOptions.
1186
+ // Example: "amazon/nova-2-lite" → "bedrock"
1187
+ const SLUG_OVERRIDES: Record<string, string> = {
1188
+ amazon: "bedrock",
1189
+ }
1190
+
1191
+ export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
1192
+ if (model.api.npm === "@ai-sdk/gateway") {
1193
+ // Gateway providerOptions are split across two namespaces:
1194
+ // - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
1195
+ // - `<upstream slug>`: provider-specific model options (anthropic/openai/...)
1196
+ // We keep `gateway` as-is and route every other top-level option under the
1197
+ // model-derived upstream slug.
1198
+ const i = model.api.id.indexOf("/")
1199
+ const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined
1200
+ const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined
1201
+ const gateway = options.gateway
1202
+ const rest = Object.fromEntries(Object.entries(options).filter(([k]) => k !== "gateway"))
1203
+ const has = Object.keys(rest).length > 0
1204
+
1205
+ const result: Record<string, any> = {}
1206
+ if (gateway !== undefined) result.gateway = gateway
1207
+
1208
+ if (has) {
1209
+ if (slug) {
1210
+ // Route model-specific options under the provider slug
1211
+ result[slug] = rest
1212
+ } else if (gateway && typeof gateway === "object" && !Array.isArray(gateway)) {
1213
+ result.gateway = { ...gateway, ...rest }
1214
+ } else {
1215
+ result.gateway = rest
1216
+ }
1217
+ }
1218
+
1219
+ return result
1220
+ }
1221
+
1222
+ // AI SDK packages that resolve providerOptionsName by splitting the
1223
+ // provider name on "." (e.g. "wafer.ai" -> "wafer") need the same
1224
+ // logic here so the key we write matches the key they read.
1225
+ // Other SDKs (xai, mistral, groq, cohere, etc.) use hardcoded keys
1226
+ // like "xai" or "cohere" - applying .split(".")[0] would break those.
1227
+ const usesDotSplitOptions =
1228
+ model.api.npm === "@ai-sdk/openai-compatible" ||
1229
+ model.api.npm === "@ai-sdk/openai" ||
1230
+ model.api.npm === "@ai-sdk/anthropic"
1231
+ const key = sdkKey(model.api.npm) ?? (usesDotSplitOptions ? model.providerID.split(".")[0] : model.providerID)
1232
+ // @ai-sdk/azure delegates to OpenAIChatLanguageModel which reads from
1233
+ // providerOptions["openai"], but OpenAIResponsesLanguageModel checks
1234
+ // "azure" first. Pass both so model options work on either code path.
1235
+ if (model.api.npm === "@ai-sdk/azure") {
1236
+ return { openai: options, azure: options }
1237
+ }
1238
+ return { [key]: options }
1239
+ }
1240
+
1241
+ export function maxOutputTokens(model: Provider.Model, outputTokenMax?: number): number {
1242
+ // Use model's actual output limit if available, otherwise fall back to provided max or default
1243
+ if (model.limit.output > 0) return model.limit.output
1244
+ if (outputTokenMax && outputTokenMax > 0) return outputTokenMax
1245
+ return DEFAULT_OUTPUT_TOKEN_MAX
1246
+ }
1247
+
1248
+ export function schema(model: Provider.Model, schema: JSONSchema7): JSONSchema7 {
1249
+ /*
1250
+ if (["openai", "azure"].includes(providerID)) {
1251
+ if (schema.type === "object" && schema.properties) {
1252
+ for (const [key, value] of Object.entries(schema.properties)) {
1253
+ if (schema.required?.includes(key)) continue
1254
+ schema.properties[key] = {
1255
+ anyOf: [
1256
+ value as JSONSchema.JSONSchema,
1257
+ {
1258
+ type: "null",
1259
+ },
1260
+ ],
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+ */
1266
+
1267
+ if (model.providerID === "moonshotai" || model.api.id.toLowerCase().includes("kimi")) {
1268
+ const sanitizeMoonshot = (obj: unknown): unknown => {
1269
+ if (obj === null || typeof obj !== "object") return obj
1270
+ if (Array.isArray(obj)) return obj.map(sanitizeMoonshot)
1271
+ // Moonshot expands $ref before validation and rejects sibling keywords like description on the same node.
1272
+ if ("$ref" in obj && typeof obj.$ref === "string") return { $ref: obj.$ref }
1273
+ const result = Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, sanitizeMoonshot(value)]))
1274
+ // MFJS does not support tuple-style `items` arrays; it requires one schema object for all array items.
1275
+ if (Array.isArray(result.items)) result.items = result.items[0] ?? {}
1276
+ return result
1277
+ }
1278
+
1279
+ const sanitized = sanitizeMoonshot(schema)
1280
+ if (typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized)) {
1281
+ schema = sanitized
1282
+ }
1283
+ }
1284
+
1285
+ // Convert integer enums to string enums for Google/Gemini
1286
+ if (model.providerID === "google" || model.api.id.includes("gemini")) {
1287
+ const isPlainObject = (node: unknown): node is Record<string, any> =>
1288
+ typeof node === "object" && node !== null && !Array.isArray(node)
1289
+ const hasCombiner = (node: unknown) =>
1290
+ isPlainObject(node) && (Array.isArray(node.anyOf) || Array.isArray(node.oneOf) || Array.isArray(node.allOf))
1291
+ const hasSchemaIntent = (node: unknown) => {
1292
+ if (!isPlainObject(node)) return false
1293
+ if (hasCombiner(node)) return true
1294
+ return [
1295
+ "type",
1296
+ "properties",
1297
+ "items",
1298
+ "prefixItems",
1299
+ "enum",
1300
+ "const",
1301
+ "$ref",
1302
+ "additionalProperties",
1303
+ "patternProperties",
1304
+ "required",
1305
+ "not",
1306
+ "if",
1307
+ "then",
1308
+ "else",
1309
+ ].some((key) => key in node)
1310
+ }
1311
+
1312
+ const sanitizeGemini = (obj: any): any => {
1313
+ if (obj === null || typeof obj !== "object") {
1314
+ return obj
1315
+ }
1316
+
1317
+ if (Array.isArray(obj)) {
1318
+ return obj.map(sanitizeGemini)
1319
+ }
1320
+
1321
+ const result: any = {}
1322
+ for (const [key, value] of Object.entries(obj)) {
1323
+ if (key === "enum" && Array.isArray(value)) {
1324
+ // Convert all enum values to strings
1325
+ result[key] = value.map((v) => String(v))
1326
+ // If we have integer type with enum, change type to string
1327
+ if (result.type === "integer" || result.type === "number") {
1328
+ result.type = "string"
1329
+ }
1330
+ } else if (typeof value === "object" && value !== null) {
1331
+ result[key] = sanitizeGemini(value)
1332
+ } else {
1333
+ result[key] = value
1334
+ }
1335
+ }
1336
+
1337
+ // Filter required array to only include fields that exist in properties
1338
+ if (result.type === "object" && result.properties && Array.isArray(result.required)) {
1339
+ result.required = result.required.filter((field: any) => field in result.properties)
1340
+ }
1341
+
1342
+ if (result.type === "array" && !hasCombiner(result)) {
1343
+ if (result.items == null) {
1344
+ result.items = {}
1345
+ }
1346
+ // Ensure items has a type only when it's still schema-empty.
1347
+ if (isPlainObject(result.items) && !hasSchemaIntent(result.items)) {
1348
+ result.items.type = "string"
1349
+ }
1350
+ }
1351
+
1352
+ // Remove properties/required from non-object types (Gemini rejects these)
1353
+ if (result.type && result.type !== "object" && !hasCombiner(result)) {
1354
+ delete result.properties
1355
+ delete result.required
1356
+ }
1357
+
1358
+ return result
1359
+ }
1360
+
1361
+ schema = sanitizeGemini(schema)
1362
+ }
1363
+
1364
+ return schema
1365
+ }
1366
+
1367
+ export * as ProviderTransform from "./transform"