mastracode 0.0.1

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 (336) hide show
  1. package/AGENTS.md +27 -0
  2. package/Dockerfile +18 -0
  3. package/README.md +15 -0
  4. package/bin/opencode +84 -0
  5. package/bunfig.toml +4 -0
  6. package/package.json +113 -0
  7. package/parsers-config.ts +239 -0
  8. package/script/build.ts +167 -0
  9. package/script/postinstall.mjs +122 -0
  10. package/script/publish-registries.ts +187 -0
  11. package/script/publish.ts +70 -0
  12. package/script/schema.ts +47 -0
  13. package/src/acp/README.md +164 -0
  14. package/src/acp/agent.ts +1051 -0
  15. package/src/acp/session.ts +101 -0
  16. package/src/acp/types.ts +22 -0
  17. package/src/agent/agent.ts +398 -0
  18. package/src/agent/generate.txt +75 -0
  19. package/src/agent/prompt/compaction.txt +12 -0
  20. package/src/agent/prompt/explore.txt +18 -0
  21. package/src/agent/prompt/summary.txt +10 -0
  22. package/src/agent/prompt/title.txt +36 -0
  23. package/src/auth/index.ts +70 -0
  24. package/src/bun/index.ts +114 -0
  25. package/src/bus/bus-event.ts +43 -0
  26. package/src/bus/global.ts +10 -0
  27. package/src/bus/index.ts +105 -0
  28. package/src/cli/bootstrap.ts +17 -0
  29. package/src/cli/cmd/acp.ts +88 -0
  30. package/src/cli/cmd/agent.ts +256 -0
  31. package/src/cli/cmd/auth.ts +391 -0
  32. package/src/cli/cmd/cmd.ts +7 -0
  33. package/src/cli/cmd/debug/config.ts +15 -0
  34. package/src/cli/cmd/debug/file.ts +91 -0
  35. package/src/cli/cmd/debug/index.ts +43 -0
  36. package/src/cli/cmd/debug/lsp.ts +48 -0
  37. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  38. package/src/cli/cmd/debug/scrap.ts +15 -0
  39. package/src/cli/cmd/debug/skill.ts +15 -0
  40. package/src/cli/cmd/debug/snapshot.ts +48 -0
  41. package/src/cli/cmd/export.ts +88 -0
  42. package/src/cli/cmd/generate.ts +38 -0
  43. package/src/cli/cmd/github.ts +1408 -0
  44. package/src/cli/cmd/import.ts +98 -0
  45. package/src/cli/cmd/mcp.ts +654 -0
  46. package/src/cli/cmd/models.ts +77 -0
  47. package/src/cli/cmd/pr.ts +112 -0
  48. package/src/cli/cmd/run.ts +368 -0
  49. package/src/cli/cmd/serve.ts +31 -0
  50. package/src/cli/cmd/session.ts +106 -0
  51. package/src/cli/cmd/stats.ts +298 -0
  52. package/src/cli/cmd/tui/app.tsx +686 -0
  53. package/src/cli/cmd/tui/attach.ts +30 -0
  54. package/src/cli/cmd/tui/component/border.tsx +21 -0
  55. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  56. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  57. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  58. package/src/cli/cmd/tui/component/dialog-model.tsx +230 -0
  59. package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
  60. package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
  61. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  62. package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
  63. package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
  64. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  65. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  66. package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
  67. package/src/cli/cmd/tui/component/logo.tsx +27 -0
  68. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
  69. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  70. package/src/cli/cmd/tui/component/prompt/index.tsx +1117 -0
  71. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  72. package/src/cli/cmd/tui/component/tips.ts +103 -0
  73. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  74. package/src/cli/cmd/tui/context/args.tsx +14 -0
  75. package/src/cli/cmd/tui/context/directory.ts +13 -0
  76. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  77. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  78. package/src/cli/cmd/tui/context/keybind.tsx +101 -0
  79. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  80. package/src/cli/cmd/tui/context/local.tsx +339 -0
  81. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  82. package/src/cli/cmd/tui/context/route.tsx +46 -0
  83. package/src/cli/cmd/tui/context/sdk.tsx +74 -0
  84. package/src/cli/cmd/tui/context/sync.tsx +372 -0
  85. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  86. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  87. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  88. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  89. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  90. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  91. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  92. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  93. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  94. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  95. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  96. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  97. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  98. package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
  99. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  100. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  101. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  102. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  103. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  104. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  105. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  106. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  107. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  108. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  109. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  110. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  111. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  112. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  113. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  114. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  115. package/src/cli/cmd/tui/context/theme.tsx +1109 -0
  116. package/src/cli/cmd/tui/event.ts +40 -0
  117. package/src/cli/cmd/tui/routes/home.tsx +140 -0
  118. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
  119. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
  120. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  121. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  122. package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
  123. package/src/cli/cmd/tui/routes/session/header.tsx +141 -0
  124. package/src/cli/cmd/tui/routes/session/index.tsx +1885 -0
  125. package/src/cli/cmd/tui/routes/session/sidebar.tsx +322 -0
  126. package/src/cli/cmd/tui/spawn.ts +60 -0
  127. package/src/cli/cmd/tui/thread.ts +120 -0
  128. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  129. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  130. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  131. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
  132. package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
  133. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  134. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  135. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  136. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  137. package/src/cli/cmd/tui/util/editor.ts +32 -0
  138. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  139. package/src/cli/cmd/tui/worker.ts +63 -0
  140. package/src/cli/cmd/uninstall.ts +344 -0
  141. package/src/cli/cmd/upgrade.ts +67 -0
  142. package/src/cli/cmd/web.ts +84 -0
  143. package/src/cli/error.ts +56 -0
  144. package/src/cli/ui.ts +84 -0
  145. package/src/cli/upgrade.ts +25 -0
  146. package/src/command/index.ts +80 -0
  147. package/src/command/template/initialize.txt +10 -0
  148. package/src/command/template/review.txt +97 -0
  149. package/src/config/config.ts +997 -0
  150. package/src/config/markdown.ts +41 -0
  151. package/src/env/index.ts +26 -0
  152. package/src/file/ignore.ts +83 -0
  153. package/src/file/index.ts +328 -0
  154. package/src/file/ripgrep.ts +393 -0
  155. package/src/file/time.ts +64 -0
  156. package/src/file/watcher.ts +103 -0
  157. package/src/flag/flag.ts +46 -0
  158. package/src/format/formatter.ts +315 -0
  159. package/src/format/index.ts +137 -0
  160. package/src/global/index.ts +52 -0
  161. package/src/id/id.ts +73 -0
  162. package/src/ide/index.ts +76 -0
  163. package/src/index.ts +158 -0
  164. package/src/installation/index.ts +196 -0
  165. package/src/lsp/client.ts +229 -0
  166. package/src/lsp/index.ts +485 -0
  167. package/src/lsp/language.ts +116 -0
  168. package/src/lsp/server.ts +1895 -0
  169. package/src/mcp/auth.ts +135 -0
  170. package/src/mcp/index.ts +654 -0
  171. package/src/mcp/oauth-callback.ts +200 -0
  172. package/src/mcp/oauth-provider.ts +154 -0
  173. package/src/patch/index.ts +622 -0
  174. package/src/permission/index.ts +199 -0
  175. package/src/plugin/index.ts +91 -0
  176. package/src/project/bootstrap.ts +31 -0
  177. package/src/project/instance.ts +78 -0
  178. package/src/project/project.ts +221 -0
  179. package/src/project/state.ts +65 -0
  180. package/src/project/vcs.ts +76 -0
  181. package/src/provider/auth.ts +143 -0
  182. package/src/provider/models-macro.ts +11 -0
  183. package/src/provider/models.ts +106 -0
  184. package/src/provider/provider.ts +1056 -0
  185. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  186. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  187. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  188. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  189. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  190. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  191. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  192. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  193. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  194. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  195. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  196. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  197. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  198. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  199. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  200. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  201. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  202. package/src/provider/transform.ts +455 -0
  203. package/src/pty/index.ts +231 -0
  204. package/src/server/error.ts +36 -0
  205. package/src/server/project.ts +79 -0
  206. package/src/server/server.ts +2642 -0
  207. package/src/server/tui.ts +71 -0
  208. package/src/session/compaction.ts +223 -0
  209. package/src/session/index.ts +458 -0
  210. package/src/session/llm-mastra.ts +412 -0
  211. package/src/session/llm-shared.ts +172 -0
  212. package/src/session/llm.ts +439 -0
  213. package/src/session/message-v2.ts +675 -0
  214. package/src/session/message.ts +189 -0
  215. package/src/session/processor.ts +171 -0
  216. package/src/session/prompt/anthropic-20250930.txt +166 -0
  217. package/src/session/prompt/anthropic.txt +105 -0
  218. package/src/session/prompt/anthropic_spoof.txt +1 -0
  219. package/src/session/prompt/beast.txt +147 -0
  220. package/src/session/prompt/build-switch.txt +5 -0
  221. package/src/session/prompt/codex.txt +318 -0
  222. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  223. package/src/session/prompt/gemini.txt +155 -0
  224. package/src/session/prompt/max-steps.txt +16 -0
  225. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  226. package/src/session/prompt/plan.txt +26 -0
  227. package/src/session/prompt/polaris.txt +107 -0
  228. package/src/session/prompt/qwen.txt +109 -0
  229. package/src/session/prompt.ts +1454 -0
  230. package/src/session/retry.ts +86 -0
  231. package/src/session/revert.ts +108 -0
  232. package/src/session/status.ts +76 -0
  233. package/src/session/summary.ts +194 -0
  234. package/src/session/system.ts +120 -0
  235. package/src/session/todo.ts +37 -0
  236. package/src/share/share-next.ts +194 -0
  237. package/src/share/share.ts +87 -0
  238. package/src/shell/shell.ts +67 -0
  239. package/src/skill/index.ts +1 -0
  240. package/src/skill/skill.ts +83 -0
  241. package/src/snapshot/index.ts +197 -0
  242. package/src/storage/storage.ts +226 -0
  243. package/src/tool/bash.ts +306 -0
  244. package/src/tool/bash.txt +158 -0
  245. package/src/tool/batch.ts +175 -0
  246. package/src/tool/batch.txt +24 -0
  247. package/src/tool/codesearch.ts +138 -0
  248. package/src/tool/codesearch.txt +12 -0
  249. package/src/tool/edit.ts +675 -0
  250. package/src/tool/edit.txt +10 -0
  251. package/src/tool/glob.ts +65 -0
  252. package/src/tool/glob.txt +6 -0
  253. package/src/tool/grep.ts +121 -0
  254. package/src/tool/grep.txt +8 -0
  255. package/src/tool/invalid.ts +17 -0
  256. package/src/tool/ls.ts +110 -0
  257. package/src/tool/ls.txt +1 -0
  258. package/src/tool/lsp-diagnostics.ts +26 -0
  259. package/src/tool/lsp-diagnostics.txt +1 -0
  260. package/src/tool/lsp-hover.ts +31 -0
  261. package/src/tool/lsp-hover.txt +1 -0
  262. package/src/tool/lsp.ts +87 -0
  263. package/src/tool/lsp.txt +19 -0
  264. package/src/tool/multiedit.ts +46 -0
  265. package/src/tool/multiedit.txt +41 -0
  266. package/src/tool/patch.ts +233 -0
  267. package/src/tool/patch.txt +1 -0
  268. package/src/tool/read.ts +219 -0
  269. package/src/tool/read.txt +12 -0
  270. package/src/tool/registry.ts +162 -0
  271. package/src/tool/skill.ts +100 -0
  272. package/src/tool/task.ts +136 -0
  273. package/src/tool/task.txt +60 -0
  274. package/src/tool/todo.ts +39 -0
  275. package/src/tool/todoread.txt +14 -0
  276. package/src/tool/todowrite.txt +167 -0
  277. package/src/tool/tool.ts +71 -0
  278. package/src/tool/webfetch.ts +187 -0
  279. package/src/tool/webfetch.txt +13 -0
  280. package/src/tool/websearch.ts +150 -0
  281. package/src/tool/websearch.txt +11 -0
  282. package/src/tool/write.ts +110 -0
  283. package/src/tool/write.txt +8 -0
  284. package/src/util/archive.ts +16 -0
  285. package/src/util/color.ts +19 -0
  286. package/src/util/context.ts +25 -0
  287. package/src/util/defer.ts +12 -0
  288. package/src/util/eventloop.ts +20 -0
  289. package/src/util/filesystem.ts +83 -0
  290. package/src/util/fn.ts +11 -0
  291. package/src/util/iife.ts +3 -0
  292. package/src/util/keybind.ts +102 -0
  293. package/src/util/lazy.ts +11 -0
  294. package/src/util/locale.ts +81 -0
  295. package/src/util/lock.ts +98 -0
  296. package/src/util/log.ts +180 -0
  297. package/src/util/queue.ts +32 -0
  298. package/src/util/rpc.ts +42 -0
  299. package/src/util/scrap.ts +10 -0
  300. package/src/util/signal.ts +12 -0
  301. package/src/util/timeout.ts +14 -0
  302. package/src/util/token.ts +7 -0
  303. package/src/util/wildcard.ts +54 -0
  304. package/sst-env.d.ts +9 -0
  305. package/test/agent/agent.test.ts +146 -0
  306. package/test/bun.test.ts +53 -0
  307. package/test/cli/github-remote.test.ts +80 -0
  308. package/test/config/agent-color.test.ts +66 -0
  309. package/test/config/config.test.ts +535 -0
  310. package/test/config/markdown.test.ts +89 -0
  311. package/test/file/ignore.test.ts +10 -0
  312. package/test/fixture/fixture.ts +34 -0
  313. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  314. package/test/ide/ide.test.ts +82 -0
  315. package/test/keybind.test.ts +421 -0
  316. package/test/lsp/client.test.ts +95 -0
  317. package/test/mcp/headers.test.ts +153 -0
  318. package/test/patch/patch.test.ts +348 -0
  319. package/test/preload.ts +57 -0
  320. package/test/project/project.test.ts +72 -0
  321. package/test/provider/provider.test.ts +1809 -0
  322. package/test/provider/transform.test.ts +411 -0
  323. package/test/session/retry.test.ts +61 -0
  324. package/test/session/session.test.ts +71 -0
  325. package/test/skill/skill.test.ts +131 -0
  326. package/test/snapshot/snapshot.test.ts +939 -0
  327. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  328. package/test/tool/bash.test.ts +434 -0
  329. package/test/tool/grep.test.ts +108 -0
  330. package/test/tool/patch.test.ts +259 -0
  331. package/test/tool/read.test.ts +42 -0
  332. package/test/util/iife.test.ts +36 -0
  333. package/test/util/lazy.test.ts +50 -0
  334. package/test/util/timeout.test.ts +21 -0
  335. package/test/util/wildcard.test.ts +55 -0
  336. package/tsconfig.json +16 -0
