cerebras-cli 1.0.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 (314) hide show
  1. package/AGENTS.md +27 -0
  2. package/Dockerfile +10 -0
  3. package/README.md +15 -0
  4. package/bin/opencode +84 -0
  5. package/bunfig.toml +4 -0
  6. package/package.json +128 -0
  7. package/parsers-config.ts +239 -0
  8. package/script/build.ts +151 -0
  9. package/script/postinstall.mjs +122 -0
  10. package/script/publish.ts +256 -0
  11. package/script/schema.ts +47 -0
  12. package/src/acp/README.md +164 -0
  13. package/src/acp/agent.ts +812 -0
  14. package/src/acp/session.ts +70 -0
  15. package/src/acp/types.ts +22 -0
  16. package/src/agent/agent.ts +310 -0
  17. package/src/agent/generate.txt +75 -0
  18. package/src/auth/index.ts +70 -0
  19. package/src/bun/index.ts +152 -0
  20. package/src/bus/global.ts +10 -0
  21. package/src/bus/index.ts +142 -0
  22. package/src/cli/bootstrap.ts +17 -0
  23. package/src/cli/cmd/acp.ts +88 -0
  24. package/src/cli/cmd/agent.ts +165 -0
  25. package/src/cli/cmd/auth.ts +369 -0
  26. package/src/cli/cmd/cmd.ts +7 -0
  27. package/src/cli/cmd/debug/config.ts +15 -0
  28. package/src/cli/cmd/debug/file.ts +91 -0
  29. package/src/cli/cmd/debug/index.ts +41 -0
  30. package/src/cli/cmd/debug/lsp.ts +47 -0
  31. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  32. package/src/cli/cmd/debug/scrap.ts +15 -0
  33. package/src/cli/cmd/debug/snapshot.ts +48 -0
  34. package/src/cli/cmd/export.ts +88 -0
  35. package/src/cli/cmd/generate.ts +38 -0
  36. package/src/cli/cmd/github.ts +1200 -0
  37. package/src/cli/cmd/import.ts +98 -0
  38. package/src/cli/cmd/mcp.ts +400 -0
  39. package/src/cli/cmd/models.ts +77 -0
  40. package/src/cli/cmd/pr.ts +112 -0
  41. package/src/cli/cmd/run.ts +342 -0
  42. package/src/cli/cmd/serve.ts +31 -0
  43. package/src/cli/cmd/session.ts +106 -0
  44. package/src/cli/cmd/stats.ts +298 -0
  45. package/src/cli/cmd/tui/app.tsx +732 -0
  46. package/src/cli/cmd/tui/attach.ts +25 -0
  47. package/src/cli/cmd/tui/component/border.tsx +21 -0
  48. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  49. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  50. package/src/cli/cmd/tui/component/dialog-feedback.tsx +160 -0
  51. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  52. package/src/cli/cmd/tui/component/dialog-model.tsx +223 -0
  53. package/src/cli/cmd/tui/component/dialog-notification.tsx +78 -0
  54. package/src/cli/cmd/tui/component/dialog-provider.tsx +222 -0
  55. package/src/cli/cmd/tui/component/dialog-session-list.tsx +97 -0
  56. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  57. package/src/cli/cmd/tui/component/dialog-status.tsx +114 -0
  58. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  59. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  60. package/src/cli/cmd/tui/component/logo.tsx +37 -0
  61. package/src/cli/cmd/tui/component/notification-banner.tsx +58 -0
  62. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +530 -0
  63. package/src/cli/cmd/tui/component/prompt/history.tsx +107 -0
  64. package/src/cli/cmd/tui/component/prompt/index.tsx +931 -0
  65. package/src/cli/cmd/tui/context/args.tsx +14 -0
  66. package/src/cli/cmd/tui/context/directory.ts +12 -0
  67. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  68. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  69. package/src/cli/cmd/tui/context/keybind.tsx +111 -0
  70. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  71. package/src/cli/cmd/tui/context/local.tsx +339 -0
  72. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  73. package/src/cli/cmd/tui/context/route.tsx +45 -0
  74. package/src/cli/cmd/tui/context/sdk.tsx +75 -0
  75. package/src/cli/cmd/tui/context/sync.tsx +374 -0
  76. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  77. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  78. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  79. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  80. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  81. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  82. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  83. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  84. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  85. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  86. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  87. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  88. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  89. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  90. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  91. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  92. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  93. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  94. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  95. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  96. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  97. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  98. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  99. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  100. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  101. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  102. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  103. package/src/cli/cmd/tui/context/theme.tsx +1077 -0
  104. package/src/cli/cmd/tui/event.ts +39 -0
  105. package/src/cli/cmd/tui/routes/home.tsx +104 -0
  106. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +93 -0
  107. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +37 -0
  108. package/src/cli/cmd/tui/routes/session/footer.tsx +76 -0
  109. package/src/cli/cmd/tui/routes/session/header.tsx +183 -0
  110. package/src/cli/cmd/tui/routes/session/index.tsx +1703 -0
  111. package/src/cli/cmd/tui/routes/session/sidebar.tsx +586 -0
  112. package/src/cli/cmd/tui/spawn.ts +60 -0
  113. package/src/cli/cmd/tui/thread.ts +120 -0
  114. package/src/cli/cmd/tui/ui/dialog-alert.tsx +55 -0
  115. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +81 -0
  116. package/src/cli/cmd/tui/ui/dialog-help.tsx +36 -0
  117. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +75 -0
  118. package/src/cli/cmd/tui/ui/dialog-select.tsx +317 -0
  119. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  120. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  121. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  122. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  123. package/src/cli/cmd/tui/util/editor.ts +32 -0
  124. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  125. package/src/cli/cmd/tui/worker.ts +63 -0
  126. package/src/cli/cmd/uninstall.ts +344 -0
  127. package/src/cli/cmd/upgrade.ts +67 -0
  128. package/src/cli/cmd/web.ts +84 -0
  129. package/src/cli/error.ts +55 -0
  130. package/src/cli/ui.ts +84 -0
  131. package/src/cli/upgrade.ts +25 -0
  132. package/src/command/index.ts +79 -0
  133. package/src/command/template/initialize.txt +10 -0
  134. package/src/command/template/review.txt +73 -0
  135. package/src/config/config.ts +886 -0
  136. package/src/config/markdown.ts +41 -0
  137. package/src/env/index.ts +26 -0
  138. package/src/file/fzf.ts +124 -0
  139. package/src/file/ignore.ts +83 -0
  140. package/src/file/index.ts +326 -0
  141. package/src/file/ripgrep.ts +391 -0
  142. package/src/file/time.ts +38 -0
  143. package/src/file/watcher.ts +89 -0
  144. package/src/flag/flag.ts +28 -0
  145. package/src/format/formatter.ts +277 -0
  146. package/src/format/index.ts +137 -0
  147. package/src/global/index.ts +52 -0
  148. package/src/id/id.ts +73 -0
  149. package/src/ide/index.ts +75 -0
  150. package/src/index.ts +158 -0
  151. package/src/installation/index.ts +194 -0
  152. package/src/lsp/client.ts +215 -0
  153. package/src/lsp/index.ts +370 -0
  154. package/src/lsp/language.ts +111 -0
  155. package/src/lsp/server.ts +1327 -0
  156. package/src/mcp/auth.ts +82 -0
  157. package/src/mcp/index.ts +576 -0
  158. package/src/mcp/oauth-callback.ts +203 -0
  159. package/src/mcp/oauth-provider.ts +132 -0
  160. package/src/notification/index.ts +101 -0
  161. package/src/patch/index.ts +622 -0
  162. package/src/permission/index.ts +198 -0
  163. package/src/plugin/index.ts +95 -0
  164. package/src/project/bootstrap.ts +31 -0
  165. package/src/project/instance.ts +68 -0
  166. package/src/project/project.ts +133 -0
  167. package/src/project/state.ts +65 -0
  168. package/src/project/vcs.ts +77 -0
  169. package/src/provider/auth.ts +143 -0
  170. package/src/provider/models-macro.ts +11 -0
  171. package/src/provider/models.ts +93 -0
  172. package/src/provider/provider.ts +996 -0
  173. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  174. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  175. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  176. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  177. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +27 -0
  178. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  179. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  180. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  181. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  182. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  183. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  184. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  185. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  186. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  187. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  188. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  189. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  190. package/src/provider/transform.ts +406 -0
  191. package/src/pty/index.ts +226 -0
  192. package/src/ratelimit/index.ts +185 -0
  193. package/src/server/error.ts +36 -0
  194. package/src/server/project.ts +50 -0
  195. package/src/server/server.ts +2463 -0
  196. package/src/server/tui.ts +71 -0
  197. package/src/session/compaction.ts +257 -0
  198. package/src/session/index.ts +470 -0
  199. package/src/session/message-v2.ts +641 -0
  200. package/src/session/message.ts +189 -0
  201. package/src/session/processor.ts +443 -0
  202. package/src/session/prompt/anthropic-20250930.txt +166 -0
  203. package/src/session/prompt/anthropic.txt +105 -0
  204. package/src/session/prompt/anthropic_spoof.txt +1 -0
  205. package/src/session/prompt/beast.txt +147 -0
  206. package/src/session/prompt/build-switch.txt +5 -0
  207. package/src/session/prompt/codex.txt +318 -0
  208. package/src/session/prompt/compaction.txt +12 -0
  209. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  210. package/src/session/prompt/gemini.txt +155 -0
  211. package/src/session/prompt/max-steps.txt +16 -0
  212. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  213. package/src/session/prompt/plan.txt +26 -0
  214. package/src/session/prompt/polaris.txt +107 -0
  215. package/src/session/prompt/qwen.txt +109 -0
  216. package/src/session/prompt/summarize.txt +4 -0
  217. package/src/session/prompt/title.txt +36 -0
  218. package/src/session/prompt.ts +1541 -0
  219. package/src/session/retry.ts +82 -0
  220. package/src/session/revert.ts +108 -0
  221. package/src/session/status.ts +75 -0
  222. package/src/session/summary.ts +203 -0
  223. package/src/session/system.ts +148 -0
  224. package/src/session/todo.ts +36 -0
  225. package/src/share/share-next.ts +195 -0
  226. package/src/share/share.ts +87 -0
  227. package/src/snapshot/index.ts +197 -0
  228. package/src/storage/storage.ts +226 -0
  229. package/src/telemetry/index.ts +232 -0
  230. package/src/tool/bash.ts +365 -0
  231. package/src/tool/bash.txt +128 -0
  232. package/src/tool/batch.ts +173 -0
  233. package/src/tool/batch.txt +28 -0
  234. package/src/tool/codesearch.ts +138 -0
  235. package/src/tool/codesearch.txt +12 -0
  236. package/src/tool/edit.ts +674 -0
  237. package/src/tool/edit.txt +10 -0
  238. package/src/tool/glob.ts +65 -0
  239. package/src/tool/glob.txt +6 -0
  240. package/src/tool/grep.ts +120 -0
  241. package/src/tool/grep.txt +8 -0
  242. package/src/tool/invalid.ts +17 -0
  243. package/src/tool/ls.ts +110 -0
  244. package/src/tool/ls.txt +1 -0
  245. package/src/tool/lsp-diagnostics.ts +26 -0
  246. package/src/tool/lsp-diagnostics.txt +1 -0
  247. package/src/tool/lsp-hover.ts +31 -0
  248. package/src/tool/lsp-hover.txt +1 -0
  249. package/src/tool/multiedit.ts +46 -0
  250. package/src/tool/multiedit.txt +41 -0
  251. package/src/tool/patch.ts +233 -0
  252. package/src/tool/patch.txt +1 -0
  253. package/src/tool/read.ts +217 -0
  254. package/src/tool/read.txt +12 -0
  255. package/src/tool/registry.ts +148 -0
  256. package/src/tool/task.ts +135 -0
  257. package/src/tool/task.txt +60 -0
  258. package/src/tool/todo.ts +39 -0
  259. package/src/tool/todoread.txt +14 -0
  260. package/src/tool/todowrite.txt +167 -0
  261. package/src/tool/tool.ts +66 -0
  262. package/src/tool/webfetch.ts +187 -0
  263. package/src/tool/webfetch.txt +14 -0
  264. package/src/tool/websearch.ts +150 -0
  265. package/src/tool/websearch.txt +11 -0
  266. package/src/tool/write.ts +99 -0
  267. package/src/tool/write.txt +8 -0
  268. package/src/types/shims.d.ts +3 -0
  269. package/src/util/color.ts +19 -0
  270. package/src/util/context.ts +25 -0
  271. package/src/util/defer.ts +12 -0
  272. package/src/util/eventloop.ts +20 -0
  273. package/src/util/filesystem.ts +69 -0
  274. package/src/util/fn.ts +11 -0
  275. package/src/util/iife.ts +3 -0
  276. package/src/util/keybind.ts +79 -0
  277. package/src/util/lazy.ts +11 -0
  278. package/src/util/locale.ts +81 -0
  279. package/src/util/lock.ts +98 -0
  280. package/src/util/log.ts +177 -0
  281. package/src/util/queue.ts +32 -0
  282. package/src/util/rpc.ts +42 -0
  283. package/src/util/scrap.ts +10 -0
  284. package/src/util/signal.ts +12 -0
  285. package/src/util/timeout.ts +14 -0
  286. package/src/util/token.ts +7 -0
  287. package/src/util/wildcard.ts +54 -0
  288. package/sst-env.d.ts +9 -0
  289. package/test/bun.test.ts +53 -0
  290. package/test/config/agent-color.test.ts +66 -0
  291. package/test/config/config.test.ts +503 -0
  292. package/test/config/markdown.test.ts +89 -0
  293. package/test/file/ignore.test.ts +10 -0
  294. package/test/fixture/fixture.ts +28 -0
  295. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  296. package/test/ide/ide.test.ts +82 -0
  297. package/test/keybind.test.ts +317 -0
  298. package/test/lsp/client.test.ts +95 -0
  299. package/test/patch/patch.test.ts +348 -0
  300. package/test/preload.ts +38 -0
  301. package/test/project/project.test.ts +42 -0
  302. package/test/provider/provider.test.ts +1809 -0
  303. package/test/provider/transform.test.ts +305 -0
  304. package/test/session/retry.test.ts +61 -0
  305. package/test/session/session.test.ts +71 -0
  306. package/test/snapshot/snapshot.test.ts +939 -0
  307. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  308. package/test/tool/bash.test.ts +55 -0
  309. package/test/tool/patch.test.ts +259 -0
  310. package/test/util/iife.test.ts +36 -0
  311. package/test/util/lazy.test.ts +50 -0
  312. package/test/util/timeout.test.ts +21 -0
  313. package/test/util/wildcard.test.ts +55 -0
  314. package/tsconfig.json +17 -0
