alchemylab-opencode 1.0.6 → 1.0.8

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 (413) hide show
  1. package/dist/arktype-aI7TBD0R-HX4BR3RX.js +21 -0
  2. package/dist/arktype-aI7TBD0R-HX4BR3RX.js.map +7 -0
  3. package/dist/{chunk-UWZSRZHA.js → chunk-23UYOGZG.js} +817 -315
  4. package/dist/chunk-23UYOGZG.js.map +7 -0
  5. package/dist/chunk-3AF5MXLF.js +49 -0
  6. package/dist/chunk-3AF5MXLF.js.map +7 -0
  7. package/dist/chunk-FMP3YLWX.js +30 -0
  8. package/dist/chunk-FMP3YLWX.js.map +7 -0
  9. package/dist/{chunk-ANDTK76Q.js → chunk-GSE2T5UY.js} +3 -3
  10. package/dist/{chunk-CYXMV3J3.js → chunk-LU5XVQUD.js} +4 -4
  11. package/dist/chunk-O5VWOPCL.js +106 -0
  12. package/dist/chunk-O5VWOPCL.js.map +7 -0
  13. package/dist/chunk-PWFRMDDF.js +12 -0
  14. package/dist/{chunk-DSPC5QIG.js → chunk-SM6WZDN6.js} +9 -9
  15. package/dist/{chunk-MCWTARFP.js → chunk-TASPMBUL.js} +4 -4
  16. package/dist/chunk-VL3RSEZ2.js +160 -0
  17. package/dist/chunk-VL3RSEZ2.js.map +7 -0
  18. package/dist/default-u_dwuiYb-WOFHQQ3K.js +22 -0
  19. package/dist/effect-QlVUlMFu-UERLUUQW.js +29 -0
  20. package/dist/effect-QlVUlMFu-UERLUUQW.js.map +7 -0
  21. package/dist/{mcp-VAU4RMWH.js → mcp-XFGN3FWW.js} +2 -2
  22. package/dist/{registry-PYMN6L3G.js → registry-W4MBVBYD.js} +3 -3
  23. package/dist/{run-YFVCQJQM.js → run-73HUV4H3.js} +9 -6
  24. package/dist/{run-YFVCQJQM.js.map → run-73HUV4H3.js.map} +1 -1
  25. package/dist/run.js +1 -1
  26. package/dist/serve.js +1 -1
  27. package/dist/{server-K3UGWASP.js → server-2CPOG3LS.js} +9 -6
  28. package/dist/{session-HJFWTM7C.js → session-M6HOZ56B.js} +3 -3
  29. package/dist/session-M6HOZ56B.js.map +7 -0
  30. package/dist/{share-next-55VCIMUH.js → share-next-QRBVSD37.js} +4 -4
  31. package/dist/share-next-QRBVSD37.js.map +7 -0
  32. package/dist/sury-CWZTCd75-XC3NCMTP.js +29 -0
  33. package/dist/sury-CWZTCd75-XC3NCMTP.js.map +7 -0
  34. package/dist/typebox-Dei93FPO-QOBKW4L7.js +21 -0
  35. package/dist/typebox-Dei93FPO-QOBKW4L7.js.map +7 -0
  36. package/dist/valibot--1zFm7rT-LHHLSLYY.js +29 -0
  37. package/dist/valibot--1zFm7rT-LHHLSLYY.js.map +7 -0
  38. package/dist/valibot-D_HTw1Gn-UR62BIDY.js +60 -0
  39. package/dist/valibot-D_HTw1Gn-UR62BIDY.js.map +7 -0
  40. package/dist/{worktree-6GGDLADY.js → worktree-3GUHT2P7.js} +5 -5
  41. package/dist/zod-Bwrt9trS-J2GOMA2R.js +43 -0
  42. package/dist/zod-Bwrt9trS-J2GOMA2R.js.map +7 -0
  43. package/dist/zod-DSgpEGAE-VTXQW6SB.js +56 -0
  44. package/dist/zod-DSgpEGAE-VTXQW6SB.js.map +7 -0
  45. package/package.json +2 -15
  46. package/build.mjs +0 -182
  47. package/dist/chunk-UWZSRZHA.js.map +0 -7
  48. package/packages/opencode/package.json +0 -138
  49. package/packages/opencode/src/account/account.sql.ts +0 -39
  50. package/packages/opencode/src/account/index.ts +0 -463
  51. package/packages/opencode/src/account/repo.ts +0 -163
  52. package/packages/opencode/src/account/schema.ts +0 -91
  53. package/packages/opencode/src/acp/README.md +0 -174
  54. package/packages/opencode/src/acp/agent.ts +0 -1847
  55. package/packages/opencode/src/acp/session.ts +0 -116
  56. package/packages/opencode/src/acp/types.ts +0 -24
  57. package/packages/opencode/src/agent/agent.ts +0 -420
  58. package/packages/opencode/src/agent/generate.txt +0 -75
  59. package/packages/opencode/src/agent/prompt/compaction.txt +0 -15
  60. package/packages/opencode/src/agent/prompt/explore.txt +0 -18
  61. package/packages/opencode/src/agent/prompt/summary.txt +0 -11
  62. package/packages/opencode/src/agent/prompt/title.txt +0 -44
  63. package/packages/opencode/src/auth/index.ts +0 -110
  64. package/packages/opencode/src/bus/bus-event.ts +0 -40
  65. package/packages/opencode/src/bus/global.ts +0 -10
  66. package/packages/opencode/src/bus/index.ts +0 -185
  67. package/packages/opencode/src/cli/bootstrap.ts +0 -17
  68. package/packages/opencode/src/cli/cmd/cmd.ts +0 -7
  69. package/packages/opencode/src/cli/cmd/run.ts +0 -689
  70. package/packages/opencode/src/cli/cmd/serve.ts +0 -24
  71. package/packages/opencode/src/cli/cmd/tui/event.ts +0 -49
  72. package/packages/opencode/src/cli/effect/prompt.ts +0 -25
  73. package/packages/opencode/src/cli/error.ts +0 -46
  74. package/packages/opencode/src/cli/heap.ts +0 -59
  75. package/packages/opencode/src/cli/logo.ts +0 -6
  76. package/packages/opencode/src/cli/network.ts +0 -60
  77. package/packages/opencode/src/cli/ui.ts +0 -133
  78. package/packages/opencode/src/cli/upgrade.ts +0 -31
  79. package/packages/opencode/src/command/index.ts +0 -195
  80. package/packages/opencode/src/command/template/initialize.txt +0 -66
  81. package/packages/opencode/src/command/template/review.txt +0 -101
  82. package/packages/opencode/src/config/config.ts +0 -1599
  83. package/packages/opencode/src/config/console-state.ts +0 -15
  84. package/packages/opencode/src/config/markdown.ts +0 -99
  85. package/packages/opencode/src/config/paths.ts +0 -167
  86. package/packages/opencode/src/config/tui-migrate.ts +0 -155
  87. package/packages/opencode/src/config/tui-schema.ts +0 -37
  88. package/packages/opencode/src/config/tui.ts +0 -179
  89. package/packages/opencode/src/control-plane/adaptors/index.ts +0 -20
  90. package/packages/opencode/src/control-plane/adaptors/worktree.ts +0 -42
  91. package/packages/opencode/src/control-plane/schema.ts +0 -17
  92. package/packages/opencode/src/control-plane/sse.ts +0 -66
  93. package/packages/opencode/src/control-plane/types.ts +0 -32
  94. package/packages/opencode/src/control-plane/workspace.sql.ts +0 -17
  95. package/packages/opencode/src/control-plane/workspace.ts +0 -168
  96. package/packages/opencode/src/effect/cross-spawn-spawner.ts +0 -502
  97. package/packages/opencode/src/effect/instance-ref.ts +0 -6
  98. package/packages/opencode/src/effect/instance-registry.ts +0 -12
  99. package/packages/opencode/src/effect/instance-state.ts +0 -82
  100. package/packages/opencode/src/effect/run-service.ts +0 -33
  101. package/packages/opencode/src/effect/runner.ts +0 -216
  102. package/packages/opencode/src/env/index.ts +0 -28
  103. package/packages/opencode/src/file/ignore.ts +0 -82
  104. package/packages/opencode/src/file/index.ts +0 -686
  105. package/packages/opencode/src/file/protected.ts +0 -59
  106. package/packages/opencode/src/file/ripgrep.ts +0 -376
  107. package/packages/opencode/src/file/time.ts +0 -133
  108. package/packages/opencode/src/file/watcher.ts +0 -173
  109. package/packages/opencode/src/filesystem/index.ts +0 -236
  110. package/packages/opencode/src/flag/flag.ts +0 -156
  111. package/packages/opencode/src/format/formatter.ts +0 -413
  112. package/packages/opencode/src/format/index.ts +0 -203
  113. package/packages/opencode/src/git/index.ts +0 -303
  114. package/packages/opencode/src/global/index.ts +0 -56
  115. package/packages/opencode/src/id/id.ts +0 -85
  116. package/packages/opencode/src/ide/index.ts +0 -74
  117. package/packages/opencode/src/installation/index.ts +0 -355
  118. package/packages/opencode/src/installation/meta.ts +0 -7
  119. package/packages/opencode/src/lsp/client.ts +0 -252
  120. package/packages/opencode/src/lsp/index.ts +0 -558
  121. package/packages/opencode/src/lsp/language.ts +0 -120
  122. package/packages/opencode/src/lsp/launch.ts +0 -21
  123. package/packages/opencode/src/lsp/server.ts +0 -1968
  124. package/packages/opencode/src/mcp/auth.ts +0 -173
  125. package/packages/opencode/src/mcp/index.ts +0 -931
  126. package/packages/opencode/src/mcp/oauth-callback.ts +0 -216
  127. package/packages/opencode/src/mcp/oauth-provider.ts +0 -185
  128. package/packages/opencode/src/node-loader.mjs +0 -104
  129. package/packages/opencode/src/node-preload.mjs +0 -39
  130. package/packages/opencode/src/node.ts +0 -1
  131. package/packages/opencode/src/npm/index.ts +0 -188
  132. package/packages/opencode/src/patch/index.ts +0 -680
  133. package/packages/opencode/src/permission/arity.ts +0 -163
  134. package/packages/opencode/src/permission/evaluate.ts +0 -15
  135. package/packages/opencode/src/permission/index.ts +0 -325
  136. package/packages/opencode/src/permission/schema.ts +0 -17
  137. package/packages/opencode/src/plugin/cloudflare.ts +0 -67
  138. package/packages/opencode/src/plugin/codex.ts +0 -608
  139. package/packages/opencode/src/plugin/github-copilot/copilot.ts +0 -361
  140. package/packages/opencode/src/plugin/github-copilot/models.ts +0 -144
  141. package/packages/opencode/src/plugin/index.ts +0 -299
  142. package/packages/opencode/src/plugin/install.ts +0 -439
  143. package/packages/opencode/src/plugin/loader.ts +0 -174
  144. package/packages/opencode/src/plugin/meta.ts +0 -188
  145. package/packages/opencode/src/plugin/shared.ts +0 -323
  146. package/packages/opencode/src/project/bootstrap.ts +0 -31
  147. package/packages/opencode/src/project/instance.ts +0 -175
  148. package/packages/opencode/src/project/project.sql.ts +0 -16
  149. package/packages/opencode/src/project/project.ts +0 -519
  150. package/packages/opencode/src/project/schema.ts +0 -16
  151. package/packages/opencode/src/project/state.ts +0 -70
  152. package/packages/opencode/src/project/vcs.ts +0 -240
  153. package/packages/opencode/src/provider/auth.ts +0 -253
  154. package/packages/opencode/src/provider/error.ts +0 -197
  155. package/packages/opencode/src/provider/models.ts +0 -162
  156. package/packages/opencode/src/provider/provider.ts +0 -1709
  157. package/packages/opencode/src/provider/schema.ts +0 -38
  158. package/packages/opencode/src/provider/sdk/copilot/README.md +0 -5
  159. package/packages/opencode/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +0 -170
  160. package/packages/opencode/src/provider/sdk/copilot/chat/get-response-metadata.ts +0 -15
  161. package/packages/opencode/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +0 -19
  162. package/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +0 -64
  163. package/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +0 -815
  164. package/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +0 -28
  165. package/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +0 -44
  166. package/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +0 -83
  167. package/packages/opencode/src/provider/sdk/copilot/copilot-provider.ts +0 -100
  168. package/packages/opencode/src/provider/sdk/copilot/index.ts +0 -2
  169. package/packages/opencode/src/provider/sdk/copilot/openai-compatible-error.ts +0 -27
  170. package/packages/opencode/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +0 -335
  171. package/packages/opencode/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +0 -22
  172. package/packages/opencode/src/provider/sdk/copilot/responses/openai-config.ts +0 -18
  173. package/packages/opencode/src/provider/sdk/copilot/responses/openai-error.ts +0 -22
  174. package/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +0 -214
  175. package/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +0 -1769
  176. package/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +0 -173
  177. package/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-settings.ts +0 -1
  178. package/packages/opencode/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +0 -87
  179. package/packages/opencode/src/provider/sdk/copilot/responses/tool/file-search.ts +0 -127
  180. package/packages/opencode/src/provider/sdk/copilot/responses/tool/image-generation.ts +0 -114
  181. package/packages/opencode/src/provider/sdk/copilot/responses/tool/local-shell.ts +0 -64
  182. package/packages/opencode/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +0 -103
  183. package/packages/opencode/src/provider/sdk/copilot/responses/tool/web-search.ts +0 -102
  184. package/packages/opencode/src/provider/transform.ts +0 -1050
  185. package/packages/opencode/src/pty/index.ts +0 -397
  186. package/packages/opencode/src/pty/pty.node.ts +0 -27
  187. package/packages/opencode/src/pty/pty.ts +0 -25
  188. package/packages/opencode/src/pty/schema.ts +0 -17
  189. package/packages/opencode/src/question/index.ts +0 -224
  190. package/packages/opencode/src/question/schema.ts +0 -17
  191. package/packages/opencode/src/run.ts +0 -28
  192. package/packages/opencode/src/serve.ts +0 -45
  193. package/packages/opencode/src/server/error.ts +0 -36
  194. package/packages/opencode/src/server/event.ts +0 -7
  195. package/packages/opencode/src/server/instance.ts +0 -324
  196. package/packages/opencode/src/server/mdns.ts +0 -60
  197. package/packages/opencode/src/server/middleware.ts +0 -33
  198. package/packages/opencode/src/server/projectors.ts +0 -28
  199. package/packages/opencode/src/server/proxy.ts +0 -131
  200. package/packages/opencode/src/server/router.ts +0 -105
  201. package/packages/opencode/src/server/routes/config.ts +0 -92
  202. package/packages/opencode/src/server/routes/event.ts +0 -83
  203. package/packages/opencode/src/server/routes/experimental.ts +0 -374
  204. package/packages/opencode/src/server/routes/file.ts +0 -197
  205. package/packages/opencode/src/server/routes/global.ts +0 -312
  206. package/packages/opencode/src/server/routes/mcp.ts +0 -225
  207. package/packages/opencode/src/server/routes/permission.ts +0 -69
  208. package/packages/opencode/src/server/routes/project.ts +0 -118
  209. package/packages/opencode/src/server/routes/provider.ts +0 -171
  210. package/packages/opencode/src/server/routes/pty.ts +0 -210
  211. package/packages/opencode/src/server/routes/question.ts +0 -99
  212. package/packages/opencode/src/server/routes/session.ts +0 -1030
  213. package/packages/opencode/src/server/routes/tui.ts +0 -379
  214. package/packages/opencode/src/server/routes/workspace.ts +0 -94
  215. package/packages/opencode/src/server/server.ts +0 -353
  216. package/packages/opencode/src/session/compaction.ts +0 -428
  217. package/packages/opencode/src/session/index.ts +0 -887
  218. package/packages/opencode/src/session/instruction.ts +0 -258
  219. package/packages/opencode/src/session/llm.ts +0 -354
  220. package/packages/opencode/src/session/message-v2.ts +0 -1031
  221. package/packages/opencode/src/session/message.ts +0 -191
  222. package/packages/opencode/src/session/overflow.ts +0 -22
  223. package/packages/opencode/src/session/processor.ts +0 -523
  224. package/packages/opencode/src/session/projectors.ts +0 -135
  225. package/packages/opencode/src/session/prompt/anthropic.txt +0 -105
  226. package/packages/opencode/src/session/prompt/beast.txt +0 -147
  227. package/packages/opencode/src/session/prompt/build-switch.txt +0 -5
  228. package/packages/opencode/src/session/prompt/codex.txt +0 -79
  229. package/packages/opencode/src/session/prompt/copilot-gpt-5.txt +0 -143
  230. package/packages/opencode/src/session/prompt/default.txt +0 -105
  231. package/packages/opencode/src/session/prompt/gemini.txt +0 -155
  232. package/packages/opencode/src/session/prompt/gpt.txt +0 -107
  233. package/packages/opencode/src/session/prompt/kimi.txt +0 -95
  234. package/packages/opencode/src/session/prompt/max-steps.txt +0 -16
  235. package/packages/opencode/src/session/prompt/plan-reminder-anthropic.txt +0 -67
  236. package/packages/opencode/src/session/prompt/plan.txt +0 -26
  237. package/packages/opencode/src/session/prompt/trinity.txt +0 -97
  238. package/packages/opencode/src/session/prompt.ts +0 -1911
  239. package/packages/opencode/src/session/retry.ts +0 -106
  240. package/packages/opencode/src/session/revert.ts +0 -176
  241. package/packages/opencode/src/session/schema.ts +0 -38
  242. package/packages/opencode/src/session/session.sql.ts +0 -103
  243. package/packages/opencode/src/session/status.ts +0 -102
  244. package/packages/opencode/src/session/summary.ts +0 -177
  245. package/packages/opencode/src/session/system.ts +0 -76
  246. package/packages/opencode/src/session/todo.ts +0 -95
  247. package/packages/opencode/src/share/share-next.ts +0 -369
  248. package/packages/opencode/src/share/share.sql.ts +0 -13
  249. package/packages/opencode/src/shell/shell.ts +0 -110
  250. package/packages/opencode/src/skill/discovery.ts +0 -116
  251. package/packages/opencode/src/skill/index.ts +0 -277
  252. package/packages/opencode/src/snapshot/index.ts +0 -726
  253. package/packages/opencode/src/sql.d.ts +0 -4
  254. package/packages/opencode/src/storage/db.node.ts +0 -8
  255. package/packages/opencode/src/storage/db.ts +0 -177
  256. package/packages/opencode/src/storage/json-migration.ts +0 -425
  257. package/packages/opencode/src/storage/schema.sql.ts +0 -10
  258. package/packages/opencode/src/storage/schema.ts +0 -5
  259. package/packages/opencode/src/storage/storage.ts +0 -353
  260. package/packages/opencode/src/sync/README.md +0 -179
  261. package/packages/opencode/src/sync/event.sql.ts +0 -16
  262. package/packages/opencode/src/sync/index.ts +0 -263
  263. package/packages/opencode/src/sync/schema.ts +0 -14
  264. package/packages/opencode/src/tool/apply_patch.ts +0 -281
  265. package/packages/opencode/src/tool/apply_patch.txt +0 -33
  266. package/packages/opencode/src/tool/bash.ts +0 -496
  267. package/packages/opencode/src/tool/bash.txt +0 -117
  268. package/packages/opencode/src/tool/batch.ts +0 -183
  269. package/packages/opencode/src/tool/batch.txt +0 -24
  270. package/packages/opencode/src/tool/codesearch.ts +0 -132
  271. package/packages/opencode/src/tool/codesearch.txt +0 -12
  272. package/packages/opencode/src/tool/edit.ts +0 -667
  273. package/packages/opencode/src/tool/edit.txt +0 -10
  274. package/packages/opencode/src/tool/external-directory.ts +0 -46
  275. package/packages/opencode/src/tool/glob.ts +0 -78
  276. package/packages/opencode/src/tool/glob.txt +0 -6
  277. package/packages/opencode/src/tool/grep.ts +0 -156
  278. package/packages/opencode/src/tool/grep.txt +0 -8
  279. package/packages/opencode/src/tool/invalid.ts +0 -17
  280. package/packages/opencode/src/tool/ls.ts +0 -121
  281. package/packages/opencode/src/tool/ls.txt +0 -1
  282. package/packages/opencode/src/tool/lsp.ts +0 -97
  283. package/packages/opencode/src/tool/lsp.txt +0 -19
  284. package/packages/opencode/src/tool/multiedit.ts +0 -46
  285. package/packages/opencode/src/tool/multiedit.txt +0 -41
  286. package/packages/opencode/src/tool/plan-enter.txt +0 -14
  287. package/packages/opencode/src/tool/plan-exit.txt +0 -13
  288. package/packages/opencode/src/tool/plan.ts +0 -131
  289. package/packages/opencode/src/tool/question.ts +0 -46
  290. package/packages/opencode/src/tool/question.txt +0 -10
  291. package/packages/opencode/src/tool/read.ts +0 -332
  292. package/packages/opencode/src/tool/read.txt +0 -14
  293. package/packages/opencode/src/tool/registry.ts +0 -265
  294. package/packages/opencode/src/tool/schema.ts +0 -17
  295. package/packages/opencode/src/tool/skill.ts +0 -105
  296. package/packages/opencode/src/tool/task.ts +0 -166
  297. package/packages/opencode/src/tool/task.txt +0 -60
  298. package/packages/opencode/src/tool/todo.ts +0 -48
  299. package/packages/opencode/src/tool/todowrite.txt +0 -167
  300. package/packages/opencode/src/tool/tool.ts +0 -112
  301. package/packages/opencode/src/tool/truncate.ts +0 -144
  302. package/packages/opencode/src/tool/truncation-dir.ts +0 -4
  303. package/packages/opencode/src/tool/webfetch.ts +0 -206
  304. package/packages/opencode/src/tool/webfetch.txt +0 -13
  305. package/packages/opencode/src/tool/websearch.ts +0 -150
  306. package/packages/opencode/src/tool/websearch.txt +0 -14
  307. package/packages/opencode/src/tool/write.ts +0 -84
  308. package/packages/opencode/src/tool/write.txt +0 -8
  309. package/packages/opencode/src/util/abort.ts +0 -35
  310. package/packages/opencode/src/util/archive.ts +0 -17
  311. package/packages/opencode/src/util/color.ts +0 -19
  312. package/packages/opencode/src/util/context.ts +0 -25
  313. package/packages/opencode/src/util/data-url.ts +0 -9
  314. package/packages/opencode/src/util/defer.ts +0 -12
  315. package/packages/opencode/src/util/effect-http-client.ts +0 -11
  316. package/packages/opencode/src/util/effect-zod.ts +0 -98
  317. package/packages/opencode/src/util/error.ts +0 -77
  318. package/packages/opencode/src/util/filesystem.ts +0 -245
  319. package/packages/opencode/src/util/flock.ts +0 -333
  320. package/packages/opencode/src/util/fn.ts +0 -21
  321. package/packages/opencode/src/util/format.ts +0 -20
  322. package/packages/opencode/src/util/glob.ts +0 -34
  323. package/packages/opencode/src/util/hash.ts +0 -7
  324. package/packages/opencode/src/util/iife.ts +0 -3
  325. package/packages/opencode/src/util/keybind.ts +0 -103
  326. package/packages/opencode/src/util/lazy.ts +0 -23
  327. package/packages/opencode/src/util/locale.ts +0 -81
  328. package/packages/opencode/src/util/lock.ts +0 -98
  329. package/packages/opencode/src/util/log.ts +0 -182
  330. package/packages/opencode/src/util/network.ts +0 -9
  331. package/packages/opencode/src/util/process.ts +0 -176
  332. package/packages/opencode/src/util/queue.ts +0 -32
  333. package/packages/opencode/src/util/record.ts +0 -3
  334. package/packages/opencode/src/util/rpc.ts +0 -66
  335. package/packages/opencode/src/util/schema.ts +0 -53
  336. package/packages/opencode/src/util/scrap.ts +0 -10
  337. package/packages/opencode/src/util/signal.ts +0 -12
  338. package/packages/opencode/src/util/timeout.ts +0 -14
  339. package/packages/opencode/src/util/token.ts +0 -7
  340. package/packages/opencode/src/util/update-schema.ts +0 -13
  341. package/packages/opencode/src/util/which.ts +0 -14
  342. package/packages/opencode/src/util/wildcard.ts +0 -59
  343. package/packages/opencode/src/worktree/index.ts +0 -612
  344. package/packages/plugin/package.json +0 -38
  345. package/packages/plugin/src/example.ts +0 -18
  346. package/packages/plugin/src/index.ts +0 -282
  347. package/packages/plugin/src/shell.ts +0 -136
  348. package/packages/plugin/src/tool.ts +0 -38
  349. package/packages/plugin/src/tui.ts +0 -508
  350. package/packages/script/package.json +0 -16
  351. package/packages/script/src/index.ts +0 -77
  352. package/packages/sdk/package.json +0 -33
  353. package/packages/sdk/src/client.ts +0 -55
  354. package/packages/sdk/src/gen/client/client.gen.ts +0 -212
  355. package/packages/sdk/src/gen/client/index.ts +0 -25
  356. package/packages/sdk/src/gen/client/types.gen.ts +0 -222
  357. package/packages/sdk/src/gen/client/utils.gen.ts +0 -287
  358. package/packages/sdk/src/gen/client.gen.ts +0 -22
  359. package/packages/sdk/src/gen/core/auth.gen.ts +0 -41
  360. package/packages/sdk/src/gen/core/bodySerializer.gen.ts +0 -74
  361. package/packages/sdk/src/gen/core/params.gen.ts +0 -144
  362. package/packages/sdk/src/gen/core/pathSerializer.gen.ts +0 -167
  363. package/packages/sdk/src/gen/core/queryKeySerializer.gen.ts +0 -111
  364. package/packages/sdk/src/gen/core/serverSentEvents.gen.ts +0 -210
  365. package/packages/sdk/src/gen/core/types.gen.ts +0 -91
  366. package/packages/sdk/src/gen/core/utils.gen.ts +0 -109
  367. package/packages/sdk/src/gen/sdk.gen.ts +0 -1197
  368. package/packages/sdk/src/gen/types.gen.ts +0 -3904
  369. package/packages/sdk/src/index.ts +0 -21
  370. package/packages/sdk/src/process.ts +0 -31
  371. package/packages/sdk/src/server.ts +0 -134
  372. package/packages/sdk/src/v2/client.ts +0 -81
  373. package/packages/sdk/src/v2/gen/client/client.gen.ts +0 -285
  374. package/packages/sdk/src/v2/gen/client/index.ts +0 -25
  375. package/packages/sdk/src/v2/gen/client/types.gen.ts +0 -202
  376. package/packages/sdk/src/v2/gen/client/utils.gen.ts +0 -289
  377. package/packages/sdk/src/v2/gen/client.gen.ts +0 -18
  378. package/packages/sdk/src/v2/gen/core/auth.gen.ts +0 -41
  379. package/packages/sdk/src/v2/gen/core/bodySerializer.gen.ts +0 -82
  380. package/packages/sdk/src/v2/gen/core/params.gen.ts +0 -169
  381. package/packages/sdk/src/v2/gen/core/pathSerializer.gen.ts +0 -167
  382. package/packages/sdk/src/v2/gen/core/queryKeySerializer.gen.ts +0 -111
  383. package/packages/sdk/src/v2/gen/core/serverSentEvents.gen.ts +0 -239
  384. package/packages/sdk/src/v2/gen/core/types.gen.ts +0 -86
  385. package/packages/sdk/src/v2/gen/core/utils.gen.ts +0 -137
  386. package/packages/sdk/src/v2/gen/sdk.gen.ts +0 -4223
  387. package/packages/sdk/src/v2/gen/types.gen.ts +0 -5265
  388. package/packages/sdk/src/v2/index.ts +0 -21
  389. package/packages/sdk/src/v2/server.ts +0 -134
  390. package/packages/util/package.json +0 -20
  391. package/packages/util/src/array.ts +0 -10
  392. package/packages/util/src/binary.ts +0 -41
  393. package/packages/util/src/encode.ts +0 -51
  394. package/packages/util/src/error.ts +0 -54
  395. package/packages/util/src/fn.ts +0 -11
  396. package/packages/util/src/identifier.ts +0 -48
  397. package/packages/util/src/iife.ts +0 -3
  398. package/packages/util/src/lazy.ts +0 -11
  399. package/packages/util/src/module.ts +0 -10
  400. package/packages/util/src/path.ts +0 -37
  401. package/packages/util/src/retry.ts +0 -41
  402. package/packages/util/src/slug.ts +0 -74
  403. package/postinstall.cjs +0 -55
  404. /package/dist/{chunk-ANDTK76Q.js.map → chunk-GSE2T5UY.js.map} +0 -0
  405. /package/dist/{chunk-CYXMV3J3.js.map → chunk-LU5XVQUD.js.map} +0 -0
  406. /package/dist/{mcp-VAU4RMWH.js.map → chunk-PWFRMDDF.js.map} +0 -0
  407. /package/dist/{chunk-DSPC5QIG.js.map → chunk-SM6WZDN6.js.map} +0 -0
  408. /package/dist/{chunk-MCWTARFP.js.map → chunk-TASPMBUL.js.map} +0 -0
  409. /package/dist/{registry-PYMN6L3G.js.map → default-u_dwuiYb-WOFHQQ3K.js.map} +0 -0
  410. /package/dist/{server-K3UGWASP.js.map → mcp-XFGN3FWW.js.map} +0 -0
  411. /package/dist/{session-HJFWTM7C.js.map → registry-W4MBVBYD.js.map} +0 -0
  412. /package/dist/{share-next-55VCIMUH.js.map → server-2CPOG3LS.js.map} +0 -0
  413. /package/dist/{worktree-6GGDLADY.js.map → worktree-3GUHT2P7.js.map} +0 -0