@@ -0,0 +1,412 @@
1
+ import { Provider } from "@/provider/provider"
2
+ import { ProviderTransform } from "@/provider/transform"
3
+ import type { DynamicAgentInstructions } from "@mastra/core/agent"
4
+ import type { LLMShared } from "./llm-shared"
5
+ import type { MastraModelOutput, ChunkType } from "@mastra/core/stream"
6
+ import { SessionStatus } from "./status"
7
+ import { MessageV2 } from "./message-v2"
8
+ import { Identifier } from "@/id/id"
9
+ import { Session } from "."
10
+ import { Agent } from "@/agent/agent"
11
+ import { Permission } from "@/permission"
12
+ import { Snapshot } from "@/snapshot"
13
+ import { SessionSummary } from "./summary"
14
+ import { Plugin } from "@/plugin"
15
+ import type { Log } from "@/util/log"
16
+
17
+ export namespace MastraLLM {
18
+ export type ProcessStreamInput = {
19
+ model: Provider.Model
20
+ assistantMessage: MessageV2.Assistant
21
+ sessionID: string
22
+ abort: AbortSignal
23
+ }
24
+
25
+ export type ProcessStreamOptions = {
26
+ DOOM_LOOP_THRESHOLD: number
27
+ shouldBreak: boolean
28
+ logger: Log.Logger
29
+ }
30
+
31
+ export type ProcessStreamState = {
32
+ toolcalls: Record<string, MessageV2.ToolPart>
33
+ getSnapshot: () => string | undefined
34
+ setSnapshot: (snapshot: string | undefined) => void
35
+ setBlocked: (blocked: boolean) => void
36
+ }
37
+
38
+ export async function stream(prepared: LLMShared.PreparedStream): Promise<MastraModelOutput> {
39
+ const { log: l, system, params, maxOutputTokens, tools, langModel, input } = prepared
40
+ const { Agent } = await import("@mastra/core/agent")
41
+
42
+ const agent = new Agent({
43
+ id: input.agent.name,
44
+ name: input.agent.name,
45
+ instructions: system.map((x) => ({
46
+ role: "system",
47
+ content: x,
48
+ })) as DynamicAgentInstructions,
49
+ // Cast to work around @ai-sdk/provider version mismatch between AI SDK and Mastra
50
+ model: langModel as any,
51
+ tools,
52
+ })
53
+
54
+ return agent.stream(input.messages, {
55
+ onError(error) {
56
+ l.error("stream error", {
57
+ error,
58
+ })
59
+ },
60
+ maxSteps: input.maxSteps,
61
+ modelSettings: {
62
+ temperature: params.temperature,
63
+ topP: params.topP,
64
+ topK: params.topK,
65
+ maxOutputTokens,
66
+ maxRetries: input.retries ?? 0,
67
+ },
68
+ activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
69
+ providerOptions: ProviderTransform.providerOptions(input.model, params.options),
70
+ abortSignal: input.abort,
71
+ })
72
+ }
73
+
74
+ export async function processStream(
75
+ stream: MastraModelOutput,
76
+ args: {
77
+ input: ProcessStreamInput
78
+ options: ProcessStreamOptions
79
+ state: ProcessStreamState
80
+ },
81
+ ) {
82
+ const { input, options, state } = args
83
+ const { model, assistantMessage, sessionID, abort } = input
84
+ const { DOOM_LOOP_THRESHOLD, shouldBreak, logger } = options
85
+ const { toolcalls, getSnapshot, setSnapshot, setBlocked } = state
86
+
87
+ let currentText: MessageV2.TextPart | undefined
88
+ let reasoningMap: Record<string, MessageV2.ReasoningPart> = {}
89
+
90
+ for await (const value of stream.fullStream) {
91
+ abort.throwIfAborted()
92
+ const chunk = value as ChunkType
93
+
94
+ switch (chunk.type) {
95
+ case "start":
96
+ SessionStatus.set(sessionID, { type: "busy" })
97
+ break
98
+
99
+ case "reasoning-start": {
100
+ const payload = chunk.payload
101
+ if (payload.id in reasoningMap) {
102
+ continue
103
+ }
104
+ reasoningMap[payload.id] = {
105
+ id: Identifier.ascending("part"),
106
+ messageID: assistantMessage.id,
107
+ sessionID: assistantMessage.sessionID,
108
+ type: "reasoning",
109
+ text: "",
110
+ time: {
111
+ start: Date.now(),
112
+ },
113
+ metadata: payload.providerMetadata,
114
+ }
115
+ break
116
+ }
117
+
118
+ case "reasoning-delta": {
119
+ const payload = chunk.payload
120
+ if (payload.id in reasoningMap) {
121
+ const part = reasoningMap[payload.id]
122
+ part.text += payload.text
123
+ if (payload.providerMetadata) part.metadata = payload.providerMetadata
124
+ if (part.text) await Session.updatePart({ part, delta: payload.text })
125
+ }
126
+ break
127
+ }
128
+
129
+ case "reasoning-end": {
130
+ const payload = chunk.payload
131
+ if (payload.id in reasoningMap) {
132
+ const part = reasoningMap[payload.id]
133
+ part.text = part.text.trimEnd()
134
+
135
+ part.time = {
136
+ ...part.time,
137
+ end: Date.now(),
138
+ }
139
+ if (payload.providerMetadata) part.metadata = payload.providerMetadata
140
+ await Session.updatePart(part)
141
+ delete reasoningMap[payload.id]
142
+ }
143
+ break
144
+ }
145
+
146
+ case "tool-call-input-streaming-start": {
147
+ const payload = chunk.payload
148
+ const part = await Session.updatePart({
149
+ id: toolcalls[payload.toolCallId]?.id ?? Identifier.ascending("part"),
150
+ messageID: assistantMessage.id,
151
+ sessionID: assistantMessage.sessionID,
152
+ type: "tool",
153
+ tool: payload.toolName,
154
+ callID: payload.toolCallId,
155
+ state: {
156
+ status: "pending",
157
+ input: {},
158
+ raw: "",
159
+ },
160
+ })
161
+ toolcalls[payload.toolCallId] = part as MessageV2.ToolPart
162
+ break
163
+ }
164
+
165
+ case "tool-call-delta":
166
+ break
167
+
168
+ case "tool-call": {
169
+ const payload = chunk.payload
170
+ const match = toolcalls[payload.toolCallId]
171
+ if (match) {
172
+ const part = await Session.updatePart({
173
+ ...match,
174
+ tool: payload.toolName,
175
+ state: {
176
+ status: "running",
177
+ input: (payload.args ?? {}) as Record<string, any>,
178
+ time: {
179
+ start: Date.now(),
180
+ },
181
+ },
182
+ metadata: payload.providerMetadata,
183
+ })
184
+ toolcalls[payload.toolCallId] = part as MessageV2.ToolPart
185
+
186
+ const parts = await MessageV2.parts(assistantMessage.id)
187
+ const lastThree = parts.slice(-DOOM_LOOP_THRESHOLD)
188
+
189
+ if (
190
+ lastThree.length === DOOM_LOOP_THRESHOLD &&
191
+ lastThree.every(
192
+ (p) =>
193
+ p.type === "tool" &&
194
+ p.tool === payload.toolName &&
195
+ p.state.status !== "pending" &&
196
+ JSON.stringify(p.state.input) === JSON.stringify(payload.args),
197
+ )
198
+ ) {
199
+ const permission = await Agent.get(assistantMessage.mode).then((x) => x.permission)
200
+
201
+ if (permission.doom_loop === "ask") {
202
+ await Permission.ask({
203
+ type: "doom_loop",
204
+ pattern: payload.toolName,
205
+ sessionID: assistantMessage.sessionID,
206
+ messageID: assistantMessage.id,
207
+ callID: payload.toolCallId,
208
+ title: `Possible doom loop: "${payload.toolName}" called ${DOOM_LOOP_THRESHOLD} times with identical arguments`,
209
+ metadata: {
210
+ tool: payload.toolName,
211
+ input: payload.args,
212
+ },
213
+ })
214
+ } else if (permission.doom_loop === "deny") {
215
+ throw new Permission.RejectedError(
216
+ assistantMessage.sessionID,
217
+ "doom_loop",
218
+ payload.toolCallId,
219
+ {
220
+ tool: payload.toolName,
221
+ input: payload.args,
222
+ },
223
+ `You seem to be stuck in a doom loop, please stop repeating the same action`,
224
+ )
225
+ }
226
+ }
227
+ }
228
+ break
229
+ }
230
+
231
+ case "tool-result": {
232
+ const payload = chunk.payload
233
+ const match = toolcalls[payload.toolCallId]
234
+ if (match && match.state.status === "running") {
235
+ if (payload.isError) {
236
+ await Session.updatePart({
237
+ ...match,
238
+ state: {
239
+ status: "error",
240
+ input: (payload.args ?? {}) as Record<string, any>,
241
+ error: typeof payload.result === "string" ? payload.result : JSON.stringify(payload.result),
242
+ metadata: payload.providerMetadata,
243
+ time: {
244
+ start: match.state.time.start,
245
+ end: Date.now(),
246
+ },
247
+ },
248
+ })
249
+ } else {
250
+ await Session.updatePart({
251
+ ...match,
252
+ state: {
253
+ status: "completed",
254
+ input: (payload.args ?? {}) as Record<string, any>,
255
+ output: typeof payload.result === "string" ? payload.result : JSON.stringify(payload.result),
256
+ title: payload.toolName,
257
+ metadata: payload.providerMetadata ?? {},
258
+ time: {
259
+ start: match.state.time.start,
260
+ end: Date.now(),
261
+ },
262
+ },
263
+ })
264
+ }
265
+
266
+ delete toolcalls[payload.toolCallId]
267
+ }
268
+ break
269
+ }
270
+
271
+ case "error":
272
+ throw (chunk as any).error
273
+
274
+ case "step-start": {
275
+ const snapshot = await Snapshot.track()
276
+ setSnapshot(snapshot)
277
+
278
+ await Session.updatePart({
279
+ id: Identifier.ascending("part"),
280
+ messageID: assistantMessage.id,
281
+ sessionID: sessionID,
282
+ snapshot,
283
+ type: "step-start",
284
+ })
285
+ break
286
+ }
287
+
288
+ case "step-finish": {
289
+ // Mastra StepFinishPayload structure:
290
+ // - output.usage for usage data
291
+ // - stepResult.reason for finish reason
292
+ // - providerMetadata or metadata.providerMetadata for metadata
293
+ const payload = chunk.payload
294
+
295
+ const rawUsage = payload.output?.usage ?? {
296
+ inputTokens: 0,
297
+ outputTokens: 0,
298
+ totalTokens: 0,
299
+ }
300
+
301
+ const usage = Session.getUsage({
302
+ model: model,
303
+ usage: rawUsage,
304
+ metadata: payload.providerMetadata ?? payload.metadata?.providerMetadata,
305
+ })
306
+
307
+ const finishReason = payload.stepResult?.reason ?? "unknown"
308
+ assistantMessage.finish = finishReason
309
+ assistantMessage.cost += usage.cost
310
+ assistantMessage.tokens = usage.tokens
311
+ await Session.updatePart({
312
+ id: Identifier.ascending("part"),
313
+ reason: finishReason,
314
+ snapshot: await Snapshot.track(),
315
+ messageID: assistantMessage.id,
316
+ sessionID: assistantMessage.sessionID,
317
+ type: "step-finish",
318
+ tokens: usage.tokens,
319
+ cost: usage.cost,
320
+ })
321
+ await Session.updateMessage(assistantMessage)
322
+
323
+ const snapshot = getSnapshot()
324
+ if (snapshot) {
325
+ const patch = await Snapshot.patch(snapshot)
326
+ if (patch.files.length) {
327
+ await Session.updatePart({
328
+ id: Identifier.ascending("part"),
329
+ messageID: assistantMessage.id,
330
+ sessionID: sessionID,
331
+ type: "patch",
332
+ hash: patch.hash,
333
+ files: patch.files,
334
+ })
335
+ }
336
+ setSnapshot(undefined)
337
+ }
338
+
339
+ SessionSummary.summarize({
340
+ sessionID: sessionID,
341
+ messageID: assistantMessage.parentID,
342
+ })
343
+ break
344
+ }
345
+
346
+ case "text-start": {
347
+ const payload = chunk.payload
348
+ currentText = {
349
+ id: Identifier.ascending("part"),
350
+ messageID: assistantMessage.id,
351
+ sessionID: assistantMessage.sessionID,
352
+ type: "text",
353
+ text: "",
354
+ time: {
355
+ start: Date.now(),
356
+ },
357
+ metadata: payload.providerMetadata,
358
+ }
359
+ break
360
+ }
361
+
362
+ case "text-delta": {
363
+ const payload = chunk.payload
364
+ if (currentText) {
365
+ currentText.text += payload.text
366
+ if (payload.providerMetadata) currentText.metadata = payload.providerMetadata
367
+ if (currentText.text)
368
+ await Session.updatePart({
369
+ part: currentText,
370
+ delta: payload.text,
371
+ })
372
+ }
373
+ break
374
+ }
375
+
376
+ case "text-end": {
377
+ const payload = chunk.payload
378
+ if (currentText) {
379
+ currentText.text = currentText.text.trimEnd()
380
+ const textOutput = await Plugin.trigger(
381
+ "experimental.text.complete",
382
+ {
383
+ sessionID: sessionID,
384
+ messageID: assistantMessage.id,
385
+ partID: currentText.id,
386
+ },
387
+ { text: currentText.text },
388
+ )
389
+ currentText.text = textOutput.text
390
+ currentText.time = {
391
+ start: Date.now(),
392
+ end: Date.now(),
393
+ }
394
+ if (payload.providerMetadata) currentText.metadata = payload.providerMetadata
395
+ await Session.updatePart(currentText)
396
+ }
397
+ currentText = undefined
398
+ break
399
+ }
400
+
401
+ case "finish":
402
+ break
403
+
404
+ default:
405
+ logger.info("unhandled", {
406
+ type: chunk.type,
407
+ })
408
+ continue
409
+ }
410
+ }
411
+ }
412
+ }
@@ -0,0 +1,172 @@
1
+ import { Provider } from "@/provider/provider"
2
+ import { Log } from "@/util/log"
3
+ import { wrapLanguageModel, type ModelMessage, type StreamTextResult, type Tool, type ToolSet } from "ai"
4
+ import { clone, mergeDeep, pipe } from "remeda"
5
+ import { ProviderTransform } from "@/provider/transform"
6
+ import { Config } from "@/config/config"
7
+ import type { Agent } from "@/agent/agent"
8
+ import type { MessageV2 } from "./message-v2"
9
+ import { Plugin } from "@/plugin"
10
+ import { SystemPrompt } from "./system"
11
+ import { ToolRegistry } from "@/tool/registry"
12
+ import { Flag } from "@/flag/flag"
13
+
14
+ export namespace LLMShared {
15
+ const log = Log.create({ service: "llm" })
16
+
17
+ export const OUTPUT_TOKEN_MAX = Flag.OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX || 32_000
18
+
19
+ export type StreamInput = {
20
+ user: MessageV2.User
21
+ sessionID: string
22
+ model: Provider.Model
23
+ agent: Agent.Info
24
+ system: string[]
25
+ abort: AbortSignal
26
+ messages: ModelMessage[]
27
+ small?: boolean
28
+ tools: Record<string, Tool>
29
+ retries?: number
30
+ maxSteps?: number
31
+ }
32
+
33
+ export type StreamOutput = StreamTextResult<ToolSet, unknown>
34
+
35
+ export type PreparedStream = {
36
+ log: typeof log
37
+ config: Config.Info
38
+ system: string[]
39
+ params: {
40
+ temperature: number | undefined
41
+ topP: number | undefined
42
+ topK: number | undefined
43
+ options: Record<string, unknown>
44
+ }
45
+ maxOutputTokens: number
46
+ tools: Record<string, Tool>
47
+ langModel: ReturnType<typeof wrapLanguageModel>
48
+ input: StreamInput
49
+ }
50
+
51
+ export async function prepare(input: StreamInput): Promise<PreparedStream> {
52
+ const l = log
53
+ .clone()
54
+ .tag("providerID", input.model.providerID)
55
+ .tag("modelID", input.model.id)
56
+ .tag("sessionID", input.sessionID)
57
+ .tag("small", (input.small ?? false).toString())
58
+ .tag("agent", input.agent.name)
59
+ l.info("stream", {
60
+ modelID: input.model.id,
61
+ providerID: input.model.providerID,
62
+ })
63
+
64
+ const [language, cfg] = await Promise.all([Provider.getLanguage(input.model), Config.get()])
65
+
66
+ const system = SystemPrompt.header(input.model.providerID)
67
+ system.push(
68
+ [
69
+ // use agent prompt otherwise provider prompt
70
+ ...(input.agent.prompt ? [input.agent.prompt] : SystemPrompt.provider(input.model)),
71
+ // any custom prompt passed into this call
72
+ ...input.system,
73
+ // any custom prompt from last user message
74
+ ...(input.user.system ? [input.user.system] : []),
75
+ ]
76
+ .filter((x) => x)
77
+ .join("\n"),
78
+ )
79
+
80
+ const header = system[0]
81
+ const original = clone(system)
82
+ await Plugin.trigger("experimental.chat.system.transform", {}, { system })
83
+ if (system.length === 0) {
84
+ system.push(...original)
85
+ }
86
+ // rejoin to maintain 2-part structure for caching if header unchanged
87
+ if (system.length > 2 && system[0] === header) {
88
+ const rest = system.slice(1)
89
+ system.length = 0
90
+ system.push(header, rest.join("\n"))
91
+ }
92
+
93
+ const provider = await Provider.getProvider(input.model.providerID)
94
+
95
+ const params = await Plugin.trigger(
96
+ "chat.params",
97
+ {
98
+ sessionID: input.sessionID,
99
+ agent: input.agent,
100
+ model: input.model,
101
+ provider: Provider.getProvider(input.model.providerID),
102
+ message: input.user,
103
+ },
104
+ {
105
+ temperature: input.model.capabilities.temperature
106
+ ? (input.agent.temperature ?? ProviderTransform.temperature(input.model))
107
+ : undefined,
108
+ topP: input.agent.topP ?? ProviderTransform.topP(input.model),
109
+ topK: ProviderTransform.topK(input.model),
110
+ options: pipe(
111
+ {},
112
+ mergeDeep(ProviderTransform.options(input.model, input.sessionID, provider.options)),
113
+ input.small ? mergeDeep(ProviderTransform.smallOptions(input.model)) : mergeDeep({}),
114
+ mergeDeep(input.model.options),
115
+ mergeDeep(input.agent.options),
116
+ ),
117
+ },
118
+ )
119
+
120
+ l.info("params", {
121
+ params,
122
+ })
123
+
124
+ const maxOutputTokens = ProviderTransform.maxOutputTokens(
125
+ input.model.api.npm,
126
+ params.options,
127
+ input.model.limit.output,
128
+ OUTPUT_TOKEN_MAX,
129
+ )
130
+
131
+ const tools = await resolveTools(input)
132
+
133
+ // Cast to work around @ai-sdk/provider version mismatch between dependencies
134
+ const langModel = wrapLanguageModel({
135
+ model: language as Parameters<typeof wrapLanguageModel>[0]["model"],
136
+ middleware: [
137
+ {
138
+ async transformParams(args) {
139
+ if (args.type === "stream") {
140
+ // @ts-expect-error
141
+ args.params.prompt = ProviderTransform.message(args.params.prompt, input.model)
142
+ }
143
+ return args.params
144
+ },
145
+ },
146
+ ],
147
+ })
148
+
149
+ return {
150
+ log: l,
151
+ config: cfg,
152
+ system,
153
+ params,
154
+ maxOutputTokens,
155
+ tools,
156
+ langModel,
157
+ input,
158
+ }
159
+ }
160
+
161
+ async function resolveTools(input: Pick<StreamInput, "tools" | "agent" | "user">) {
162
+ const enabled = pipe(
163
+ input.agent.tools,
164
+ mergeDeep(await ToolRegistry.enabled(input.agent)),
165
+ mergeDeep(input.user.tools ?? {}),
166
+ )
167
+ for (const [key, value] of Object.entries(enabled)) {
168
+ if (value === false) delete input.tools[key]
169
+ }
170
+ return input.tools
171
+ }
172
+ }