tulingcode 0.1.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 (1119) hide show
  1. package/AGENTS.md +134 -0
  2. package/Dockerfile +18 -0
  3. package/README.md +15 -0
  4. package/bin/tuling +179 -0
  5. package/bunfig.toml +7 -0
  6. package/drizzle.config.ts +10 -0
  7. package/git +0 -0
  8. package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
  9. package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
  10. package/migration/20260211171708_add_project_commands/migration.sql +1 -0
  11. package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
  12. package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
  13. package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
  14. package/migration/20260225215848_workspace/migration.sql +7 -0
  15. package/migration/20260225215848_workspace/snapshot.json +959 -0
  16. package/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
  17. package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
  18. package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
  19. package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
  20. package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
  21. package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
  22. package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
  23. package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
  24. package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
  25. package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
  26. package/migration/20260323234822_events/migration.sql +13 -0
  27. package/migration/20260323234822_events/snapshot.json +1271 -0
  28. package/migration/20260410174513_workspace-name/migration.sql +16 -0
  29. package/migration/20260410174513_workspace-name/snapshot.json +1271 -0
  30. package/migration/20260413175956_chief_energizer/migration.sql +13 -0
  31. package/migration/20260413175956_chief_energizer/snapshot.json +1399 -0
  32. package/migration/20260422160000_context_inheritance/migration.sql +3 -0
  33. package/migration/20260422170000_task_registry/migration.sql +18 -0
  34. package/migration/20260423145421_remove_session_entry/migration.sql +4 -0
  35. package/migration/20260515000000_actor_rename/migration.sql +7 -0
  36. package/migration/20260515010000_memory_fts/migration.sql +33 -0
  37. package/migration/20260515020000_user_task/migration.sql +29 -0
  38. package/migration/20260519000000_last_checkpoint_message_id/migration.sql +1 -0
  39. package/migration/20260521000000_message_agent_id/migration.sql +2 -0
  40. package/migration/20260521000100_actor_registry_v6/migration.sql +25 -0
  41. package/migration/20260521010000_memory_fts_v6/migration.sql +33 -0
  42. package/migration/20260521020000_memory_fts_triggers/migration.sql +17 -0
  43. package/migration/20260526000000_agent_id_main/migration.sql +14 -0
  44. package/migration/20260527000000_actor_lifecycle/migration.sql +8 -0
  45. package/migration/20260527000100_inbox/migration.sql +12 -0
  46. package/migration/20260529000000_task_todo_redesign/migration.sql +16 -0
  47. package/migration/20260603000000_task_in_progress_owner/migration.sql +1 -0
  48. package/migration/20260603000000_workflow_run/migration.sql +17 -0
  49. package/migration/20260604000000_workflow_script_sha/migration.sql +1 -0
  50. package/migration/20260608000000_claude_import/migration.sql +7 -0
  51. package/migration/20260608010000_claude_import_message_ids/migration.sql +1 -0
  52. package/migration/20260609000000_history_fts/migration.sql +29 -0
  53. package/migration/20260609230000_workflow_agent_timeout/migration.sql +1 -0
  54. package/package.json +196 -0
  55. package/parsers-config.ts +290 -0
  56. package/script/build.ts +267 -0
  57. package/script/check-migrations.ts +16 -0
  58. package/script/fix-node-pty.ts +28 -0
  59. package/script/generate.ts +23 -0
  60. package/script/postinstall.mjs +102 -0
  61. package/script/publish.ts +60 -0
  62. package/script/run-workspace-server +106 -0
  63. package/script/schema.ts +63 -0
  64. package/script/time.ts +6 -0
  65. package/script/trace-imports.ts +153 -0
  66. package/script/upgrade-opentui.ts +64 -0
  67. package/src/account/account.sql.ts +39 -0
  68. package/src/account/account.ts +456 -0
  69. package/src/account/repo.ts +166 -0
  70. package/src/account/schema.ts +99 -0
  71. package/src/account/url.ts +8 -0
  72. package/src/acp/README.md +174 -0
  73. package/src/acp/agent.ts +1783 -0
  74. package/src/acp/session.ts +116 -0
  75. package/src/acp/types.ts +24 -0
  76. package/src/actor/actor.sql.ts +38 -0
  77. package/src/actor/events.ts +67 -0
  78. package/src/actor/index.ts +2 -0
  79. package/src/actor/registry.ts +412 -0
  80. package/src/actor/return-header.ts +24 -0
  81. package/src/actor/schema.ts +47 -0
  82. package/src/actor/spawn-ref.ts +16 -0
  83. package/src/actor/spawn.ts +741 -0
  84. package/src/actor/turn.ts +49 -0
  85. package/src/actor/waiter.ts +166 -0
  86. package/src/agent/agent.ts +554 -0
  87. package/src/agent/config.ts +5 -0
  88. package/src/agent/generate.txt +75 -0
  89. package/src/agent/prompt/checkpoint-writer.txt +167 -0
  90. package/src/agent/prompt/compaction.txt +9 -0
  91. package/src/agent/prompt/distill.txt +199 -0
  92. package/src/agent/prompt/dream.txt +155 -0
  93. package/src/agent/prompt/explore.txt +18 -0
  94. package/src/agent/prompt/summary.txt +11 -0
  95. package/src/agent/prompt/title.txt +44 -0
  96. package/src/audio.d.ts +9 -0
  97. package/src/auth/index.ts +97 -0
  98. package/src/bus/bus-event.ts +33 -0
  99. package/src/bus/global.ts +12 -0
  100. package/src/bus/index.ts +193 -0
  101. package/src/cli/bootstrap.ts +33 -0
  102. package/src/cli/cmd/account.ts +258 -0
  103. package/src/cli/cmd/acp.ts +70 -0
  104. package/src/cli/cmd/agent.ts +248 -0
  105. package/src/cli/cmd/cmd.ts +7 -0
  106. package/src/cli/cmd/db.ts +120 -0
  107. package/src/cli/cmd/debug/agent.ts +192 -0
  108. package/src/cli/cmd/debug/config.ts +17 -0
  109. package/src/cli/cmd/debug/file.ts +100 -0
  110. package/src/cli/cmd/debug/index.ts +48 -0
  111. package/src/cli/cmd/debug/lsp.ts +61 -0
  112. package/src/cli/cmd/debug/ripgrep.ts +105 -0
  113. package/src/cli/cmd/debug/scrap.ts +16 -0
  114. package/src/cli/cmd/debug/skill.ts +23 -0
  115. package/src/cli/cmd/debug/snapshot.ts +53 -0
  116. package/src/cli/cmd/export.ts +306 -0
  117. package/src/cli/cmd/generate.ts +50 -0
  118. package/src/cli/cmd/github.ts +1647 -0
  119. package/src/cli/cmd/import.ts +208 -0
  120. package/src/cli/cmd/mcp.ts +812 -0
  121. package/src/cli/cmd/models.ts +88 -0
  122. package/src/cli/cmd/plug.ts +233 -0
  123. package/src/cli/cmd/pr.ts +138 -0
  124. package/src/cli/cmd/providers.ts +705 -0
  125. package/src/cli/cmd/run-completion.ts +77 -0
  126. package/src/cli/cmd/run.ts +694 -0
  127. package/src/cli/cmd/serve.ts +21 -0
  128. package/src/cli/cmd/session.ts +181 -0
  129. package/src/cli/cmd/stats.ts +413 -0
  130. package/src/cli/cmd/tui/app.tsx +1130 -0
  131. package/src/cli/cmd/tui/asset/TEN_VAD_LICENSE +12 -0
  132. package/src/cli/cmd/tui/asset/charge.wav +0 -0
  133. package/src/cli/cmd/tui/asset/pulse-a.wav +0 -0
  134. package/src/cli/cmd/tui/asset/pulse-b.wav +0 -0
  135. package/src/cli/cmd/tui/asset/pulse-c.wav +0 -0
  136. package/src/cli/cmd/tui/asset/ten_vad.wasm +0 -0
  137. package/src/cli/cmd/tui/asset/ten_vad_loader.js +30 -0
  138. package/src/cli/cmd/tui/attach.ts +83 -0
  139. package/src/cli/cmd/tui/component/background-image.tsx +150 -0
  140. package/src/cli/cmd/tui/component/bg-pulse.tsx +130 -0
  141. package/src/cli/cmd/tui/component/border.tsx +21 -0
  142. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  143. package/src/cli/cmd/tui/component/dialog-command.tsx +208 -0
  144. package/src/cli/cmd/tui/component/dialog-console-org.tsx +103 -0
  145. package/src/cli/cmd/tui/component/dialog-go-upsell.tsx +157 -0
  146. package/src/cli/cmd/tui/component/dialog-image-list.tsx +111 -0
  147. package/src/cli/cmd/tui/component/dialog-logo-design.tsx +37 -0
  148. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  149. package/src/cli/cmd/tui/component/dialog-mimo-login.tsx +224 -0
  150. package/src/cli/cmd/tui/component/dialog-model.tsx +253 -0
  151. package/src/cli/cmd/tui/component/dialog-provider.tsx +490 -0
  152. package/src/cli/cmd/tui/component/dialog-session-delete-failed.tsx +101 -0
  153. package/src/cli/cmd/tui/component/dialog-session-list.tsx +269 -0
  154. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  155. package/src/cli/cmd/tui/component/dialog-skill.tsx +42 -0
  156. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  157. package/src/cli/cmd/tui/component/dialog-status.tsx +170 -0
  158. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  159. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  160. package/src/cli/cmd/tui/component/dialog-variant.tsx +39 -0
  161. package/src/cli/cmd/tui/component/dialog-workflows.tsx +62 -0
  162. package/src/cli/cmd/tui/component/dialog-workspace-create.tsx +289 -0
  163. package/src/cli/cmd/tui/component/dialog-workspace-unavailable.tsx +81 -0
  164. package/src/cli/cmd/tui/component/dialog-worktree.tsx +90 -0
  165. package/src/cli/cmd/tui/component/error-component.tsx +92 -0
  166. package/src/cli/cmd/tui/component/logo.tsx +961 -0
  167. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  168. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +684 -0
  169. package/src/cli/cmd/tui/component/prompt/cwd.ts +0 -0
  170. package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
  171. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  172. package/src/cli/cmd/tui/component/prompt/index.tsx +1812 -0
  173. package/src/cli/cmd/tui/component/prompt/part.ts +16 -0
  174. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  175. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  176. package/src/cli/cmd/tui/component/starry-background.tsx +305 -0
  177. package/src/cli/cmd/tui/component/startup-loading.tsx +67 -0
  178. package/src/cli/cmd/tui/component/task-item.tsx +63 -0
  179. package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
  180. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  181. package/src/cli/cmd/tui/config/cwd.ts +5 -0
  182. package/src/cli/cmd/tui/config/tui-migrate.ts +151 -0
  183. package/src/cli/cmd/tui/config/tui-schema.ts +38 -0
  184. package/src/cli/cmd/tui/config/tui.ts +219 -0
  185. package/src/cli/cmd/tui/context/args.tsx +16 -0
  186. package/src/cli/cmd/tui/context/directory.ts +15 -0
  187. package/src/cli/cmd/tui/context/event.ts +45 -0
  188. package/src/cli/cmd/tui/context/exit.tsx +65 -0
  189. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  190. package/src/cli/cmd/tui/context/keybind.tsx +105 -0
  191. package/src/cli/cmd/tui/context/kv.tsx +76 -0
  192. package/src/cli/cmd/tui/context/language.tsx +91 -0
  193. package/src/cli/cmd/tui/context/local.tsx +455 -0
  194. package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
  195. package/src/cli/cmd/tui/context/project.tsx +109 -0
  196. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  197. package/src/cli/cmd/tui/context/route.tsx +61 -0
  198. package/src/cli/cmd/tui/context/sdk.tsx +150 -0
  199. package/src/cli/cmd/tui/context/sync.tsx +828 -0
  200. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  201. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  202. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  203. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +230 -0
  204. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +230 -0
  205. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  206. package/src/cli/cmd/tui/context/theme/cobalt2.json +225 -0
  207. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  208. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  209. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  210. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  211. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  212. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  213. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  214. package/src/cli/cmd/tui/context/theme/lucent-orng.json +234 -0
  215. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  216. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  217. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  218. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  219. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  220. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  221. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  222. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  223. package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
  224. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  225. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  226. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  227. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  228. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  229. package/src/cli/cmd/tui/context/theme/tulingcode.json +245 -0
  230. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  231. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  232. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  233. package/src/cli/cmd/tui/context/theme.tsx +1298 -0
  234. package/src/cli/cmd/tui/context/thinking.ts +48 -0
  235. package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
  236. package/src/cli/cmd/tui/event.ts +56 -0
  237. package/src/cli/cmd/tui/feature-plugins/home/footer.tsx +93 -0
  238. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +193 -0
  239. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +54 -0
  240. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +114 -0
  241. package/src/cli/cmd/tui/feature-plugins/sidebar/cwd.tsx +45 -0
  242. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
  243. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
  244. package/src/cli/cmd/tui/feature-plugins/sidebar/goal.tsx +84 -0
  245. package/src/cli/cmd/tui/feature-plugins/sidebar/instructions.tsx +54 -0
  246. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
  247. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +98 -0
  248. package/src/cli/cmd/tui/feature-plugins/sidebar/task.tsx +95 -0
  249. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +51 -0
  250. package/src/cli/cmd/tui/feature-plugins/sidebar/tps.ts +31 -0
  251. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +274 -0
  252. package/src/cli/cmd/tui/i18n/en.ts +397 -0
  253. package/src/cli/cmd/tui/i18n/es.ts +433 -0
  254. package/src/cli/cmd/tui/i18n/fr.ts +440 -0
  255. package/src/cli/cmd/tui/i18n/ja.ts +392 -0
  256. package/src/cli/cmd/tui/i18n/locales.ts +82 -0
  257. package/src/cli/cmd/tui/i18n/ru.ts +452 -0
  258. package/src/cli/cmd/tui/i18n/zh.ts +390 -0
  259. package/src/cli/cmd/tui/i18n/zht.ts +360 -0
  260. package/src/cli/cmd/tui/layer.ts +6 -0
  261. package/src/cli/cmd/tui/plugin/api.tsx +402 -0
  262. package/src/cli/cmd/tui/plugin/index.ts +3 -0
  263. package/src/cli/cmd/tui/plugin/internal.ts +35 -0
  264. package/src/cli/cmd/tui/plugin/runtime.ts +1030 -0
  265. package/src/cli/cmd/tui/plugin/slots.tsx +60 -0
  266. package/src/cli/cmd/tui/routes/home.tsx +165 -0
  267. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +76 -0
  268. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +116 -0
  269. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +47 -0
  270. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  271. package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
  272. package/src/cli/cmd/tui/routes/session/index.tsx +2532 -0
  273. package/src/cli/cmd/tui/routes/session/permission.tsx +691 -0
  274. package/src/cli/cmd/tui/routes/session/question.tsx +488 -0
  275. package/src/cli/cmd/tui/routes/session/sidebar.tsx +97 -0
  276. package/src/cli/cmd/tui/routes/session/subagent-footer.tsx +142 -0
  277. package/src/cli/cmd/tui/thread.ts +246 -0
  278. package/src/cli/cmd/tui/ui/dialog-alert.tsx +61 -0
  279. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +95 -0
  280. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +223 -0
  281. package/src/cli/cmd/tui/ui/dialog-help.tsx +42 -0
  282. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +123 -0
  283. package/src/cli/cmd/tui/ui/dialog-select.tsx +452 -0
  284. package/src/cli/cmd/tui/ui/dialog.tsx +207 -0
  285. package/src/cli/cmd/tui/ui/link.tsx +28 -0
  286. package/src/cli/cmd/tui/ui/spinner.ts +378 -0
  287. package/src/cli/cmd/tui/ui/toast.tsx +102 -0
  288. package/src/cli/cmd/tui/util/clipboard.ts +203 -0
  289. package/src/cli/cmd/tui/util/editor.ts +35 -0
  290. package/src/cli/cmd/tui/util/image-protocol.ts +35 -0
  291. package/src/cli/cmd/tui/util/index.ts +6 -0
  292. package/src/cli/cmd/tui/util/model.ts +23 -0
  293. package/src/cli/cmd/tui/util/provider-origin.ts +7 -0
  294. package/src/cli/cmd/tui/util/revert-diff.ts +18 -0
  295. package/src/cli/cmd/tui/util/scroll.ts +23 -0
  296. package/src/cli/cmd/tui/util/selection.ts +23 -0
  297. package/src/cli/cmd/tui/util/signal.ts +41 -0
  298. package/src/cli/cmd/tui/util/sound.ts +154 -0
  299. package/src/cli/cmd/tui/util/system-locale.ts +209 -0
  300. package/src/cli/cmd/tui/util/terminal.ts +110 -0
  301. package/src/cli/cmd/tui/util/transcript.ts +112 -0
  302. package/src/cli/cmd/tui/util/vad.ts +229 -0
  303. package/src/cli/cmd/tui/util/voice.ts +360 -0
  304. package/src/cli/cmd/tui/win32.ts +130 -0
  305. package/src/cli/cmd/tui/worker.ts +104 -0
  306. package/src/cli/cmd/uninstall.ts +351 -0
  307. package/src/cli/cmd/upgrade.ts +79 -0
  308. package/src/cli/cmd/web.ts +81 -0
  309. package/src/cli/effect/prompt.ts +25 -0
  310. package/src/cli/error.ts +82 -0
  311. package/src/cli/heap.ts +59 -0
  312. package/src/cli/i18n.ts +15 -0
  313. package/src/cli/logo.ts +53 -0
  314. package/src/cli/network.ts +62 -0
  315. package/src/cli/ui.ts +133 -0
  316. package/src/cli/upgrade.ts +41 -0
  317. package/src/command/index.ts +276 -0
  318. package/src/command/template/initialize.txt +66 -0
  319. package/src/command/template/review.txt +101 -0
  320. package/src/config/agent.ts +197 -0
  321. package/src/config/command.ts +69 -0
  322. package/src/config/config.ts +1024 -0
  323. package/src/config/console-state.ts +16 -0
  324. package/src/config/entry-name.ts +16 -0
  325. package/src/config/error.ts +21 -0
  326. package/src/config/formatter.ts +17 -0
  327. package/src/config/history.ts +21 -0
  328. package/src/config/index.ts +16 -0
  329. package/src/config/keybinds.ts +127 -0
  330. package/src/config/layout.ts +10 -0
  331. package/src/config/lsp.ts +45 -0
  332. package/src/config/managed.ts +70 -0
  333. package/src/config/markdown.ts +97 -0
  334. package/src/config/mcp.ts +172 -0
  335. package/src/config/model-id.ts +14 -0
  336. package/src/config/parse.ts +44 -0
  337. package/src/config/paths.ts +73 -0
  338. package/src/config/permission.ts +76 -0
  339. package/src/config/plugin.ts +88 -0
  340. package/src/config/provider.ts +118 -0
  341. package/src/config/server.ts +20 -0
  342. package/src/config/skills.ts +16 -0
  343. package/src/config/variable.ts +90 -0
  344. package/src/control-plane/adaptors/index.ts +52 -0
  345. package/src/control-plane/adaptors/worktree.ts +47 -0
  346. package/src/control-plane/dev/debug-workspace-plugin.ts +73 -0
  347. package/src/control-plane/schema.ts +19 -0
  348. package/src/control-plane/sse.ts +66 -0
  349. package/src/control-plane/types.ts +34 -0
  350. package/src/control-plane/util.ts +37 -0
  351. package/src/control-plane/workspace-context.ts +26 -0
  352. package/src/control-plane/workspace.sql.ts +17 -0
  353. package/src/control-plane/workspace.ts +615 -0
  354. package/src/effect/app-runtime.ts +146 -0
  355. package/src/effect/bootstrap-runtime.ts +33 -0
  356. package/src/effect/bridge.ts +48 -0
  357. package/src/effect/cross-spawn-spawner.ts +514 -0
  358. package/src/effect/index.ts +5 -0
  359. package/src/effect/instance-ref.ts +11 -0
  360. package/src/effect/instance-registry.ts +12 -0
  361. package/src/effect/instance-state.ts +81 -0
  362. package/src/effect/logger.ts +73 -0
  363. package/src/effect/memo-map.ts +3 -0
  364. package/src/effect/observability.ts +107 -0
  365. package/src/effect/run-service.ts +52 -0
  366. package/src/effect/runner.ts +210 -0
  367. package/src/effect/runtime.ts +19 -0
  368. package/src/env/index.ts +37 -0
  369. package/src/file/ignore.ts +81 -0
  370. package/src/file/index.ts +664 -0
  371. package/src/file/protected.ts +59 -0
  372. package/src/file/ripgrep.ts +485 -0
  373. package/src/file/watcher.ts +163 -0
  374. package/src/flag/flag.ts +164 -0
  375. package/src/format/formatter.ts +403 -0
  376. package/src/format/index.ts +203 -0
  377. package/src/git/index.ts +260 -0
  378. package/src/global/index.ts +54 -0
  379. package/src/history/backfill.ts +162 -0
  380. package/src/history/extract.ts +67 -0
  381. package/src/history/fts-query.ts +15 -0
  382. package/src/history/fts.sql.ts +20 -0
  383. package/src/history/index.ts +10 -0
  384. package/src/history/resolve.ts +65 -0
  385. package/src/history/service.ts +258 -0
  386. package/src/history/writer.ts +112 -0
  387. package/src/id/id.ts +87 -0
  388. package/src/ide/index.ts +73 -0
  389. package/src/inbox/inbox-ref.ts +38 -0
  390. package/src/inbox/inbox.sql.ts +26 -0
  391. package/src/inbox/inbox.ts +223 -0
  392. package/src/inbox/index.ts +3 -0
  393. package/src/inbox/render.ts +40 -0
  394. package/src/index.ts +260 -0
  395. package/src/installation/index.ts +351 -0
  396. package/src/installation/version.ts +8 -0
  397. package/src/lsp/client.ts +249 -0
  398. package/src/lsp/diagnostic.ts +29 -0
  399. package/src/lsp/index.ts +3 -0
  400. package/src/lsp/language.ts +120 -0
  401. package/src/lsp/launch.ts +21 -0
  402. package/src/lsp/lsp.ts +519 -0
  403. package/src/lsp/server.ts +1956 -0
  404. package/src/mcp/auth.ts +144 -0
  405. package/src/mcp/index.ts +944 -0
  406. package/src/mcp/oauth-callback.ts +232 -0
  407. package/src/mcp/oauth-provider.ts +214 -0
  408. package/src/memory/fts-query.ts +37 -0
  409. package/src/memory/fts.sql.ts +19 -0
  410. package/src/memory/index.ts +1 -0
  411. package/src/memory/paths.ts +116 -0
  412. package/src/memory/reconcile.ts +144 -0
  413. package/src/memory/service.ts +144 -0
  414. package/src/metrics/client.ts +40 -0
  415. package/src/metrics/event.ts +43 -0
  416. package/src/metrics/index.ts +5 -0
  417. package/src/metrics/installation.ts +18 -0
  418. package/src/metrics/subscriber.ts +58 -0
  419. package/src/metrics/util.ts +9 -0
  420. package/src/node.ts +6 -0
  421. package/src/npm/config.ts +0 -0
  422. package/src/npm/index.ts +293 -0
  423. package/src/npmcli-config.d.ts +43 -0
  424. package/src/patch/index.ts +680 -0
  425. package/src/permission/arity.ts +163 -0
  426. package/src/permission/evaluate.ts +15 -0
  427. package/src/permission/index.ts +379 -0
  428. package/src/permission/schema.ts +17 -0
  429. package/src/plugin/checkpoint-splitover.ts +60 -0
  430. package/src/plugin/cloud-ai.ts +329 -0
  431. package/src/plugin/cloudflare.ts +76 -0
  432. package/src/plugin/codex.ts +607 -0
  433. package/src/plugin/github-copilot/copilot.ts +368 -0
  434. package/src/plugin/github-copilot/models.ts +153 -0
  435. package/src/plugin/index.ts +493 -0
  436. package/src/plugin/install.ts +439 -0
  437. package/src/plugin/loader.ts +216 -0
  438. package/src/plugin/matcher.ts +33 -0
  439. package/src/plugin/meta.ts +188 -0
  440. package/src/plugin/mimo-free.ts +153 -0
  441. package/src/plugin/mimo.ts +124 -0
  442. package/src/plugin/shared.ts +323 -0
  443. package/src/plugin/subagent-progress-checker.ts +147 -0
  444. package/src/project/bootstrap.ts +59 -0
  445. package/src/project/index.ts +2 -0
  446. package/src/project/instance.ts +190 -0
  447. package/src/project/project-id.ts +48 -0
  448. package/src/project/project.sql.ts +16 -0
  449. package/src/project/project.ts +501 -0
  450. package/src/project/schema.ts +15 -0
  451. package/src/project/vcs.ts +227 -0
  452. package/src/provider/auth.ts +234 -0
  453. package/src/provider/error.ts +216 -0
  454. package/src/provider/index.ts +5 -0
  455. package/src/provider/models.ts +180 -0
  456. package/src/provider/provider.ts +1782 -0
  457. package/src/provider/schema.ts +36 -0
  458. package/src/provider/sdk/copilot/README.md +5 -0
  459. package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
  460. package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
  461. package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
  462. package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
  463. package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +815 -0
  464. package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
  465. package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
  466. package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +83 -0
  467. package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
  468. package/src/provider/sdk/copilot/index.ts +2 -0
  469. package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
  470. package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
  471. package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
  472. package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
  473. package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
  474. package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
  475. package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1770 -0
  476. package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
  477. package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
  478. package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
  479. package/src/provider/sdk/copilot/responses/tool/file-search.ts +127 -0
  480. package/src/provider/sdk/copilot/responses/tool/image-generation.ts +114 -0
  481. package/src/provider/sdk/copilot/responses/tool/local-shell.ts +64 -0
  482. package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
  483. package/src/provider/sdk/copilot/responses/tool/web-search.ts +102 -0
  484. package/src/provider/transform.ts +1322 -0
  485. package/src/pty/index.ts +364 -0
  486. package/src/pty/pty.bun.ts +26 -0
  487. package/src/pty/pty.node.ts +27 -0
  488. package/src/pty/pty.ts +25 -0
  489. package/src/pty/schema.ts +17 -0
  490. package/src/question/index.ts +252 -0
  491. package/src/question/schema.ts +17 -0
  492. package/src/server/adapter.bun.ts +40 -0
  493. package/src/server/adapter.node.ts +66 -0
  494. package/src/server/adapter.ts +21 -0
  495. package/src/server/error.ts +53 -0
  496. package/src/server/event.ts +7 -0
  497. package/src/server/fence.ts +81 -0
  498. package/src/server/mdns.ts +60 -0
  499. package/src/server/middleware.ts +92 -0
  500. package/src/server/projectors.ts +28 -0
  501. package/src/server/proxy.ts +171 -0
  502. package/src/server/routes/control/index.ts +160 -0
  503. package/src/server/routes/control/workspace.ts +203 -0
  504. package/src/server/routes/global.ts +287 -0
  505. package/src/server/routes/instance/bash-interactive.ts +82 -0
  506. package/src/server/routes/instance/config.ts +89 -0
  507. package/src/server/routes/instance/event.ts +88 -0
  508. package/src/server/routes/instance/experimental.ts +408 -0
  509. package/src/server/routes/instance/file.ts +190 -0
  510. package/src/server/routes/instance/httpapi/config.ts +51 -0
  511. package/src/server/routes/instance/httpapi/permission.ts +72 -0
  512. package/src/server/routes/instance/httpapi/project.ts +62 -0
  513. package/src/server/routes/instance/httpapi/provider.ts +142 -0
  514. package/src/server/routes/instance/httpapi/question.ts +121 -0
  515. package/src/server/routes/instance/httpapi/server.ts +136 -0
  516. package/src/server/routes/instance/index.ts +301 -0
  517. package/src/server/routes/instance/mcp.ts +260 -0
  518. package/src/server/routes/instance/middleware.ts +35 -0
  519. package/src/server/routes/instance/permission.ts +73 -0
  520. package/src/server/routes/instance/project.ts +122 -0
  521. package/src/server/routes/instance/provider.ts +158 -0
  522. package/src/server/routes/instance/pty.ts +247 -0
  523. package/src/server/routes/instance/question.ts +162 -0
  524. package/src/server/routes/instance/session.ts +1296 -0
  525. package/src/server/routes/instance/sync.ts +143 -0
  526. package/src/server/routes/instance/trace.ts +59 -0
  527. package/src/server/routes/instance/tui.ts +384 -0
  528. package/src/server/routes/instance/workflows.ts +72 -0
  529. package/src/server/routes/ui.ts +55 -0
  530. package/src/server/server.ts +136 -0
  531. package/src/server/workspace.ts +122 -0
  532. package/src/session/auto-dream.ts +123 -0
  533. package/src/session/boundary.ts +77 -0
  534. package/src/session/budgeted-read.ts +118 -0
  535. package/src/session/checkpoint-align.ts +29 -0
  536. package/src/session/checkpoint-context.ts +36 -0
  537. package/src/session/checkpoint-paths.ts +86 -0
  538. package/src/session/checkpoint-progress-reconcile.ts +111 -0
  539. package/src/session/checkpoint-retry.ts +192 -0
  540. package/src/session/checkpoint-templates.ts +114 -0
  541. package/src/session/checkpoint-validator.ts +259 -0
  542. package/src/session/checkpoint.ts +1478 -0
  543. package/src/session/classify.ts +92 -0
  544. package/src/session/claude-import.sql.ts +13 -0
  545. package/src/session/claude-import.ts +379 -0
  546. package/src/session/compaction.ts +543 -0
  547. package/src/session/goal.ts +232 -0
  548. package/src/session/index.ts +1 -0
  549. package/src/session/instruction.ts +276 -0
  550. package/src/session/last-message-info.ts +32 -0
  551. package/src/session/llm-request-prefix.ts +82 -0
  552. package/src/session/llm.ts +735 -0
  553. package/src/session/max-mode.ts +397 -0
  554. package/src/session/message-v2.ts +1136 -0
  555. package/src/session/message.ts +191 -0
  556. package/src/session/overflow.ts +53 -0
  557. package/src/session/prefix-capture-ref.ts +48 -0
  558. package/src/session/processor.ts +962 -0
  559. package/src/session/projectors.ts +137 -0
  560. package/src/session/prompt/anthropic.txt +154 -0
  561. package/src/session/prompt/beast.txt +155 -0
  562. package/src/session/prompt/build-switch.txt +5 -0
  563. package/src/session/prompt/codex.txt +79 -0
  564. package/src/session/prompt/compose.txt +115 -0
  565. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  566. package/src/session/prompt/default.txt +151 -0
  567. package/src/session/prompt/gemini.txt +155 -0
  568. package/src/session/prompt/gpt.txt +107 -0
  569. package/src/session/prompt/kimi.txt +95 -0
  570. package/src/session/prompt/max-steps.txt +16 -0
  571. package/src/session/prompt/trinity.txt +97 -0
  572. package/src/session/prompt.ts +3355 -0
  573. package/src/session/prune.ts +481 -0
  574. package/src/session/retry.ts +166 -0
  575. package/src/session/revert.ts +161 -0
  576. package/src/session/run-state.ts +135 -0
  577. package/src/session/schema.ts +36 -0
  578. package/src/session/session.sql.ts +110 -0
  579. package/src/session/session.ts +908 -0
  580. package/src/session/status.ts +89 -0
  581. package/src/session/summary.ts +163 -0
  582. package/src/session/system.ts +86 -0
  583. package/src/session/todo.ts +77 -0
  584. package/src/share/index.ts +2 -0
  585. package/src/share/session.ts +57 -0
  586. package/src/share/share-next.ts +381 -0
  587. package/src/share/share.sql.ts +13 -0
  588. package/src/shell/shell.ts +110 -0
  589. package/src/skill/compose/.bundle/ask/SKILL.md +58 -0
  590. package/src/skill/compose/.bundle/brainstorm/SKILL.md +220 -0
  591. package/src/skill/compose/.bundle/brainstorm/scripts/frame-template.html +214 -0
  592. package/src/skill/compose/.bundle/brainstorm/scripts/helper.js +88 -0
  593. package/src/skill/compose/.bundle/brainstorm/scripts/server.cjs +354 -0
  594. package/src/skill/compose/.bundle/brainstorm/scripts/start-server.sh +148 -0
  595. package/src/skill/compose/.bundle/brainstorm/scripts/stop-server.sh +56 -0
  596. package/src/skill/compose/.bundle/brainstorm/spec-document-reviewer-prompt.md +50 -0
  597. package/src/skill/compose/.bundle/brainstorm/visual-companion.md +287 -0
  598. package/src/skill/compose/.bundle/debug/CREATION-LOG.md +119 -0
  599. package/src/skill/compose/.bundle/debug/SKILL.md +297 -0
  600. package/src/skill/compose/.bundle/debug/condition-based-waiting-example.ts +158 -0
  601. package/src/skill/compose/.bundle/debug/condition-based-waiting.md +115 -0
  602. package/src/skill/compose/.bundle/debug/defense-in-depth.md +122 -0
  603. package/src/skill/compose/.bundle/debug/find-polluter.sh +63 -0
  604. package/src/skill/compose/.bundle/debug/root-cause-tracing.md +169 -0
  605. package/src/skill/compose/.bundle/debug/test-academic.md +14 -0
  606. package/src/skill/compose/.bundle/debug/test-pressure-1.md +58 -0
  607. package/src/skill/compose/.bundle/debug/test-pressure-2.md +68 -0
  608. package/src/skill/compose/.bundle/debug/test-pressure-3.md +69 -0
  609. package/src/skill/compose/.bundle/execute/SKILL.md +71 -0
  610. package/src/skill/compose/.bundle/feedback/SKILL.md +214 -0
  611. package/src/skill/compose/.bundle/merge/SKILL.md +252 -0
  612. package/src/skill/compose/.bundle/new-skill/SKILL.md +656 -0
  613. package/src/skill/compose/.bundle/new-skill/anthropic-best-practices.md +1150 -0
  614. package/src/skill/compose/.bundle/new-skill/examples/CLAUDE_MD_TESTING.md +189 -0
  615. package/src/skill/compose/.bundle/new-skill/graphviz-conventions.dot +172 -0
  616. package/src/skill/compose/.bundle/new-skill/persuasion-principles.md +187 -0
  617. package/src/skill/compose/.bundle/new-skill/render-graphs.js +168 -0
  618. package/src/skill/compose/.bundle/new-skill/testing-skills-with-subagents.md +384 -0
  619. package/src/skill/compose/.bundle/parallel/SKILL.md +182 -0
  620. package/src/skill/compose/.bundle/plan/SKILL.md +161 -0
  621. package/src/skill/compose/.bundle/plan/plan-document-reviewer-prompt.md +50 -0
  622. package/src/skill/compose/.bundle/report/SKILL.md +180 -0
  623. package/src/skill/compose/.bundle/review/SKILL.md +104 -0
  624. package/src/skill/compose/.bundle/review/code-reviewer.md +171 -0
  625. package/src/skill/compose/.bundle/subagent/SKILL.md +344 -0
  626. package/src/skill/compose/.bundle/subagent/code-quality-reviewer-prompt.md +24 -0
  627. package/src/skill/compose/.bundle/subagent/implementer-prompt.md +126 -0
  628. package/src/skill/compose/.bundle/subagent/spec-reviewer-prompt.md +112 -0
  629. package/src/skill/compose/.bundle/tdd/SKILL.md +372 -0
  630. package/src/skill/compose/.bundle/tdd/testing-anti-patterns.md +299 -0
  631. package/src/skill/compose/.bundle/verify/SKILL.md +140 -0
  632. package/src/skill/compose/.bundle/worktree/SKILL.md +234 -0
  633. package/src/skill/compose/LICENSE-karpathy +28 -0
  634. package/src/skill/compose/LICENSE-superpowers +26 -0
  635. package/src/skill/compose/bundle.macro.ts +30 -0
  636. package/src/skill/compose/extract.ts +85 -0
  637. package/src/skill/discovery.ts +116 -0
  638. package/src/skill/index.ts +311 -0
  639. package/src/snapshot/index.ts +777 -0
  640. package/src/sql.d.ts +4 -0
  641. package/src/storage/db.bun.ts +8 -0
  642. package/src/storage/db.node.ts +8 -0
  643. package/src/storage/db.ts +172 -0
  644. package/src/storage/index.ts +26 -0
  645. package/src/storage/json-migration.ts +426 -0
  646. package/src/storage/schema.sql.ts +10 -0
  647. package/src/storage/schema.ts +7 -0
  648. package/src/storage/storage.ts +331 -0
  649. package/src/sync/README.md +179 -0
  650. package/src/sync/event.sql.ts +16 -0
  651. package/src/sync/index.ts +278 -0
  652. package/src/sync/schema.ts +14 -0
  653. package/src/task/events.ts +28 -0
  654. package/src/task/gate-state.ts +54 -0
  655. package/src/task/gate.ts +116 -0
  656. package/src/task/index.ts +1 -0
  657. package/src/task/registry.ts +387 -0
  658. package/src/task/schema.ts +43 -0
  659. package/src/task/task.sql.ts +50 -0
  660. package/src/team/events.ts +22 -0
  661. package/src/team/index.ts +113 -0
  662. package/src/team/schema.ts +31 -0
  663. package/src/temporary.ts +33 -0
  664. package/src/tool/actor.shell.txt +72 -0
  665. package/src/tool/actor.ts +803 -0
  666. package/src/tool/actor.txt +103 -0
  667. package/src/tool/apply_patch.ts +308 -0
  668. package/src/tool/apply_patch.txt +33 -0
  669. package/src/tool/bash-interactive.ts +183 -0
  670. package/src/tool/bash.ts +696 -0
  671. package/src/tool/bash.txt +123 -0
  672. package/src/tool/change-directory.ts +91 -0
  673. package/src/tool/codesearch.ts +63 -0
  674. package/src/tool/codesearch.txt +12 -0
  675. package/src/tool/edit.ts +685 -0
  676. package/src/tool/edit.txt +10 -0
  677. package/src/tool/external-directory.ts +132 -0
  678. package/src/tool/glob.ts +100 -0
  679. package/src/tool/glob.txt +6 -0
  680. package/src/tool/grep.ts +145 -0
  681. package/src/tool/grep.txt +8 -0
  682. package/src/tool/history.ts +146 -0
  683. package/src/tool/history.txt +17 -0
  684. package/src/tool/index.ts +4 -0
  685. package/src/tool/invalid.ts +20 -0
  686. package/src/tool/invocation-style.ts +17 -0
  687. package/src/tool/lsp.ts +91 -0
  688. package/src/tool/lsp.txt +19 -0
  689. package/src/tool/mcp-exa.ts +78 -0
  690. package/src/tool/memory-path-guard.ts +162 -0
  691. package/src/tool/memory.ts +81 -0
  692. package/src/tool/memory.txt +69 -0
  693. package/src/tool/multiedit.ts +61 -0
  694. package/src/tool/multiedit.txt +41 -0
  695. package/src/tool/plan-enter.txt +14 -0
  696. package/src/tool/plan-exit.txt +13 -0
  697. package/src/tool/plan.ts +90 -0
  698. package/src/tool/question.ts +67 -0
  699. package/src/tool/question.txt +10 -0
  700. package/src/tool/read.ts +327 -0
  701. package/src/tool/read.txt +14 -0
  702. package/src/tool/registry.ts +413 -0
  703. package/src/tool/schema.ts +17 -0
  704. package/src/tool/session-cwd.ts +35 -0
  705. package/src/tool/shell-tokenize.ts +346 -0
  706. package/src/tool/shell-wrap.ts +190 -0
  707. package/src/tool/skill.ts +76 -0
  708. package/src/tool/skill.txt +5 -0
  709. package/src/tool/task.shell.txt +57 -0
  710. package/src/tool/task.ts +456 -0
  711. package/src/tool/task.txt +56 -0
  712. package/src/tool/tool.ts +153 -0
  713. package/src/tool/truncate.ts +201 -0
  714. package/src/tool/truncation-dir.ts +4 -0
  715. package/src/tool/webfetch.ts +199 -0
  716. package/src/tool/webfetch.txt +13 -0
  717. package/src/tool/websearch/index.ts +104 -0
  718. package/src/tool/websearch/mimo.ts +118 -0
  719. package/src/tool/websearch/websearch.txt +14 -0
  720. package/src/tool/workflow.ts +164 -0
  721. package/src/tool/workflow.txt +25 -0
  722. package/src/tool/write.ts +88 -0
  723. package/src/tool/write.txt +9 -0
  724. package/src/util/abort.ts +35 -0
  725. package/src/util/archive.ts +15 -0
  726. package/src/util/color.ts +17 -0
  727. package/src/util/data-url.ts +9 -0
  728. package/src/util/defer.ts +10 -0
  729. package/src/util/effect-http-client.ts +11 -0
  730. package/src/util/effect-zod.ts +367 -0
  731. package/src/util/error.ts +78 -0
  732. package/src/util/filesystem.ts +243 -0
  733. package/src/util/fn.ts +21 -0
  734. package/src/util/format.ts +20 -0
  735. package/src/util/iife.ts +3 -0
  736. package/src/util/index.ts +12 -0
  737. package/src/util/keybind.ts +101 -0
  738. package/src/util/lazy.ts +18 -0
  739. package/src/util/local-context.ts +23 -0
  740. package/src/util/locale.ts +79 -0
  741. package/src/util/lock.ts +96 -0
  742. package/src/util/log.ts +197 -0
  743. package/src/util/media.ts +26 -0
  744. package/src/util/mimo-process.ts +24 -0
  745. package/src/util/network.ts +9 -0
  746. package/src/util/process.ts +174 -0
  747. package/src/util/queue.ts +32 -0
  748. package/src/util/record.ts +3 -0
  749. package/src/util/rpc.ts +64 -0
  750. package/src/util/schema.ts +53 -0
  751. package/src/util/scrap.ts +10 -0
  752. package/src/util/signal.ts +12 -0
  753. package/src/util/timeout.ts +14 -0
  754. package/src/util/token.ts +5 -0
  755. package/src/util/update-schema.ts +13 -0
  756. package/src/util/which.ts +14 -0
  757. package/src/util/wildcard.ts +57 -0
  758. package/src/workflow/builtin/deep-research.js +391 -0
  759. package/src/workflow/builtin.ts +54 -0
  760. package/src/workflow/events.ts +72 -0
  761. package/src/workflow/meta.ts +335 -0
  762. package/src/workflow/persistence.ts +312 -0
  763. package/src/workflow/resolve.ts +45 -0
  764. package/src/workflow/runtime-ref.ts +18 -0
  765. package/src/workflow/runtime.ts +1234 -0
  766. package/src/workflow/sandbox.ts +280 -0
  767. package/src/workflow/workflow.sql.ts +31 -0
  768. package/src/workflow/workspace.ts +69 -0
  769. package/src/worktree/index.ts +614 -0
  770. package/sst-env.d.ts +10 -0
  771. package/test/AGENTS.md +133 -0
  772. package/test/account/repo.test.ts +352 -0
  773. package/test/account/service.test.ts +456 -0
  774. package/test/acp/agent-interface.test.ts +51 -0
  775. package/test/acp/event-subscription.test.ts +725 -0
  776. package/test/actor/cancel-cascade.test.ts +432 -0
  777. package/test/actor/no-completion-listener.test.ts +41 -0
  778. package/test/actor/poststop-progress-write-permission.repro.test.ts +414 -0
  779. package/test/actor/registry-render.test.ts +113 -0
  780. package/test/actor/registry-status.test.ts +111 -0
  781. package/test/actor/registry.test.ts +619 -0
  782. package/test/actor/return-header.test.ts +40 -0
  783. package/test/actor/spawn-lifecycle.test.ts +346 -0
  784. package/test/actor/spawn-no-deadlock.test.ts +340 -0
  785. package/test/actor/spawn-notification.test.ts +393 -0
  786. package/test/actor/spawn-task-autostart.test.ts +530 -0
  787. package/test/actor/spawn.test.ts +1072 -0
  788. package/test/actor/status-event-payload.test.ts +132 -0
  789. package/test/actor/terminology.test.ts +39 -0
  790. package/test/actor/turn.test.ts +125 -0
  791. package/test/actor/waiter.test.ts +246 -0
  792. package/test/agent/agent.test.ts +874 -0
  793. package/test/agent/allowlist.test.ts +45 -0
  794. package/test/auth/auth.test.ts +86 -0
  795. package/test/bus/bus-effect.test.ts +162 -0
  796. package/test/bus/bus-integration.test.ts +87 -0
  797. package/test/bus/bus.test.ts +219 -0
  798. package/test/cli/account.test.ts +26 -0
  799. package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
  800. package/test/cli/error.test.ts +18 -0
  801. package/test/cli/github-action.test.ts +198 -0
  802. package/test/cli/github-remote.test.ts +80 -0
  803. package/test/cli/import.test.ts +54 -0
  804. package/test/cli/plugin-auth-picker.test.ts +120 -0
  805. package/test/cli/run-completion.test.ts +131 -0
  806. package/test/cli/tui/keybind-plugin.test.ts +90 -0
  807. package/test/cli/tui/plugin-add.test.ts +111 -0
  808. package/test/cli/tui/plugin-install.test.ts +87 -0
  809. package/test/cli/tui/plugin-lifecycle.test.ts +224 -0
  810. package/test/cli/tui/plugin-loader-entrypoint.test.ts +484 -0
  811. package/test/cli/tui/plugin-loader-pure.test.ts +71 -0
  812. package/test/cli/tui/plugin-loader.test.ts +816 -0
  813. package/test/cli/tui/plugin-toggle.test.ts +157 -0
  814. package/test/cli/tui/revert-diff.test.ts +35 -0
  815. package/test/cli/tui/route-agent-id.test.ts +26 -0
  816. package/test/cli/tui/sidebar-tps.test.ts +63 -0
  817. package/test/cli/tui/slot-replace.test.tsx +47 -0
  818. package/test/cli/tui/sync-bucket.test.ts +29 -0
  819. package/test/cli/tui/theme-store.test.ts +51 -0
  820. package/test/cli/tui/thread.test.ts +121 -0
  821. package/test/cli/tui/transcript.test.ts +426 -0
  822. package/test/cli/tui/use-event.test.tsx +175 -0
  823. package/test/cli/tui/voice.test.ts +269 -0
  824. package/test/command/deep-research-command.test.ts +16 -0
  825. package/test/config/agent-color.test.ts +77 -0
  826. package/test/config/checkpoint-fork.test.ts +21 -0
  827. package/test/config/config.test.ts +2577 -0
  828. package/test/config/fixtures/empty-frontmatter.md +4 -0
  829. package/test/config/fixtures/frontmatter.md +28 -0
  830. package/test/config/fixtures/markdown-header.md +11 -0
  831. package/test/config/fixtures/no-frontmatter.md +1 -0
  832. package/test/config/fixtures/weird-model-id.md +13 -0
  833. package/test/config/lsp.test.ts +87 -0
  834. package/test/config/markdown.test.ts +228 -0
  835. package/test/config/plugin.test.ts +0 -0
  836. package/test/config/tui.test.ts +627 -0
  837. package/test/control-plane/adaptors.test.ts +71 -0
  838. package/test/control-plane/sse.test.ts +56 -0
  839. package/test/effect/app-runtime-logger.test.ts +92 -0
  840. package/test/effect/cross-spawn-spawner.test.ts +411 -0
  841. package/test/effect/instance-state.test.ts +482 -0
  842. package/test/effect/observability.test.ts +46 -0
  843. package/test/effect/run-service.test.ts +46 -0
  844. package/test/effect/runner-warn-log.test.ts +111 -0
  845. package/test/effect/runner.test.ts +494 -0
  846. package/test/fake/provider.ts +90 -0
  847. package/test/file/fsmonitor.test.ts +68 -0
  848. package/test/file/ignore.test.ts +10 -0
  849. package/test/file/index.test.ts +956 -0
  850. package/test/file/path-traversal.test.ts +204 -0
  851. package/test/file/ripgrep.test.ts +214 -0
  852. package/test/file/watcher.test.ts +249 -0
  853. package/test/filesystem/filesystem.test.ts +319 -0
  854. package/test/fixture/db.ts +11 -0
  855. package/test/fixture/fixture.test.ts +58 -0
  856. package/test/fixture/fixture.ts +190 -0
  857. package/test/fixture/flock-worker.ts +72 -0
  858. package/test/fixture/lsp/fake-lsp-server.js +75 -0
  859. package/test/fixture/plug-worker.ts +93 -0
  860. package/test/fixture/plugin-meta-worker.ts +19 -0
  861. package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
  862. package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
  863. package/test/fixture/skills/cloudflare/SKILL.md +211 -0
  864. package/test/fixture/skills/index.json +6 -0
  865. package/test/fixture/tui-plugin.ts +328 -0
  866. package/test/fixture/tui-runtime.ts +31 -0
  867. package/test/format/format.test.ts +244 -0
  868. package/test/git/git.test.ts +128 -0
  869. package/test/global/fixture/global-paths-worker.ts +17 -0
  870. package/test/global/mimocode-home.test.ts +143 -0
  871. package/test/history/backfill.test.ts +149 -0
  872. package/test/history/extract.test.ts +106 -0
  873. package/test/history/fts-query.test.ts +30 -0
  874. package/test/history/resolve.test.ts +130 -0
  875. package/test/history/service.test.ts +210 -0
  876. package/test/history/writer.test.ts +163 -0
  877. package/test/ide/ide.test.ts +82 -0
  878. package/test/inbox/drain-in-loop.test.ts +230 -0
  879. package/test/inbox/fork-agent-compat.test.ts +387 -0
  880. package/test/inbox/gc-on-init.test.ts +167 -0
  881. package/test/inbox/send-no-block.test.ts +120 -0
  882. package/test/inbox/sender-cancel-independence.test.ts +160 -0
  883. package/test/inbox/wake-matrix.test.ts +141 -0
  884. package/test/installation/installation.test.ts +226 -0
  885. package/test/keybind.test.ts +421 -0
  886. package/test/lib/effect.ts +53 -0
  887. package/test/lib/filesystem.ts +10 -0
  888. package/test/lib/llm-server.ts +770 -0
  889. package/test/lib/scripted-llm-server.ts +245 -0
  890. package/test/lsp/client.test.ts +98 -0
  891. package/test/lsp/index.test.ts +109 -0
  892. package/test/lsp/launch.test.ts +22 -0
  893. package/test/lsp/lifecycle.test.ts +184 -0
  894. package/test/mcp/headers.test.ts +178 -0
  895. package/test/mcp/lifecycle.test.ts +824 -0
  896. package/test/mcp/oauth-auto-connect.test.ts +281 -0
  897. package/test/mcp/oauth-browser.test.ts +268 -0
  898. package/test/mcp/oauth-callback.test.ts +34 -0
  899. package/test/memory/abort-leak-webfetch.ts +49 -0
  900. package/test/memory/abort-leak.test.ts +127 -0
  901. package/test/memory/cc-frontmatter.test.ts +85 -0
  902. package/test/memory/cc-paths.test.ts +60 -0
  903. package/test/memory/cc-reconcile.test.ts +239 -0
  904. package/test/memory/cc-search.test.ts +151 -0
  905. package/test/memory/fts-query.test.ts +48 -0
  906. package/test/memory/fts-rowid-stability.test.ts +271 -0
  907. package/test/memory/paths.test.ts +210 -0
  908. package/test/memory/reconcile.test.ts +115 -0
  909. package/test/memory/service.test.ts +169 -0
  910. package/test/npm.test.ts +18 -0
  911. package/test/patch/patch.test.ts +348 -0
  912. package/test/permission/abort.test.ts +116 -0
  913. package/test/permission/arity.test.ts +33 -0
  914. package/test/permission/disabled.test.ts +51 -0
  915. package/test/permission/next.test.ts +1080 -0
  916. package/test/permission/non-interactive.test.ts +55 -0
  917. package/test/permission-task.test.ts +326 -0
  918. package/test/plugin/actor-hooks.test.ts +1471 -0
  919. package/test/plugin/auth-override.test.ts +79 -0
  920. package/test/plugin/checkpoint-splitover.test.ts +434 -0
  921. package/test/plugin/cloudflare.test.ts +68 -0
  922. package/test/plugin/codex.test.ts +123 -0
  923. package/test/plugin/github-copilot-models.test.ts +163 -0
  924. package/test/plugin/install-concurrency.test.ts +140 -0
  925. package/test/plugin/install.test.ts +570 -0
  926. package/test/plugin/loader-shared.test.ts +1169 -0
  927. package/test/plugin/matcher.test.ts +97 -0
  928. package/test/plugin/meta.test.ts +137 -0
  929. package/test/plugin/mimo.test.ts +257 -0
  930. package/test/plugin/shared.test.ts +88 -0
  931. package/test/plugin/subagent-progress-checker.test.ts +227 -0
  932. package/test/plugin/trigger.test.ts +116 -0
  933. package/test/plugin/workspace-adaptor.test.ts +109 -0
  934. package/test/preload.ts +102 -0
  935. package/test/project/migrate-global.test.ts +150 -0
  936. package/test/project/project-id.test.ts +64 -0
  937. package/test/project/project.test.ts +481 -0
  938. package/test/project/vcs.test.ts +286 -0
  939. package/test/project/worktree-remove.test.ts +126 -0
  940. package/test/project/worktree.test.ts +214 -0
  941. package/test/provider/amazon-bedrock.test.ts +462 -0
  942. package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
  943. package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
  944. package/test/provider/error.test.ts +160 -0
  945. package/test/provider/gitlab-duo.test.ts +413 -0
  946. package/test/provider/model-groups.test.ts +389 -0
  947. package/test/provider/provider-chunk-timeout.test.ts +23 -0
  948. package/test/provider/provider.test.ts +2648 -0
  949. package/test/provider/transform.test.ts +3379 -0
  950. package/test/pty/pty-output-isolation.test.ts +146 -0
  951. package/test/pty/pty-session.test.ts +102 -0
  952. package/test/pty/pty-shell.test.ts +69 -0
  953. package/test/question/question.test.ts +464 -0
  954. package/test/server/global-session-list.test.ts +105 -0
  955. package/test/server/project-init-git.test.ts +122 -0
  956. package/test/server/session-actions.test.ts +49 -0
  957. package/test/server/session-list.test.ts +110 -0
  958. package/test/server/session-messages.test.ts +220 -0
  959. package/test/server/session-prompt-busy.test.ts +146 -0
  960. package/test/server/session-select.test.ts +100 -0
  961. package/test/server/session-task-route.test.ts +165 -0
  962. package/test/server/summarize-route-main-slice.test.ts +99 -0
  963. package/test/server/trace-attributes.test.ts +76 -0
  964. package/test/server/workflows-route.test.ts +279 -0
  965. package/test/session/bootstrap-skip-system.test.ts +121 -0
  966. package/test/session/boundary.test.ts +33 -0
  967. package/test/session/budgeted-read.test.ts +74 -0
  968. package/test/session/checkpoint-align.test.ts +58 -0
  969. package/test/session/checkpoint-boundary.test.ts +186 -0
  970. package/test/session/checkpoint-child-session.test.ts +508 -0
  971. package/test/session/checkpoint-context.test.ts +141 -0
  972. package/test/session/checkpoint-drain.test.ts +188 -0
  973. package/test/session/checkpoint-extract-titles.test.ts +58 -0
  974. package/test/session/checkpoint-fork-mode.test.ts +576 -0
  975. package/test/session/checkpoint-main-slice.test.ts +259 -0
  976. package/test/session/checkpoint-paths.test.ts +78 -0
  977. package/test/session/checkpoint-permission.test.ts +136 -0
  978. package/test/session/checkpoint-progress-reconcile.test.ts +219 -0
  979. package/test/session/checkpoint-rebuild-unify.test.ts +143 -0
  980. package/test/session/checkpoint-rebuild-v3.test.ts +248 -0
  981. package/test/session/checkpoint-render-verify.test.ts +512 -0
  982. package/test/session/checkpoint-retry.test.ts +150 -0
  983. package/test/session/checkpoint-splitover-integration.test.ts +533 -0
  984. package/test/session/checkpoint-templates.test.ts +51 -0
  985. package/test/session/checkpoint-thresholds.test.ts +120 -0
  986. package/test/session/checkpoint-validator.test.ts +189 -0
  987. package/test/session/classify-integration.test.ts +476 -0
  988. package/test/session/classify.test.ts +335 -0
  989. package/test/session/compaction-agent-scope.test.ts +164 -0
  990. package/test/session/context-inheritance.test.ts +46 -0
  991. package/test/session/fork-prefix-invariant.test.ts +116 -0
  992. package/test/session/goal.test.ts +106 -0
  993. package/test/session/instruction.test.ts +387 -0
  994. package/test/session/invalid-output-continuation.test.ts +150 -0
  995. package/test/session/last-message-info.test.ts +47 -0
  996. package/test/session/length-tool-safety.test.ts +121 -0
  997. package/test/session/llm-request-prefix.test.ts +197 -0
  998. package/test/session/llm-retry.test.ts +59 -0
  999. package/test/session/llm-system-prompt.test.ts +479 -0
  1000. package/test/session/llm.test.ts +1272 -0
  1001. package/test/session/main-lifecycle.test.ts +51 -0
  1002. package/test/session/main-runloop-history-invariant.test.ts +182 -0
  1003. package/test/session/max-mode-econnreset.test.ts +229 -0
  1004. package/test/session/max-mode.test.ts +54 -0
  1005. package/test/session/message-v2-filter.test.ts +197 -0
  1006. package/test/session/message-v2.test.ts +1119 -0
  1007. package/test/session/messages-default-main.test.ts +105 -0
  1008. package/test/session/messages-pagination.test.ts +888 -0
  1009. package/test/session/overflow.test.ts +576 -0
  1010. package/test/session/processor-effect.test.ts +853 -0
  1011. package/test/session/prompt-effect.test.ts +1574 -0
  1012. package/test/session/prompt-rebuild-loop.test.ts +108 -0
  1013. package/test/session/prompt-rebuild-reset.test.ts +67 -0
  1014. package/test/session/prompt-sweep.test.ts +145 -0
  1015. package/test/session/prompt-task-gate.test.ts +127 -0
  1016. package/test/session/prompt.test.ts +703 -0
  1017. package/test/session/prune-main-slice.test.ts +272 -0
  1018. package/test/session/prune-skip-system.test.ts +346 -0
  1019. package/test/session/prune.test.ts +419 -0
  1020. package/test/session/rebuild-microcompact.test.ts +318 -0
  1021. package/test/session/recall-reminder.test.ts +37 -0
  1022. package/test/session/retry.test.ts +410 -0
  1023. package/test/session/revert-compact.test.ts +639 -0
  1024. package/test/session/run-state-tuple-key.test.ts +152 -0
  1025. package/test/session/session-create-registers-main.test.ts +70 -0
  1026. package/test/session/session.test.ts +181 -0
  1027. package/test/session/snapshot-tool-race.test.ts +301 -0
  1028. package/test/session/structured-output-integration.test.ts +264 -0
  1029. package/test/session/structured-output-retry.test.ts +127 -0
  1030. package/test/session/structured-output.test.ts +397 -0
  1031. package/test/session/summary-main-slice.test.ts +170 -0
  1032. package/test/session/system.test.ts +72 -0
  1033. package/test/share/share-next.test.ts +332 -0
  1034. package/test/shell/shell.test.ts +73 -0
  1035. package/test/skill/compose-review.test.ts +141 -0
  1036. package/test/skill/discovery.test.ts +116 -0
  1037. package/test/skill/skill.test.ts +465 -0
  1038. package/test/snapshot/snapshot.test.ts +1531 -0
  1039. package/test/storage/db.test.ts +16 -0
  1040. package/test/storage/json-migration.test.ts +831 -0
  1041. package/test/storage/storage.test.ts +293 -0
  1042. package/test/sync/index.test.ts +237 -0
  1043. package/test/task/gate-state.test.ts +66 -0
  1044. package/test/task/gate.test.ts +167 -0
  1045. package/test/task/registry.test.ts +152 -0
  1046. package/test/task/state-machine.test.ts +292 -0
  1047. package/test/team/migrate-to-inbox.test.ts +124 -0
  1048. package/test/team/team.test.ts +75 -0
  1049. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  1050. package/test/tool/actor-cancel.test.ts +206 -0
  1051. package/test/tool/actor-recover.test.ts +50 -0
  1052. package/test/tool/actor-send.test.ts +200 -0
  1053. package/test/tool/actor-status.test.ts +296 -0
  1054. package/test/tool/actor-wait.test.ts +193 -0
  1055. package/test/tool/actor.shell.test.ts +250 -0
  1056. package/test/tool/actor.test.ts +748 -0
  1057. package/test/tool/apply_patch.test.ts +626 -0
  1058. package/test/tool/bash.test.ts +1195 -0
  1059. package/test/tool/describe-workflow.test.ts +12 -0
  1060. package/test/tool/edit.test.ts +691 -0
  1061. package/test/tool/external-directory.test.ts +207 -0
  1062. package/test/tool/fixtures/large-image.png +0 -0
  1063. package/test/tool/fixtures/models-api.json +65179 -0
  1064. package/test/tool/glob.test.ts +81 -0
  1065. package/test/tool/grep.test.ts +114 -0
  1066. package/test/tool/history.test.ts +144 -0
  1067. package/test/tool/invocation-style.test.ts +30 -0
  1068. package/test/tool/memory-edit-ask-skip.test.ts +62 -0
  1069. package/test/tool/memory-path-guard.test.ts +594 -0
  1070. package/test/tool/memory.test.ts +71 -0
  1071. package/test/tool/question.test.ts +167 -0
  1072. package/test/tool/read.test.ts +483 -0
  1073. package/test/tool/registry-invocation-style.test.ts +121 -0
  1074. package/test/tool/registry.test.ts +164 -0
  1075. package/test/tool/shell-tokenize.test.ts +273 -0
  1076. package/test/tool/shell-wrap-missing-script.test.ts +128 -0
  1077. package/test/tool/shell-wrap.test.ts +257 -0
  1078. package/test/tool/skill.test.ts +99 -0
  1079. package/test/tool/task-recover.test.ts +36 -0
  1080. package/test/tool/task.shell.test.ts +234 -0
  1081. package/test/tool/task.test.ts +296 -0
  1082. package/test/tool/tool-def-shell-shape.test.ts +23 -0
  1083. package/test/tool/tool-define.test.ts +59 -0
  1084. package/test/tool/truncation.test.ts +253 -0
  1085. package/test/tool/webfetch.test.ts +103 -0
  1086. package/test/tool/whitelist.test.ts +373 -0
  1087. package/test/tool/write.test.ts +244 -0
  1088. package/test/util/data-url.test.ts +14 -0
  1089. package/test/util/effect-zod.test.ts +869 -0
  1090. package/test/util/error.test.ts +38 -0
  1091. package/test/util/filesystem.test.ts +656 -0
  1092. package/test/util/format.test.ts +59 -0
  1093. package/test/util/glob.test.ts +164 -0
  1094. package/test/util/iife.test.ts +36 -0
  1095. package/test/util/lazy.test.ts +50 -0
  1096. package/test/util/lock.test.ts +72 -0
  1097. package/test/util/log.test.ts +44 -0
  1098. package/test/util/module.test.ts +59 -0
  1099. package/test/util/process.test.ts +128 -0
  1100. package/test/util/timeout.test.ts +21 -0
  1101. package/test/util/which.test.ts +100 -0
  1102. package/test/util/wildcard.test.ts +90 -0
  1103. package/test/workflow/builtin.test.ts +22 -0
  1104. package/test/workflow/deep-research-cluster.test.ts +47 -0
  1105. package/test/workflow/lib.ts +243 -0
  1106. package/test/workflow/meta.test.ts +142 -0
  1107. package/test/workflow/model-routing.test.ts +68 -0
  1108. package/test/workflow/persistence.test.ts +229 -0
  1109. package/test/workflow/resolve.test.ts +37 -0
  1110. package/test/workflow/runtime-nested.test.ts +419 -0
  1111. package/test/workflow/runtime-worktree.test.ts +261 -0
  1112. package/test/workflow/runtime.test.ts +1078 -0
  1113. package/test/workflow/sandbox.test.ts +259 -0
  1114. package/test/workflow/tool.test.ts +473 -0
  1115. package/test/workflow/verify-wow.test.ts +144 -0
  1116. package/test/workflow/workspace.test.ts +88 -0
  1117. package/test/workspace/workspace-restore.test.ts +281 -0
  1118. package/test/worktree/index.test.ts +30 -0
  1119. package/tsconfig.json +24 -0