@@ -0,0 +1,103 @@
1
+ import { createProviderDefinedToolFactory } from "@ai-sdk/provider-utils"
2
+ import { z } from "zod/v4"
3
+
4
+ export const webSearchArgsSchema = z.object({
5
+ filters: z
6
+ .object({
7
+ allowedDomains: z.array(z.string()).optional(),
8
+ })
9
+ .optional(),
10
+
11
+ searchContextSize: z.enum(["low", "medium", "high"]).optional(),
12
+
13
+ userLocation: z
14
+ .object({
15
+ type: z.literal("approximate"),
16
+ country: z.string().optional(),
17
+ city: z.string().optional(),
18
+ region: z.string().optional(),
19
+ timezone: z.string().optional(),
20
+ })
21
+ .optional(),
22
+ })
23
+
24
+ export const webSearchToolFactory = createProviderDefinedToolFactory<
25
+ {
26
+ // Web search doesn't take input parameters - it's controlled by the prompt
27
+ },
28
+ {
29
+ /**
30
+ * Filters for the search.
31
+ */
32
+ filters?: {
33
+ /**
34
+ * Allowed domains for the search.
35
+ * If not provided, all domains are allowed.
36
+ * Subdomains of the provided domains are allowed as well.
37
+ */
38
+ allowedDomains?: string[]
39
+ }
40
+
41
+ /**
42
+ * Search context size to use for the web search.
43
+ * - high: Most comprehensive context, highest cost, slower response
44
+ * - medium: Balanced context, cost, and latency (default)
45
+ * - low: Least context, lowest cost, fastest response
46
+ */
47
+ searchContextSize?: "low" | "medium" | "high"
48
+
49
+ /**
50
+ * User location information to provide geographically relevant search results.
51
+ */
52
+ userLocation?: {
53
+ /**
54
+ * Type of location (always 'approximate')
55
+ */
56
+ type: "approximate"
57
+ /**
58
+ * Two-letter ISO country code (e.g., 'US', 'GB')
59
+ */
60
+ country?: string
61
+ /**
62
+ * City name (free text, e.g., 'Minneapolis')
63
+ */
64
+ city?: string
65
+ /**
66
+ * Region name (free text, e.g., 'Minnesota')
67
+ */
68
+ region?: string
69
+ /**
70
+ * IANA timezone (e.g., 'America/Chicago')
71
+ */
72
+ timezone?: string
73
+ }
74
+ }
75
+ >({
76
+ id: "openai.web_search",
77
+ name: "web_search",
78
+ inputSchema: z.object({
79
+ action: z
80
+ .discriminatedUnion("type", [
81
+ z.object({
82
+ type: z.literal("search"),
83
+ query: z.string().nullish(),
84
+ }),
85
+ z.object({
86
+ type: z.literal("open_page"),
87
+ url: z.string(),
88
+ }),
89
+ z.object({
90
+ type: z.literal("find"),
91
+ url: z.string(),
92
+ pattern: z.string(),
93
+ }),
94
+ ])
95
+ .nullish(),
96
+ }),
97
+ })
98
+
99
+ export const webSearch = (
100
+ args: Parameters<typeof webSearchToolFactory>[0] = {}, // default
101
+ ) => {
102
+ return webSearchToolFactory(args)
103
+ }
@@ -0,0 +1,406 @@
1
+ import type { APICallError, ModelMessage } from "ai"
2
+ import { unique } from "remeda"
3
+ import type { JSONSchema } from "zod/v4/core"
4
+ import type { Provider } from "./provider"
5
+ import type { ModelsDev } from "./models"
6
+
7
+ type Modality = NonNullable<ModelsDev.Model["modalities"]>["input"][number]
8
+
9
+ function mimeToModality(mime: string): Modality | undefined {
10
+ if (mime.startsWith("image/")) return "image"
11
+ if (mime.startsWith("audio/")) return "audio"
12
+ if (mime.startsWith("video/")) return "video"
13
+ if (mime === "application/pdf") return "pdf"
14
+ return undefined
15
+ }
16
+
17
+ export namespace ProviderTransform {
18
+ function normalizeMessages(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
19
+ if (model.api.id.includes("claude")) {
20
+ return msgs.map((msg) => {
21
+ if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) {
22
+ msg.content = msg.content.map((part) => {
23
+ if ((part.type === "tool-call" || part.type === "tool-result") && "toolCallId" in part) {
24
+ return {
25
+ ...part,
26
+ toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"),
27
+ }
28
+ }
29
+ return part
30
+ })
31
+ }
32
+ return msg
33
+ })
34
+ }
35
+ if (model.providerID === "mistral" || model.api.id.toLowerCase().includes("mistral")) {
36
+ const result: ModelMessage[] = []
37
+ for (let i = 0; i < msgs.length; i++) {
38
+ const msg = msgs[i]
39
+ const nextMsg = msgs[i + 1]
40
+
41
+ if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) {
42
+ msg.content = msg.content.map((part) => {
43
+ if ((part.type === "tool-call" || part.type === "tool-result") && "toolCallId" in part) {
44
+ // Mistral requires alphanumeric tool call IDs with exactly 9 characters
45
+ const normalizedId = part.toolCallId
46
+ .replace(/[^a-zA-Z0-9]/g, "") // Remove non-alphanumeric characters
47
+ .substring(0, 9) // Take first 9 characters
48
+ .padEnd(9, "0") // Pad with zeros if less than 9 characters
49
+
50
+ return {
51
+ ...part,
52
+ toolCallId: normalizedId,
53
+ }
54
+ }
55
+ return part
56
+ })
57
+ }
58
+
59
+ result.push(msg)
60
+
61
+ // Fix message sequence: tool messages cannot be followed by user messages
62
+ if (msg.role === "tool" && nextMsg?.role === "user") {
63
+ result.push({
64
+ role: "assistant",
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: "Done.",
69
+ },
70
+ ],
71
+ })
72
+ }
73
+ }
74
+ return result
75
+ }
76
+
77
+ // DeepSeek: Handle reasoning_content for tool call continuations
78
+ // - With tool calls: Include reasoning_content in providerOptions so model can continue reasoning
79
+ // - Without tool calls: Strip reasoning (new turn doesn't need previous reasoning)
80
+ // See: https://api-docs.deepseek.com/guides/thinking_mode
81
+ if (model.providerID === "deepseek" || model.api.id.toLowerCase().includes("deepseek")) {
82
+ return msgs.map((msg) => {
83
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
84
+ const reasoningParts = msg.content.filter((part: any) => part.type === "reasoning")
85
+ const hasToolCalls = msg.content.some((part: any) => part.type === "tool-call")
86
+ const reasoningText = reasoningParts.map((part: any) => part.text).join("")
87
+
88
+ // Filter out reasoning parts from content
89
+ const filteredContent = msg.content.filter((part: any) => part.type !== "reasoning")
90
+
91
+ // If this message has tool calls and reasoning, include reasoning_content
92
+ // so DeepSeek can continue reasoning after tool execution
93
+ if (hasToolCalls && reasoningText) {
94
+ return {
95
+ ...msg,
96
+ content: filteredContent,
97
+ providerOptions: {
98
+ ...msg.providerOptions,
99
+ openaiCompatible: {
100
+ ...(msg.providerOptions as any)?.openaiCompatible,
101
+ reasoning_content: reasoningText,
102
+ },
103
+ },
104
+ }
105
+ }
106
+
107
+ // For final answers (no tool calls), just strip reasoning
108
+ return {
109
+ ...msg,
110
+ content: filteredContent,
111
+ }
112
+ }
113
+ return msg
114
+ })
115
+ }
116
+
117
+ return msgs
118
+ }
119
+
120
+ function applyCaching(msgs: ModelMessage[], providerID: string): ModelMessage[] {
121
+ const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
122
+ const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
123
+
124
+ const providerOptions = {
125
+ anthropic: {
126
+ cacheControl: { type: "ephemeral" },
127
+ },
128
+ openrouter: {
129
+ cache_control: { type: "ephemeral" },
130
+ },
131
+ bedrock: {
132
+ cachePoint: { type: "ephemeral" },
133
+ },
134
+ openaiCompatible: {
135
+ cache_control: { type: "ephemeral" },
136
+ },
137
+ }
138
+
139
+ for (const msg of unique([...system, ...final])) {
140
+ const shouldUseContentOptions = providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0
141
+
142
+ if (shouldUseContentOptions) {
143
+ const lastContent = msg.content[msg.content.length - 1]
144
+ if (lastContent && typeof lastContent === "object") {
145
+ lastContent.providerOptions = {
146
+ ...lastContent.providerOptions,
147
+ ...providerOptions,
148
+ }
149
+ continue
150
+ }
151
+ }
152
+
153
+ msg.providerOptions = {
154
+ ...msg.providerOptions,
155
+ ...providerOptions,
156
+ }
157
+ }
158
+
159
+ return msgs
160
+ }
161
+
162
+ function unsupportedParts(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
163
+ return msgs.map((msg) => {
164
+ if (msg.role !== "user" || !Array.isArray(msg.content)) return msg
165
+
166
+ const filtered = msg.content.map((part) => {
167
+ if (part.type !== "file" && part.type !== "image") return part
168
+
169
+ const mime = part.type === "image" ? part.image.toString().split(";")[0].replace("data:", "") : part.mediaType
170
+ const filename = part.type === "file" ? part.filename : undefined
171
+ const modality = mimeToModality(mime)
172
+ if (!modality) return part
173
+ if (model.capabilities.input[modality]) return part
174
+
175
+ const name = filename ? `"${filename}"` : modality
176
+ return {
177
+ type: "text" as const,
178
+ text: `ERROR: Cannot read ${name} (this model does not support ${modality} input). Inform the user.`,
179
+ }
180
+ })
181
+
182
+ return { ...msg, content: filtered }
183
+ })
184
+ }
185
+
186
+ export function message(msgs: ModelMessage[], model: Provider.Model) {
187
+ msgs = unsupportedParts(msgs, model)
188
+ msgs = normalizeMessages(msgs, model)
189
+ if (model.providerID === "anthropic" || model.api.id.includes("anthropic") || model.api.id.includes("claude")) {
190
+ msgs = applyCaching(msgs, model.providerID)
191
+ }
192
+
193
+ return msgs
194
+ }
195
+
196
+ export function temperature(model: Provider.Model) {
197
+ if (model.api.id.toLowerCase().includes("qwen")) return 0.55
198
+ if (model.api.id.toLowerCase().includes("claude")) return undefined
199
+ if (model.api.id.toLowerCase().includes("gemini-3-pro")) return 1.0
200
+ return 0
201
+ }
202
+
203
+ export function topP(model: Provider.Model) {
204
+ if (model.api.id.toLowerCase().includes("qwen")) return 1
205
+ return undefined
206
+ }
207
+
208
+ export function options(
209
+ model: Provider.Model,
210
+ sessionID: string,
211
+ providerOptions?: Record<string, any>,
212
+ ): Record<string, any> {
213
+ const result: Record<string, any> = {}
214
+
215
+ // switch to providerID later, for now use this
216
+ if (model.api.npm === "@openrouter/ai-sdk-provider") {
217
+ result["usage"] = {
218
+ include: true,
219
+ }
220
+ }
221
+
222
+ if (model.providerID === "openai" || providerOptions?.setCacheKey) {
223
+ result["promptCacheKey"] = sessionID
224
+ }
225
+
226
+ if (
227
+ model.providerID === "google" ||
228
+ (model.providerID.startsWith("opencode") && model.api.id.includes("gemini-3"))
229
+ ) {
230
+ result["thinkingConfig"] = {
231
+ includeThoughts: true,
232
+ }
233
+ }
234
+
235
+ if (model.api.id.includes("gpt-5") && !model.api.id.includes("gpt-5-chat")) {
236
+ if (model.providerID.includes("codex")) {
237
+ result["store"] = false
238
+ }
239
+
240
+ if (!model.api.id.includes("codex") && !model.api.id.includes("gpt-5-pro")) {
241
+ result["reasoningEffort"] = "medium"
242
+ }
243
+
244
+ if (model.api.id.endsWith("gpt-5.1") && model.providerID !== "azure") {
245
+ result["textVerbosity"] = "low"
246
+ }
247
+
248
+ if (model.providerID.startsWith("opencode")) {
249
+ result["promptCacheKey"] = sessionID
250
+ result["include"] = ["reasoning.encrypted_content"]
251
+ result["reasoningSummary"] = "auto"
252
+ }
253
+ }
254
+ return result
255
+ }
256
+
257
+ export function smallOptions(model: Provider.Model) {
258
+ const options: Record<string, any> = {}
259
+
260
+ if (model.providerID === "openai" || model.api.id.includes("gpt-5")) {
261
+ if (model.api.id.includes("5.1")) {
262
+ options["reasoningEffort"] = "low"
263
+ } else {
264
+ options["reasoningEffort"] = "minimal"
265
+ }
266
+ }
267
+ if (model.providerID === "google") {
268
+ options["thinkingConfig"] = {
269
+ thinkingBudget: 0,
270
+ }
271
+ }
272
+
273
+ return options
274
+ }
275
+
276
+ export function providerOptions(npm: string | undefined, providerID: string, options: { [x: string]: any }) {
277
+ switch (npm) {
278
+ case "@ai-sdk/openai":
279
+ case "@ai-sdk/azure":
280
+ return {
281
+ ["openai" as string]: options,
282
+ }
283
+ case "@ai-sdk/amazon-bedrock":
284
+ return {
285
+ ["bedrock" as string]: options,
286
+ }
287
+ case "@ai-sdk/anthropic":
288
+ return {
289
+ ["anthropic" as string]: options,
290
+ }
291
+ case "@ai-sdk/google":
292
+ return {
293
+ ["google" as string]: options,
294
+ }
295
+ case "@ai-sdk/gateway":
296
+ return {
297
+ ["gateway" as string]: options,
298
+ }
299
+ case "@openrouter/ai-sdk-provider":
300
+ return {
301
+ ["openrouter" as string]: options,
302
+ }
303
+ default:
304
+ return {
305
+ [providerID]: options,
306
+ }
307
+ }
308
+ }
309
+
310
+ export function maxOutputTokens(
311
+ npm: string,
312
+ options: Record<string, any>,
313
+ modelLimit: number,
314
+ globalLimit: number,
315
+ ): number {
316
+ const modelCap = modelLimit || globalLimit
317
+ const standardLimit = Math.min(modelCap, globalLimit)
318
+
319
+ if (npm === "@ai-sdk/anthropic") {
320
+ const thinking = options?.["thinking"]
321
+ const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0
322
+ const enabled = thinking?.["type"] === "enabled"
323
+ if (enabled && budgetTokens > 0) {
324
+ // Return text tokens so that text + thinking <= model cap, preferring 32k text when possible.
325
+ if (budgetTokens + standardLimit <= modelCap) {
326
+ return standardLimit
327
+ }
328
+ return modelCap - budgetTokens
329
+ }
330
+ }
331
+
332
+ return standardLimit
333
+ }
334
+
335
+ export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema) {
336
+ /*
337
+ if (["openai", "azure"].includes(providerID)) {
338
+ if (schema.type === "object" && schema.properties) {
339
+ for (const [key, value] of Object.entries(schema.properties)) {
340
+ if (schema.required?.includes(key)) continue
341
+ schema.properties[key] = {
342
+ anyOf: [
343
+ value as JSONSchema.JSONSchema,
344
+ {
345
+ type: "null",
346
+ },
347
+ ],
348
+ }
349
+ }
350
+ }
351
+ }
352
+ */
353
+
354
+ // Convert integer enums to string enums for Google/Gemini
355
+ if (model.providerID === "google" || model.api.id.includes("gemini")) {
356
+ const sanitizeGemini = (obj: any): any => {
357
+ if (obj === null || typeof obj !== "object") {
358
+ return obj
359
+ }
360
+
361
+ if (Array.isArray(obj)) {
362
+ return obj.map(sanitizeGemini)
363
+ }
364
+
365
+ const result: any = {}
366
+ for (const [key, value] of Object.entries(obj)) {
367
+ if (key === "enum" && Array.isArray(value)) {
368
+ // Convert all enum values to strings
369
+ result[key] = value.map((v) => String(v))
370
+ // If we have integer type with enum, change type to string
371
+ if (result.type === "integer" || result.type === "number") {
372
+ result.type = "string"
373
+ }
374
+ } else if (typeof value === "object" && value !== null) {
375
+ result[key] = sanitizeGemini(value)
376
+ } else {
377
+ result[key] = value
378
+ }
379
+ }
380
+
381
+ // Filter required array to only include fields that exist in properties
382
+ if (result.type === "object" && result.properties && Array.isArray(result.required)) {
383
+ result.required = result.required.filter((field: any) => field in result.properties)
384
+ }
385
+
386
+ return result
387
+ }
388
+
389
+ schema = sanitizeGemini(schema)
390
+ }
391
+
392
+ return schema
393
+ }
394
+
395
+ export function error(providerID: string, error: APICallError) {
396
+ let message = error.message
397
+ if (providerID === "github-copilot" && message.includes("The requested model is not supported")) {
398
+ return (
399
+ message +
400
+ "\n\nMake sure the model is enabled in your copilot settings: https://github.com/settings/copilot/features"
401
+ )
402
+ }
403
+
404
+ return message
405
+ }
406
+ }