@@ -1,1847 +0,0 @@
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 PlanEntry,
18
- type PromptRequest,
19
- type ResumeSessionRequest,
20
- type ResumeSessionResponse,
21
- type Role,
22
- type SessionInfo,
23
- type SetSessionModelRequest,
24
- type SessionConfigOption,
25
- type SetSessionConfigOptionRequest,
26
- type SetSessionConfigOptionResponse,
27
- type SetSessionModeRequest,
28
- type SetSessionModeResponse,
29
- type ToolCallContent,
30
- type ToolKind,
31
- type Usage,
32
- } from "@agentclientprotocol/sdk"
33
-
34
- import { Log } from "../util/log"
35
- import { pathToFileURL } from "url"
36
- import { Filesystem } from "../util/filesystem"
37
- import { Hash } from "../util/hash"
38
- import { ACPSessionManager } from "./session"
39
- import type { ACPConfig } from "./types"
40
- import { Provider } from "../provider/provider"
41
- import { ModelID, ProviderID } from "../provider/schema"
42
- import { Agent as AgentModule } from "../agent/agent"
43
- import { Installation } from "@/installation"
44
- import { MessageV2 } from "@/session/message-v2"
45
- import { Config } from "@/config/config"
46
- import { Todo } from "@/session/todo"
47
- import { z } from "zod"
48
- import { LoadAPIKeyError } from "ai"
49
- import type { AssistantMessage, Event, OpencodeClient, SessionMessageResponse, ToolPart } from "@opencode-ai/sdk/v2"
50
- import { applyPatch } from "diff"
51
-
52
- type ModeOption = { id: string; name: string; description?: string }
53
- type ModelOption = { modelId: string; name: string }
54
-
55
- const DEFAULT_VARIANT_VALUE = "default"
56
-
57
- export namespace ACP {
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 }, { 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 as any)?.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
- 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
- if (part.tool === "todowrite") {
373
- const parsedTodos = z.array(Todo.Info).safeParse(JSON.parse(part.state.output))
374
- if (parsedTodos.success) {
375
- await this.connection
376
- .sessionUpdate({
377
- sessionId,
378
- update: {
379
- sessionUpdate: "plan",
380
- entries: parsedTodos.data.map((todo) => {
381
- const status: PlanEntry["status"] =
382
- todo.status === "cancelled" ? "completed" : (todo.status as PlanEntry["status"])
383
- return {
384
- priority: "medium",
385
- status,
386
- content: todo.content,
387
- }
388
- }),
389
- },
390
- })
391
- .catch((error) => {
392
- log.error("failed to send session update for todo", { error })
393
- })
394
- } else {
395
- log.error("failed to parse todo output", { error: parsedTodos.error })
396
- }
397
- }
398
-
399
- await this.connection
400
- .sessionUpdate({
401
- sessionId,
402
- update: {
403
- sessionUpdate: "tool_call_update",
404
- toolCallId: part.callID,
405
- status: "completed",
406
- kind,
407
- content,
408
- title: part.state.title,
409
- rawInput: part.state.input,
410
- rawOutput: {
411
- output: part.state.output,
412
- metadata: part.state.metadata,
413
- },
414
- },
415
- })
416
- .catch((error) => {
417
- log.error("failed to send tool completed to ACP", { error })
418
- })
419
- return
420
- }
421
- case "error":
422
- this.toolStarts.delete(part.callID)
423
- this.bashSnapshots.delete(part.callID)
424
- await this.connection
425
- .sessionUpdate({
426
- sessionId,
427
- update: {
428
- sessionUpdate: "tool_call_update",
429
- toolCallId: part.callID,
430
- status: "failed",
431
- kind: toToolKind(part.tool),
432
- title: part.tool,
433
- rawInput: part.state.input,
434
- content: [
435
- {
436
- type: "content",
437
- content: {
438
- type: "text",
439
- text: part.state.error,
440
- },
441
- },
442
- ],
443
- rawOutput: {
444
- error: part.state.error,
445
- metadata: part.state.metadata,
446
- },
447
- },
448
- })
449
- .catch((error) => {
450
- log.error("failed to send tool error to ACP", { error })
451
- })
452
- return
453
- }
454
- }
455
- if (part.type !== "text" && part.type !== "file") return
456
- const msg = await this.sdk.session
457
- .message(
458
- { sessionID: part.sessionID, messageID: part.messageID, directory: session.cwd },
459
- { throwOnError: true },
460
- )
461
- .then((x) => x.data)
462
- .catch((err) => {
463
- log.error("failed to fetch message for user chunk", { error: err })
464
- return undefined
465
- })
466
- if (!msg || msg.info.role !== "user") return
467
- await this.processMessage({ info: msg.info, parts: [part] })
468
- return
469
- }
470
-
471
- case "message.part.delta": {
472
- const props = event.properties
473
- const session = this.sessionManager.tryGet(props.sessionID)
474
- if (!session) return
475
- const sessionId = session.id
476
-
477
- const message = await this.sdk.session
478
- .message(
479
- {
480
- sessionID: props.sessionID,
481
- messageID: props.messageID,
482
- directory: session.cwd,
483
- },
484
- { throwOnError: true },
485
- )
486
- .then((x) => x.data)
487
- .catch((error) => {
488
- log.error("unexpected error when fetching message", { error })
489
- return undefined
490
- })
491
-
492
- if (!message || message.info.role !== "assistant") return
493
-
494
- const part = message.parts.find((p) => p.id === props.partID)
495
- if (!part) return
496
-
497
- if (part.type === "text" && props.field === "text" && part.ignored !== true) {
498
- await this.connection
499
- .sessionUpdate({
500
- sessionId,
501
- update: {
502
- sessionUpdate: "agent_message_chunk",
503
- messageId: props.messageID,
504
- content: {
505
- type: "text",
506
- text: props.delta,
507
- },
508
- },
509
- })
510
- .catch((error) => {
511
- log.error("failed to send text delta to ACP", { error })
512
- })
513
- return
514
- }
515
-
516
- if (part.type === "reasoning" && props.field === "text") {
517
- await this.connection
518
- .sessionUpdate({
519
- sessionId,
520
- update: {
521
- sessionUpdate: "agent_thought_chunk",
522
- messageId: props.messageID,
523
- content: {
524
- type: "text",
525
- text: props.delta,
526
- },
527
- },
528
- })
529
- .catch((error) => {
530
- log.error("failed to send reasoning delta to ACP", { error })
531
- })
532
- }
533
- return
534
- }
535
- }
536
- }
537
-
538
- async initialize(params: InitializeRequest): Promise<InitializeResponse> {
539
- log.info("initialize", { protocolVersion: params.protocolVersion })
540
-
541
- const authMethod: AuthMethod = {
542
- description: "Run `opencode auth login` in the terminal",
543
- name: "Login with opencode",
544
- id: "opencode-login",
545
- }
546
-
547
- // If client supports terminal-auth capability, use that instead.
548
- if (params.clientCapabilities?._meta?.["terminal-auth"] === true) {
549
- authMethod._meta = {
550
- "terminal-auth": {
551
- command: "opencode",
552
- args: ["auth", "login"],
553
- label: "OpenCode Login",
554
- },
555
- }
556
- }
557
-
558
- return {
559
- protocolVersion: 1,
560
- agentCapabilities: {
561
- loadSession: true,
562
- mcpCapabilities: {
563
- http: true,
564
- sse: true,
565
- },
566
- promptCapabilities: {
567
- embeddedContext: true,
568
- image: true,
569
- },
570
- sessionCapabilities: {
571
- fork: {},
572
- list: {},
573
- resume: {},
574
- },
575
- },
576
- authMethods: [authMethod],
577
- agentInfo: {
578
- name: "OpenCode",
579
- version: Installation.VERSION,
580
- },
581
- }
582
- }
583
-
584
- async authenticate(_params: AuthenticateRequest) {
585
- throw new Error("Authentication not implemented")
586
- }
587
-
588
- async newSession(params: NewSessionRequest) {
589
- const directory = params.cwd
590
- try {
591
- const model = await defaultModel(this.config, directory)
592
-
593
- // Store ACP session state
594
- const state = await this.sessionManager.create(params.cwd, params.mcpServers, model)
595
- const sessionId = state.id
596
-
597
- log.info("creating_session", { sessionId, mcpServers: params.mcpServers.length })
598
-
599
- const load = await this.loadSessionMode({
600
- cwd: directory,
601
- mcpServers: params.mcpServers,
602
- sessionId,
603
- })
604
-
605
- return {
606
- sessionId,
607
- configOptions: load.configOptions,
608
- models: load.models,
609
- modes: load.modes,
610
- _meta: load._meta,
611
- }
612
- } catch (e) {
613
- const error = MessageV2.fromError(e, {
614
- providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
615
- })
616
- if (LoadAPIKeyError.isInstance(error)) {
617
- throw RequestError.authRequired()
618
- }
619
- throw e
620
- }
621
- }
622
-
623
- async loadSession(params: LoadSessionRequest) {
624
- const directory = params.cwd
625
- const sessionId = params.sessionId
626
-
627
- try {
628
- const model = await defaultModel(this.config, directory)
629
-
630
- // Store ACP session state
631
- await this.sessionManager.load(sessionId, params.cwd, params.mcpServers, model)
632
-
633
- log.info("load_session", { sessionId, mcpServers: params.mcpServers.length })
634
-
635
- const result = await this.loadSessionMode({
636
- cwd: directory,
637
- mcpServers: params.mcpServers,
638
- sessionId,
639
- })
640
-
641
- // Replay session history
642
- const messages = await this.sdk.session
643
- .messages(
644
- {
645
- sessionID: sessionId,
646
- directory,
647
- },
648
- { throwOnError: true },
649
- )
650
- .then((x) => x.data)
651
- .catch((err) => {
652
- log.error("unexpected error when fetching message", { error: err })
653
- return undefined
654
- })
655
-
656
- const lastUser = messages?.findLast((m) => m.info.role === "user")?.info
657
- if (lastUser?.role === "user") {
658
- result.models.currentModelId = `${lastUser.model.providerID}/${lastUser.model.modelID}`
659
- this.sessionManager.setModel(sessionId, {
660
- providerID: ProviderID.make(lastUser.model.providerID),
661
- modelID: ModelID.make(lastUser.model.modelID),
662
- })
663
- if (result.modes?.availableModes.some((m) => m.id === lastUser.agent)) {
664
- result.modes.currentModeId = lastUser.agent
665
- this.sessionManager.setMode(sessionId, lastUser.agent)
666
- }
667
- result.configOptions = buildConfigOptions({
668
- currentModelId: result.models.currentModelId,
669
- availableModels: result.models.availableModels,
670
- modes: result.modes,
671
- })
672
- }
673
-
674
- for (const msg of messages ?? []) {
675
- log.debug("replay message", msg)
676
- await this.processMessage(msg)
677
- }
678
-
679
- await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
680
-
681
- return result
682
- } catch (e) {
683
- const error = MessageV2.fromError(e, {
684
- providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
685
- })
686
- if (LoadAPIKeyError.isInstance(error)) {
687
- throw RequestError.authRequired()
688
- }
689
- throw e
690
- }
691
- }
692
-
693
- async listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse> {
694
- try {
695
- const cursor = params.cursor ? Number(params.cursor) : undefined
696
- const limit = 100
697
-
698
- const sessions = await this.sdk.session
699
- .list(
700
- {
701
- directory: params.cwd ?? undefined,
702
- roots: true,
703
- },
704
- { throwOnError: true },
705
- )
706
- .then((x) => x.data ?? [])
707
-
708
- const sorted = sessions.toSorted((a, b) => b.time.updated - a.time.updated)
709
- const filtered = cursor ? sorted.filter((s) => s.time.updated < cursor) : sorted
710
- const page = filtered.slice(0, limit)
711
-
712
- const entries: SessionInfo[] = page.map((session) => ({
713
- sessionId: session.id,
714
- cwd: session.directory,
715
- title: session.title,
716
- updatedAt: new Date(session.time.updated).toISOString(),
717
- }))
718
-
719
- const last = page[page.length - 1]
720
- const next = filtered.length > limit && last ? String(last.time.updated) : undefined
721
-
722
- const response: ListSessionsResponse = {
723
- sessions: entries,
724
- }
725
- if (next) response.nextCursor = next
726
- return response
727
- } catch (e) {
728
- const error = MessageV2.fromError(e, {
729
- providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
730
- })
731
- if (LoadAPIKeyError.isInstance(error)) {
732
- throw RequestError.authRequired()
733
- }
734
- throw e
735
- }
736
- }
737
-
738
- async unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse> {
739
- const directory = params.cwd
740
- const mcpServers = params.mcpServers ?? []
741
-
742
- try {
743
- const model = await defaultModel(this.config, directory)
744
-
745
- const forked = await this.sdk.session
746
- .fork(
747
- {
748
- sessionID: params.sessionId,
749
- directory,
750
- },
751
- { throwOnError: true },
752
- )
753
- .then((x) => x.data)
754
-
755
- if (!forked) {
756
- throw new Error("Fork session returned no data")
757
- }
758
-
759
- const sessionId = forked.id
760
- await this.sessionManager.load(sessionId, directory, mcpServers, model)
761
-
762
- log.info("fork_session", { sessionId, mcpServers: mcpServers.length })
763
-
764
- const mode = await this.loadSessionMode({
765
- cwd: directory,
766
- mcpServers,
767
- sessionId,
768
- })
769
-
770
- const messages = await this.sdk.session
771
- .messages(
772
- {
773
- sessionID: sessionId,
774
- directory,
775
- },
776
- { throwOnError: true },
777
- )
778
- .then((x) => x.data)
779
- .catch((err) => {
780
- log.error("unexpected error when fetching message", { error: err })
781
- return undefined
782
- })
783
-
784
- for (const msg of messages ?? []) {
785
- log.debug("replay message", msg)
786
- await this.processMessage(msg)
787
- }
788
-
789
- await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
790
-
791
- return mode
792
- } catch (e) {
793
- const error = MessageV2.fromError(e, {
794
- providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
795
- })
796
- if (LoadAPIKeyError.isInstance(error)) {
797
- throw RequestError.authRequired()
798
- }
799
- throw e
800
- }
801
- }
802
-
803
- async unstable_resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse> {
804
- const directory = params.cwd
805
- const sessionId = params.sessionId
806
- const mcpServers = params.mcpServers ?? []
807
-
808
- try {
809
- const model = await defaultModel(this.config, directory)
810
- await this.sessionManager.load(sessionId, directory, mcpServers, model)
811
-
812
- log.info("resume_session", { sessionId, mcpServers: mcpServers.length })
813
-
814
- const result = await this.loadSessionMode({
815
- cwd: directory,
816
- mcpServers,
817
- sessionId,
818
- })
819
-
820
- await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
821
-
822
- return result
823
- } catch (e) {
824
- const error = MessageV2.fromError(e, {
825
- providerID: ProviderID.make(this.config.defaultModel?.providerID ?? "unknown"),
826
- })
827
- if (LoadAPIKeyError.isInstance(error)) {
828
- throw RequestError.authRequired()
829
- }
830
- throw e
831
- }
832
- }
833
-
834
- private async processMessage(message: SessionMessageResponse) {
835
- log.debug("process message", message)
836
- if (message.info.role !== "assistant" && message.info.role !== "user") return
837
- const sessionId = message.info.sessionID
838
-
839
- for (const part of message.parts) {
840
- if (part.type === "tool") {
841
- await this.toolStart(sessionId, part)
842
- switch (part.state.status) {
843
- case "pending":
844
- this.bashSnapshots.delete(part.callID)
845
- break
846
- case "running":
847
- const output = this.bashOutput(part)
848
- const runningContent: ToolCallContent[] = []
849
- if (output) {
850
- runningContent.push({
851
- type: "content",
852
- content: {
853
- type: "text",
854
- text: output,
855
- },
856
- })
857
- }
858
- await this.connection
859
- .sessionUpdate({
860
- sessionId,
861
- update: {
862
- sessionUpdate: "tool_call_update",
863
- toolCallId: part.callID,
864
- status: "in_progress",
865
- kind: toToolKind(part.tool),
866
- title: part.tool,
867
- locations: toLocations(part.tool, part.state.input),
868
- rawInput: part.state.input,
869
- ...(runningContent.length > 0 && { content: runningContent }),
870
- },
871
- })
872
- .catch((err) => {
873
- log.error("failed to send tool in_progress to ACP", { error: err })
874
- })
875
- break
876
- case "completed":
877
- this.toolStarts.delete(part.callID)
878
- this.bashSnapshots.delete(part.callID)
879
- const kind = toToolKind(part.tool)
880
- const content: ToolCallContent[] = [
881
- {
882
- type: "content",
883
- content: {
884
- type: "text",
885
- text: part.state.output,
886
- },
887
- },
888
- ]
889
-
890
- if (kind === "edit") {
891
- const input = part.state.input
892
- const filePath = typeof input["filePath"] === "string" ? input["filePath"] : ""
893
- const oldText = typeof input["oldString"] === "string" ? input["oldString"] : ""
894
- const newText =
895
- typeof input["newString"] === "string"
896
- ? input["newString"]
897
- : typeof input["content"] === "string"
898
- ? input["content"]
899
- : ""
900
- content.push({
901
- type: "diff",
902
- path: filePath,
903
- oldText,
904
- newText,
905
- })
906
- }
907
-
908
- if (part.tool === "todowrite") {
909
- const parsedTodos = z.array(Todo.Info).safeParse(JSON.parse(part.state.output))
910
- if (parsedTodos.success) {
911
- await this.connection
912
- .sessionUpdate({
913
- sessionId,
914
- update: {
915
- sessionUpdate: "plan",
916
- entries: parsedTodos.data.map((todo) => {
917
- const status: PlanEntry["status"] =
918
- todo.status === "cancelled" ? "completed" : (todo.status as PlanEntry["status"])
919
- return {
920
- priority: "medium",
921
- status,
922
- content: todo.content,
923
- }
924
- }),
925
- },
926
- })
927
- .catch((err) => {
928
- log.error("failed to send session update for todo", { error: err })
929
- })
930
- } else {
931
- log.error("failed to parse todo output", { error: parsedTodos.error })
932
- }
933
- }
934
-
935
- await this.connection
936
- .sessionUpdate({
937
- sessionId,
938
- update: {
939
- sessionUpdate: "tool_call_update",
940
- toolCallId: part.callID,
941
- status: "completed",
942
- kind,
943
- content,
944
- title: part.state.title,
945
- rawInput: part.state.input,
946
- rawOutput: {
947
- output: part.state.output,
948
- metadata: part.state.metadata,
949
- },
950
- },
951
- })
952
- .catch((err) => {
953
- log.error("failed to send tool completed to ACP", { error: err })
954
- })
955
- break
956
- case "error":
957
- this.toolStarts.delete(part.callID)
958
- this.bashSnapshots.delete(part.callID)
959
- await this.connection
960
- .sessionUpdate({
961
- sessionId,
962
- update: {
963
- sessionUpdate: "tool_call_update",
964
- toolCallId: part.callID,
965
- status: "failed",
966
- kind: toToolKind(part.tool),
967
- title: part.tool,
968
- rawInput: part.state.input,
969
- content: [
970
- {
971
- type: "content",
972
- content: {
973
- type: "text",
974
- text: part.state.error,
975
- },
976
- },
977
- ],
978
- rawOutput: {
979
- error: part.state.error,
980
- metadata: part.state.metadata,
981
- },
982
- },
983
- })
984
- .catch((err) => {
985
- log.error("failed to send tool error to ACP", { error: err })
986
- })
987
- break
988
- }
989
- } else if (part.type === "text") {
990
- if (part.text) {
991
- const audience: Role[] | undefined = part.synthetic ? ["assistant"] : part.ignored ? ["user"] : undefined
992
- await this.connection
993
- .sessionUpdate({
994
- sessionId,
995
- update: {
996
- sessionUpdate: message.info.role === "user" ? "user_message_chunk" : "agent_message_chunk",
997
- messageId: message.info.id,
998
- content: {
999
- type: "text",
1000
- text: part.text,
1001
- ...(audience && { annotations: { audience } }),
1002
- },
1003
- },
1004
- })
1005
- .catch((err) => {
1006
- log.error("failed to send text to ACP", { error: err })
1007
- })
1008
- }
1009
- } else if (part.type === "file") {
1010
- // Replay file attachments as appropriate ACP content blocks.
1011
- // OpenCode stores files internally as { type: "file", url, filename, mime }.
1012
- // We convert these back to ACP blocks based on the URL scheme and MIME type:
1013
- // - file:// URLs → resource_link
1014
- // - data: URLs with image/* → image block
1015
- // - data: URLs with text/* or application/json → resource with text
1016
- // - data: URLs with other types → resource with blob
1017
- const url = part.url
1018
- const filename = part.filename ?? "file"
1019
- const mime = part.mime || "application/octet-stream"
1020
- const messageChunk = message.info.role === "user" ? "user_message_chunk" : "agent_message_chunk"
1021
-
1022
- if (url.startsWith("file://")) {
1023
- // Local file reference - send as resource_link
1024
- await this.connection
1025
- .sessionUpdate({
1026
- sessionId,
1027
- update: {
1028
- sessionUpdate: messageChunk,
1029
- messageId: message.info.id,
1030
- content: { type: "resource_link", uri: url, name: filename, mimeType: mime },
1031
- },
1032
- })
1033
- .catch((err) => {
1034
- log.error("failed to send resource_link to ACP", { error: err })
1035
- })
1036
- } else if (url.startsWith("data:")) {
1037
- // Embedded content - parse data URL and send as appropriate block type
1038
- const base64Match = url.match(/^data:([^;]+);base64,(.*)$/)
1039
- const dataMime = base64Match?.[1]
1040
- const base64Data = base64Match?.[2] ?? ""
1041
-
1042
- const effectiveMime = dataMime || mime
1043
-
1044
- if (effectiveMime.startsWith("image/")) {
1045
- // Image - send as image block
1046
- await this.connection
1047
- .sessionUpdate({
1048
- sessionId,
1049
- update: {
1050
- sessionUpdate: messageChunk,
1051
- messageId: message.info.id,
1052
- content: {
1053
- type: "image",
1054
- mimeType: effectiveMime,
1055
- data: base64Data,
1056
- uri: pathToFileURL(filename).href,
1057
- },
1058
- },
1059
- })
1060
- .catch((err) => {
1061
- log.error("failed to send image to ACP", { error: err })
1062
- })
1063
- } else {
1064
- // Non-image: text types get decoded, binary types stay as blob
1065
- const isText = effectiveMime.startsWith("text/") || effectiveMime === "application/json"
1066
- const fileUri = pathToFileURL(filename).href
1067
- const resource = isText
1068
- ? {
1069
- uri: fileUri,
1070
- mimeType: effectiveMime,
1071
- text: Buffer.from(base64Data, "base64").toString("utf-8"),
1072
- }
1073
- : { uri: fileUri, mimeType: effectiveMime, blob: base64Data }
1074
-
1075
- await this.connection
1076
- .sessionUpdate({
1077
- sessionId,
1078
- update: {
1079
- sessionUpdate: messageChunk,
1080
- messageId: message.info.id,
1081
- content: { type: "resource", resource },
1082
- },
1083
- })
1084
- .catch((err) => {
1085
- log.error("failed to send resource to ACP", { error: err })
1086
- })
1087
- }
1088
- }
1089
- // URLs that don't match file:// or data: are skipped (unsupported)
1090
- } else if (part.type === "reasoning") {
1091
- if (part.text) {
1092
- await this.connection
1093
- .sessionUpdate({
1094
- sessionId,
1095
- update: {
1096
- sessionUpdate: "agent_thought_chunk",
1097
- messageId: message.info.id,
1098
- content: {
1099
- type: "text",
1100
- text: part.text,
1101
- },
1102
- },
1103
- })
1104
- .catch((err) => {
1105
- log.error("failed to send reasoning to ACP", { error: err })
1106
- })
1107
- }
1108
- }
1109
- }
1110
- }
1111
-
1112
- private bashOutput(part: ToolPart) {
1113
- if (part.tool !== "bash") return
1114
- if (!("metadata" in part.state) || !part.state.metadata || typeof part.state.metadata !== "object") return
1115
- const output = part.state.metadata["output"]
1116
- if (typeof output !== "string") return
1117
- return output
1118
- }
1119
-
1120
- private async toolStart(sessionId: string, part: ToolPart) {
1121
- if (this.toolStarts.has(part.callID)) return
1122
- this.toolStarts.add(part.callID)
1123
- await this.connection
1124
- .sessionUpdate({
1125
- sessionId,
1126
- update: {
1127
- sessionUpdate: "tool_call",
1128
- toolCallId: part.callID,
1129
- title: part.tool,
1130
- kind: toToolKind(part.tool),
1131
- status: "pending",
1132
- locations: [],
1133
- rawInput: {},
1134
- },
1135
- })
1136
- .catch((error) => {
1137
- log.error("failed to send tool pending to ACP", { error })
1138
- })
1139
- }
1140
-
1141
- private async loadAvailableModes(directory: string): Promise<ModeOption[]> {
1142
- const agents = await this.config.sdk.app
1143
- .agents(
1144
- {
1145
- directory,
1146
- },
1147
- { throwOnError: true },
1148
- )
1149
- .then((resp) => resp.data!)
1150
-
1151
- return agents
1152
- .filter((agent) => agent.mode !== "subagent" && !agent.hidden)
1153
- .map((agent) => ({
1154
- id: agent.name,
1155
- name: agent.name,
1156
- description: agent.description,
1157
- }))
1158
- }
1159
-
1160
- private async resolveModeState(
1161
- directory: string,
1162
- sessionId: string,
1163
- ): Promise<{ availableModes: ModeOption[]; currentModeId?: string }> {
1164
- const availableModes = await this.loadAvailableModes(directory)
1165
- const currentModeId =
1166
- this.sessionManager.get(sessionId).modeId ||
1167
- (await (async () => {
1168
- if (!availableModes.length) return undefined
1169
- const defaultAgentName = await AgentModule.defaultAgent()
1170
- const resolvedModeId =
1171
- availableModes.find((mode) => mode.name === defaultAgentName)?.id ?? availableModes[0].id
1172
- this.sessionManager.setMode(sessionId, resolvedModeId)
1173
- return resolvedModeId
1174
- })())
1175
-
1176
- return { availableModes, currentModeId }
1177
- }
1178
-
1179
- private async loadSessionMode(params: LoadSessionRequest) {
1180
- const directory = params.cwd
1181
- const model = await defaultModel(this.config, directory)
1182
- const sessionId = params.sessionId
1183
-
1184
- const providers = await this.sdk.config.providers({ directory }).then((x) => x.data!.providers)
1185
- const entries = sortProvidersByName(providers)
1186
- const availableVariants = modelVariantsFromProviders(entries, model)
1187
- const currentVariant = this.sessionManager.getVariant(sessionId)
1188
- if (currentVariant && !availableVariants.includes(currentVariant)) {
1189
- this.sessionManager.setVariant(sessionId, undefined)
1190
- }
1191
- const availableModels = buildAvailableModels(entries, { includeVariants: true })
1192
- const modeState = await this.resolveModeState(directory, sessionId)
1193
- const currentModeId = modeState.currentModeId
1194
- const modes = currentModeId
1195
- ? {
1196
- availableModes: modeState.availableModes,
1197
- currentModeId,
1198
- }
1199
- : undefined
1200
-
1201
- const commands = await this.config.sdk.command
1202
- .list(
1203
- {
1204
- directory,
1205
- },
1206
- { throwOnError: true },
1207
- )
1208
- .then((resp) => resp.data!)
1209
-
1210
- const availableCommands = commands.map((command) => ({
1211
- name: command.name,
1212
- description: command.description ?? "",
1213
- }))
1214
- const names = new Set(availableCommands.map((c) => c.name))
1215
- if (!names.has("compact"))
1216
- availableCommands.push({
1217
- name: "compact",
1218
- description: "compact the session",
1219
- })
1220
-
1221
- const mcpServers: Record<string, Config.Mcp> = {}
1222
- for (const server of params.mcpServers) {
1223
- if ("type" in server) {
1224
- mcpServers[server.name] = {
1225
- url: server.url,
1226
- headers: server.headers.reduce<Record<string, string>>((acc, { name, value }) => {
1227
- acc[name] = value
1228
- return acc
1229
- }, {}),
1230
- type: "remote",
1231
- }
1232
- } else {
1233
- mcpServers[server.name] = {
1234
- type: "local",
1235
- command: [server.command, ...server.args],
1236
- environment: server.env.reduce<Record<string, string>>((acc, { name, value }) => {
1237
- acc[name] = value
1238
- return acc
1239
- }, {}),
1240
- }
1241
- }
1242
- }
1243
-
1244
- await Promise.all(
1245
- Object.entries(mcpServers).map(async ([key, mcp]) => {
1246
- await this.sdk.mcp
1247
- .add(
1248
- {
1249
- directory,
1250
- name: key,
1251
- config: mcp,
1252
- },
1253
- { throwOnError: true },
1254
- )
1255
- .catch((error) => {
1256
- log.error("failed to add mcp server", { name: key, error })
1257
- })
1258
- }),
1259
- )
1260
-
1261
- setTimeout(() => {
1262
- this.connection.sessionUpdate({
1263
- sessionId,
1264
- update: {
1265
- sessionUpdate: "available_commands_update",
1266
- availableCommands,
1267
- },
1268
- })
1269
- }, 0)
1270
-
1271
- return {
1272
- sessionId,
1273
- models: {
1274
- currentModelId: formatModelIdWithVariant(model, currentVariant, availableVariants, true),
1275
- availableModels,
1276
- },
1277
- modes,
1278
- configOptions: buildConfigOptions({
1279
- currentModelId: formatModelIdWithVariant(model, currentVariant, availableVariants, true),
1280
- availableModels,
1281
- modes,
1282
- }),
1283
- _meta: buildVariantMeta({
1284
- model,
1285
- variant: this.sessionManager.getVariant(sessionId),
1286
- availableVariants,
1287
- }),
1288
- }
1289
- }
1290
-
1291
- async unstable_setSessionModel(params: SetSessionModelRequest) {
1292
- const session = this.sessionManager.get(params.sessionId)
1293
- const providers = await this.sdk.config
1294
- .providers({ directory: session.cwd }, { throwOnError: true })
1295
- .then((x) => x.data!.providers)
1296
-
1297
- const selection = parseModelSelection(params.modelId, providers)
1298
- this.sessionManager.setModel(session.id, selection.model)
1299
- this.sessionManager.setVariant(session.id, selection.variant)
1300
-
1301
- const entries = sortProvidersByName(providers)
1302
- const availableVariants = modelVariantsFromProviders(entries, selection.model)
1303
-
1304
- return {
1305
- _meta: buildVariantMeta({
1306
- model: selection.model,
1307
- variant: selection.variant,
1308
- availableVariants,
1309
- }),
1310
- }
1311
- }
1312
-
1313
- async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse | void> {
1314
- const session = this.sessionManager.get(params.sessionId)
1315
- const availableModes = await this.loadAvailableModes(session.cwd)
1316
- if (!availableModes.some((mode) => mode.id === params.modeId)) {
1317
- throw new Error(`Agent not found: ${params.modeId}`)
1318
- }
1319
- this.sessionManager.setMode(params.sessionId, params.modeId)
1320
- }
1321
-
1322
- async setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse> {
1323
- const session = this.sessionManager.get(params.sessionId)
1324
- const providers = await this.sdk.config
1325
- .providers({ directory: session.cwd }, { throwOnError: true })
1326
- .then((x) => x.data!.providers)
1327
- const entries = sortProvidersByName(providers)
1328
-
1329
- if (params.configId === "model") {
1330
- if (typeof params.value !== "string") throw RequestError.invalidParams("model value must be a string")
1331
- const selection = parseModelSelection(params.value, providers)
1332
- this.sessionManager.setModel(session.id, selection.model)
1333
- this.sessionManager.setVariant(session.id, selection.variant)
1334
- } else if (params.configId === "mode") {
1335
- if (typeof params.value !== "string") throw RequestError.invalidParams("mode value must be a string")
1336
- const availableModes = await this.loadAvailableModes(session.cwd)
1337
- if (!availableModes.some((mode) => mode.id === params.value)) {
1338
- throw RequestError.invalidParams(JSON.stringify({ error: `Mode not found: ${params.value}` }))
1339
- }
1340
- this.sessionManager.setMode(session.id, params.value)
1341
- } else {
1342
- throw RequestError.invalidParams(JSON.stringify({ error: `Unknown config option: ${params.configId}` }))
1343
- }
1344
-
1345
- const updatedSession = this.sessionManager.get(session.id)
1346
- const model = updatedSession.model ?? (await defaultModel(this.config, session.cwd))
1347
- const availableVariants = modelVariantsFromProviders(entries, model)
1348
- const currentModelId = formatModelIdWithVariant(model, updatedSession.variant, availableVariants, true)
1349
- const availableModels = buildAvailableModels(entries, { includeVariants: true })
1350
- const modeState = await this.resolveModeState(session.cwd, session.id)
1351
- const modes = modeState.currentModeId
1352
- ? { availableModes: modeState.availableModes, currentModeId: modeState.currentModeId }
1353
- : undefined
1354
-
1355
- return {
1356
- configOptions: buildConfigOptions({ currentModelId, availableModels, modes }),
1357
- }
1358
- }
1359
-
1360
- async prompt(params: PromptRequest) {
1361
- const sessionID = params.sessionId
1362
- const session = this.sessionManager.get(sessionID)
1363
- const directory = session.cwd
1364
-
1365
- const current = session.model
1366
- const model = current ?? (await defaultModel(this.config, directory))
1367
- if (!current) {
1368
- this.sessionManager.setModel(session.id, model)
1369
- }
1370
- const agent = session.modeId ?? (await AgentModule.defaultAgent())
1371
-
1372
- const parts: Array<
1373
- | { type: "text"; text: string; synthetic?: boolean; ignored?: boolean }
1374
- | { type: "file"; url: string; filename: string; mime: string }
1375
- > = []
1376
- for (const part of params.prompt) {
1377
- switch (part.type) {
1378
- case "text":
1379
- const audience = part.annotations?.audience
1380
- const forAssistant = audience?.length === 1 && audience[0] === "assistant"
1381
- const forUser = audience?.length === 1 && audience[0] === "user"
1382
- parts.push({
1383
- type: "text" as const,
1384
- text: part.text,
1385
- ...(forAssistant && { synthetic: true }),
1386
- ...(forUser && { ignored: true }),
1387
- })
1388
- break
1389
- case "image": {
1390
- const parsed = parseUri(part.uri ?? "")
1391
- const filename = parsed.type === "file" ? parsed.filename : "image"
1392
- if (part.data) {
1393
- parts.push({
1394
- type: "file",
1395
- url: `data:${part.mimeType};base64,${part.data}`,
1396
- filename,
1397
- mime: part.mimeType,
1398
- })
1399
- } else if (part.uri && part.uri.startsWith("http:")) {
1400
- parts.push({
1401
- type: "file",
1402
- url: part.uri,
1403
- filename,
1404
- mime: part.mimeType,
1405
- })
1406
- }
1407
- break
1408
- }
1409
-
1410
- case "resource_link":
1411
- const parsed = parseUri(part.uri)
1412
- // Use the name from resource_link if available
1413
- if (part.name && parsed.type === "file") {
1414
- parsed.filename = part.name
1415
- }
1416
- parts.push(parsed)
1417
-
1418
- break
1419
-
1420
- case "resource": {
1421
- const resource = part.resource
1422
- if ("text" in resource && resource.text) {
1423
- parts.push({
1424
- type: "text",
1425
- text: resource.text,
1426
- })
1427
- } else if ("blob" in resource && resource.blob && resource.mimeType) {
1428
- // Binary resource (PDFs, etc.): store as file part with data URL
1429
- const parsed = parseUri(resource.uri ?? "")
1430
- const filename = parsed.type === "file" ? parsed.filename : "file"
1431
- parts.push({
1432
- type: "file",
1433
- url: `data:${resource.mimeType};base64,${resource.blob}`,
1434
- filename,
1435
- mime: resource.mimeType,
1436
- })
1437
- }
1438
- break
1439
- }
1440
-
1441
- default:
1442
- break
1443
- }
1444
- }
1445
-
1446
- log.info("parts", { parts })
1447
-
1448
- const cmd = (() => {
1449
- const text = parts
1450
- .filter((p): p is { type: "text"; text: string } => p.type === "text")
1451
- .map((p) => p.text)
1452
- .join("")
1453
- .trim()
1454
-
1455
- if (!text.startsWith("/")) return
1456
-
1457
- const [name, ...rest] = text.slice(1).split(/\s+/)
1458
- return { name, args: rest.join(" ").trim() }
1459
- })()
1460
-
1461
- const buildUsage = (msg: AssistantMessage): Usage => ({
1462
- totalTokens:
1463
- msg.tokens.input +
1464
- msg.tokens.output +
1465
- msg.tokens.reasoning +
1466
- (msg.tokens.cache?.read ?? 0) +
1467
- (msg.tokens.cache?.write ?? 0),
1468
- inputTokens: msg.tokens.input,
1469
- outputTokens: msg.tokens.output,
1470
- thoughtTokens: msg.tokens.reasoning || undefined,
1471
- cachedReadTokens: msg.tokens.cache?.read || undefined,
1472
- cachedWriteTokens: msg.tokens.cache?.write || undefined,
1473
- })
1474
-
1475
- if (!cmd) {
1476
- const response = await this.sdk.session.prompt({
1477
- sessionID,
1478
- model: {
1479
- providerID: model.providerID,
1480
- modelID: model.modelID,
1481
- },
1482
- variant: this.sessionManager.getVariant(sessionID),
1483
- parts,
1484
- agent,
1485
- directory,
1486
- })
1487
- const msg = response.data?.info
1488
-
1489
- await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
1490
-
1491
- return {
1492
- stopReason: "end_turn" as const,
1493
- usage: msg ? buildUsage(msg) : undefined,
1494
- _meta: {},
1495
- }
1496
- }
1497
-
1498
- const command = await this.config.sdk.command
1499
- .list({ directory }, { throwOnError: true })
1500
- .then((x) => x.data!.find((c) => c.name === cmd.name))
1501
- if (command) {
1502
- const response = await this.sdk.session.command({
1503
- sessionID,
1504
- command: command.name,
1505
- arguments: cmd.args,
1506
- model: model.providerID + "/" + model.modelID,
1507
- agent,
1508
- directory,
1509
- })
1510
- const msg = response.data?.info
1511
-
1512
- await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
1513
-
1514
- return {
1515
- stopReason: "end_turn" as const,
1516
- usage: msg ? buildUsage(msg) : undefined,
1517
- _meta: {},
1518
- }
1519
- }
1520
-
1521
- switch (cmd.name) {
1522
- case "compact":
1523
- await this.config.sdk.session.summarize(
1524
- {
1525
- sessionID,
1526
- directory,
1527
- providerID: model.providerID,
1528
- modelID: model.modelID,
1529
- },
1530
- { throwOnError: true },
1531
- )
1532
- break
1533
- }
1534
-
1535
- await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
1536
-
1537
- return {
1538
- stopReason: "end_turn" as const,
1539
- _meta: {},
1540
- }
1541
- }
1542
-
1543
- async cancel(params: CancelNotification) {
1544
- const session = this.sessionManager.get(params.sessionId)
1545
- await this.config.sdk.session.abort(
1546
- {
1547
- sessionID: params.sessionId,
1548
- directory: session.cwd,
1549
- },
1550
- { throwOnError: true },
1551
- )
1552
- }
1553
- }
1554
-
1555
- function toToolKind(toolName: string): ToolKind {
1556
- const tool = toolName.toLocaleLowerCase()
1557
- switch (tool) {
1558
- case "bash":
1559
- return "execute"
1560
- case "webfetch":
1561
- return "fetch"
1562
-
1563
- case "edit":
1564
- case "patch":
1565
- case "write":
1566
- return "edit"
1567
-
1568
- case "grep":
1569
- case "glob":
1570
- case "context7_resolve_library_id":
1571
- case "context7_get_library_docs":
1572
- return "search"
1573
-
1574
- case "list":
1575
- case "read":
1576
- return "read"
1577
-
1578
- default:
1579
- return "other"
1580
- }
1581
- }
1582
-
1583
- function toLocations(toolName: string, input: Record<string, any>): { path: string }[] {
1584
- const tool = toolName.toLocaleLowerCase()
1585
- switch (tool) {
1586
- case "read":
1587
- case "edit":
1588
- case "write":
1589
- return input["filePath"] ? [{ path: input["filePath"] }] : []
1590
- case "glob":
1591
- case "grep":
1592
- return input["path"] ? [{ path: input["path"] }] : []
1593
- case "bash":
1594
- return []
1595
- case "list":
1596
- return input["path"] ? [{ path: input["path"] }] : []
1597
- default:
1598
- return []
1599
- }
1600
- }
1601
-
1602
- async function defaultModel(config: ACPConfig, cwd?: string): Promise<{ providerID: ProviderID; modelID: ModelID }> {
1603
- const sdk = config.sdk
1604
- const configured = config.defaultModel
1605
- if (configured) return configured
1606
-
1607
- const directory = cwd ?? process.cwd()
1608
-
1609
- const specified = await sdk.config
1610
- .get({ directory }, { throwOnError: true })
1611
- .then((resp) => {
1612
- const cfg = resp.data
1613
- if (!cfg || !cfg.model) return undefined
1614
- return Provider.parseModel(cfg.model)
1615
- })
1616
- .catch((error) => {
1617
- log.error("failed to load user config for default model", { error })
1618
- return undefined
1619
- })
1620
-
1621
- const providers = await sdk.config
1622
- .providers({ directory }, { throwOnError: true })
1623
- .then((x) => x.data?.providers ?? [])
1624
- .catch((error) => {
1625
- log.error("failed to list providers for default model", { error })
1626
- return []
1627
- })
1628
-
1629
- if (specified && providers.length) {
1630
- const provider = providers.find((p) => p.id === specified.providerID)
1631
- if (provider && provider.models[specified.modelID]) return specified
1632
- }
1633
-
1634
- if (specified && !providers.length) return specified
1635
-
1636
- const opencodeProvider = providers.find((p) => p.id === "opencode")
1637
- if (opencodeProvider) {
1638
- if (opencodeProvider.models["big-pickle"]) {
1639
- return { providerID: ProviderID.opencode, modelID: ModelID.make("big-pickle") }
1640
- }
1641
- const [best] = Provider.sort(Object.values(opencodeProvider.models))
1642
- if (best) {
1643
- return {
1644
- providerID: ProviderID.make(best.providerID),
1645
- modelID: ModelID.make(best.id),
1646
- }
1647
- }
1648
- }
1649
-
1650
- const models = providers.flatMap((p) => Object.values(p.models))
1651
- const [best] = Provider.sort(models)
1652
- if (best) {
1653
- return {
1654
- providerID: ProviderID.make(best.providerID),
1655
- modelID: ModelID.make(best.id),
1656
- }
1657
- }
1658
-
1659
- if (specified) return specified
1660
-
1661
- return { providerID: ProviderID.opencode, modelID: ModelID.make("big-pickle") }
1662
- }
1663
-
1664
- function parseUri(
1665
- uri: string,
1666
- ): { type: "file"; url: string; filename: string; mime: string } | { type: "text"; text: string } {
1667
- try {
1668
- if (uri.startsWith("file://")) {
1669
- const path = uri.slice(7)
1670
- const name = path.split("/").pop() || path
1671
- return {
1672
- type: "file",
1673
- url: uri,
1674
- filename: name,
1675
- mime: "text/plain",
1676
- }
1677
- }
1678
- if (uri.startsWith("zed://")) {
1679
- const url = new URL(uri)
1680
- const path = url.searchParams.get("path")
1681
- if (path) {
1682
- const name = path.split("/").pop() || path
1683
- return {
1684
- type: "file",
1685
- url: pathToFileURL(path).href,
1686
- filename: name,
1687
- mime: "text/plain",
1688
- }
1689
- }
1690
- }
1691
- return {
1692
- type: "text",
1693
- text: uri,
1694
- }
1695
- } catch {
1696
- return {
1697
- type: "text",
1698
- text: uri,
1699
- }
1700
- }
1701
- }
1702
-
1703
- function getNewContent(fileOriginal: string, unifiedDiff: string): string | undefined {
1704
- const result = applyPatch(fileOriginal, unifiedDiff)
1705
- if (result === false) {
1706
- log.error("Failed to apply unified diff (context mismatch)")
1707
- return undefined
1708
- }
1709
- return result
1710
- }
1711
-
1712
- function sortProvidersByName<T extends { name: string }>(providers: T[]): T[] {
1713
- return [...providers].sort((a, b) => {
1714
- const nameA = a.name.toLowerCase()
1715
- const nameB = b.name.toLowerCase()
1716
- if (nameA < nameB) return -1
1717
- if (nameA > nameB) return 1
1718
- return 0
1719
- })
1720
- }
1721
-
1722
- function modelVariantsFromProviders(
1723
- providers: Array<{ id: string; models: Record<string, { variants?: Record<string, any> }> }>,
1724
- model: { providerID: ProviderID; modelID: ModelID },
1725
- ): string[] {
1726
- const provider = providers.find((entry) => entry.id === model.providerID)
1727
- if (!provider) return []
1728
- const modelInfo = provider.models[model.modelID]
1729
- if (!modelInfo?.variants) return []
1730
- return Object.keys(modelInfo.variants)
1731
- }
1732
-
1733
- function buildAvailableModels(
1734
- providers: Array<{ id: string; name: string; models: Record<string, any> }>,
1735
- options: { includeVariants?: boolean } = {},
1736
- ): ModelOption[] {
1737
- const includeVariants = options.includeVariants ?? false
1738
- return providers.flatMap((provider) => {
1739
- const unsorted: Array<{ id: string; name: string; variants?: Record<string, any> }> = Object.values(
1740
- provider.models,
1741
- )
1742
- const models = Provider.sort(unsorted)
1743
- return models.flatMap((model) => {
1744
- const base: ModelOption = {
1745
- modelId: `${provider.id}/${model.id}`,
1746
- name: `${provider.name}/${model.name}`,
1747
- }
1748
- if (!includeVariants || !model.variants) return [base]
1749
- const variants = Object.keys(model.variants).filter((variant) => variant !== DEFAULT_VARIANT_VALUE)
1750
- const variantOptions = variants.map((variant) => ({
1751
- modelId: `${provider.id}/${model.id}/${variant}`,
1752
- name: `${provider.name}/${model.name} (${variant})`,
1753
- }))
1754
- return [base, ...variantOptions]
1755
- })
1756
- })
1757
- }
1758
-
1759
- function formatModelIdWithVariant(
1760
- model: { providerID: ProviderID; modelID: ModelID },
1761
- variant: string | undefined,
1762
- availableVariants: string[],
1763
- includeVariant: boolean,
1764
- ) {
1765
- const base = `${model.providerID}/${model.modelID}`
1766
- if (!includeVariant || !variant || !availableVariants.includes(variant)) return base
1767
- return `${base}/${variant}`
1768
- }
1769
-
1770
- function buildVariantMeta(input: {
1771
- model: { providerID: ProviderID; modelID: ModelID }
1772
- variant?: string
1773
- availableVariants: string[]
1774
- }) {
1775
- return {
1776
- opencode: {
1777
- modelId: `${input.model.providerID}/${input.model.modelID}`,
1778
- variant: input.variant ?? null,
1779
- availableVariants: input.availableVariants,
1780
- },
1781
- }
1782
- }
1783
-
1784
- function parseModelSelection(
1785
- modelId: string,
1786
- providers: Array<{ id: string; models: Record<string, { variants?: Record<string, any> }> }>,
1787
- ): { model: { providerID: ProviderID; modelID: ModelID }; variant?: string } {
1788
- const parsed = Provider.parseModel(modelId)
1789
- const provider = providers.find((p) => p.id === parsed.providerID)
1790
- if (!provider) {
1791
- return { model: parsed, variant: undefined }
1792
- }
1793
-
1794
- // Check if modelID exists directly
1795
- if (provider.models[parsed.modelID]) {
1796
- return { model: parsed, variant: undefined }
1797
- }
1798
-
1799
- // Try to extract variant from end of modelID (e.g., "claude-sonnet-4/high" -> model: "claude-sonnet-4", variant: "high")
1800
- const segments = parsed.modelID.split("/")
1801
- if (segments.length > 1) {
1802
- const candidateVariant = segments[segments.length - 1]
1803
- const baseModelId = segments.slice(0, -1).join("/")
1804
- const baseModelInfo = provider.models[baseModelId]
1805
- if (baseModelInfo?.variants && candidateVariant in baseModelInfo.variants) {
1806
- return {
1807
- model: { providerID: parsed.providerID, modelID: ModelID.make(baseModelId) },
1808
- variant: candidateVariant,
1809
- }
1810
- }
1811
- }
1812
-
1813
- return { model: parsed, variant: undefined }
1814
- }
1815
-
1816
- function buildConfigOptions(input: {
1817
- currentModelId: string
1818
- availableModels: ModelOption[]
1819
- modes?: { availableModes: ModeOption[]; currentModeId: string } | undefined
1820
- }): SessionConfigOption[] {
1821
- const options: SessionConfigOption[] = [
1822
- {
1823
- id: "model",
1824
- name: "Model",
1825
- category: "model",
1826
- type: "select",
1827
- currentValue: input.currentModelId,
1828
- options: input.availableModels.map((m) => ({ value: m.modelId, name: m.name })),
1829
- },
1830
- ]
1831
- if (input.modes) {
1832
- options.push({
1833
- id: "mode",
1834
- name: "Session Mode",
1835
- category: "mode",
1836
- type: "select",
1837
- currentValue: input.modes.currentModeId,
1838
- options: input.modes.availableModes.map((m) => ({
1839
- value: m.id,
1840
- name: m.name,
1841
- ...(m.description ? { description: m.description } : {}),
1842
- })),
1843
- })
1844
- }
1845
- return options
1846
- }
1847
- }