@@ -0,0 +1,1783 @@
1
+ import {
2
+ RequestError,
3
+ type Agent as ACPAgent,
4
+ type AgentSideConnection,
5
+ type AuthenticateRequest,
6
+ type AuthMethod,
7
+ type CancelNotification,
8
+ type ForkSessionRequest,
9
+ type ForkSessionResponse,
10
+ type InitializeRequest,
11
+ type InitializeResponse,
12
+ type ListSessionsRequest,
13
+ type ListSessionsResponse,
14
+ type LoadSessionRequest,
15
+ type NewSessionRequest,
16
+ type PermissionOption,
17
+ type PromptRequest,
18
+ type ResumeSessionRequest,
19
+ type ResumeSessionResponse,
20
+ type Role,
21
+ type SessionInfo,
22
+ type SetSessionModelRequest,
23
+ type SessionConfigOption,
24
+ type SetSessionConfigOptionRequest,
25
+ type SetSessionConfigOptionResponse,
26
+ type SetSessionModeRequest,
27
+ type SetSessionModeResponse,
28
+ type ToolCallContent,
29
+ type ToolKind,
30
+ type Usage,
31
+ } from "@agentclientprotocol/sdk"
32
+
33
+ import { Log } from "../util"
34
+ import { pathToFileURL } from "url"
35
+ import { Filesystem } from "../util"
36
+ import { Hash } from "@tuling-ai/shared/util/hash"
37
+ import { ACPSessionManager } from "./session"
38
+ import type { ACPConfig } from "./types"
39
+ import { Provider } from "../provider"
40
+ import { ModelID, ProviderID } from "../provider/schema"
41
+ import { Agent as AgentModule } from "../agent/agent"
42
+ import { AppRuntime } from "@/effect/app-runtime"
43
+ import { Installation } from "@/installation"
44
+ import { MessageV2 } from "@/session/message-v2"
45
+ import { Config } from "@/config"
46
+ import { ConfigMCP } from "@/config/mcp"
47
+ import { z } from "zod"
48
+ import { LoadAPIKeyError } from "ai"
49
+ import type { AssistantMessage, Event, OpencodeClient, SessionMessageResponse, ToolPart } from "@tuling-ai/sdk/v2"
50
+ import { applyPatch } from "diff"
51
+ import { InstallationVersion } from "@/installation/version"
52
+
53
+ type ModeOption = { id: string; name: string; description?: string }
54
+ type ModelOption = { modelId: string; name: string }
55
+
56
+ const DEFAULT_VARIANT_VALUE = "default"
57
+
58
+ const log = Log.create({ service: "acp-agent" })
59
+
60
+ async function getContextLimit(
61
+ sdk: OpencodeClient,
62
+ providerID: ProviderID,
63
+ modelID: ModelID,
64
+ directory: string,
65
+ ): Promise<number | null> {
66
+ const providers = await sdk.config
67
+ .providers({ directory })
68
+ .then((x) => x.data?.providers ?? [])
69
+ .catch((error) => {
70
+ log.error("failed to get providers for context limit", { error })
71
+ return []
72
+ })
73
+
74
+ const provider = providers.find((p) => p.id === providerID)
75
+ const model = provider?.models[modelID]
76
+ return model?.limit.context ?? null
77
+ }
78
+
79
+ async function sendUsageUpdate(
80
+ connection: AgentSideConnection,
81
+ sdk: OpencodeClient,
82
+ sessionID: string,
83
+ directory: string,
84
+ ): Promise<void> {
85
+ const messages = await sdk.session
86
+ .messages({ sessionID, directory, agent_id: "main" }, { throwOnError: true })
87
+ .then((x) => x.data)
88
+ .catch((error) => {
89
+ log.error("failed to fetch messages for usage update", { error })
90
+ return undefined
91
+ })
92
+
93
+ if (!messages) return
94
+
95
+ const assistantMessages = messages.filter(
96
+ (m): m is { info: AssistantMessage; parts: SessionMessageResponse["parts"] } => m.info.role === "assistant",
97
+ )
98
+
99
+ const lastAssistant = assistantMessages[assistantMessages.length - 1]
100
+ if (!lastAssistant) return
101
+
102
+ const msg = lastAssistant.info
103
+ if (!msg.providerID || !msg.modelID) return
104
+ const size = await getContextLimit(sdk, ProviderID.make(msg.providerID), ModelID.make(msg.modelID), directory)
105
+
106
+ if (!size) {
107
+ // Cannot calculate usage without known context size
108
+ return
109
+ }
110
+
111
+ const used = msg.tokens.input + (msg.tokens.cache?.read ?? 0)
112
+ const totalCost = assistantMessages.reduce((sum, m) => sum + m.info.cost, 0)
113
+
114
+ await connection
115
+ .sessionUpdate({
116
+ sessionId: sessionID,
117
+ update: {
118
+ sessionUpdate: "usage_update",
119
+ used,
120
+ size,
121
+ cost: { amount: totalCost, currency: "USD" },
122
+ },
123
+ })
124
+ .catch((error) => {
125
+ log.error("failed to send usage update", { error })
126
+ })
127
+ }
128
+
129
+ export async function init({ sdk: _sdk }: { sdk: OpencodeClient }) {
130
+ return {
131
+ create: (connection: AgentSideConnection, fullConfig: ACPConfig) => {
132
+ return new Agent(connection, fullConfig)
133
+ },
134
+ }
135
+ }
136
+
137
+ export class Agent implements ACPAgent {
138
+ private connection: AgentSideConnection
139
+ private config: ACPConfig
140
+ private sdk: OpencodeClient
141
+ private sessionManager: ACPSessionManager
142
+ private eventAbort = new AbortController()
143
+ private eventStarted = false
144
+ private bashSnapshots = new Map<string, string>()
145
+ private toolStarts = new Set<string>()
146
+ private permissionQueues = new Map<string, Promise<void>>()
147
+ private permissionOptions: PermissionOption[] = [
148
+ { optionId: "once", kind: "allow_once", name: "Allow once" },
149
+ { optionId: "always", kind: "allow_always", name: "Always allow" },
150
+ { optionId: "reject", kind: "reject_once", name: "Reject" },
151
+ ]
152
+
153
+ constructor(connection: AgentSideConnection, config: ACPConfig) {
154
+ this.connection = connection
155
+ this.config = config
156
+ this.sdk = config.sdk
157
+ this.sessionManager = new ACPSessionManager(this.sdk)
158
+ this.startEventSubscription()
159
+ }
160
+
161
+ private startEventSubscription() {
162
+ if (this.eventStarted) return
163
+ this.eventStarted = true
164
+ this.runEventSubscription().catch((error) => {
165
+ if (this.eventAbort.signal.aborted) return
166
+ log.error("event subscription failed", { error })
167
+ })
168
+ }
169
+
170
+ private async runEventSubscription() {
171
+ while (true) {
172
+ if (this.eventAbort.signal.aborted) return
173
+ const events = await this.sdk.global.event({
174
+ signal: this.eventAbort.signal,
175
+ })
176
+ for await (const event of events.stream) {
177
+ if (this.eventAbort.signal.aborted) return
178
+ const payload = event?.payload
179
+ if (!payload) continue
180
+ await this.handleEvent(payload as Event).catch((error) => {
181
+ log.error("failed to handle event", { error, type: payload.type })
182
+ })
183
+ }
184
+ }
185
+ }
186
+
187
+ private async handleEvent(event: Event) {
188
+ switch (event.type) {
189
+ case "permission.asked": {
190
+ const permission = event.properties
191
+ const session = this.sessionManager.tryGet(permission.sessionID)
192
+ if (!session) return
193
+
194
+ const prev = this.permissionQueues.get(permission.sessionID) ?? Promise.resolve()
195
+ const next = prev
196
+ .then(async () => {
197
+ const directory = session.cwd
198
+
199
+ const res = await this.connection
200
+ .requestPermission({
201
+ sessionId: permission.sessionID,
202
+ toolCall: {
203
+ toolCallId: permission.tool?.callID ?? permission.id,
204
+ status: "pending",
205
+ title: permission.permission,
206
+ rawInput: permission.metadata,
207
+ kind: toToolKind(permission.permission),
208
+ locations: toLocations(permission.permission, permission.metadata),
209
+ },
210
+ options: this.permissionOptions,
211
+ })
212
+ .catch(async (error) => {
213
+ log.error("failed to request permission from ACP", {
214
+ error,
215
+ permissionID: permission.id,
216
+ sessionID: permission.sessionID,
217
+ })
218
+ await this.sdk.permission.reply({
219
+ requestID: permission.id,
220
+ reply: "reject",
221
+ directory,
222
+ })
223
+ return undefined
224
+ })
225
+
226
+ if (!res) return
227
+ if (res.outcome.outcome !== "selected") {
228
+ await this.sdk.permission.reply({
229
+ requestID: permission.id,
230
+ reply: "reject",
231
+ directory,
232
+ })
233
+ return
234
+ }
235
+
236
+ if (res.outcome.optionId !== "reject" && permission.permission == "edit") {
237
+ const metadata = permission.metadata || {}
238
+ const filepath = typeof metadata["filepath"] === "string" ? metadata["filepath"] : ""
239
+ const diff = typeof metadata["diff"] === "string" ? metadata["diff"] : ""
240
+ const content = (await Filesystem.exists(filepath)) ? await Filesystem.readText(filepath) : ""
241
+ const newContent = getNewContent(content, diff)
242
+
243
+ if (newContent) {
244
+ void this.connection.writeTextFile({
245
+ sessionId: session.id,
246
+ path: filepath,
247
+ content: newContent,
248
+ })
249
+ }
250
+ }
251
+
252
+ await this.sdk.permission.reply({
253
+ requestID: permission.id,
254
+ reply: res.outcome.optionId as "once" | "always" | "reject",
255
+ directory,
256
+ })
257
+ })
258
+ .catch((error) => {
259
+ log.error("failed to handle permission", { error, permissionID: permission.id })
260
+ })
261
+ .finally(() => {
262
+ if (this.permissionQueues.get(permission.sessionID) === next) {
263
+ this.permissionQueues.delete(permission.sessionID)
264
+ }
265
+ })
266
+ this.permissionQueues.set(permission.sessionID, next)
267
+ return
268
+ }
269
+
270
+ case "message.part.updated": {
271
+ log.info("message part updated", { event: event.properties })
272
+ const props = event.properties
273
+ const part = props.part
274
+ const session = this.sessionManager.tryGet(part.sessionID)
275
+ if (!session) return
276
+ const sessionId = session.id
277
+
278
+ if (part.type === "tool") {
279
+ await this.toolStart(sessionId, part)
280
+
281
+ switch (part.state.status) {
282
+ case "pending":
283
+ this.bashSnapshots.delete(part.callID)
284
+ return
285
+
286
+ case "running":
287
+ const output = this.bashOutput(part)
288
+ const content: ToolCallContent[] = []
289
+ if (output) {
290
+ const hash = Hash.fast(output)
291
+ if (part.tool === "bash") {
292
+ if (this.bashSnapshots.get(part.callID) === hash) {
293
+ await this.connection
294
+ .sessionUpdate({
295
+ sessionId,
296
+ update: {
297
+ sessionUpdate: "tool_call_update",
298
+ toolCallId: part.callID,
299
+ status: "in_progress",
300
+ kind: toToolKind(part.tool),
301
+ title: part.tool,
302
+ locations: toLocations(part.tool, part.state.input),
303
+ rawInput: part.state.input,
304
+ },
305
+ })
306
+ .catch((error) => {
307
+ log.error("failed to send tool in_progress to ACP", { error })
308
+ })
309
+ return
310
+ }
311
+ this.bashSnapshots.set(part.callID, hash)
312
+ }
313
+ content.push({
314
+ type: "content",
315
+ content: {
316
+ type: "text",
317
+ text: output,
318
+ },
319
+ })
320
+ }
321
+ await this.connection
322
+ .sessionUpdate({
323
+ sessionId,
324
+ update: {
325
+ sessionUpdate: "tool_call_update",
326
+ toolCallId: part.callID,
327
+ status: "in_progress",
328
+ kind: toToolKind(part.tool),
329
+ title: part.tool,
330
+ locations: toLocations(part.tool, part.state.input),
331
+ rawInput: part.state.input,
332
+ ...(content.length > 0 && { content }),
333
+ },
334
+ })
335
+ .catch((error) => {
336
+ log.error("failed to send tool in_progress to ACP", { error })
337
+ })
338
+ return
339
+
340
+ case "completed": {
341
+ this.toolStarts.delete(part.callID)
342
+ this.bashSnapshots.delete(part.callID)
343
+ const kind = toToolKind(part.tool)
344
+ const content: ToolCallContent[] = [
345
+ {
346
+ type: "content",
347
+ content: {
348
+ type: "text",
349
+ text: part.state.output,
350
+ },
351
+ },
352
+ ]
353
+
354
+ if (kind === "edit") {
355
+ const input = part.state.input
356
+ const filePath = typeof input["filePath"] === "string" ? input["filePath"] : ""
357
+ const oldText = typeof input["oldString"] === "string" ? input["oldString"] : ""
358
+ const newText =
359
+ typeof input["newString"] === "string"
360
+ ? input["newString"]
361
+ : typeof input["content"] === "string"
362
+ ? input["content"]
363
+ : ""
364
+ content.push({
365
+ type: "diff",
366
+ path: filePath,
367
+ oldText,
368
+ newText,
369
+ })
370
+ }
371
+
372
+ await this.connection
373
+ .sessionUpdate({
374
+ sessionId,
375
+ update: {
376
+ sessionUpdate: "tool_call_update",
377
+ toolCallId: part.callID,
378
+ status: "completed",
379
+ kind,
380
+ content,
381
+ title: part.state.title,
382
+ rawInput: part.state.input,
383
+ rawOutput: {
384
+ output: part.state.output,
385
+ metadata: part.state.metadata,
386
+ },
387
+ },
388
+ })
389
+ .catch((error) => {
390
+ log.error("failed to send tool completed to ACP", { error })
391
+ })
392
+ return
393
+ }
394
+ case "error":
395
+ this.toolStarts.delete(part.callID)
396
+ this.bashSnapshots.delete(part.callID)
397
+ await this.connection
398
+ .sessionUpdate({
399
+ sessionId,
400
+ update: {
401
+ sessionUpdate: "tool_call_update",
402
+ toolCallId: part.callID,
403
+ status: "failed",
404
+ kind: toToolKind(part.tool),
405
+ title: part.tool,
406
+ rawInput: part.state.input,
407
+ content: [
408
+ {
409
+ type: "content",
410
+ content: {
411
+ type: "text",
412
+ text: part.state.error,
413
+ },
414
+ },
415
+ ],
416
+ rawOutput: {
417
+ error: part.state.error,
418
+ metadata: part.state.metadata,
419
+ },
420
+ },
421
+ })
422
+ .catch((error) => {
423
+ log.error("failed to send tool error to ACP", { error })
424
+ })
425
+ return
426
+ }
427
+ }
428
+
429
+ // ACP clients already know the prompt they just submitted, so replaying
430
+ // live user parts duplicates the message. We still replay user history in
431
+ // loadSession() and forkSession() via processMessage().
432
+ if (part.type !== "text" && part.type !== "file") return
433
+
434
+ return
435
+ }
436
+
437
+ case "message.part.delta": {
438
+ const props = event.properties
439
+ const session = this.sessionManager.tryGet(props.sessionID)
440
+ if (!session) return
441
+ const sessionId = session.id
442
+
443
+ const message = await this.sdk.session
444
+ .message(
445
+ {
446
+ sessionID: props.sessionID,
447
+ messageID: props.messageID,
448
+ directory: session.cwd,
449
+ },
450
+ { throwOnError: true },
451
+ )
452
+ .then((x) => x.data)
453
+ .catch((error) => {
454
+ log.error("unexpected error when fetching message", { error })
455
+ return undefined
456
+ })
457
+
458
+ if (!message || message.info.role !== "assistant") return
459
+
460
+ const part = message.parts.find((p) => p.id === props.partID)
461
+ if (!part) return
462
+
463
+ if (part.type === "text" && props.field === "text" && part.ignored !== true) {
464
+ await this.connection
465
+ .sessionUpdate({
466
+ sessionId,
467
+ update: {
468
+ sessionUpdate: "agent_message_chunk",
469
+ messageId: props.messageID,
470
+ content: {
471
+ type: "text",
472
+ text: props.delta,
473
+ },
474
+ },
475
+ })
476
+ .catch((error) => {
477
+ log.error("failed to send text delta to ACP", { error })
478
+ })
479
+ return
480
+ }
481
+
482
+ if (part.type === "reasoning" && props.field === "text") {
483
+ await this.connection
484
+ .sessionUpdate({
485
+ sessionId,
486
+ update: {
487
+ sessionUpdate: "agent_thought_chunk",
488
+ messageId: props.messageID,
489
+ content: {
490
+ type: "text",
491
+ text: props.delta,
492
+ },
493
+ },
494
+ })
495
+ .catch((error) => {
496
+ log.error("failed to send reasoning delta to ACP", { error })
497
+ })
498
+ }
499
+ return
500
+ }
501
+ }
502
+ }
503
+
504
+ async initialize(params: InitializeRequest): Promise<InitializeResponse> {
505
+ log.info("initialize", { protocolVersion: params.protocolVersion })
506
+
507
+ const authMethod: AuthMethod = {
508
+ description: "Run `opencode auth login` in the terminal",
509
+ name: "Login with opencode",
510
+ id: "tulingcode-login",
511
+ }
512
+
513
+ // If client supports terminal-auth capability, use that instead.
514
+ if (params.clientCapabilities?._meta?.["terminal-auth"] === true) {
515
+ authMethod._meta = {
516
+ "terminal-auth": {
517
+ command: "tulingcode",
518
+ args: ["auth", "login"],
519
+ label: "TulingCode Login",
520
+ },
521
+ }
522
+ }
523
+
524
+ return {
525
+ protocolVersion: 1,
526
+ agentCapabilities: {
527
+ loadSession: true,
528
+ mcpCapabilities: {
529
+ http: true,
530
+ sse: true,
531
+ },
532
+ promptCapabilities: {
533
+ embeddedContext: true,
534
+ image: true,
535
+ },
536
+ sessionCapabilities: {
537
+ fork: {},
538
+ list: {},
539
+ resume: {},
540
+ },
541
+ },
542
+ authMethods: [authMethod],
543
+ agentInfo: {
544
+ name: "TulingCode",
545
+ version: InstallationVersion,
546
+ },
547
+ }
548
+ }
549
+
550
+ async authenticate(_params: AuthenticateRequest) {
551
+ throw new Error("Authentication not implemented")
552
+ }
553
+
554
+ async newSession(params: NewSessionRequest) {
555
+ const directory = params.cwd
556
+ try {
557
+ const model = await defaultModel(this.config, directory)
558
+
559
+ // Store ACP session state
560
+ const state = await this.sessionManager.create(params.cwd, params.mcpServers, model)
561
+ const sessionId = state.id
562
+
563
+ log.info("creating_session", { sessionId, mcpServers: params.mcpServers.length })
564
+
565
+ const load = await this.loadSessionMode({
566
+ cwd: directory,
567
+ mcpServers: params.mcpServers,
568
+ sessionId,
569
+ })
570
+
571
+ return {
572
+ sessionId,
573
+ configOptions: load.configOptions,
574
+ models: load.models,
575
+ modes: load.modes,
576
+ _meta: load._meta,
577
+ }
578
+ } catch (e) {
579
+ const error = MessageV2.fromError(e, {
580
+ providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
581
+ })
582
+ if (LoadAPIKeyError.isInstance(error)) {
583
+ throw RequestError.authRequired()
584
+ }
585
+ throw e
586
+ }
587
+ }
588
+
589
+ async loadSession(params: LoadSessionRequest) {
590
+ const directory = params.cwd
591
+ const sessionId = params.sessionId
592
+
593
+ try {
594
+ const model = await defaultModel(this.config, directory)
595
+
596
+ // Store ACP session state
597
+ await this.sessionManager.load(sessionId, params.cwd, params.mcpServers, model)
598
+
599
+ log.info("load_session", { sessionId, mcpServers: params.mcpServers.length })
600
+
601
+ const result = await this.loadSessionMode({
602
+ cwd: directory,
603
+ mcpServers: params.mcpServers,
604
+ sessionId,
605
+ })
606
+
607
+ // Replay session history
608
+ const messages = await this.sdk.session
609
+ .messages(
610
+ {
611
+ sessionID: sessionId,
612
+ directory,
613
+ agent_id: "main",
614
+ },
615
+ { throwOnError: true },
616
+ )
617
+ .then((x) => x.data)
618
+ .catch((err) => {
619
+ log.error("unexpected error when fetching message", { error: err })
620
+ return undefined
621
+ })
622
+
623
+ const lastUser = messages?.findLast((m) => m.info.role === "user")?.info
624
+ if (lastUser?.role === "user") {
625
+ result.models.currentModelId = `${lastUser.model.providerID}/${lastUser.model.modelID}`
626
+ this.sessionManager.setModel(sessionId, {
627
+ providerID: ProviderID.make(lastUser.model.providerID),
628
+ modelID: ModelID.make(lastUser.model.modelID),
629
+ })
630
+ if (result.modes?.availableModes.some((m) => m.id === lastUser.agent)) {
631
+ result.modes.currentModeId = lastUser.agent
632
+ this.sessionManager.setMode(sessionId, lastUser.agent)
633
+ }
634
+ result.configOptions = buildConfigOptions({
635
+ currentModelId: result.models.currentModelId,
636
+ availableModels: result.models.availableModels,
637
+ modes: result.modes,
638
+ })
639
+ }
640
+
641
+ for (const msg of messages ?? []) {
642
+ log.debug("replay message", msg)
643
+ await this.processMessage(msg)
644
+ }
645
+
646
+ await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
647
+
648
+ return result
649
+ } catch (e) {
650
+ const error = MessageV2.fromError(e, {
651
+ providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
652
+ })
653
+ if (LoadAPIKeyError.isInstance(error)) {
654
+ throw RequestError.authRequired()
655
+ }
656
+ throw e
657
+ }
658
+ }
659
+
660
+ async listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse> {
661
+ try {
662
+ const cursor = params.cursor ? Number(params.cursor) : undefined
663
+ const limit = 100
664
+
665
+ const sessions = await this.sdk.session
666
+ .list(
667
+ {
668
+ directory: params.cwd ?? undefined,
669
+ roots: true,
670
+ },
671
+ { throwOnError: true },
672
+ )
673
+ .then((x) => x.data ?? [])
674
+
675
+ const sorted = sessions.toSorted((a, b) => b.time.updated - a.time.updated)
676
+ const filtered = cursor ? sorted.filter((s) => s.time.updated < cursor) : sorted
677
+ const page = filtered.slice(0, limit)
678
+
679
+ const entries: SessionInfo[] = page.map((session) => ({
680
+ sessionId: session.id,
681
+ cwd: session.directory,
682
+ title: session.title,
683
+ updatedAt: new Date(session.time.updated).toISOString(),
684
+ }))
685
+
686
+ const last = page[page.length - 1]
687
+ const next = filtered.length > limit && last ? String(last.time.updated) : undefined
688
+
689
+ const response: ListSessionsResponse = {
690
+ sessions: entries,
691
+ }
692
+ if (next) response.nextCursor = next
693
+ return response
694
+ } catch (e) {
695
+ const error = MessageV2.fromError(e, {
696
+ providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
697
+ })
698
+ if (LoadAPIKeyError.isInstance(error)) {
699
+ throw RequestError.authRequired()
700
+ }
701
+ throw e
702
+ }
703
+ }
704
+
705
+ async unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse> {
706
+ const directory = params.cwd
707
+ const mcpServers = params.mcpServers ?? []
708
+
709
+ try {
710
+ const model = await defaultModel(this.config, directory)
711
+
712
+ const forked = await this.sdk.session
713
+ .fork(
714
+ {
715
+ sessionID: params.sessionId,
716
+ directory,
717
+ },
718
+ { throwOnError: true },
719
+ )
720
+ .then((x) => x.data)
721
+
722
+ if (!forked) {
723
+ throw new Error("Fork session returned no data")
724
+ }
725
+
726
+ const sessionId = forked.id
727
+ await this.sessionManager.load(sessionId, directory, mcpServers, model)
728
+
729
+ log.info("fork_session", { sessionId, mcpServers: mcpServers.length })
730
+
731
+ const mode = await this.loadSessionMode({
732
+ cwd: directory,
733
+ mcpServers,
734
+ sessionId,
735
+ })
736
+
737
+ const messages = await this.sdk.session
738
+ .messages(
739
+ {
740
+ sessionID: sessionId,
741
+ directory,
742
+ agent_id: "main",
743
+ },
744
+ { throwOnError: true },
745
+ )
746
+ .then((x) => x.data)
747
+ .catch((err) => {
748
+ log.error("unexpected error when fetching message", { error: err })
749
+ return undefined
750
+ })
751
+
752
+ for (const msg of messages ?? []) {
753
+ log.debug("replay message", msg)
754
+ await this.processMessage(msg)
755
+ }
756
+
757
+ await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
758
+
759
+ return mode
760
+ } catch (e) {
761
+ const error = MessageV2.fromError(e, {
762
+ providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
763
+ })
764
+ if (LoadAPIKeyError.isInstance(error)) {
765
+ throw RequestError.authRequired()
766
+ }
767
+ throw e
768
+ }
769
+ }
770
+
771
+ async unstable_resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse> {
772
+ const directory = params.cwd
773
+ const sessionId = params.sessionId
774
+ const mcpServers = params.mcpServers ?? []
775
+
776
+ try {
777
+ const model = await defaultModel(this.config, directory)
778
+ await this.sessionManager.load(sessionId, directory, mcpServers, model)
779
+
780
+ log.info("resume_session", { sessionId, mcpServers: mcpServers.length })
781
+
782
+ const result = await this.loadSessionMode({
783
+ cwd: directory,
784
+ mcpServers,
785
+ sessionId,
786
+ })
787
+
788
+ await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
789
+
790
+ return result
791
+ } catch (e) {
792
+ const error = MessageV2.fromError(e, {
793
+ providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
794
+ })
795
+ if (LoadAPIKeyError.isInstance(error)) {
796
+ throw RequestError.authRequired()
797
+ }
798
+ throw e
799
+ }
800
+ }
801
+
802
+ private async processMessage(message: SessionMessageResponse) {
803
+ log.debug("process message", message)
804
+ if (message.info.role !== "assistant" && message.info.role !== "user") return
805
+ const sessionId = message.info.sessionID
806
+
807
+ for (const part of message.parts) {
808
+ if (part.type === "tool") {
809
+ await this.toolStart(sessionId, part)
810
+ switch (part.state.status) {
811
+ case "pending":
812
+ this.bashSnapshots.delete(part.callID)
813
+ break
814
+ case "running":
815
+ const output = this.bashOutput(part)
816
+ const runningContent: ToolCallContent[] = []
817
+ if (output) {
818
+ runningContent.push({
819
+ type: "content",
820
+ content: {
821
+ type: "text",
822
+ text: output,
823
+ },
824
+ })
825
+ }
826
+ await this.connection
827
+ .sessionUpdate({
828
+ sessionId,
829
+ update: {
830
+ sessionUpdate: "tool_call_update",
831
+ toolCallId: part.callID,
832
+ status: "in_progress",
833
+ kind: toToolKind(part.tool),
834
+ title: part.tool,
835
+ locations: toLocations(part.tool, part.state.input),
836
+ rawInput: part.state.input,
837
+ ...(runningContent.length > 0 && { content: runningContent }),
838
+ },
839
+ })
840
+ .catch((err) => {
841
+ log.error("failed to send tool in_progress to ACP", { error: err })
842
+ })
843
+ break
844
+ case "completed":
845
+ this.toolStarts.delete(part.callID)
846
+ this.bashSnapshots.delete(part.callID)
847
+ const kind = toToolKind(part.tool)
848
+ const content: ToolCallContent[] = [
849
+ {
850
+ type: "content",
851
+ content: {
852
+ type: "text",
853
+ text: part.state.output,
854
+ },
855
+ },
856
+ ]
857
+
858
+ if (kind === "edit") {
859
+ const input = part.state.input
860
+ const filePath = typeof input["filePath"] === "string" ? input["filePath"] : ""
861
+ const oldText = typeof input["oldString"] === "string" ? input["oldString"] : ""
862
+ const newText =
863
+ typeof input["newString"] === "string"
864
+ ? input["newString"]
865
+ : typeof input["content"] === "string"
866
+ ? input["content"]
867
+ : ""
868
+ content.push({
869
+ type: "diff",
870
+ path: filePath,
871
+ oldText,
872
+ newText,
873
+ })
874
+ }
875
+
876
+ await this.connection
877
+ .sessionUpdate({
878
+ sessionId,
879
+ update: {
880
+ sessionUpdate: "tool_call_update",
881
+ toolCallId: part.callID,
882
+ status: "completed",
883
+ kind,
884
+ content,
885
+ title: part.state.title,
886
+ rawInput: part.state.input,
887
+ rawOutput: {
888
+ output: part.state.output,
889
+ metadata: part.state.metadata,
890
+ },
891
+ },
892
+ })
893
+ .catch((err) => {
894
+ log.error("failed to send tool completed to ACP", { error: err })
895
+ })
896
+ break
897
+ case "error":
898
+ this.toolStarts.delete(part.callID)
899
+ this.bashSnapshots.delete(part.callID)
900
+ await this.connection
901
+ .sessionUpdate({
902
+ sessionId,
903
+ update: {
904
+ sessionUpdate: "tool_call_update",
905
+ toolCallId: part.callID,
906
+ status: "failed",
907
+ kind: toToolKind(part.tool),
908
+ title: part.tool,
909
+ rawInput: part.state.input,
910
+ content: [
911
+ {
912
+ type: "content",
913
+ content: {
914
+ type: "text",
915
+ text: part.state.error,
916
+ },
917
+ },
918
+ ],
919
+ rawOutput: {
920
+ error: part.state.error,
921
+ metadata: part.state.metadata,
922
+ },
923
+ },
924
+ })
925
+ .catch((err) => {
926
+ log.error("failed to send tool error to ACP", { error: err })
927
+ })
928
+ break
929
+ }
930
+ } else if (part.type === "text") {
931
+ if (part.text) {
932
+ const audience: Role[] | undefined = part.synthetic ? ["assistant"] : part.ignored ? ["user"] : undefined
933
+ await this.connection
934
+ .sessionUpdate({
935
+ sessionId,
936
+ update: {
937
+ sessionUpdate: message.info.role === "user" ? "user_message_chunk" : "agent_message_chunk",
938
+ messageId: message.info.id,
939
+ content: {
940
+ type: "text",
941
+ text: part.text,
942
+ ...(audience && { annotations: { audience } }),
943
+ },
944
+ },
945
+ })
946
+ .catch((err) => {
947
+ log.error("failed to send text to ACP", { error: err })
948
+ })
949
+ }
950
+ } else if (part.type === "file") {
951
+ // Replay file attachments as appropriate ACP content blocks.
952
+ // OpenCode stores files internally as { type: "file", url, filename, mime }.
953
+ // We convert these back to ACP blocks based on the URL scheme and MIME type:
954
+ // - file:// URLs → resource_link
955
+ // - data: URLs with image/* → image block
956
+ // - data: URLs with text/* or application/json → resource with text
957
+ // - data: URLs with other types → resource with blob
958
+ const url = part.url
959
+ const filename = part.filename ?? "file"
960
+ const mime = part.mime || "application/octet-stream"
961
+ const messageChunk = message.info.role === "user" ? "user_message_chunk" : "agent_message_chunk"
962
+
963
+ if (url.startsWith("file://")) {
964
+ // Local file reference - send as resource_link
965
+ await this.connection
966
+ .sessionUpdate({
967
+ sessionId,
968
+ update: {
969
+ sessionUpdate: messageChunk,
970
+ messageId: message.info.id,
971
+ content: { type: "resource_link", uri: url, name: filename, mimeType: mime },
972
+ },
973
+ })
974
+ .catch((err) => {
975
+ log.error("failed to send resource_link to ACP", { error: err })
976
+ })
977
+ } else if (url.startsWith("data:")) {
978
+ // Embedded content - parse data URL and send as appropriate block type
979
+ const base64Match = url.match(/^data:([^;]+);base64,(.*)$/)
980
+ const dataMime = base64Match?.[1]
981
+ const base64Data = base64Match?.[2] ?? ""
982
+
983
+ const effectiveMime = dataMime || mime
984
+
985
+ if (effectiveMime.startsWith("image/")) {
986
+ // Image - send as image block
987
+ await this.connection
988
+ .sessionUpdate({
989
+ sessionId,
990
+ update: {
991
+ sessionUpdate: messageChunk,
992
+ messageId: message.info.id,
993
+ content: {
994
+ type: "image",
995
+ mimeType: effectiveMime,
996
+ data: base64Data,
997
+ uri: pathToFileURL(filename).href,
998
+ },
999
+ },
1000
+ })
1001
+ .catch((err) => {
1002
+ log.error("failed to send image to ACP", { error: err })
1003
+ })
1004
+ } else {
1005
+ // Non-image: text types get decoded, binary types stay as blob
1006
+ const isText = effectiveMime.startsWith("text/") || effectiveMime === "application/json"
1007
+ const fileUri = pathToFileURL(filename).href
1008
+ const resource = isText
1009
+ ? {
1010
+ uri: fileUri,
1011
+ mimeType: effectiveMime,
1012
+ text: Buffer.from(base64Data, "base64").toString("utf-8"),
1013
+ }
1014
+ : { uri: fileUri, mimeType: effectiveMime, blob: base64Data }
1015
+
1016
+ await this.connection
1017
+ .sessionUpdate({
1018
+ sessionId,
1019
+ update: {
1020
+ sessionUpdate: messageChunk,
1021
+ messageId: message.info.id,
1022
+ content: { type: "resource", resource },
1023
+ },
1024
+ })
1025
+ .catch((err) => {
1026
+ log.error("failed to send resource to ACP", { error: err })
1027
+ })
1028
+ }
1029
+ }
1030
+ // URLs that don't match file:// or data: are skipped (unsupported)
1031
+ } else if (part.type === "reasoning") {
1032
+ if (part.text) {
1033
+ await this.connection
1034
+ .sessionUpdate({
1035
+ sessionId,
1036
+ update: {
1037
+ sessionUpdate: "agent_thought_chunk",
1038
+ messageId: message.info.id,
1039
+ content: {
1040
+ type: "text",
1041
+ text: part.text,
1042
+ },
1043
+ },
1044
+ })
1045
+ .catch((err) => {
1046
+ log.error("failed to send reasoning to ACP", { error: err })
1047
+ })
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+
1053
+ private bashOutput(part: ToolPart) {
1054
+ if (part.tool !== "bash") return
1055
+ if (!("metadata" in part.state) || !part.state.metadata || typeof part.state.metadata !== "object") return
1056
+ const output = part.state.metadata["output"]
1057
+ if (typeof output !== "string") return
1058
+ return output
1059
+ }
1060
+
1061
+ private async toolStart(sessionId: string, part: ToolPart) {
1062
+ if (this.toolStarts.has(part.callID)) return
1063
+ this.toolStarts.add(part.callID)
1064
+ await this.connection
1065
+ .sessionUpdate({
1066
+ sessionId,
1067
+ update: {
1068
+ sessionUpdate: "tool_call",
1069
+ toolCallId: part.callID,
1070
+ title: part.tool,
1071
+ kind: toToolKind(part.tool),
1072
+ status: "pending",
1073
+ locations: [],
1074
+ rawInput: {},
1075
+ },
1076
+ })
1077
+ .catch((error) => {
1078
+ log.error("failed to send tool pending to ACP", { error })
1079
+ })
1080
+ }
1081
+
1082
+ private async loadAvailableModes(directory: string): Promise<ModeOption[]> {
1083
+ const agents = await this.config.sdk.app
1084
+ .agents(
1085
+ {
1086
+ directory,
1087
+ },
1088
+ { throwOnError: true },
1089
+ )
1090
+ .then((resp) => resp.data!)
1091
+
1092
+ return agents
1093
+ .filter((agent) => agent.mode !== "subagent" && !agent.hidden)
1094
+ .map((agent) => ({
1095
+ id: agent.name,
1096
+ name: agent.name,
1097
+ description: agent.description,
1098
+ }))
1099
+ }
1100
+
1101
+ private async resolveModeState(
1102
+ directory: string,
1103
+ sessionId: string,
1104
+ ): Promise<{ availableModes: ModeOption[]; currentModeId?: string }> {
1105
+ const availableModes = await this.loadAvailableModes(directory)
1106
+ const currentModeId =
1107
+ this.sessionManager.get(sessionId).modeId ||
1108
+ (await (async () => {
1109
+ if (!availableModes.length) return undefined
1110
+ const defaultAgentName = await AppRuntime.runPromise(AgentModule.Service.use((svc) => svc.defaultAgent()))
1111
+ const resolvedModeId = availableModes.find((mode) => mode.name === defaultAgentName)?.id ?? availableModes[0].id
1112
+ this.sessionManager.setMode(sessionId, resolvedModeId)
1113
+ return resolvedModeId
1114
+ })())
1115
+
1116
+ return { availableModes, currentModeId }
1117
+ }
1118
+
1119
+ private async loadSessionMode(params: LoadSessionRequest) {
1120
+ const directory = params.cwd
1121
+ const model = await defaultModel(this.config, directory)
1122
+ const sessionId = params.sessionId
1123
+
1124
+ const providers = await this.sdk.config.providers({ directory }).then((x) => x.data!.providers)
1125
+ const entries = sortProvidersByName(providers)
1126
+ const availableVariants = modelVariantsFromProviders(entries, model)
1127
+ const currentVariant = this.sessionManager.getVariant(sessionId)
1128
+ if (currentVariant && !availableVariants.includes(currentVariant)) {
1129
+ this.sessionManager.setVariant(sessionId, undefined)
1130
+ }
1131
+ const availableModels = buildAvailableModels(entries, { includeVariants: true })
1132
+ const modeState = await this.resolveModeState(directory, sessionId)
1133
+ const currentModeId = modeState.currentModeId
1134
+ const modes = currentModeId
1135
+ ? {
1136
+ availableModes: modeState.availableModes,
1137
+ currentModeId,
1138
+ }
1139
+ : undefined
1140
+
1141
+ const commands = await this.config.sdk.command
1142
+ .list(
1143
+ {
1144
+ directory,
1145
+ },
1146
+ { throwOnError: true },
1147
+ )
1148
+ .then((resp) => resp.data!)
1149
+
1150
+ const availableCommands = commands.map((command) => ({
1151
+ name: command.name,
1152
+ description: command.description ?? "",
1153
+ }))
1154
+ const names = new Set(availableCommands.map((c) => c.name))
1155
+ if (!names.has("compact"))
1156
+ availableCommands.push({
1157
+ name: "compact",
1158
+ description: "summarize the session to reduce context",
1159
+ })
1160
+
1161
+ const mcpServers: Record<string, ConfigMCP.Info> = {}
1162
+ for (const server of params.mcpServers) {
1163
+ if ("type" in server) {
1164
+ mcpServers[server.name] = {
1165
+ url: server.url,
1166
+ headers: server.headers.reduce<Record<string, string>>((acc, { name, value }) => {
1167
+ acc[name] = value
1168
+ return acc
1169
+ }, {}),
1170
+ type: "remote",
1171
+ }
1172
+ } else {
1173
+ mcpServers[server.name] = {
1174
+ type: "local",
1175
+ command: [server.command, ...server.args],
1176
+ environment: server.env.reduce<Record<string, string>>((acc, { name, value }) => {
1177
+ acc[name] = value
1178
+ return acc
1179
+ }, {}),
1180
+ }
1181
+ }
1182
+ }
1183
+
1184
+ await Promise.all(
1185
+ Object.entries(mcpServers).map(async ([key, mcp]) => {
1186
+ await this.sdk.mcp
1187
+ .add(
1188
+ {
1189
+ directory,
1190
+ name: key,
1191
+ config: mcp,
1192
+ },
1193
+ { throwOnError: true },
1194
+ )
1195
+ .catch((error) => {
1196
+ log.error("failed to add mcp server", { name: key, error })
1197
+ })
1198
+ }),
1199
+ )
1200
+
1201
+ setTimeout(() => {
1202
+ void this.connection.sessionUpdate({
1203
+ sessionId,
1204
+ update: {
1205
+ sessionUpdate: "available_commands_update",
1206
+ availableCommands,
1207
+ },
1208
+ })
1209
+ }, 0)
1210
+
1211
+ return {
1212
+ sessionId,
1213
+ models: {
1214
+ currentModelId: formatModelIdWithVariant(model, currentVariant, availableVariants, true),
1215
+ availableModels,
1216
+ },
1217
+ modes,
1218
+ configOptions: buildConfigOptions({
1219
+ currentModelId: formatModelIdWithVariant(model, currentVariant, availableVariants, true),
1220
+ availableModels,
1221
+ modes,
1222
+ }),
1223
+ _meta: buildVariantMeta({
1224
+ model,
1225
+ variant: this.sessionManager.getVariant(sessionId),
1226
+ availableVariants,
1227
+ }),
1228
+ }
1229
+ }
1230
+
1231
+ async unstable_setSessionModel(params: SetSessionModelRequest) {
1232
+ const session = this.sessionManager.get(params.sessionId)
1233
+ const providers = await this.sdk.config
1234
+ .providers({ directory: session.cwd }, { throwOnError: true })
1235
+ .then((x) => x.data!.providers)
1236
+
1237
+ const selection = parseModelSelection(params.modelId, providers)
1238
+ this.sessionManager.setModel(session.id, selection.model)
1239
+ this.sessionManager.setVariant(session.id, selection.variant)
1240
+
1241
+ const entries = sortProvidersByName(providers)
1242
+ const availableVariants = modelVariantsFromProviders(entries, selection.model)
1243
+
1244
+ return {
1245
+ _meta: buildVariantMeta({
1246
+ model: selection.model,
1247
+ variant: selection.variant,
1248
+ availableVariants,
1249
+ }),
1250
+ }
1251
+ }
1252
+
1253
+ async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse | void> {
1254
+ const session = this.sessionManager.get(params.sessionId)
1255
+ const availableModes = await this.loadAvailableModes(session.cwd)
1256
+ if (!availableModes.some((mode) => mode.id === params.modeId)) {
1257
+ throw new Error(`Agent not found: ${params.modeId}`)
1258
+ }
1259
+ this.sessionManager.setMode(params.sessionId, params.modeId)
1260
+ }
1261
+
1262
+ async setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse> {
1263
+ const session = this.sessionManager.get(params.sessionId)
1264
+ const providers = await this.sdk.config
1265
+ .providers({ directory: session.cwd }, { throwOnError: true })
1266
+ .then((x) => x.data!.providers)
1267
+ const entries = sortProvidersByName(providers)
1268
+
1269
+ if (params.configId === "model") {
1270
+ if (typeof params.value !== "string") throw RequestError.invalidParams("model value must be a string")
1271
+ const selection = parseModelSelection(params.value, providers)
1272
+ this.sessionManager.setModel(session.id, selection.model)
1273
+ this.sessionManager.setVariant(session.id, selection.variant)
1274
+ } else if (params.configId === "mode") {
1275
+ if (typeof params.value !== "string") throw RequestError.invalidParams("mode value must be a string")
1276
+ const availableModes = await this.loadAvailableModes(session.cwd)
1277
+ if (!availableModes.some((mode) => mode.id === params.value)) {
1278
+ throw RequestError.invalidParams(JSON.stringify({ error: `Mode not found: ${params.value}` }))
1279
+ }
1280
+ this.sessionManager.setMode(session.id, params.value)
1281
+ } else {
1282
+ throw RequestError.invalidParams(JSON.stringify({ error: `Unknown config option: ${params.configId}` }))
1283
+ }
1284
+
1285
+ const updatedSession = this.sessionManager.get(session.id)
1286
+ const model = updatedSession.model ?? (await defaultModel(this.config, session.cwd))
1287
+ const availableVariants = modelVariantsFromProviders(entries, model)
1288
+ const currentModelId = formatModelIdWithVariant(model, updatedSession.variant, availableVariants, true)
1289
+ const availableModels = buildAvailableModels(entries, { includeVariants: true })
1290
+ const modeState = await this.resolveModeState(session.cwd, session.id)
1291
+ const modes = modeState.currentModeId
1292
+ ? { availableModes: modeState.availableModes, currentModeId: modeState.currentModeId }
1293
+ : undefined
1294
+
1295
+ return {
1296
+ configOptions: buildConfigOptions({ currentModelId, availableModels, modes }),
1297
+ }
1298
+ }
1299
+
1300
+ async prompt(params: PromptRequest) {
1301
+ const sessionID = params.sessionId
1302
+ const session = this.sessionManager.get(sessionID)
1303
+ const directory = session.cwd
1304
+
1305
+ const current = session.model
1306
+ const model = current ?? (await defaultModel(this.config, directory))
1307
+ if (!current) {
1308
+ this.sessionManager.setModel(session.id, model)
1309
+ }
1310
+ const agent = session.modeId ?? (await AppRuntime.runPromise(AgentModule.Service.use((svc) => svc.defaultAgent())))
1311
+
1312
+ const parts: Array<
1313
+ | { type: "text"; text: string; synthetic?: boolean; ignored?: boolean }
1314
+ | { type: "file"; url: string; filename: string; mime: string }
1315
+ > = []
1316
+ for (const part of params.prompt) {
1317
+ switch (part.type) {
1318
+ case "text":
1319
+ const audience = part.annotations?.audience
1320
+ const forAssistant = audience?.length === 1 && audience[0] === "assistant"
1321
+ const forUser = audience?.length === 1 && audience[0] === "user"
1322
+ parts.push({
1323
+ type: "text" as const,
1324
+ text: part.text,
1325
+ ...(forAssistant && { synthetic: true }),
1326
+ ...(forUser && { ignored: true }),
1327
+ })
1328
+ break
1329
+ case "image": {
1330
+ const parsed = parseUri(part.uri ?? "")
1331
+ const filename = parsed.type === "file" ? parsed.filename : "image"
1332
+ if (part.data) {
1333
+ parts.push({
1334
+ type: "file",
1335
+ url: `data:${part.mimeType};base64,${part.data}`,
1336
+ filename,
1337
+ mime: part.mimeType,
1338
+ })
1339
+ } else if (part.uri && part.uri.startsWith("http:")) {
1340
+ parts.push({
1341
+ type: "file",
1342
+ url: part.uri,
1343
+ filename,
1344
+ mime: part.mimeType,
1345
+ })
1346
+ }
1347
+ break
1348
+ }
1349
+
1350
+ case "resource_link":
1351
+ const parsed = parseUri(part.uri)
1352
+ // Use the name from resource_link if available
1353
+ if (part.name && parsed.type === "file") {
1354
+ parsed.filename = part.name
1355
+ }
1356
+ parts.push(parsed)
1357
+
1358
+ break
1359
+
1360
+ case "resource": {
1361
+ const resource = part.resource
1362
+ if ("text" in resource && resource.text) {
1363
+ parts.push({
1364
+ type: "text",
1365
+ text: resource.text,
1366
+ })
1367
+ } else if ("blob" in resource && resource.blob && resource.mimeType) {
1368
+ // Binary resource (PDFs, etc.): store as file part with data URL
1369
+ const parsed = parseUri(resource.uri ?? "")
1370
+ const filename = parsed.type === "file" ? parsed.filename : "file"
1371
+ parts.push({
1372
+ type: "file",
1373
+ url: `data:${resource.mimeType};base64,${resource.blob}`,
1374
+ filename,
1375
+ mime: resource.mimeType,
1376
+ })
1377
+ }
1378
+ break
1379
+ }
1380
+
1381
+ default:
1382
+ break
1383
+ }
1384
+ }
1385
+
1386
+ log.info("parts", { parts })
1387
+
1388
+ const cmd = (() => {
1389
+ const text = parts
1390
+ .filter((p): p is { type: "text"; text: string } => p.type === "text")
1391
+ .map((p) => p.text)
1392
+ .join("")
1393
+ .trim()
1394
+
1395
+ if (!text.startsWith("/")) return
1396
+
1397
+ const [name, ...rest] = text.slice(1).split(/\s+/)
1398
+ return { name, args: rest.join(" ").trim() }
1399
+ })()
1400
+
1401
+ const buildUsage = (msg: AssistantMessage): Usage => ({
1402
+ totalTokens:
1403
+ msg.tokens.input +
1404
+ msg.tokens.output +
1405
+ msg.tokens.reasoning +
1406
+ (msg.tokens.cache?.read ?? 0) +
1407
+ (msg.tokens.cache?.write ?? 0),
1408
+ inputTokens: msg.tokens.input,
1409
+ outputTokens: msg.tokens.output,
1410
+ thoughtTokens: msg.tokens.reasoning || undefined,
1411
+ cachedReadTokens: msg.tokens.cache?.read || undefined,
1412
+ cachedWriteTokens: msg.tokens.cache?.write || undefined,
1413
+ })
1414
+
1415
+ if (!cmd) {
1416
+ const response = await this.sdk.session.prompt({
1417
+ sessionID,
1418
+ model: {
1419
+ providerID: model.providerID,
1420
+ modelID: model.modelID,
1421
+ },
1422
+ variant: this.sessionManager.getVariant(sessionID),
1423
+ parts,
1424
+ agent,
1425
+ directory,
1426
+ })
1427
+ const msg = response.data?.info
1428
+
1429
+ await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
1430
+
1431
+ return {
1432
+ stopReason: "end_turn" as const,
1433
+ usage: msg ? buildUsage(msg) : undefined,
1434
+ _meta: {},
1435
+ }
1436
+ }
1437
+
1438
+ const command = await this.config.sdk.command
1439
+ .list({ directory }, { throwOnError: true })
1440
+ .then((x) => x.data!.find((c) => c.name === cmd.name))
1441
+ if (command) {
1442
+ const response = await this.sdk.session.command({
1443
+ sessionID,
1444
+ command: command.name,
1445
+ arguments: cmd.args,
1446
+ model: model.providerID + "/" + model.modelID,
1447
+ agent,
1448
+ directory,
1449
+ })
1450
+ const msg = response.data?.info
1451
+
1452
+ await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
1453
+
1454
+ return {
1455
+ stopReason: "end_turn" as const,
1456
+ usage: msg ? buildUsage(msg) : undefined,
1457
+ _meta: {},
1458
+ }
1459
+ }
1460
+
1461
+ switch (cmd.name) {
1462
+ case "compact":
1463
+ await this.config.sdk.session.summarize(
1464
+ {
1465
+ sessionID,
1466
+ directory,
1467
+ providerID: model.providerID,
1468
+ modelID: model.modelID,
1469
+ },
1470
+ { throwOnError: true },
1471
+ )
1472
+ break
1473
+ }
1474
+
1475
+ await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
1476
+
1477
+ return {
1478
+ stopReason: "end_turn" as const,
1479
+ _meta: {},
1480
+ }
1481
+ }
1482
+
1483
+ async cancel(params: CancelNotification) {
1484
+ const session = this.sessionManager.get(params.sessionId)
1485
+ await this.config.sdk.session.abort(
1486
+ {
1487
+ sessionID: params.sessionId,
1488
+ directory: session.cwd,
1489
+ },
1490
+ { throwOnError: true },
1491
+ )
1492
+ }
1493
+ }
1494
+
1495
+ function toToolKind(toolName: string): ToolKind {
1496
+ const tool = toolName.toLocaleLowerCase()
1497
+ switch (tool) {
1498
+ case "bash":
1499
+ return "execute"
1500
+ case "webfetch":
1501
+ return "fetch"
1502
+
1503
+ case "edit":
1504
+ case "patch":
1505
+ case "write":
1506
+ return "edit"
1507
+
1508
+ case "grep":
1509
+ case "glob":
1510
+ case "context7_resolve_library_id":
1511
+ case "context7_get_library_docs":
1512
+ return "search"
1513
+
1514
+ case "read":
1515
+ return "read"
1516
+
1517
+ default:
1518
+ return "other"
1519
+ }
1520
+ }
1521
+
1522
+ function toLocations(toolName: string, input: Record<string, any>): { path: string }[] {
1523
+ const tool = toolName.toLocaleLowerCase()
1524
+ switch (tool) {
1525
+ case "read":
1526
+ case "edit":
1527
+ case "write":
1528
+ return input["filePath"] ? [{ path: input["filePath"] }] : []
1529
+ case "glob":
1530
+ case "grep":
1531
+ return input["path"] ? [{ path: input["path"] }] : []
1532
+ case "bash":
1533
+ return []
1534
+ default:
1535
+ return []
1536
+ }
1537
+ }
1538
+
1539
+ async function defaultModel(config: ACPConfig, cwd?: string): Promise<{ providerID: ProviderID; modelID: ModelID }> {
1540
+ const sdk = config.sdk
1541
+ const configured = config.defaultModel
1542
+ if (configured) return configured
1543
+
1544
+ const directory = cwd ?? process.cwd()
1545
+
1546
+ const specified = await sdk.config
1547
+ .get({ directory }, { throwOnError: true })
1548
+ .then((resp) => {
1549
+ const cfg = resp.data
1550
+ if (!cfg || !cfg.model) return undefined
1551
+ return Provider.parseModel(cfg.model)
1552
+ })
1553
+ .catch((error) => {
1554
+ log.error("failed to load user config for default model", { error })
1555
+ return undefined
1556
+ })
1557
+
1558
+ const providers = await sdk.config
1559
+ .providers({ directory }, { throwOnError: true })
1560
+ .then((x) => x.data?.providers ?? [])
1561
+ .catch((error) => {
1562
+ log.error("failed to list providers for default model", { error })
1563
+ return []
1564
+ })
1565
+
1566
+ if (specified && providers.length) {
1567
+ const provider = providers.find((p) => p.id === specified.providerID)
1568
+ if (provider && provider.models[specified.modelID]) return specified
1569
+ }
1570
+
1571
+ if (specified && !providers.length) return specified
1572
+
1573
+ const tulingcodeProvider = providers.find((p) => p.id === "tulingcode")
1574
+ if (tulingcodeProvider) {
1575
+ if (tulingcodeProvider.models["big-pickle"]) {
1576
+ return { providerID: ProviderID.opencode, modelID: ModelID.make("big-pickle") }
1577
+ }
1578
+ const [best] = Provider.sort(Object.values(tulingcodeProvider.models))
1579
+ if (best) {
1580
+ return {
1581
+ providerID: ProviderID.make(best.providerID),
1582
+ modelID: ModelID.make(best.id),
1583
+ }
1584
+ }
1585
+ }
1586
+
1587
+ const models = providers.flatMap((p) => Object.values(p.models))
1588
+ const [best] = Provider.sort(models)
1589
+ if (best) {
1590
+ return {
1591
+ providerID: ProviderID.make(best.providerID),
1592
+ modelID: ModelID.make(best.id),
1593
+ }
1594
+ }
1595
+
1596
+ if (specified) return specified
1597
+
1598
+ return { providerID: ProviderID.opencode, modelID: ModelID.make("big-pickle") }
1599
+ }
1600
+
1601
+ function parseUri(
1602
+ uri: string,
1603
+ ): { type: "file"; url: string; filename: string; mime: string } | { type: "text"; text: string } {
1604
+ try {
1605
+ if (uri.startsWith("file://")) {
1606
+ const path = uri.slice(7)
1607
+ const name = path.split("/").pop() || path
1608
+ return {
1609
+ type: "file",
1610
+ url: uri,
1611
+ filename: name,
1612
+ mime: "text/plain",
1613
+ }
1614
+ }
1615
+ if (uri.startsWith("zed://")) {
1616
+ const url = new URL(uri)
1617
+ const path = url.searchParams.get("path")
1618
+ if (path) {
1619
+ const name = path.split("/").pop() || path
1620
+ return {
1621
+ type: "file",
1622
+ url: pathToFileURL(path).href,
1623
+ filename: name,
1624
+ mime: "text/plain",
1625
+ }
1626
+ }
1627
+ }
1628
+ return {
1629
+ type: "text",
1630
+ text: uri,
1631
+ }
1632
+ } catch {
1633
+ return {
1634
+ type: "text",
1635
+ text: uri,
1636
+ }
1637
+ }
1638
+ }
1639
+
1640
+ function getNewContent(fileOriginal: string, unifiedDiff: string): string | undefined {
1641
+ const result = applyPatch(fileOriginal, unifiedDiff)
1642
+ if (result === false) {
1643
+ log.error("Failed to apply unified diff (context mismatch)")
1644
+ return undefined
1645
+ }
1646
+ return result
1647
+ }
1648
+
1649
+ function sortProvidersByName<T extends { name: string }>(providers: T[]): T[] {
1650
+ return [...providers].sort((a, b) => {
1651
+ const nameA = a.name.toLowerCase()
1652
+ const nameB = b.name.toLowerCase()
1653
+ if (nameA < nameB) return -1
1654
+ if (nameA > nameB) return 1
1655
+ return 0
1656
+ })
1657
+ }
1658
+
1659
+ function modelVariantsFromProviders(
1660
+ providers: Array<{ id: string; models: Record<string, { variants?: Record<string, any> }> }>,
1661
+ model: { providerID: ProviderID; modelID: ModelID },
1662
+ ): string[] {
1663
+ const provider = providers.find((entry) => entry.id === model.providerID)
1664
+ if (!provider) return []
1665
+ const modelInfo = provider.models[model.modelID]
1666
+ if (!modelInfo?.variants) return []
1667
+ return Object.keys(modelInfo.variants)
1668
+ }
1669
+
1670
+ function buildAvailableModels(
1671
+ providers: Array<{ id: string; name: string; models: Record<string, any> }>,
1672
+ options: { includeVariants?: boolean } = {},
1673
+ ): ModelOption[] {
1674
+ const includeVariants = options.includeVariants ?? false
1675
+ return providers.flatMap((provider) => {
1676
+ const unsorted: Array<{ id: string; name: string; variants?: Record<string, any> }> = Object.values(provider.models)
1677
+ const models = Provider.sort(unsorted)
1678
+ return models.flatMap((model) => {
1679
+ const base: ModelOption = {
1680
+ modelId: `${provider.id}/${model.id}`,
1681
+ name: `${provider.name}/${model.name}`,
1682
+ }
1683
+ if (!includeVariants || !model.variants) return [base]
1684
+ const variants = Object.keys(model.variants).filter((variant) => variant !== DEFAULT_VARIANT_VALUE)
1685
+ const variantOptions = variants.map((variant) => ({
1686
+ modelId: `${provider.id}/${model.id}/${variant}`,
1687
+ name: `${provider.name}/${model.name} (${variant})`,
1688
+ }))
1689
+ return [base, ...variantOptions]
1690
+ })
1691
+ })
1692
+ }
1693
+
1694
+ function formatModelIdWithVariant(
1695
+ model: { providerID: ProviderID; modelID: ModelID },
1696
+ variant: string | undefined,
1697
+ availableVariants: string[],
1698
+ includeVariant: boolean,
1699
+ ) {
1700
+ const base = `${model.providerID}/${model.modelID}`
1701
+ if (!includeVariant || !variant || !availableVariants.includes(variant)) return base
1702
+ return `${base}/${variant}`
1703
+ }
1704
+
1705
+ function buildVariantMeta(input: {
1706
+ model: { providerID: ProviderID; modelID: ModelID }
1707
+ variant?: string
1708
+ availableVariants: string[]
1709
+ }) {
1710
+ return {
1711
+ opencode: {
1712
+ modelId: `${input.model.providerID}/${input.model.modelID}`,
1713
+ variant: input.variant ?? null,
1714
+ availableVariants: input.availableVariants,
1715
+ },
1716
+ }
1717
+ }
1718
+
1719
+ function parseModelSelection(
1720
+ modelId: string,
1721
+ providers: Array<{ id: string; models: Record<string, { variants?: Record<string, any> }> }>,
1722
+ ): { model: { providerID: ProviderID; modelID: ModelID }; variant?: string } {
1723
+ const parsed = Provider.parseModel(modelId)
1724
+ const provider = providers.find((p) => p.id === parsed.providerID)
1725
+ if (!provider) {
1726
+ return { model: parsed, variant: undefined }
1727
+ }
1728
+
1729
+ // Check if modelID exists directly
1730
+ if (provider.models[parsed.modelID]) {
1731
+ return { model: parsed, variant: undefined }
1732
+ }
1733
+
1734
+ // Try to extract variant from end of modelID (e.g., "claude-sonnet-4/high" -> model: "claude-sonnet-4", variant: "high")
1735
+ const segments = parsed.modelID.split("/")
1736
+ if (segments.length > 1) {
1737
+ const candidateVariant = segments[segments.length - 1]
1738
+ const baseModelId = segments.slice(0, -1).join("/")
1739
+ const baseModelInfo = provider.models[baseModelId]
1740
+ if (baseModelInfo?.variants && candidateVariant in baseModelInfo.variants) {
1741
+ return {
1742
+ model: { providerID: parsed.providerID, modelID: ModelID.make(baseModelId) },
1743
+ variant: candidateVariant,
1744
+ }
1745
+ }
1746
+ }
1747
+
1748
+ return { model: parsed, variant: undefined }
1749
+ }
1750
+
1751
+ function buildConfigOptions(input: {
1752
+ currentModelId: string
1753
+ availableModels: ModelOption[]
1754
+ modes?: { availableModes: ModeOption[]; currentModeId: string } | undefined
1755
+ }): SessionConfigOption[] {
1756
+ const options: SessionConfigOption[] = [
1757
+ {
1758
+ id: "model",
1759
+ name: "Model",
1760
+ category: "model",
1761
+ type: "select",
1762
+ currentValue: input.currentModelId,
1763
+ options: input.availableModels.map((m) => ({ value: m.modelId, name: m.name })),
1764
+ },
1765
+ ]
1766
+ if (input.modes) {
1767
+ options.push({
1768
+ id: "mode",
1769
+ name: "Session Mode",
1770
+ category: "mode",
1771
+ type: "select",
1772
+ currentValue: input.modes.currentModeId,
1773
+ options: input.modes.availableModes.map((m) => ({
1774
+ value: m.id,
1775
+ name: m.name,
1776
+ ...(m.description ? { description: m.description } : {}),
1777
+ })),
1778
+ })
1779
+ }
1780
+ return options
1781
+ }
1782
+
1783
+ export * as ACP from "./agent"