@otto-code/server 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (734) hide show
  1. package/.env.example +31 -0
  2. package/README.md +111 -0
  3. package/dist/scripts/dev-runner.js +37 -0
  4. package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
  5. package/dist/scripts/supervisor-entrypoint.js +141 -0
  6. package/dist/scripts/supervisor-log-config.js +31 -0
  7. package/dist/scripts/supervisor.js +252 -0
  8. package/dist/server/executable-resolution/executable-resolution.d.ts +10 -0
  9. package/dist/server/executable-resolution/executable-resolution.js +118 -0
  10. package/dist/server/executable-resolution/windows.d.ts +18 -0
  11. package/dist/server/executable-resolution/windows.js +65 -0
  12. package/dist/server/server/agent/activity-curator.d.ts +28 -0
  13. package/dist/server/server/agent/activity-curator.js +227 -0
  14. package/dist/server/server/agent/agent-archive.d.ts +11 -0
  15. package/dist/server/server/agent/agent-archive.js +16 -0
  16. package/dist/server/server/agent/agent-loading.d.ts +14 -0
  17. package/dist/server/server/agent/agent-loading.js +54 -0
  18. package/dist/server/server/agent/agent-manager.d.ts +470 -0
  19. package/dist/server/server/agent/agent-manager.js +3049 -0
  20. package/dist/server/server/agent/agent-personalities.d.ts +59 -0
  21. package/dist/server/server/agent/agent-personalities.js +103 -0
  22. package/dist/server/server/agent/agent-projections.d.ts +35 -0
  23. package/dist/server/server/agent/agent-projections.js +488 -0
  24. package/dist/server/server/agent/agent-prompt.d.ts +76 -0
  25. package/dist/server/server/agent/agent-prompt.js +230 -0
  26. package/dist/server/server/agent/agent-response-loop.d.ts +72 -0
  27. package/dist/server/server/agent/agent-response-loop.js +333 -0
  28. package/dist/server/server/agent/agent-sdk-types.d.ts +736 -0
  29. package/dist/server/server/agent/agent-sdk-types.js +22 -0
  30. package/dist/server/server/agent/agent-storage.d.ts +147 -0
  31. package/dist/server/server/agent/agent-storage.js +326 -0
  32. package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
  33. package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
  34. package/dist/server/server/agent/agent-timeline-store-types.d.ts +50 -0
  35. package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
  36. package/dist/server/server/agent/agent-timeline-store.d.ts +27 -0
  37. package/dist/server/server/agent/agent-timeline-store.js +230 -0
  38. package/dist/server/server/agent/audio-utils.d.ts +3 -0
  39. package/dist/server/server/agent/audio-utils.js +19 -0
  40. package/dist/server/server/agent/create-agent/create.d.ts +91 -0
  41. package/dist/server/server/agent/create-agent/create.js +370 -0
  42. package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +54 -0
  43. package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +152 -0
  44. package/dist/server/server/agent/create-agent-mode.d.ts +13 -0
  45. package/dist/server/server/agent/create-agent-mode.js +62 -0
  46. package/dist/server/server/agent/create-agent-title.d.ts +10 -0
  47. package/dist/server/server/agent/create-agent-title.js +34 -0
  48. package/dist/server/server/agent/dictation-debug.d.ts +13 -0
  49. package/dist/server/server/agent/dictation-debug.js +50 -0
  50. package/dist/server/server/agent/effort-levels.d.ts +2 -0
  51. package/dist/server/server/agent/effort-levels.js +7 -0
  52. package/dist/server/server/agent/foreground-run-state.d.ts +50 -0
  53. package/dist/server/server/agent/foreground-run-state.js +160 -0
  54. package/dist/server/server/agent/import-sessions.d.ts +47 -0
  55. package/dist/server/server/agent/import-sessions.js +145 -0
  56. package/dist/server/server/agent/lifecycle-command.d.ts +73 -0
  57. package/dist/server/server/agent/lifecycle-command.js +92 -0
  58. package/dist/server/server/agent/mcp-server.d.ts +5 -0
  59. package/dist/server/server/agent/mcp-server.js +66 -0
  60. package/dist/server/server/agent/mcp-shared.d.ts +192 -0
  61. package/dist/server/server/agent/mcp-shared.js +201 -0
  62. package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
  63. package/dist/server/server/agent/pcm16-resampler.js +63 -0
  64. package/dist/server/server/agent/permission-response.d.ts +15 -0
  65. package/dist/server/server/agent/permission-response.js +14 -0
  66. package/dist/server/server/agent/personality-stats-store.d.ts +23 -0
  67. package/dist/server/server/agent/personality-stats-store.js +65 -0
  68. package/dist/server/server/agent/prompt-attachments.d.ts +7 -0
  69. package/dist/server/server/agent/prompt-attachments.js +117 -0
  70. package/dist/server/server/agent/provider-history-timestamps.d.ts +2 -0
  71. package/dist/server/server/agent/provider-history-timestamps.js +16 -0
  72. package/dist/server/server/agent/provider-launch-config.d.ts +43 -0
  73. package/dist/server/server/agent/provider-launch-config.js +167 -0
  74. package/dist/server/server/agent/provider-notices.d.ts +3 -0
  75. package/dist/server/server/agent/provider-notices.js +5 -0
  76. package/dist/server/server/agent/provider-registry.d.ts +41 -0
  77. package/dist/server/server/agent/provider-registry.js +532 -0
  78. package/dist/server/server/agent/provider-session-import.d.ts +10 -0
  79. package/dist/server/server/agent/provider-session-import.js +49 -0
  80. package/dist/server/server/agent/provider-snapshot-manager.d.ts +142 -0
  81. package/dist/server/server/agent/provider-snapshot-manager.js +690 -0
  82. package/dist/server/server/agent/providers/acp-agent.d.ts +353 -0
  83. package/dist/server/server/agent/providers/acp-agent.js +2439 -0
  84. package/dist/server/server/agent/providers/claude/agent.d.ts +66 -0
  85. package/dist/server/server/agent/providers/claude/agent.js +4535 -0
  86. package/dist/server/server/agent/providers/claude/feature-definitions.d.ts +8 -0
  87. package/dist/server/server/agent/providers/claude/feature-definitions.js +24 -0
  88. package/dist/server/server/agent/providers/claude/model-manifest.d.ts +90 -0
  89. package/dist/server/server/agent/providers/claude/model-manifest.js +205 -0
  90. package/dist/server/server/agent/providers/claude/models.d.ts +11 -0
  91. package/dist/server/server/agent/providers/claude/models.js +97 -0
  92. package/dist/server/server/agent/providers/claude/partial-json.d.ts +5 -0
  93. package/dist/server/server/agent/providers/claude/partial-json.js +306 -0
  94. package/dist/server/server/agent/providers/claude/project-dir.d.ts +6 -0
  95. package/dist/server/server/agent/providers/claude/project-dir.js +57 -0
  96. package/dist/server/server/agent/providers/claude/query.d.ts +17 -0
  97. package/dist/server/server/agent/providers/claude/query.js +86 -0
  98. package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
  99. package/dist/server/server/agent/providers/claude/rewind.js +30 -0
  100. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +22 -0
  101. package/dist/server/server/agent/providers/claude/sidechain-tracker.js +232 -0
  102. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +35 -0
  103. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +272 -0
  104. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
  105. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
  106. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
  107. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +130 -0
  108. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
  109. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +129 -0
  110. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +90 -0
  111. package/dist/server/server/agent/providers/codex/app-server-transport.js +262 -0
  112. package/dist/server/server/agent/providers/codex/rewind.d.ts +21 -0
  113. package/dist/server/server/agent/providers/codex/rewind.js +46 -0
  114. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts +36 -0
  115. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js +217 -0
  116. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +12 -0
  117. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
  118. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +24 -0
  119. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +830 -0
  120. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +308 -0
  121. package/dist/server/server/agent/providers/codex-app-server-agent.js +4515 -0
  122. package/dist/server/server/agent/providers/codex-feature-definitions.d.ts +11 -0
  123. package/dist/server/server/agent/providers/codex-feature-definitions.js +45 -0
  124. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +26 -0
  125. package/dist/server/server/agent/providers/copilot-acp-agent.js +188 -0
  126. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +15 -0
  127. package/dist/server/server/agent/providers/cursor-acp-agent.js +18 -0
  128. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +36 -0
  129. package/dist/server/server/agent/providers/diagnostic-utils.js +355 -0
  130. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +42 -0
  131. package/dist/server/server/agent/providers/generic-acp-agent.js +160 -0
  132. package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
  133. package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
  134. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +85 -0
  135. package/dist/server/server/agent/providers/mock-load-test-agent.js +1010 -0
  136. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +14 -0
  137. package/dist/server/server/agent/providers/mock-slow-provider.js +39 -0
  138. package/dist/server/server/agent/providers/openai-compat-agent.d.ts +422 -0
  139. package/dist/server/server/agent/providers/openai-compat-agent.js +2536 -0
  140. package/dist/server/server/agent/providers/openai-compat-feature-definitions.d.ts +40 -0
  141. package/dist/server/server/agent/providers/openai-compat-feature-definitions.js +82 -0
  142. package/dist/server/server/agent/providers/openai-compat-mcp.d.ts +87 -0
  143. package/dist/server/server/agent/providers/openai-compat-mcp.js +353 -0
  144. package/dist/server/server/agent/providers/openai-compat-otto-tool-permissions.d.ts +29 -0
  145. package/dist/server/server/agent/providers/openai-compat-otto-tool-permissions.js +96 -0
  146. package/dist/server/server/agent/providers/openai-compat-tools.d.ts +69 -0
  147. package/dist/server/server/agent/providers/openai-compat-tools.js +1224 -0
  148. package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
  149. package/dist/server/server/agent/providers/opencode/paths.js +7 -0
  150. package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
  151. package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
  152. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +91 -0
  153. package/dist/server/server/agent/providers/opencode/server-manager.js +425 -0
  154. package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +25 -0
  155. package/dist/server/server/agent/providers/opencode/test-server-manager.js +39 -0
  156. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.d.ts +92 -0
  157. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.js +260 -0
  158. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
  159. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +256 -0
  160. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
  161. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +53 -0
  162. package/dist/server/server/agent/providers/opencode-agent.d.ts +286 -0
  163. package/dist/server/server/agent/providers/opencode-agent.js +2830 -0
  164. package/dist/server/server/agent/providers/pi/agent.d.ts +132 -0
  165. package/dist/server/server/agent/providers/pi/agent.js +1649 -0
  166. package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +21 -0
  167. package/dist/server/server/agent/providers/pi/cli-runtime.js +261 -0
  168. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +9 -0
  169. package/dist/server/server/agent/providers/pi/history-mapper.js +140 -0
  170. package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
  171. package/dist/server/server/agent/providers/pi/rewind.js +8 -0
  172. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +208 -0
  173. package/dist/server/server/agent/providers/pi/rpc-types.js +2 -0
  174. package/dist/server/server/agent/providers/pi/runtime.d.ts +57 -0
  175. package/dist/server/server/agent/providers/pi/runtime.js +56 -0
  176. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +16 -0
  177. package/dist/server/server/agent/providers/pi/session-descriptor.js +346 -0
  178. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +83 -0
  179. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +184 -0
  180. package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +112 -0
  181. package/dist/server/server/agent/providers/pi/tool-call-mapper.js +284 -0
  182. package/dist/server/server/agent/providers/provider-image-output.d.ts +25 -0
  183. package/dist/server/server/agent/providers/provider-image-output.js +111 -0
  184. package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
  185. package/dist/server/server/agent/providers/provider-runner.js +81 -0
  186. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +3 -0
  187. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +60 -0
  188. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +884 -0
  189. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +778 -0
  190. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +25 -0
  191. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +208 -0
  192. package/dist/server/server/agent/providers/trae-acp-agent.d.ts +15 -0
  193. package/dist/server/server/agent/providers/trae-acp-agent.js +18 -0
  194. package/dist/server/server/agent/recordings-debug.d.ts +3 -0
  195. package/dist/server/server/agent/recordings-debug.js +19 -0
  196. package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
  197. package/dist/server/server/agent/rewind/rewind.js +29 -0
  198. package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
  199. package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
  200. package/dist/server/server/agent/runtime-mcp-config.d.ts +14 -0
  201. package/dist/server/server/agent/runtime-mcp-config.js +53 -0
  202. package/dist/server/server/agent/structured-generation-providers.d.ts +29 -0
  203. package/dist/server/server/agent/structured-generation-providers.js +236 -0
  204. package/dist/server/server/agent/stt-debug.d.ts +10 -0
  205. package/dist/server/server/agent/stt-debug.js +33 -0
  206. package/dist/server/server/agent/stt-manager.d.ts +38 -0
  207. package/dist/server/server/agent/stt-manager.js +246 -0
  208. package/dist/server/server/agent/system-prompt.d.ts +2 -0
  209. package/dist/server/server/agent/system-prompt.js +8 -0
  210. package/dist/server/server/agent/timeline-append.d.ts +10 -0
  211. package/dist/server/server/agent/timeline-append.js +27 -0
  212. package/dist/server/server/agent/timeline-projection.d.ts +71 -0
  213. package/dist/server/server/agent/timeline-projection.js +373 -0
  214. package/dist/server/server/agent/tools/otto-tools.d.ts +80 -0
  215. package/dist/server/server/agent/tools/otto-tools.js +2912 -0
  216. package/dist/server/server/agent/tools/types.d.ts +36 -0
  217. package/dist/server/server/agent/tools/types.js +2 -0
  218. package/dist/server/server/agent/tts-debug.d.ts +8 -0
  219. package/dist/server/server/agent/tts-debug.js +24 -0
  220. package/dist/server/server/agent/tts-manager.d.ts +41 -0
  221. package/dist/server/server/agent/tts-manager.js +375 -0
  222. package/dist/server/server/agent-attention-policy.d.ts +29 -0
  223. package/dist/server/server/agent-attention-policy.js +36 -0
  224. package/dist/server/server/artifact/artifact-prompt.d.ts +2 -0
  225. package/dist/server/server/artifact/artifact-prompt.js +15 -0
  226. package/dist/server/server/artifact/artifact-service.d.ts +91 -0
  227. package/dist/server/server/artifact/artifact-service.js +419 -0
  228. package/dist/server/server/artifact/artifact-store.d.ts +24 -0
  229. package/dist/server/server/artifact/artifact-store.js +168 -0
  230. package/dist/server/server/artifact/artifact-watcher.d.ts +34 -0
  231. package/dist/server/server/artifact/artifact-watcher.js +146 -0
  232. package/dist/server/server/artifact/html-validator.d.ts +26 -0
  233. package/dist/server/server/artifact/html-validator.js +92 -0
  234. package/dist/server/server/atomic-file.d.ts +5 -0
  235. package/dist/server/server/atomic-file.js +19 -0
  236. package/dist/server/server/auth.d.ts +38 -0
  237. package/dist/server/server/auth.js +143 -0
  238. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +40 -0
  239. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +92 -0
  240. package/dist/server/server/auto-archive-on-merge/index.d.ts +8 -0
  241. package/dist/server/server/auto-archive-on-merge/index.js +15 -0
  242. package/dist/server/server/bootstrap.d.ts +127 -0
  243. package/dist/server/server/bootstrap.js +1331 -0
  244. package/dist/server/server/browser-tools/broker.d.ts +45 -0
  245. package/dist/server/server/browser-tools/broker.js +390 -0
  246. package/dist/server/server/browser-tools/errors.d.ts +22 -0
  247. package/dist/server/server/browser-tools/errors.js +27 -0
  248. package/dist/server/server/browser-tools/policy.d.ts +10 -0
  249. package/dist/server/server/browser-tools/policy.js +16 -0
  250. package/dist/server/server/browser-tools/tools.d.ts +24 -0
  251. package/dist/server/server/browser-tools/tools.js +967 -0
  252. package/dist/server/server/chat/chat-mentions.d.ts +47 -0
  253. package/dist/server/server/chat/chat-mentions.js +113 -0
  254. package/dist/server/server/chat/chat-service.d.ts +78 -0
  255. package/dist/server/server/chat/chat-service.js +337 -0
  256. package/dist/server/server/checkout/status-projection.d.ts +19 -0
  257. package/dist/server/server/checkout/status-projection.js +117 -0
  258. package/dist/server/server/checkout-diff-manager.d.ts +41 -0
  259. package/dist/server/server/checkout-diff-manager.js +189 -0
  260. package/dist/server/server/checkout-git-utils.d.ts +11 -0
  261. package/dist/server/server/checkout-git-utils.js +34 -0
  262. package/dist/server/server/client-message-id.d.ts +3 -0
  263. package/dist/server/server/client-message-id.js +12 -0
  264. package/dist/server/server/config.d.ts +22 -0
  265. package/dist/server/server/config.js +330 -0
  266. package/dist/server/server/connection-offer.d.ts +20 -0
  267. package/dist/server/server/connection-offer.js +59 -0
  268. package/dist/server/server/daemon-config-store.d.ts +44 -0
  269. package/dist/server/server/daemon-config-store.js +530 -0
  270. package/dist/server/server/daemon-keypair.d.ts +8 -0
  271. package/dist/server/server/daemon-keypair.js +42 -0
  272. package/dist/server/server/daemon-version.d.ts +5 -0
  273. package/dist/server/server/daemon-version.js +22 -0
  274. package/dist/server/server/daemon-worker.d.ts +2 -0
  275. package/dist/server/server/daemon-worker.js +251 -0
  276. package/dist/server/server/dictation/dictation-stream-manager.d.ts +87 -0
  277. package/dist/server/server/dictation/dictation-stream-manager.js +572 -0
  278. package/dist/server/server/exports.d.ts +24 -0
  279. package/dist/server/server/exports.js +24 -0
  280. package/dist/server/server/file-download/token-store.d.ts +24 -0
  281. package/dist/server/server/file-download/token-store.js +40 -0
  282. package/dist/server/server/file-explorer/code-index.d.ts +28 -0
  283. package/dist/server/server/file-explorer/code-index.js +127 -0
  284. package/dist/server/server/file-explorer/file-search.d.ts +72 -0
  285. package/dist/server/server/file-explorer/file-search.js +297 -0
  286. package/dist/server/server/file-explorer/file-watcher.d.ts +40 -0
  287. package/dist/server/server/file-explorer/file-watcher.js +0 -0
  288. package/dist/server/server/file-explorer/gitignore.d.ts +39 -0
  289. package/dist/server/server/file-explorer/gitignore.js +191 -0
  290. package/dist/server/server/file-explorer/service.d.ts +103 -0
  291. package/dist/server/server/file-explorer/service.js +420 -0
  292. package/dist/server/server/file-upload/index.d.ts +27 -0
  293. package/dist/server/server/file-upload/index.js +158 -0
  294. package/dist/server/server/hostnames.d.ts +13 -0
  295. package/dist/server/server/hostnames.js +94 -0
  296. package/dist/server/server/json-utils.d.ts +11 -0
  297. package/dist/server/server/json-utils.js +45 -0
  298. package/dist/server/server/lifecycle-reasons.d.ts +4 -0
  299. package/dist/server/server/lifecycle-reasons.js +6 -0
  300. package/dist/server/server/logger.d.ts +29 -0
  301. package/dist/server/server/logger.js +125 -0
  302. package/dist/server/server/loop-service.d.ts +234 -0
  303. package/dist/server/server/loop-service.js +804 -0
  304. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  305. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  306. package/dist/server/server/messages.d.ts +9 -0
  307. package/dist/server/server/messages.js +29 -0
  308. package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
  309. package/dist/server/server/migrations/backfill-workspace-id.migration.js +64 -0
  310. package/dist/server/server/otto-env.d.ts +13 -0
  311. package/dist/server/server/otto-env.js +53 -0
  312. package/dist/server/server/otto-home.d.ts +2 -0
  313. package/dist/server/server/otto-home.js +19 -0
  314. package/dist/server/server/otto-worktree-service.d.ts +52 -0
  315. package/dist/server/server/otto-worktree-service.js +294 -0
  316. package/dist/server/server/package-version.d.ts +19 -0
  317. package/dist/server/server/package-version.js +60 -0
  318. package/dist/server/server/pagination/cursor.d.ts +16 -0
  319. package/dist/server/server/pagination/cursor.js +62 -0
  320. package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
  321. package/dist/server/server/pagination/sortable-pager.js +68 -0
  322. package/dist/server/server/pairing-offer.d.ts +18 -0
  323. package/dist/server/server/pairing-offer.js +47 -0
  324. package/dist/server/server/pairing-qr.d.ts +7 -0
  325. package/dist/server/server/pairing-qr.js +45 -0
  326. package/dist/server/server/path-utils.d.ts +5 -0
  327. package/dist/server/server/path-utils.js +34 -0
  328. package/dist/server/server/persisted-config.d.ts +417 -0
  329. package/dist/server/server/persisted-config.js +471 -0
  330. package/dist/server/server/persistence-hooks.d.ts +31 -0
  331. package/dist/server/server/persistence-hooks.js +93 -0
  332. package/dist/server/server/pid-lock.d.ts +32 -0
  333. package/dist/server/server/pid-lock.js +170 -0
  334. package/dist/server/server/preview/dev-server-manager.d.ts +108 -0
  335. package/dist/server/server/preview/dev-server-manager.js +426 -0
  336. package/dist/server/server/preview/launch-config.d.ts +37 -0
  337. package/dist/server/server/preview/launch-config.js +63 -0
  338. package/dist/server/server/preview/preview-tools.d.ts +30 -0
  339. package/dist/server/server/preview/preview-tools.js +249 -0
  340. package/dist/server/server/private-files.d.ts +6 -0
  341. package/dist/server/server/private-files.js +37 -0
  342. package/dist/server/server/process-diagnostics.d.ts +17 -0
  343. package/dist/server/server/process-diagnostics.js +22 -0
  344. package/dist/server/server/push/notifications.d.ts +9 -0
  345. package/dist/server/server/push/notifications.js +15 -0
  346. package/dist/server/server/push/push-service.d.ts +20 -0
  347. package/dist/server/server/push/push-service.js +66 -0
  348. package/dist/server/server/push/token-store.d.ts +18 -0
  349. package/dist/server/server/push/token-store.js +68 -0
  350. package/dist/server/server/relay-transport.d.ts +31 -0
  351. package/dist/server/server/relay-transport.js +514 -0
  352. package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
  353. package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
  354. package/dist/server/server/resolve-worktree-creation-intent.d.ts +32 -0
  355. package/dist/server/server/resolve-worktree-creation-intent.js +121 -0
  356. package/dist/server/server/schedule/cron.d.ts +4 -0
  357. package/dist/server/server/schedule/cron.js +81 -0
  358. package/dist/server/server/schedule/service.d.ts +89 -0
  359. package/dist/server/server/schedule/service.js +882 -0
  360. package/dist/server/server/schedule/store.d.ts +27 -0
  361. package/dist/server/server/schedule/store.js +190 -0
  362. package/dist/server/server/script-health-monitor.d.ts +39 -0
  363. package/dist/server/server/script-health-monitor.js +159 -0
  364. package/dist/server/server/script-proxy.d.ts +3 -0
  365. package/dist/server/server/script-proxy.js +2 -0
  366. package/dist/server/server/script-route-branch-handler.d.ts +10 -0
  367. package/dist/server/server/script-route-branch-handler.js +11 -0
  368. package/dist/server/server/script-status-projection.d.ts +36 -0
  369. package/dist/server/server/script-status-projection.js +200 -0
  370. package/dist/server/server/server-id.d.ts +17 -0
  371. package/dist/server/server/server-id.js +68 -0
  372. package/dist/server/server/service-proxy.d.ts +237 -0
  373. package/dist/server/server/service-proxy.js +714 -0
  374. package/dist/server/server/session/agent-config/agent-config-session.d.ts +59 -0
  375. package/dist/server/server/session/agent-config/agent-config-session.js +110 -0
  376. package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +65 -0
  377. package/dist/server/server/session/agent-updates/agent-updates-service.js +232 -0
  378. package/dist/server/server/session/artifact/artifact-session.d.ts +45 -0
  379. package/dist/server/server/session/artifact/artifact-session.js +293 -0
  380. package/dist/server/server/session/chat/chat-schedule-loop-session.d.ts +120 -0
  381. package/dist/server/server/session/chat/chat-schedule-loop-session.js +489 -0
  382. package/dist/server/server/session/checkout/checkout-session.d.ts +146 -0
  383. package/dist/server/server/session/checkout/checkout-session.js +1000 -0
  384. package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
  385. package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
  386. package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
  387. package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
  388. package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
  389. package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
  390. package/dist/server/server/session/daemon/daemon-session.d.ts +76 -0
  391. package/dist/server/server/session/daemon/daemon-session.js +148 -0
  392. package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
  393. package/dist/server/server/session/daemon/diagnostics.js +431 -0
  394. package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
  395. package/dist/server/server/session/daemon/install-origin.js +64 -0
  396. package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
  397. package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
  398. package/dist/server/server/session/files/workspace-files-session.d.ts +60 -0
  399. package/dist/server/server/session/files/workspace-files-session.js +518 -0
  400. package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
  401. package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
  402. package/dist/server/server/session/project-config/project-config-session.d.ts +34 -0
  403. package/dist/server/server/session/project-config/project-config-session.js +125 -0
  404. package/dist/server/server/session/provider/provider-catalog-session.d.ts +74 -0
  405. package/dist/server/server/session/provider/provider-catalog-session.js +343 -0
  406. package/dist/server/server/session/voice/voice-session.d.ts +167 -0
  407. package/dist/server/server/session/voice/voice-session.js +912 -0
  408. package/dist/server/server/session/voice/voice-turn-controller.d.ts +38 -0
  409. package/dist/server/server/session/voice/voice-turn-controller.js +393 -0
  410. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
  411. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
  412. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
  413. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
  414. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
  415. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
  416. package/dist/server/server/session.d.ts +450 -0
  417. package/dist/server/server/session.js +4763 -0
  418. package/dist/server/server/speech/audio.d.ts +10 -0
  419. package/dist/server/server/speech/audio.js +101 -0
  420. package/dist/server/server/speech/provider-resolver.d.ts +3 -0
  421. package/dist/server/server/speech/provider-resolver.js +7 -0
  422. package/dist/server/server/speech/providers/local/config.d.ts +30 -0
  423. package/dist/server/server/speech/providers/local/config.js +133 -0
  424. package/dist/server/server/speech/providers/local/models.d.ts +13 -0
  425. package/dist/server/server/speech/providers/local/models.js +21 -0
  426. package/dist/server/server/speech/providers/local/runtime.d.ts +31 -0
  427. package/dist/server/server/speech/providers/local/runtime.js +135 -0
  428. package/dist/server/server/speech/providers/local/sherpa/assets/silero_vad.onnx +0 -0
  429. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +109 -0
  430. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +129 -0
  431. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +16 -0
  432. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +195 -0
  433. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +49 -0
  434. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +73 -0
  435. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +9 -0
  436. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +107 -0
  437. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts +28 -0
  438. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +137 -0
  439. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +21 -0
  440. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +133 -0
  441. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts +18 -0
  442. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
  443. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +24 -0
  444. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +145 -0
  445. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts +19 -0
  446. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +49 -0
  447. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts +38 -0
  448. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js +176 -0
  449. package/dist/server/server/speech/providers/local/sherpa/tts-voices.d.ts +10 -0
  450. package/dist/server/server/speech/providers/local/sherpa/tts-voices.js +86 -0
  451. package/dist/server/server/speech/providers/local/worker-bytes.d.ts +4 -0
  452. package/dist/server/server/speech/providers/local/worker-bytes.js +9 -0
  453. package/dist/server/server/speech/providers/local/worker-client.d.ts +93 -0
  454. package/dist/server/server/speech/providers/local/worker-client.js +591 -0
  455. package/dist/server/server/speech/providers/local/worker-process.d.ts +2 -0
  456. package/dist/server/server/speech/providers/local/worker-process.js +270 -0
  457. package/dist/server/server/speech/providers/local/worker-protocol.d.ts +96 -0
  458. package/dist/server/server/speech/providers/local/worker-protocol.js +2 -0
  459. package/dist/server/server/speech/providers/openai/config.d.ts +19 -0
  460. package/dist/server/server/speech/providers/openai/config.js +160 -0
  461. package/dist/server/server/speech/providers/openai/runtime.d.ts +29 -0
  462. package/dist/server/server/speech/providers/openai/runtime.js +102 -0
  463. package/dist/server/server/speech/providers/openai/stt.d.ts +23 -0
  464. package/dist/server/server/speech/providers/openai/stt.js +209 -0
  465. package/dist/server/server/speech/providers/openai/tts.d.ts +20 -0
  466. package/dist/server/server/speech/providers/openai/tts.js +70 -0
  467. package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
  468. package/dist/server/server/speech/speech-config-resolver.js +132 -0
  469. package/dist/server/server/speech/speech-provider.d.ts +71 -0
  470. package/dist/server/server/speech/speech-provider.js +2 -0
  471. package/dist/server/server/speech/speech-runtime.d.ts +67 -0
  472. package/dist/server/server/speech/speech-runtime.js +615 -0
  473. package/dist/server/server/speech/speech-settings-options.d.ts +14 -0
  474. package/dist/server/server/speech/speech-settings-options.js +73 -0
  475. package/dist/server/server/speech/speech-types.d.ts +22 -0
  476. package/dist/server/server/speech/speech-types.js +8 -0
  477. package/dist/server/server/speech/turn-detection-provider.d.ts +23 -0
  478. package/dist/server/server/speech/turn-detection-provider.js +2 -0
  479. package/dist/server/server/utils/diff-highlighter.d.ts +57 -0
  480. package/dist/server/server/utils/diff-highlighter.js +279 -0
  481. package/dist/server/server/voice-config.d.ts +15 -0
  482. package/dist/server/server/voice-config.js +54 -0
  483. package/dist/server/server/voice-permission-policy.d.ts +4 -0
  484. package/dist/server/server/voice-permission-policy.js +13 -0
  485. package/dist/server/server/voice-types.d.ts +12 -0
  486. package/dist/server/server/voice-types.js +2 -0
  487. package/dist/server/server/web-ui.d.ts +10 -0
  488. package/dist/server/server/web-ui.js +205 -0
  489. package/dist/server/server/websocket/runtime-metrics.d.ts +94 -0
  490. package/dist/server/server/websocket/runtime-metrics.js +148 -0
  491. package/dist/server/server/websocket-server.d.ts +187 -0
  492. package/dist/server/server/websocket-server.js +1766 -0
  493. package/dist/server/server/workspace-archive-service.d.ts +65 -0
  494. package/dist/server/server/workspace-archive-service.js +229 -0
  495. package/dist/server/server/workspace-auto-name.d.ts +55 -0
  496. package/dist/server/server/workspace-auto-name.js +128 -0
  497. package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
  498. package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
  499. package/dist/server/server/workspace-directory.d.ts +93 -0
  500. package/dist/server/server/workspace-directory.js +422 -0
  501. package/dist/server/server/workspace-git-metadata.d.ts +24 -0
  502. package/dist/server/server/workspace-git-metadata.js +55 -0
  503. package/dist/server/server/workspace-git-service.d.ts +270 -0
  504. package/dist/server/server/workspace-git-service.js +1423 -0
  505. package/dist/server/server/workspace-reconciliation-service.d.ts +57 -0
  506. package/dist/server/server/workspace-reconciliation-service.js +278 -0
  507. package/dist/server/server/workspace-registry-bootstrap.d.ts +13 -0
  508. package/dist/server/server/workspace-registry-bootstrap.js +121 -0
  509. package/dist/server/server/workspace-registry-model.d.ts +55 -0
  510. package/dist/server/server/workspace-registry-model.js +190 -0
  511. package/dist/server/server/workspace-registry.d.ts +117 -0
  512. package/dist/server/server/workspace-registry.js +194 -0
  513. package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
  514. package/dist/server/server/workspace-script-runtime-store.js +78 -0
  515. package/dist/server/server/workspace-service-env.d.ts +18 -0
  516. package/dist/server/server/workspace-service-env.js +85 -0
  517. package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
  518. package/dist/server/server/workspace-service-port-registry.js +59 -0
  519. package/dist/server/server/worktree/commands.d.ts +55 -0
  520. package/dist/server/server/worktree/commands.js +107 -0
  521. package/dist/server/server/worktree-bootstrap.d.ts +76 -0
  522. package/dist/server/server/worktree-bootstrap.js +720 -0
  523. package/dist/server/server/worktree-branch-name-generator.d.ts +35 -0
  524. package/dist/server/server/worktree-branch-name-generator.js +91 -0
  525. package/dist/server/server/worktree-core.d.ts +31 -0
  526. package/dist/server/server/worktree-core.js +105 -0
  527. package/dist/server/server/worktree-errors.d.ts +12 -0
  528. package/dist/server/server/worktree-errors.js +28 -0
  529. package/dist/server/server/worktree-session.d.ts +152 -0
  530. package/dist/server/server/worktree-session.js +466 -0
  531. package/dist/server/services/git-hosting/bitbucket-cloud-service.d.ts +24 -0
  532. package/dist/server/services/git-hosting/bitbucket-cloud-service.js +754 -0
  533. package/dist/server/services/git-hosting/github-hosting-service.d.ts +4 -0
  534. package/dist/server/services/git-hosting/github-hosting-service.js +11 -0
  535. package/dist/server/services/git-hosting/hosting-http-client.d.ts +20 -0
  536. package/dist/server/services/git-hosting/hosting-http-client.js +131 -0
  537. package/dist/server/services/git-hosting/request-cache.d.ts +20 -0
  538. package/dist/server/services/git-hosting/request-cache.js +68 -0
  539. package/dist/server/services/git-hosting/resolver.d.ts +32 -0
  540. package/dist/server/services/git-hosting/resolver.js +152 -0
  541. package/dist/server/services/git-hosting/router.d.ts +4 -0
  542. package/dist/server/services/git-hosting/router.js +105 -0
  543. package/dist/server/services/git-hosting/status-poller.d.ts +39 -0
  544. package/dist/server/services/git-hosting/status-poller.js +149 -0
  545. package/dist/server/services/git-hosting/types.d.ts +53 -0
  546. package/dist/server/services/git-hosting/types.js +75 -0
  547. package/dist/server/services/github-service.d.ts +339 -0
  548. package/dist/server/services/github-service.js +2109 -0
  549. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  550. package/dist/server/services/quota-fetcher/manifest.js +52 -0
  551. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  552. package/dist/server/services/quota-fetcher/provider.js +2 -0
  553. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  554. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  555. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  556. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  557. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  558. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  559. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  560. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  561. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  562. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  563. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  564. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  565. package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
  566. package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
  567. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  568. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  569. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  570. package/dist/server/services/quota-fetcher/service.js +58 -0
  571. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  572. package/dist/server/services/quota-fetcher/usage.js +49 -0
  573. package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +20 -0
  574. package/dist/server/terminal/activity/terminal-activity-tracker.js +59 -0
  575. package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
  576. package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
  577. package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
  578. package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
  579. package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
  580. package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
  581. package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
  582. package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
  583. package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
  584. package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
  585. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
  586. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +46 -0
  587. package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
  588. package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
  589. package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
  590. package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
  591. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
  592. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
  593. package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
  594. package/dist/server/terminal/shell-integration/zsh/otto-integration.zsh +32 -0
  595. package/dist/server/terminal/terminal-capture.d.ts +12 -0
  596. package/dist/server/terminal/terminal-capture.js +43 -0
  597. package/dist/server/terminal/terminal-manager-factory.d.ts +6 -0
  598. package/dist/server/terminal/terminal-manager-factory.js +5 -0
  599. package/dist/server/terminal/terminal-manager.d.ts +78 -0
  600. package/dist/server/terminal/terminal-manager.js +319 -0
  601. package/dist/server/terminal/terminal-output-coalescer.d.ts +34 -0
  602. package/dist/server/terminal/terminal-output-coalescer.js +73 -0
  603. package/dist/server/terminal/terminal-restore.d.ts +18 -0
  604. package/dist/server/terminal/terminal-restore.js +52 -0
  605. package/dist/server/terminal/terminal-session-controller.d.ts +85 -0
  606. package/dist/server/terminal/terminal-session-controller.js +819 -0
  607. package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
  608. package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
  609. package/dist/server/terminal/terminal-worker-process.js +304 -0
  610. package/dist/server/terminal/terminal-worker-protocol.d.ts +119 -0
  611. package/dist/server/terminal/terminal-worker-protocol.js +2 -0
  612. package/dist/server/terminal/terminal.d.ts +151 -0
  613. package/dist/server/terminal/terminal.js +1155 -0
  614. package/dist/server/terminal/worker-terminal-manager.d.ts +20 -0
  615. package/dist/server/terminal/worker-terminal-manager.js +688 -0
  616. package/dist/server/utils/build-metadata-prompt.d.ts +19 -0
  617. package/dist/server/utils/build-metadata-prompt.js +29 -0
  618. package/dist/server/utils/checkout-git.d.ts +253 -0
  619. package/dist/server/utils/checkout-git.js +2241 -0
  620. package/dist/server/utils/directory-suggestions.d.ts +26 -0
  621. package/dist/server/utils/directory-suggestions.js +830 -0
  622. package/dist/server/utils/git-rev-parse-path.d.ts +3 -0
  623. package/dist/server/utils/git-rev-parse-path.js +21 -0
  624. package/dist/server/utils/github-remote.d.ts +9 -0
  625. package/dist/server/utils/github-remote.js +62 -0
  626. package/dist/server/utils/otto-config-file.d.ts +30 -0
  627. package/dist/server/utils/otto-config-file.js +90 -0
  628. package/dist/server/utils/path.d.ts +18 -0
  629. package/dist/server/utils/path.js +123 -0
  630. package/dist/server/utils/project-icon.d.ts +39 -0
  631. package/dist/server/utils/project-icon.js +364 -0
  632. package/dist/server/utils/promise-timeout.d.ts +9 -0
  633. package/dist/server/utils/promise-timeout.js +25 -0
  634. package/dist/server/utils/run-git-command.d.ts +37 -0
  635. package/dist/server/utils/run-git-command.js +232 -0
  636. package/dist/server/utils/spawn.d.ts +25 -0
  637. package/dist/server/utils/spawn.js +59 -0
  638. package/dist/server/utils/string-command-shell.d.ts +13 -0
  639. package/dist/server/utils/string-command-shell.js +37 -0
  640. package/dist/server/utils/tree-kill.d.ts +18 -0
  641. package/dist/server/utils/tree-kill.js +77 -0
  642. package/dist/server/utils/windows-command.d.ts +15 -0
  643. package/dist/server/utils/windows-command.js +41 -0
  644. package/dist/server/utils/worktree-metadata.d.ts +39 -0
  645. package/dist/server/utils/worktree-metadata.js +174 -0
  646. package/dist/server/utils/worktree.d.ts +205 -0
  647. package/dist/server/utils/worktree.js +1120 -0
  648. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
  649. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
  650. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
  651. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js +1 -0
  652. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js.br +0 -0
  653. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js.gz +0 -0
  654. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js +1 -0
  655. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js.br +0 -0
  656. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js.gz +0 -0
  657. package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js +3199 -0
  658. package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js.br +0 -0
  659. package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js.gz +0 -0
  660. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js +1 -0
  661. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js.br +0 -0
  662. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js.gz +0 -0
  663. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js +3 -0
  664. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js.br +0 -0
  665. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js.gz +0 -0
  666. package/dist/server/web-ui/apple-touch-icon.png +0 -0
  667. package/dist/server/web-ui/assets/__node_modules/@expo-google-fonts/inter/400Regular/Inter_400Regular.51b6ad87261f18b6433ec52871ddfabc.ttf +0 -0
  668. package/dist/server/web-ui/assets/__node_modules/@expo-google-fonts/jetbrains-mono/400Regular/JetBrainsMono_400Regular.a0147b5ab9e4946e81879aef45313def.ttf +0 -0
  669. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  670. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  671. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  672. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  673. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  674. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  675. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  676. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  677. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  678. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  679. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  680. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  681. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  682. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  683. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  684. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  685. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  686. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  687. package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
  688. package/dist/server/web-ui/assets/assets/images/editor-apps/clion.baa1f4eb572e962a6117bdbee95749e4.png +0 -0
  689. package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
  690. package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
  691. package/dist/server/web-ui/assets/assets/images/editor-apps/goland.0796ab2b9be2637fd1cdcde891fad3ca.png +0 -0
  692. package/dist/server/web-ui/assets/assets/images/editor-apps/intellij-idea.33bc9ff9c0815d8ad43f618c8c244401.png +0 -0
  693. package/dist/server/web-ui/assets/assets/images/editor-apps/phpstorm.c60b33bc7a8309143d232e14b76554c5.png +0 -0
  694. package/dist/server/web-ui/assets/assets/images/editor-apps/pycharm.88f25ac484989c45c25323e0adccfa9f.png +0 -0
  695. package/dist/server/web-ui/assets/assets/images/editor-apps/rider.1a1091dc3a0ede0461f0aa38a54f080a.png +0 -0
  696. package/dist/server/web-ui/assets/assets/images/editor-apps/rubymine.0e5b66fd7d43f8cbac0f05bba97c9a21.png +0 -0
  697. package/dist/server/web-ui/assets/assets/images/editor-apps/rustrover.3590ef05482369f41d3484c90272570d.png +0 -0
  698. package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
  699. package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
  700. package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
  701. package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.3b2c331d11122d8f743c73a5878593be.png +0 -0
  702. package/dist/server/web-ui/assets/assets/images/favicon-dark-running.6077c2ab71021b31c82a42e0b42ad837.png +0 -0
  703. package/dist/server/web-ui/assets/assets/images/favicon-dark.12984efe6d435a13794a74cd2f858dcd.png +0 -0
  704. package/dist/server/web-ui/assets/assets/images/favicon-light-attention.3b2c331d11122d8f743c73a5878593be.png +0 -0
  705. package/dist/server/web-ui/assets/assets/images/favicon-light-running.6077c2ab71021b31c82a42e0b42ad837.png +0 -0
  706. package/dist/server/web-ui/assets/assets/images/favicon-light.12984efe6d435a13794a74cd2f858dcd.png +0 -0
  707. package/dist/server/web-ui/assets/assets/images/notification-icon.2394c3b427c0f5ee2bb84864d8fb0f45.png +0 -0
  708. package/dist/server/web-ui/favicon.ico +0 -0
  709. package/dist/server/web-ui/index.html +147 -0
  710. package/dist/server/web-ui/index.html.br +0 -0
  711. package/dist/server/web-ui/index.html.gz +0 -0
  712. package/dist/server/web-ui/manifest.json +27 -0
  713. package/dist/server/web-ui/manifest.json.br +0 -0
  714. package/dist/server/web-ui/manifest.json.gz +0 -0
  715. package/dist/server/web-ui/metadata.json +1 -0
  716. package/dist/server/web-ui/metadata.json.br +1 -0
  717. package/dist/server/web-ui/metadata.json.gz +0 -0
  718. package/dist/server/web-ui/pwa-icon-192.png +0 -0
  719. package/dist/server/web-ui/pwa-icon-512.png +0 -0
  720. package/dist/server/web-ui/robots.txt +2 -0
  721. package/dist/src/executable-resolution/executable-resolution.js +118 -0
  722. package/dist/src/executable-resolution/windows.js +65 -0
  723. package/dist/src/server/agent/provider-launch-config.js +167 -0
  724. package/dist/src/server/otto-env.js +53 -0
  725. package/dist/src/server/otto-home.js +19 -0
  726. package/dist/src/server/persisted-config.js +471 -0
  727. package/dist/src/server/pid-lock.js +170 -0
  728. package/dist/src/server/private-files.js +37 -0
  729. package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
  730. package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
  731. package/dist/src/terminal/shell-integration/zsh/otto-integration.zsh +32 -0
  732. package/dist/src/utils/spawn.js +59 -0
  733. package/dist/src/utils/windows-command.js +41 -0
  734. package/package.json +118 -0
@@ -0,0 +1,2830 @@
1
+ import { createOpencodeClient, } from "@opencode-ai/sdk/v2/client";
2
+ import fs from "node:fs/promises";
3
+ import { createPathEquivalenceMatcher } from "../../../utils/path.js";
4
+ import pLimit from "p-limit";
5
+ import { z } from "zod";
6
+ import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
7
+ import { importSessionFromPersistence } from "../provider-session-import.js";
8
+ import { isDefaultAgentCreateConfigUnattended, resolveDefaultAgentCreateConfig, } from "../create-agent-mode.js";
9
+ import { checkProviderLaunchAvailable, createProviderEnvSpec, resolveProviderLaunch, } from "../provider-launch-config.js";
10
+ import { withTimeout } from "../../../utils/promise-timeout.js";
11
+ import { execCommand } from "../../../utils/spawn.js";
12
+ import { buildToolCallDisplayModel } from "@otto-code/protocol/tool-call-display";
13
+ import { mapOpencodeToolCall } from "./opencode/tool-call-mapper.js";
14
+ import { OpenCodeServerManager, } from "./opencode/server-manager.js";
15
+ import { resolveOpenCodeHomeDir } from "./opencode/paths.js";
16
+ import { formatProviderDiagnostic, formatProviderDiagnosticError, buildBinaryDiagnosticRows, buildCommandResolutionDiagnosticRows, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
17
+ import { runProviderTurn } from "./provider-runner.js";
18
+ import { renderPromptAttachmentAsText } from "../prompt-attachments.js";
19
+ import { composeSystemPromptParts } from "../system-prompt.js";
20
+ import { normalizeProviderReplayTimestamp } from "../provider-history-timestamps.js";
21
+ import { revertOpenCodeConversationAndFiles } from "./opencode/rewind.js";
22
+ const OPENCODE_CAPABILITIES = {
23
+ supportsStreaming: true,
24
+ supportsSessionPersistence: true,
25
+ supportsSessionListing: true,
26
+ supportsDynamicModes: true,
27
+ supportsMcpServers: true,
28
+ supportsReasoningStream: true,
29
+ supportsToolInvocations: true,
30
+ supportsRewindConversation: false,
31
+ supportsRewindFiles: false,
32
+ supportsRewindBoth: true,
33
+ };
34
+ const OPENCODE_BUILD_MODE_ID = "build";
35
+ const OPENCODE_LEGACY_FULL_ACCESS_MODE_ID = "full-access";
36
+ const OPENCODE_AUTO_ACCEPT_FEATURE_ID = "auto_accept";
37
+ const OPENCODE_PERSISTED_SESSION_LIMIT = 200;
38
+ const OPENCODE_PENDING_ABORT_START_TIMEOUT_MS = 10000;
39
+ const OPENCODE_PERMISSION_ACTION_ALLOW_ONCE = "allow_once";
40
+ const OPENCODE_PERMISSION_ACTION_ALLOW_ALWAYS = "allow_always";
41
+ const DEFAULT_MODES = [
42
+ {
43
+ id: OPENCODE_BUILD_MODE_ID,
44
+ label: "Build",
45
+ description: "Allows edits and tool execution for implementation work",
46
+ },
47
+ {
48
+ id: "plan",
49
+ label: "Plan",
50
+ description: "Read-only planning mode that avoids file edits",
51
+ },
52
+ ];
53
+ function isOpenCodeAutoAcceptEnabled(config) {
54
+ return config.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === true;
55
+ }
56
+ function withOpenCodeAutoAcceptFeature(featureValues, enabled) {
57
+ return {
58
+ ...featureValues,
59
+ [OPENCODE_AUTO_ACCEPT_FEATURE_ID]: enabled,
60
+ };
61
+ }
62
+ function resolveOpenCodeCreateConfig(input) {
63
+ const legacyFullAccess = input.requestedMode === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID;
64
+ const parent = input.parent;
65
+ const isUnattendedCreate = input.unattended || parent?.isUnattended === true;
66
+ const inheritsUnattended = input.requestedMode === undefined && isUnattendedCreate;
67
+ const inheritedOpenCodeMode = inheritsUnattended && parent?.provider === input.provider
68
+ ? (parent.modeId ?? undefined)
69
+ : undefined;
70
+ const requestedMode = legacyFullAccess
71
+ ? OPENCODE_BUILD_MODE_ID
72
+ : (input.requestedMode ?? inheritedOpenCodeMode);
73
+ const featureValues = legacyFullAccess ||
74
+ (isUnattendedCreate && input.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === undefined)
75
+ ? withOpenCodeAutoAcceptFeature(input.featureValues, true)
76
+ : input.featureValues;
77
+ if (inheritsUnattended && requestedMode === undefined) {
78
+ return { modeId: OPENCODE_BUILD_MODE_ID, featureValues };
79
+ }
80
+ const resolved = resolveDefaultAgentCreateConfig({
81
+ ...input,
82
+ requestedMode,
83
+ featureValues,
84
+ });
85
+ return { ...resolved, featureValues };
86
+ }
87
+ function isOpenCodeCreateConfigUnattended(input) {
88
+ return (isDefaultAgentCreateConfigUnattended(input) ||
89
+ input.config.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === true ||
90
+ input.features?.some((feature) => feature.id === OPENCODE_AUTO_ACCEPT_FEATURE_ID &&
91
+ (feature.value === true || feature.value === "true")) === true);
92
+ }
93
+ function buildOpenCodeAutoAcceptFeature(config) {
94
+ return {
95
+ type: "toggle",
96
+ id: OPENCODE_AUTO_ACCEPT_FEATURE_ID,
97
+ label: "Auto Accept",
98
+ description: "Automatically approves OpenCode tool permission prompts.",
99
+ tooltip: "Auto accept permission prompts",
100
+ icon: "shield-check",
101
+ value: isOpenCodeAutoAcceptEnabled(config),
102
+ };
103
+ }
104
+ function buildOpenCodePermissionActions() {
105
+ return [
106
+ {
107
+ id: "deny",
108
+ label: "Deny",
109
+ behavior: "deny",
110
+ variant: "danger",
111
+ intent: "dismiss",
112
+ },
113
+ {
114
+ id: OPENCODE_PERMISSION_ACTION_ALLOW_ALWAYS,
115
+ label: "Allow always",
116
+ behavior: "allow",
117
+ variant: "secondary",
118
+ },
119
+ {
120
+ id: OPENCODE_PERMISSION_ACTION_ALLOW_ONCE,
121
+ label: "Allow once",
122
+ behavior: "allow",
123
+ variant: "primary",
124
+ },
125
+ ];
126
+ }
127
+ function resolveOpenCodePermissionReply(response) {
128
+ if (response.behavior === "deny") {
129
+ return "reject";
130
+ }
131
+ if (response.selectedActionId === OPENCODE_PERMISSION_ACTION_ALLOW_ALWAYS) {
132
+ return "always";
133
+ }
134
+ return "once";
135
+ }
136
+ const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
137
+ const OPENCODE_PROVIDER_LIST_TIMEOUT_MS = 30000;
138
+ const OPENCODE_METADATA_CONCURRENCY = 4;
139
+ const openCodeMetadataLimit = pLimit(OPENCODE_METADATA_CONCURRENCY);
140
+ const OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS = [
141
+ {
142
+ name: "compact",
143
+ description: "Compact the current session",
144
+ argumentHint: "",
145
+ kind: "command",
146
+ },
147
+ {
148
+ name: "summarize",
149
+ description: "Compact the current session",
150
+ argumentHint: "",
151
+ kind: "command",
152
+ },
153
+ ];
154
+ const OPENCODE_HEADERS_TIMEOUT_TOKENS = [
155
+ "headers timeout",
156
+ "headers timeout error",
157
+ "headers_timeout",
158
+ "und_err_headers_timeout",
159
+ ];
160
+ const OpencodeToolStateSchema = z
161
+ .object({
162
+ status: z.string().optional(),
163
+ input: z.unknown().optional(),
164
+ output: z.unknown().optional(),
165
+ error: z.unknown().optional(),
166
+ })
167
+ .passthrough();
168
+ const OpencodeToolPartBaseSchema = z
169
+ .object({
170
+ tool: z.string().trim().min(1),
171
+ state: OpencodeToolStateSchema.optional(),
172
+ })
173
+ .passthrough();
174
+ const OpencodeToolPartWithCallIdSchema = OpencodeToolPartBaseSchema.extend({
175
+ callID: z.string().trim().min(1),
176
+ id: z.string().optional(),
177
+ }).transform((part) => ({
178
+ toolName: part.tool,
179
+ callId: part.callID,
180
+ status: part.state?.status,
181
+ input: part.state?.input,
182
+ output: part.state?.output,
183
+ error: part.state?.error,
184
+ }));
185
+ const OpencodeToolPartWithIdSchema = OpencodeToolPartBaseSchema.extend({
186
+ id: z.string().trim().min(1),
187
+ callID: z.string().optional(),
188
+ }).transform((part) => ({
189
+ toolName: part.tool,
190
+ callId: part.id,
191
+ status: part.state?.status,
192
+ input: part.state?.input,
193
+ output: part.state?.output,
194
+ error: part.state?.error,
195
+ }));
196
+ const OpencodeToolPartWithoutIdSchema = OpencodeToolPartBaseSchema.extend({
197
+ id: z.string().optional(),
198
+ callID: z.string().optional(),
199
+ }).transform((part) => ({
200
+ toolName: part.tool,
201
+ callId: undefined,
202
+ status: part.state?.status,
203
+ input: part.state?.input,
204
+ output: part.state?.output,
205
+ error: part.state?.error,
206
+ }));
207
+ const OpencodeToolPartSchema = z.union([
208
+ OpencodeToolPartWithCallIdSchema,
209
+ OpencodeToolPartWithIdSchema,
210
+ OpencodeToolPartWithoutIdSchema,
211
+ ]);
212
+ const OpencodeToolPartTimelineEnvelopeSchema = OpencodeToolPartSchema.transform((part) => ({
213
+ toolName: part.toolName,
214
+ callId: part.callId,
215
+ status: part.status,
216
+ input: part.input,
217
+ output: part.output,
218
+ error: part.error,
219
+ }));
220
+ const OpencodeToolPartToTimelineItemSchema = OpencodeToolPartTimelineEnvelopeSchema.transform((part) => mapOpencodeToolCall({
221
+ toolName: part.toolName,
222
+ callId: part.callId,
223
+ status: part.status,
224
+ input: part.input,
225
+ output: part.output,
226
+ error: part.error,
227
+ }));
228
+ function toOpenCodeMcpConfig(config) {
229
+ if (config.type === "stdio") {
230
+ return {
231
+ type: "local",
232
+ command: [config.command, ...(config.args ?? [])],
233
+ ...(config.env ? { environment: config.env } : {}),
234
+ enabled: true,
235
+ };
236
+ }
237
+ return {
238
+ type: "remote",
239
+ url: config.url,
240
+ ...(config.headers ? { headers: config.headers } : {}),
241
+ enabled: true,
242
+ };
243
+ }
244
+ function toTerminalTurnEvent(event) {
245
+ if (event.type === "turn_failed") {
246
+ return {
247
+ type: "turn_failed",
248
+ provider: "opencode",
249
+ error: toDiagnosticErrorMessage(event.error),
250
+ };
251
+ }
252
+ if (event.type === "turn_completed" || event.type === "turn_canceled") {
253
+ return event;
254
+ }
255
+ return null;
256
+ }
257
+ function isOpenCodeNotFoundError(error) {
258
+ return (typeof error === "object" &&
259
+ error !== null &&
260
+ "name" in error &&
261
+ error.name === "NotFoundError");
262
+ }
263
+ async function reconcileOpenCodeSessionClose(params) {
264
+ const { client, sessionId, directory, logger } = params;
265
+ try {
266
+ const response = await client.session.abort({
267
+ sessionID: sessionId,
268
+ directory,
269
+ });
270
+ if (response.error && !isOpenCodeNotFoundError(response.error)) {
271
+ logger.warn({
272
+ sessionId,
273
+ error: toDiagnosticErrorMessage(response.error),
274
+ }, "Failed to abort OpenCode session during close");
275
+ }
276
+ }
277
+ catch (error) {
278
+ logger.warn({
279
+ sessionId,
280
+ error: toDiagnosticErrorMessage(error),
281
+ }, "Failed to abort OpenCode session during close");
282
+ }
283
+ try {
284
+ const response = await client.session.update({
285
+ sessionID: sessionId,
286
+ directory,
287
+ time: { archived: Date.now() },
288
+ });
289
+ if (response.error && !isOpenCodeNotFoundError(response.error)) {
290
+ logger.warn({
291
+ sessionId,
292
+ error: toDiagnosticErrorMessage(response.error),
293
+ }, "Failed to archive OpenCode session during close");
294
+ }
295
+ }
296
+ catch (error) {
297
+ logger.warn({
298
+ sessionId,
299
+ error: toDiagnosticErrorMessage(error),
300
+ }, "Failed to archive OpenCode session during close");
301
+ }
302
+ }
303
+ function isOpenCodeHeadersTimeoutFailure(error) {
304
+ const diagnostics = new Set();
305
+ const queue = [error];
306
+ while (queue.length > 0) {
307
+ const current = queue.shift();
308
+ if (!current) {
309
+ continue;
310
+ }
311
+ const normalized = toDiagnosticErrorMessage(current).trim().toLowerCase();
312
+ if (normalized) {
313
+ diagnostics.add(normalized);
314
+ }
315
+ if (typeof current === "object") {
316
+ const record = current;
317
+ for (const value of [record.message, record.code, record.name]) {
318
+ if (typeof value !== "string") {
319
+ continue;
320
+ }
321
+ const diagnostic = value.trim().toLowerCase();
322
+ if (diagnostic) {
323
+ diagnostics.add(diagnostic);
324
+ }
325
+ }
326
+ if (record.cause) {
327
+ queue.push(record.cause);
328
+ }
329
+ }
330
+ }
331
+ return [...diagnostics].some((diagnostic) => OPENCODE_HEADERS_TIMEOUT_TOKENS.some((token) => diagnostic.includes(token)));
332
+ }
333
+ function isAlreadyPresentMcpError(error) {
334
+ const normalized = toDiagnosticErrorMessage(error).toLowerCase();
335
+ return MCP_ALREADY_PRESENT_ERROR_TOKENS.some((token) => normalized.includes(token));
336
+ }
337
+ function readOpenCodeMcpOperationError(data, name) {
338
+ const root = readOpenCodeRecord(data);
339
+ const entry = readOpenCodeRecord(root?.[name]);
340
+ if (!entry || entry.status !== "failed") {
341
+ return undefined;
342
+ }
343
+ return entry.error ?? `OpenCode reported MCP server '${name}' failed`;
344
+ }
345
+ function resolvePartDedupeKey(part, partType) {
346
+ if (part.id.trim().length > 0) {
347
+ return `${partType}:${part.id}`;
348
+ }
349
+ if (part.messageID.trim().length > 0) {
350
+ return `${partType}:message:${part.messageID}`;
351
+ }
352
+ return null;
353
+ }
354
+ function normalizeOpenCodeModeId(modeId) {
355
+ const trimmed = typeof modeId === "string" ? modeId.trim() : "";
356
+ if (!trimmed || trimmed === "default") {
357
+ return OPENCODE_BUILD_MODE_ID;
358
+ }
359
+ return trimmed;
360
+ }
361
+ function resolveOpenCodeRuntimeAgentId(modeId) {
362
+ const normalizedModeId = normalizeOpenCodeModeId(modeId);
363
+ return normalizedModeId === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID
364
+ ? OPENCODE_BUILD_MODE_ID
365
+ : normalizedModeId;
366
+ }
367
+ function normalizeOpenCodeConfig(config) {
368
+ if (normalizeOpenCodeModeId(config.modeId) !== OPENCODE_LEGACY_FULL_ACCESS_MODE_ID) {
369
+ return { ...config };
370
+ }
371
+ return {
372
+ ...config,
373
+ modeId: OPENCODE_BUILD_MODE_ID,
374
+ featureValues: {
375
+ ...config.featureValues,
376
+ [OPENCODE_AUTO_ACCEPT_FEATURE_ID]: true,
377
+ },
378
+ };
379
+ }
380
+ function isSelectableOpenCodeAgent(agent) {
381
+ return (agent.mode === "primary" || agent.mode === "all") && agent.hidden !== true;
382
+ }
383
+ const OPENCODE_AGENT_HEX_COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
384
+ function readOpenCodeAgentHexColor(agent) {
385
+ return typeof agent.color === "string" && OPENCODE_AGENT_HEX_COLOR_PATTERN.test(agent.color)
386
+ ? agent.color
387
+ : undefined;
388
+ }
389
+ function mapOpenCodeAgentToMode(agent) {
390
+ const colorTier = readOpenCodeAgentHexColor(agent);
391
+ return {
392
+ id: agent.name,
393
+ label: agent.name.charAt(0).toUpperCase() + agent.name.slice(1),
394
+ icon: "Bot",
395
+ description: typeof agent.description === "string" && agent.description.trim().length > 0
396
+ ? agent.description.trim()
397
+ : DEFAULT_MODES.find((mode) => mode.id === agent.name)?.description,
398
+ ...(colorTier ? { colorTier } : {}),
399
+ };
400
+ }
401
+ function mergeOpenCodeModes(discoveredModes) {
402
+ const filtered = discoveredModes.filter((mode) => mode.id !== OPENCODE_LEGACY_FULL_ACCESS_MODE_ID);
403
+ // When discovery returns results, trust them exactly — don't inject hardcoded
404
+ // defaults that the user may have intentionally disabled in their OpenCode config.
405
+ // Fall back to DEFAULT_MODES only when discovery produced nothing.
406
+ if (filtered.length > 0) {
407
+ return sortOpenCodeModes(filtered);
408
+ }
409
+ return sortOpenCodeModes([...DEFAULT_MODES]);
410
+ }
411
+ function sortOpenCodeModes(modes) {
412
+ const order = new Map(DEFAULT_MODES.map((mode, index) => [mode.id, index]));
413
+ return [...modes].sort((left, right) => {
414
+ const leftOrder = order.get(left.id) ?? Number.MAX_SAFE_INTEGER;
415
+ const rightOrder = order.get(right.id) ?? Number.MAX_SAFE_INTEGER;
416
+ if (leftOrder !== rightOrder) {
417
+ return leftOrder - rightOrder;
418
+ }
419
+ return left.label.localeCompare(right.label);
420
+ });
421
+ }
422
+ function readPositiveFiniteNumber(value) {
423
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
424
+ }
425
+ function maxFiniteNumber(left, right) {
426
+ return left === undefined ? right : Math.max(left, right);
427
+ }
428
+ function assignUsageNumber(usage, key, value) {
429
+ if (value !== undefined) {
430
+ usage[key] = value;
431
+ }
432
+ }
433
+ function buildOpenCodeModelLookupKey(providerId, modelId) {
434
+ return `${providerId}/${modelId}`;
435
+ }
436
+ function parseOpenCodeModelLookupKey(modelId) {
437
+ if (typeof modelId !== "string" || modelId.trim().length === 0) {
438
+ return undefined;
439
+ }
440
+ const slashIndex = modelId.indexOf("/");
441
+ if (slashIndex <= 0 || slashIndex === modelId.length - 1) {
442
+ return undefined;
443
+ }
444
+ const providerId = modelId.slice(0, slashIndex).trim();
445
+ const providerModelId = modelId.slice(slashIndex + 1).trim();
446
+ if (!providerId || !providerModelId) {
447
+ return undefined;
448
+ }
449
+ return buildOpenCodeModelLookupKey(providerId, providerModelId);
450
+ }
451
+ function extractOpenCodeModelContextWindow(model) {
452
+ if (!model || typeof model !== "object") {
453
+ return undefined;
454
+ }
455
+ const limit = model.limit;
456
+ return readPositiveFiniteNumber(limit?.context);
457
+ }
458
+ function buildOpenCodeModelDefinition(provider, modelId, model) {
459
+ const rawVariants = model.variants ? Object.keys(model.variants) : [];
460
+ const thinkingOptions = rawVariants.map((id, index) => ({
461
+ id,
462
+ label: id,
463
+ isDefault: index === 0,
464
+ }));
465
+ return {
466
+ provider: "opencode",
467
+ id: `${provider.id}/${modelId}`,
468
+ label: model.name,
469
+ description: `${provider.name} - ${model.family ?? ""}`.trim(),
470
+ thinkingOptions: thinkingOptions.length > 0 ? thinkingOptions : undefined,
471
+ defaultThinkingOptionId: thinkingOptions[0]?.id,
472
+ metadata: {
473
+ providerId: provider.id,
474
+ providerName: provider.name,
475
+ modelId,
476
+ family: model.family,
477
+ releaseDate: model.release_date,
478
+ supportsAttachments: model.attachment,
479
+ supportsReasoning: model.reasoning,
480
+ supportsToolCall: model.tool_call,
481
+ cost: model.cost,
482
+ contextWindowMaxTokens: extractOpenCodeModelContextWindow(model),
483
+ ...(model.limit ? { limit: model.limit } : {}),
484
+ },
485
+ };
486
+ }
487
+ function resolveOpenCodeSelectedModelContextWindow(providers, modelId) {
488
+ if (!providers) {
489
+ return undefined;
490
+ }
491
+ const modelLookupKey = parseOpenCodeModelLookupKey(modelId);
492
+ if (!modelLookupKey) {
493
+ return undefined;
494
+ }
495
+ const lookup = buildOpenCodeModelContextWindowLookup(providers);
496
+ return lookup.get(modelLookupKey);
497
+ }
498
+ function buildOpenCodeModelContextWindowLookup(providers) {
499
+ const lookup = new Map();
500
+ if (!providers) {
501
+ return lookup;
502
+ }
503
+ const connectedProviderIds = new Set(providers.connected ?? []);
504
+ for (const provider of providers.all ?? []) {
505
+ // Providers with source "api" are managed by the OpenCode console/subscription and are
506
+ // usable even though they don't appear in `connected` (which only lists env/config providers).
507
+ if (!connectedProviderIds.has(provider.id) && provider.source !== "api") {
508
+ continue;
509
+ }
510
+ for (const [modelId, modelDefinition] of Object.entries(provider.models ?? {})) {
511
+ const contextWindow = extractOpenCodeModelContextWindow(modelDefinition);
512
+ if (contextWindow === undefined) {
513
+ continue;
514
+ }
515
+ lookup.set(buildOpenCodeModelLookupKey(provider.id, modelId), contextWindow);
516
+ }
517
+ }
518
+ return lookup;
519
+ }
520
+ function resolveOpenCodeModelLookupKeyFromAssistantMessage(info) {
521
+ const providerId = info.providerID;
522
+ const modelId = info.modelID;
523
+ if (!providerId || !modelId) {
524
+ return undefined;
525
+ }
526
+ return buildOpenCodeModelLookupKey(providerId, modelId);
527
+ }
528
+ function mergeOpenCodeStepFinishUsage(usage, part, options = {}) {
529
+ const inputTokens = readPositiveFiniteNumber(part.tokens?.input);
530
+ const outputTokens = readPositiveFiniteNumber(part.tokens?.output);
531
+ const reasoningTokens = readPositiveFiniteNumber(part.tokens?.reasoning);
532
+ const cacheReadTokens = readPositiveFiniteNumber(part.tokens?.cache?.read);
533
+ const cacheWriteTokens = readPositiveFiniteNumber(part.tokens?.cache?.write);
534
+ const totalTokens = (inputTokens ?? 0) +
535
+ (outputTokens ?? 0) +
536
+ (reasoningTokens ?? 0) +
537
+ (cacheReadTokens ?? 0) +
538
+ (cacheWriteTokens ?? 0);
539
+ const cost = readPositiveFiniteNumber(part.cost);
540
+ assignUsageNumber(usage, "inputTokens", inputTokens);
541
+ assignUsageNumber(usage, "cachedInputTokens", cacheReadTokens);
542
+ assignUsageNumber(usage, "outputTokens", outputTokens);
543
+ if (totalTokens > 0) {
544
+ usage.contextWindowUsedTokens = totalTokens;
545
+ }
546
+ if (cost !== undefined) {
547
+ usage.totalCostUsd = options.totalCostUsd ?? (usage.totalCostUsd ?? 0) + cost;
548
+ }
549
+ }
550
+ function hasNormalizedOpenCodeUsage(usage) {
551
+ return [
552
+ usage.inputTokens,
553
+ usage.cachedInputTokens,
554
+ usage.outputTokens,
555
+ usage.totalCostUsd,
556
+ usage.contextWindowMaxTokens,
557
+ usage.contextWindowUsedTokens,
558
+ ].some((value) => typeof value === "number" && Number.isFinite(value));
559
+ }
560
+ function getOpenCodeAttachmentExtension(mimeType) {
561
+ switch (mimeType) {
562
+ case "image/png":
563
+ return "png";
564
+ case "image/jpeg":
565
+ return "jpg";
566
+ case "image/webp":
567
+ return "webp";
568
+ case "image/gif":
569
+ return "gif";
570
+ case "image/svg+xml":
571
+ return "svg";
572
+ default:
573
+ return "bin";
574
+ }
575
+ }
576
+ function toOpenCodeDataUrl(mimeType, data) {
577
+ const match = data.match(/^data:([^;,]+);base64,(.+)$/);
578
+ if (match) {
579
+ return {
580
+ mimeType: match[1] ?? mimeType,
581
+ url: data,
582
+ };
583
+ }
584
+ return {
585
+ mimeType,
586
+ url: `data:${mimeType};base64,${data}`,
587
+ };
588
+ }
589
+ function buildOpenCodePromptParts(prompt) {
590
+ if (typeof prompt === "string") {
591
+ return [{ type: "text", text: prompt }];
592
+ }
593
+ let attachmentOrdinal = 0;
594
+ const output = [];
595
+ for (const part of prompt) {
596
+ if (part.type === "text") {
597
+ output.push({ type: "text", text: part.text });
598
+ continue;
599
+ }
600
+ if (part.type === "image") {
601
+ attachmentOrdinal += 1;
602
+ const normalized = toOpenCodeDataUrl(part.mimeType, part.data);
603
+ output.push({
604
+ type: "file",
605
+ mime: normalized.mimeType,
606
+ filename: `attachment-${attachmentOrdinal}.${getOpenCodeAttachmentExtension(normalized.mimeType)}`,
607
+ url: normalized.url,
608
+ });
609
+ continue;
610
+ }
611
+ output.push({ type: "text", text: renderPromptAttachmentAsText(part) });
612
+ }
613
+ return output;
614
+ }
615
+ function buildOpenCodeUserTimelineText(prompt) {
616
+ if (typeof prompt === "string") {
617
+ return prompt;
618
+ }
619
+ return prompt
620
+ .map((part) => {
621
+ if (part.type === "text") {
622
+ return part.text;
623
+ }
624
+ if (part.type === "image") {
625
+ return "[Image]";
626
+ }
627
+ return renderPromptAttachmentAsText(part);
628
+ })
629
+ .filter((text) => text.trim().length > 0)
630
+ .join("\n");
631
+ }
632
+ async function collectOpenCodeImportableSessionsFromSdk(client, options) {
633
+ const limit = options?.limit ?? OPENCODE_PERSISTED_SESSION_LIMIT;
634
+ const sessionListLimit = options?.cwd ? Math.max(limit, OPENCODE_PERSISTED_SESSION_LIMIT) : limit;
635
+ const response = await client.experimental.session.list({
636
+ archived: true,
637
+ roots: true,
638
+ limit: sessionListLimit,
639
+ });
640
+ if (response.error) {
641
+ throw new Error(`Failed to list OpenCode sessions: ${JSON.stringify(response.error)}`);
642
+ }
643
+ const matchesCwd = options?.cwd ? createPathEquivalenceMatcher(options.cwd) : null;
644
+ return (response.data ?? [])
645
+ .filter((session) => !matchesCwd || matchesCwd(session.directory))
646
+ .sort((left, right) => getOpenCodeSessionTimestamp(right) - getOpenCodeSessionTimestamp(left))
647
+ .slice(0, limit)
648
+ .map((session) => ({
649
+ providerHandleId: session.id,
650
+ cwd: session.directory,
651
+ title: normalizeOpenCodeSessionTitle(session.title),
652
+ firstPromptPreview: null,
653
+ lastPromptPreview: null,
654
+ lastActivityAt: new Date(getOpenCodeSessionTimestamp(session)),
655
+ }));
656
+ }
657
+ function normalizeOpenCodeSessionTitle(title) {
658
+ const normalized = title?.trim();
659
+ return normalized ? normalized : null;
660
+ }
661
+ function getOpenCodeSessionTimestamp(session) {
662
+ return session.time?.updated ?? session.time?.created ?? 0;
663
+ }
664
+ function resolveOpenCodeReplayTimestamp(params) {
665
+ const timedPart = params.part;
666
+ const partTimestamp = timedPart?.time?.start ??
667
+ timedPart?.time?.end ??
668
+ params.message.time?.created ??
669
+ params.message.time?.completed;
670
+ return normalizeProviderReplayTimestamp(partTimestamp);
671
+ }
672
+ function buildOpenCodeReplayTimelineEvent(params) {
673
+ const timestamp = resolveOpenCodeReplayTimestamp({
674
+ message: params.message,
675
+ part: params.part,
676
+ });
677
+ return {
678
+ type: "timeline",
679
+ provider: "opencode",
680
+ item: params.item,
681
+ ...(timestamp ? { timestamp } : {}),
682
+ };
683
+ }
684
+ function buildOpenCodeReplayPartTimelineEvent(params) {
685
+ const { part, message } = params;
686
+ if (part.type === "text" && part.text) {
687
+ return buildOpenCodeReplayTimelineEvent({
688
+ item: { type: "assistant_message", text: part.text },
689
+ message,
690
+ part,
691
+ });
692
+ }
693
+ if (part.type === "reasoning" && part.text) {
694
+ return buildOpenCodeReplayTimelineEvent({
695
+ item: { type: "reasoning", text: part.text },
696
+ message,
697
+ part,
698
+ });
699
+ }
700
+ if (part.type !== "tool") {
701
+ return null;
702
+ }
703
+ if (isOpenCodeTodoWriteToolPart(part)) {
704
+ const todos = readOpenCodeTodoItemsFromToolPart(part);
705
+ if (!todos) {
706
+ return null;
707
+ }
708
+ return buildOpenCodeReplayTimelineEvent({
709
+ item: mapOpenCodeTodosToTimelineItems(todos),
710
+ message,
711
+ part,
712
+ });
713
+ }
714
+ const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
715
+ if (!parsedToolPart.success || !parsedToolPart.data) {
716
+ return null;
717
+ }
718
+ return buildOpenCodeReplayTimelineEvent({
719
+ item: parsedToolPart.data,
720
+ message,
721
+ part,
722
+ });
723
+ }
724
+ function isOpenCodeCompactionSummaryMessage(message) {
725
+ return (message.role === "assistant" &&
726
+ (message.summary === true || message.agent === "compaction" || message.mode === "compaction"));
727
+ }
728
+ function findOpenCodeCompactionPart(message) {
729
+ return message.parts.find((part) => part.type === "compaction");
730
+ }
731
+ async function readOpenCodeSessionMessagesFromSdk(client, session) {
732
+ const response = await client.session.messages({
733
+ sessionID: session.id,
734
+ directory: session.directory,
735
+ });
736
+ if (response.error || !response.data) {
737
+ return [];
738
+ }
739
+ return filterOpenCodeRevertedMessages(response.data, session.revert);
740
+ }
741
+ function buildOpenCodeSessionTimeline(messages) {
742
+ const timeline = [];
743
+ let hideNextAssistantAfterCompaction = false;
744
+ for (const message of messages) {
745
+ const compactionPart = findOpenCodeCompactionPart(message);
746
+ if (message.info.role === "assistant" && hideNextAssistantAfterCompaction) {
747
+ hideNextAssistantAfterCompaction = false;
748
+ continue;
749
+ }
750
+ if (message.info.role === "user" && !compactionPart) {
751
+ hideNextAssistantAfterCompaction = false;
752
+ }
753
+ timeline.push(...buildOpenCodeReplayTimelineEvents(message).map((event) => event.item));
754
+ if (message.info.role === "user" && compactionPart) {
755
+ timeline.push(createCompactionTimelineItem("completed", compactionPart.auto ? "auto" : "manual"));
756
+ hideNextAssistantAfterCompaction = true;
757
+ }
758
+ }
759
+ return timeline;
760
+ }
761
+ function filterOpenCodeRevertedMessages(messages, revert) {
762
+ if (!revert?.messageID || revert.partID) {
763
+ return [...messages];
764
+ }
765
+ const revertIndex = messages.findIndex((message) => message.info.id === revert.messageID);
766
+ if (revertIndex < 0) {
767
+ return [...messages];
768
+ }
769
+ return messages.slice(0, revertIndex);
770
+ }
771
+ function resolveOpenCodePersistedSessionModeId(session, messages) {
772
+ const agent = session.agent ?? messages.map(readOpenCodeMessageAgent).find(Boolean);
773
+ return agent ? normalizeOpenCodeModeId(agent) : undefined;
774
+ }
775
+ function readOpenCodeMessageAgent(message) {
776
+ const agent = message.info.agent;
777
+ return typeof agent === "string" && agent.trim() ? agent : undefined;
778
+ }
779
+ function resolveOpenCodePersistedSessionModel(session, messages) {
780
+ if (session.model) {
781
+ return buildOpenCodeModelLookupKey(session.model.providerID, session.model.id);
782
+ }
783
+ const model = messages.map(readOpenCodeMessageModel).find(Boolean);
784
+ return model ? buildOpenCodeModelLookupKey(model.providerID, model.modelID) : undefined;
785
+ }
786
+ function readOpenCodeMessageModel(message) {
787
+ const { info } = message;
788
+ if (info.role === "user") {
789
+ return info.model;
790
+ }
791
+ return {
792
+ providerID: info.providerID,
793
+ modelID: info.modelID,
794
+ };
795
+ }
796
+ function buildOpenCodeReplayTimelineEvents(message) {
797
+ const { info, parts } = message;
798
+ if (isOpenCodeCompactionSummaryMessage(info)) {
799
+ return [];
800
+ }
801
+ if (info.role === "user") {
802
+ const text = parts
803
+ .filter((part) => part.type === "text")
804
+ .map((part) => part.text)
805
+ .join("");
806
+ return text
807
+ ? [
808
+ buildOpenCodeReplayTimelineEvent({
809
+ item: { type: "user_message", text, messageId: info.id },
810
+ message: info,
811
+ }),
812
+ ]
813
+ : [];
814
+ }
815
+ const events = [];
816
+ let emittedAssistantText = false;
817
+ for (const part of parts) {
818
+ if (part.type === "text" && part.text) {
819
+ emittedAssistantText = true;
820
+ }
821
+ const event = buildOpenCodeReplayPartTimelineEvent({ part, message: info });
822
+ if (event) {
823
+ events.push(event);
824
+ }
825
+ }
826
+ if (!emittedAssistantText) {
827
+ const text = stringifyStructuredAssistantMessage(info.structured);
828
+ if (text) {
829
+ events.push(buildOpenCodeReplayTimelineEvent({
830
+ item: { type: "assistant_message", text },
831
+ message: info,
832
+ }));
833
+ }
834
+ }
835
+ return events;
836
+ }
837
+ export const __openCodeInternals = {
838
+ buildOpenCodePromptParts,
839
+ buildOpenCodeSessionTimeline,
840
+ buildOpenCodeModelContextWindowLookup,
841
+ buildOpenCodeModelDefinition,
842
+ buildOpenCodeModelLookupKey,
843
+ extractOpenCodeModelContextWindow,
844
+ hasNormalizedOpenCodeUsage,
845
+ mergeOpenCodeStepFinishUsage,
846
+ parseOpenCodeModelLookupKey,
847
+ reconcileOpenCodeSessionClose,
848
+ resolveOpenCodeModelLookupKeyFromAssistantMessage,
849
+ resolveOpenCodeSelectedModelContextWindow,
850
+ isSelectableOpenCodeAgent,
851
+ mapOpenCodeAgentToMode,
852
+ resolveOpenCodeHomeDir,
853
+ get OpenCodeAgentSession() {
854
+ return OpenCodeAgentSession;
855
+ },
856
+ };
857
+ function createSdkOpenCodeClient(options) {
858
+ return createOpencodeClient(options);
859
+ }
860
+ export class OpenCodeAgentClient {
861
+ constructor(logger, runtimeSettings, deps = {}) {
862
+ this.provider = "opencode";
863
+ this.capabilities = OPENCODE_CAPABILITIES;
864
+ this.resolveCreateConfig = resolveOpenCodeCreateConfig;
865
+ this.isCreateConfigUnattended = isOpenCodeCreateConfigUnattended;
866
+ this.modelContextWindows = new Map();
867
+ this.logger = logger.child({ module: "agent", provider: "opencode" });
868
+ this.runtimeSettings = runtimeSettings;
869
+ this.serverManager =
870
+ deps.serverManager ??
871
+ OpenCodeServerManager.getInstance(this.logger, runtimeSettings, {
872
+ managedProcesses: deps.managedProcesses,
873
+ resolveHomeDir: deps.resolveHomeDir,
874
+ });
875
+ this.createOpenCodeClient = deps.createClient ?? createSdkOpenCodeClient;
876
+ this.resolveHomeDir = deps.resolveHomeDir ?? resolveOpenCodeHomeDir;
877
+ }
878
+ async createSession(config, launchContext, options) {
879
+ const openCodeConfig = this.assertConfig(config);
880
+ const acquisition = launchContext?.env
881
+ ? await this.serverManager.acquireDedicated(launchContext.env)
882
+ : await this.serverManager.acquireCurrent();
883
+ const { url } = acquisition.server;
884
+ const client = this.createOpenCodeClient({
885
+ baseUrl: url,
886
+ directory: openCodeConfig.cwd,
887
+ });
888
+ try {
889
+ const response = await withTimeout(client.session.create({ directory: openCodeConfig.cwd }), 10000, "OpenCode session.create timed out after 10s");
890
+ if (response.error) {
891
+ throw new Error(`Failed to create OpenCode session: ${JSON.stringify(response.error)}`);
892
+ }
893
+ const session = response.data;
894
+ if (!session) {
895
+ throw new Error("OpenCode session creation returned no data");
896
+ }
897
+ await this.populateModelContextWindowCache(client, openCodeConfig.cwd);
898
+ return new OpenCodeAgentSession(openCodeConfig, client, session.id, this.logger, new Map(this.modelContextWindows), acquisition.release, options?.persistSession, launchContext?.agentId);
899
+ }
900
+ catch (error) {
901
+ acquisition.release();
902
+ throw error;
903
+ }
904
+ }
905
+ async resumeSession(handle, overrides, launchContext) {
906
+ const metadata = (handle.metadata ?? {});
907
+ const cwd = overrides?.cwd ?? metadata.cwd;
908
+ if (!cwd) {
909
+ throw new Error("OpenCode resume requires the original working directory");
910
+ }
911
+ const config = {
912
+ ...metadata,
913
+ ...overrides,
914
+ provider: "opencode",
915
+ cwd,
916
+ };
917
+ const openCodeConfig = this.assertConfig(config);
918
+ const acquisition = await this.serverManager.acquireCurrent();
919
+ const { url } = acquisition.server;
920
+ const client = this.createOpenCodeClient({
921
+ baseUrl: url,
922
+ directory: openCodeConfig.cwd,
923
+ });
924
+ try {
925
+ await this.populateModelContextWindowCache(client, openCodeConfig.cwd);
926
+ return new OpenCodeAgentSession(openCodeConfig, client, handle.sessionId, this.logger, new Map(this.modelContextWindows), acquisition.release, undefined, launchContext?.agentId);
927
+ }
928
+ catch (error) {
929
+ acquisition.release();
930
+ throw error;
931
+ }
932
+ }
933
+ async fetchCatalog(options) {
934
+ const acquisition = options.force
935
+ ? await this.serverManager.acquireNew()
936
+ : await this.serverManager.acquireCurrent();
937
+ const { url } = acquisition.server;
938
+ const isGlobalCatalog = options.scope === "global";
939
+ try {
940
+ // OpenCode treats the catalog directory as a workspace. The global catalog
941
+ // is not a project, so use the neutral OpenCode home instead of user home.
942
+ const directory = isGlobalCatalog ? this.resolveHomeDir() : options.cwd;
943
+ if (isGlobalCatalog) {
944
+ await fs.mkdir(directory, { recursive: true });
945
+ this.logger.debug({ directory }, "opencode catalog refresh: using opencode-home for global provider catalog");
946
+ }
947
+ const client = this.createOpenCodeClient({ baseUrl: url, directory });
948
+ const [models, modes] = await Promise.all([
949
+ this.fetchModelsFromClient(client, directory),
950
+ this.fetchModesFromClient(client, directory),
951
+ ]);
952
+ return { models, modes };
953
+ }
954
+ finally {
955
+ acquisition.release();
956
+ }
957
+ }
958
+ async listCommands(config) {
959
+ const openCodeConfig = this.assertConfig(config);
960
+ const acquisition = await this.serverManager.acquireCurrent();
961
+ const { url } = acquisition.server;
962
+ const client = this.createOpenCodeClient({
963
+ baseUrl: url,
964
+ directory: openCodeConfig.cwd,
965
+ });
966
+ try {
967
+ return await listOpenCodeCommandsFromSdk(client, openCodeConfig.cwd);
968
+ }
969
+ finally {
970
+ acquisition.release();
971
+ }
972
+ }
973
+ async listFeatures(config) {
974
+ return [buildOpenCodeAutoAcceptFeature(this.assertConfig(config))];
975
+ }
976
+ async listImportableSessions(options) {
977
+ const acquisition = await this.serverManager.acquireCurrent();
978
+ const { url } = acquisition.server;
979
+ const client = this.createOpenCodeClient({
980
+ baseUrl: url,
981
+ directory: options?.cwd ?? "",
982
+ });
983
+ try {
984
+ return await collectOpenCodeImportableSessionsFromSdk(client, options);
985
+ }
986
+ finally {
987
+ acquisition.release();
988
+ }
989
+ }
990
+ async importSession(input, context) {
991
+ const acquisition = await this.serverManager.acquireCurrent();
992
+ const { url } = acquisition.server;
993
+ const client = this.createOpenCodeClient({
994
+ baseUrl: url,
995
+ directory: input.cwd,
996
+ });
997
+ try {
998
+ const sessionResponse = await client.session.get({
999
+ sessionID: input.providerHandleId,
1000
+ directory: input.cwd,
1001
+ });
1002
+ if (sessionResponse.error || !sessionResponse.data) {
1003
+ throw new Error(`Failed to load OpenCode session ${input.providerHandleId}`);
1004
+ }
1005
+ const session = sessionResponse.data;
1006
+ const messages = await readOpenCodeSessionMessagesFromSdk(client, session);
1007
+ const modeId = resolveOpenCodePersistedSessionModeId(session, messages);
1008
+ const model = resolveOpenCodePersistedSessionModel(session, messages);
1009
+ return await importSessionFromPersistence({
1010
+ provider: "opencode",
1011
+ request: input,
1012
+ context,
1013
+ resumeSession: this.resumeSession.bind(this),
1014
+ config: {
1015
+ title: normalizeOpenCodeSessionTitle(session.title) ?? undefined,
1016
+ ...(modeId ? { modeId } : {}),
1017
+ ...(model ? { model } : {}),
1018
+ },
1019
+ });
1020
+ }
1021
+ finally {
1022
+ acquisition.release();
1023
+ }
1024
+ }
1025
+ async isAvailable() {
1026
+ const launch = await resolveProviderLaunch({
1027
+ commandConfig: this.runtimeSettings?.command,
1028
+ defaultBinary: "opencode",
1029
+ });
1030
+ const availability = await checkProviderLaunchAvailable(launch);
1031
+ return availability.available;
1032
+ }
1033
+ async shutdown() {
1034
+ await this.serverManager.shutdown();
1035
+ }
1036
+ async getDiagnostic() {
1037
+ try {
1038
+ const launch = await resolveProviderLaunch({
1039
+ commandConfig: this.runtimeSettings?.command,
1040
+ defaultBinary: "opencode",
1041
+ });
1042
+ const availability = await checkProviderLaunchAvailable(launch);
1043
+ let authValue = "Not checked";
1044
+ const authCommand = availability.available
1045
+ ? (availability.resolvedPath ?? launch.command)
1046
+ : null;
1047
+ if (authCommand) {
1048
+ try {
1049
+ const { stdout, stderr } = await execCommand(authCommand, [...launch.args, "auth", "list"], {
1050
+ ...createProviderEnvSpec(),
1051
+ timeout: 5000,
1052
+ });
1053
+ const text = (stdout.trim() || stderr.trim()).trim();
1054
+ authValue = text ? `\n ${text.replace(/\n/g, "\n ")}` : "(empty)";
1055
+ }
1056
+ catch (error) {
1057
+ authValue = `Error - ${toDiagnosticErrorMessage(error)}`;
1058
+ }
1059
+ }
1060
+ return {
1061
+ diagnostic: formatProviderDiagnostic("OpenCode", [
1062
+ ...(await buildCommandResolutionDiagnosticRows(launch, {
1063
+ knownBinaryNames: ["opencode"],
1064
+ })),
1065
+ ...(await buildBinaryDiagnosticRows(launch, availability)),
1066
+ { label: "Auth", value: authValue },
1067
+ ]),
1068
+ };
1069
+ }
1070
+ catch (error) {
1071
+ return {
1072
+ diagnostic: formatProviderDiagnosticError("OpenCode", error),
1073
+ };
1074
+ }
1075
+ }
1076
+ async fetchModelsFromClient(client, directory) {
1077
+ const response = await openCodeMetadataLimit(() => withTimeout(client.provider.list({ directory }), OPENCODE_PROVIDER_LIST_TIMEOUT_MS, `OpenCode provider.list timed out after ${OPENCODE_PROVIDER_LIST_TIMEOUT_MS / 1000}s - server may not be authenticated or connected to any providers`));
1078
+ if (response.error) {
1079
+ throw new Error(`Failed to fetch OpenCode providers: ${JSON.stringify(response.error)}`);
1080
+ }
1081
+ const providers = response.data;
1082
+ if (!providers) {
1083
+ return [];
1084
+ }
1085
+ const connectedProviderIds = new Set(providers.connected);
1086
+ const isAccessible = (provider) => connectedProviderIds.has(provider.id) || provider.source === "api";
1087
+ if (!providers.all.some(isAccessible)) {
1088
+ throw new Error("OpenCode has no connected providers. Please authenticate with at least one provider " +
1089
+ "(e.g., openai, anthropic), set appropriate environment variables (e.g., OPENAI_API_KEY), " +
1090
+ "or log in to OpenCode Go via the console.");
1091
+ }
1092
+ const models = [];
1093
+ this.modelContextWindows.clear();
1094
+ for (const provider of providers.all) {
1095
+ if (!isAccessible(provider)) {
1096
+ continue;
1097
+ }
1098
+ for (const [modelId, model] of Object.entries(provider.models)) {
1099
+ const definition = buildOpenCodeModelDefinition(provider, modelId, model);
1100
+ const contextWindowMaxTokens = extractOpenCodeModelContextWindow(model);
1101
+ if (contextWindowMaxTokens !== undefined) {
1102
+ this.modelContextWindows.set(buildOpenCodeModelLookupKey(provider.id, modelId), contextWindowMaxTokens);
1103
+ }
1104
+ models.push(definition);
1105
+ }
1106
+ }
1107
+ return models;
1108
+ }
1109
+ async fetchModesFromClient(client, directory) {
1110
+ const response = await openCodeMetadataLimit(() => withTimeout(client.app.agents({ directory }), 10000, "OpenCode app.agents timed out after 10s"));
1111
+ if (response.error || !response.data) {
1112
+ return DEFAULT_MODES;
1113
+ }
1114
+ const discovered = response.data.filter(isSelectableOpenCodeAgent).map(mapOpenCodeAgentToMode);
1115
+ return mergeOpenCodeModes(discovered);
1116
+ }
1117
+ assertConfig(config) {
1118
+ if (config.provider !== "opencode") {
1119
+ throw new Error(`OpenCodeAgentClient received config for provider '${config.provider}'`);
1120
+ }
1121
+ return normalizeOpenCodeConfig({ ...config, provider: "opencode" });
1122
+ }
1123
+ async populateModelContextWindowCache(client, cwd) {
1124
+ const response = await openCodeMetadataLimit(() => client.provider.list({ directory: cwd }));
1125
+ if (response.error || !response.data) {
1126
+ return;
1127
+ }
1128
+ const lookup = buildOpenCodeModelContextWindowLookup(response.data);
1129
+ this.modelContextWindows.clear();
1130
+ for (const [modelLookupKey, contextWindowMaxTokens] of lookup.entries()) {
1131
+ this.modelContextWindows.set(modelLookupKey, contextWindowMaxTokens);
1132
+ }
1133
+ }
1134
+ }
1135
+ const MAX_OPENCODE_SUB_AGENT_ACTIONS = 200;
1136
+ const MAX_OPENCODE_PENDING_CHILD_TOOL_PARTS = 200;
1137
+ function stringifyStructuredAssistantMessage(value) {
1138
+ if (value === undefined) {
1139
+ return null;
1140
+ }
1141
+ if (typeof value === "string") {
1142
+ const trimmed = value.trim();
1143
+ return trimmed.length > 0 ? trimmed : null;
1144
+ }
1145
+ try {
1146
+ return JSON.stringify(value);
1147
+ }
1148
+ catch {
1149
+ return null;
1150
+ }
1151
+ }
1152
+ async function listOpenCodeCommandsFromSdk(client, directory) {
1153
+ const result = await client.command.list({ directory });
1154
+ const commandsByName = new Map(OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS.map((command) => [command.name, command]));
1155
+ if (result.error || !result.data) {
1156
+ return Array.from(commandsByName.values());
1157
+ }
1158
+ for (const cmd of result.data) {
1159
+ commandsByName.set(cmd.name, {
1160
+ name: cmd.name,
1161
+ description: cmd.description ?? "",
1162
+ argumentHint: cmd.hints?.length ? cmd.hints.join(" ") : "",
1163
+ kind: cmd.source === "skill" ? "skill" : "command",
1164
+ });
1165
+ }
1166
+ return Array.from(commandsByName.values());
1167
+ }
1168
+ function readOpenCodeRecord(value) {
1169
+ return typeof value === "object" && value !== null && !Array.isArray(value)
1170
+ ? value
1171
+ : null;
1172
+ }
1173
+ function readNonEmptyString(value) {
1174
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
1175
+ }
1176
+ function isOpenCodeTodoWriteToolPart(part) {
1177
+ return part.type === "tool" && part.tool.trim().toLowerCase() === "todowrite";
1178
+ }
1179
+ function readOpenCodeTodoItems(value) {
1180
+ if (typeof value === "string") {
1181
+ try {
1182
+ return readOpenCodeTodoItems(JSON.parse(value));
1183
+ }
1184
+ catch {
1185
+ return null;
1186
+ }
1187
+ }
1188
+ if (Array.isArray(value)) {
1189
+ return value.flatMap((entry) => {
1190
+ const record = readOpenCodeRecord(entry);
1191
+ if (!record) {
1192
+ return [];
1193
+ }
1194
+ const content = readNonEmptyString(record.content);
1195
+ if (!content) {
1196
+ return [];
1197
+ }
1198
+ return [
1199
+ {
1200
+ content,
1201
+ status: readNonEmptyString(record.status),
1202
+ },
1203
+ ];
1204
+ });
1205
+ }
1206
+ const record = readOpenCodeRecord(value);
1207
+ if (!record) {
1208
+ return null;
1209
+ }
1210
+ return readOpenCodeTodoItems(record.todos);
1211
+ }
1212
+ function readOpenCodeTodoItemsFromToolPart(part) {
1213
+ const state = readOpenCodeRecord(part.state);
1214
+ return (readOpenCodeTodoItems(state?.input) ??
1215
+ readOpenCodeTodoItems(state?.output) ??
1216
+ readOpenCodeTodoItems(state?.metadata));
1217
+ }
1218
+ function mapOpenCodeTodosToTimelineItems(todos) {
1219
+ return {
1220
+ type: "todo",
1221
+ items: todos.flatMap((todo) => {
1222
+ const text = readNonEmptyString(todo.content);
1223
+ if (!text) {
1224
+ return [];
1225
+ }
1226
+ return [
1227
+ {
1228
+ text,
1229
+ completed: todo.status === "completed",
1230
+ },
1231
+ ];
1232
+ }),
1233
+ };
1234
+ }
1235
+ function createCompactionTimelineItem(status, trigger) {
1236
+ return {
1237
+ type: "compaction",
1238
+ status,
1239
+ ...(trigger ? { trigger } : {}),
1240
+ };
1241
+ }
1242
+ const PERMISSION_COMMAND_KEYS = ["command", "cmd", "shellCommand"];
1243
+ const PERMISSION_CWD_KEYS = ["cwd", "directory", "path", "workdir"];
1244
+ const PERMISSION_REASON_KEYS = ["reason", "purpose", "description", "message"];
1245
+ const PERMISSION_TITLE_BY_NAME = {
1246
+ external_directory: "Access external directory",
1247
+ bash: "Run shell command",
1248
+ read: "Read files",
1249
+ read_file: "Read files",
1250
+ write: "Write files",
1251
+ write_file: "Write files",
1252
+ create_file: "Write files",
1253
+ edit: "Edit files",
1254
+ apply_patch: "Edit files",
1255
+ apply_diff: "Edit files",
1256
+ };
1257
+ function toHumanReadablePermissionTitle(permission) {
1258
+ const mapped = PERMISSION_TITLE_BY_NAME[permission];
1259
+ if (mapped) {
1260
+ return mapped;
1261
+ }
1262
+ const normalized = permission
1263
+ .split(/[\s_-]+/)
1264
+ .map((part) => part.trim())
1265
+ .filter((part) => part.length > 0)
1266
+ .map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
1267
+ .join(" ");
1268
+ return normalized.length > 0 ? normalized : "Permission request";
1269
+ }
1270
+ function readFirstStringFromRecord(record, keys) {
1271
+ if (!record) {
1272
+ return null;
1273
+ }
1274
+ for (const key of keys) {
1275
+ const value = readNonEmptyString(record[key]);
1276
+ if (value) {
1277
+ return value;
1278
+ }
1279
+ }
1280
+ return null;
1281
+ }
1282
+ function readPermissionField(metadata, keys) {
1283
+ const direct = readFirstStringFromRecord(metadata, keys);
1284
+ if (direct) {
1285
+ return direct;
1286
+ }
1287
+ const nestedInput = readOpenCodeRecord(metadata?.input);
1288
+ return readFirstStringFromRecord(nestedInput, keys);
1289
+ }
1290
+ function buildOpenCodePermissionInput(params) {
1291
+ return {
1292
+ ...(params.patterns.length > 0 ? { patterns: params.patterns } : {}),
1293
+ ...(params.metadata ? { metadata: params.metadata } : {}),
1294
+ ...(params.tool ? { tool: params.tool } : {}),
1295
+ ...(params.command ? { command: params.command } : {}),
1296
+ };
1297
+ }
1298
+ function buildOpenCodePermissionDetail(params) {
1299
+ if (params.command) {
1300
+ return {
1301
+ type: "shell",
1302
+ command: params.command,
1303
+ ...(params.cwd ? { cwd: params.cwd } : {}),
1304
+ };
1305
+ }
1306
+ return {
1307
+ type: "unknown",
1308
+ input: {
1309
+ permission: params.permission,
1310
+ ...params.input,
1311
+ },
1312
+ output: null,
1313
+ };
1314
+ }
1315
+ function buildOpenCodePermissionDescription(params) {
1316
+ const parts = [];
1317
+ if (params.reason) {
1318
+ parts.push(params.reason);
1319
+ }
1320
+ if (params.patterns.length > 0) {
1321
+ parts.push(`Scope: ${params.patterns.join(", ")}`);
1322
+ }
1323
+ return parts.length > 0 ? parts.join(" - ") : undefined;
1324
+ }
1325
+ export function translateOpenCodeEvent(event, state) {
1326
+ const events = [];
1327
+ switch (event.type) {
1328
+ case "session.created":
1329
+ case "session.updated":
1330
+ appendOpenCodeSessionCreatedOrUpdated(event, state, events);
1331
+ break;
1332
+ case "message.updated":
1333
+ appendOpenCodeMessageUpdated(event, state, events);
1334
+ break;
1335
+ case "message.part.updated":
1336
+ appendOpenCodeMessagePartUpdated(event, state, events);
1337
+ break;
1338
+ case "message.part.delta":
1339
+ appendOpenCodeMessagePartDelta(event, state, events);
1340
+ break;
1341
+ case "permission.asked":
1342
+ appendOpenCodePermissionAsked(event, state, events);
1343
+ break;
1344
+ case "question.asked":
1345
+ appendOpenCodeQuestionAsked(event, state, events);
1346
+ break;
1347
+ case "todo.updated":
1348
+ if (event.properties.sessionID === state.sessionId) {
1349
+ events.push({
1350
+ type: "timeline",
1351
+ provider: "opencode",
1352
+ item: mapOpenCodeTodosToTimelineItems(event.properties.todos),
1353
+ });
1354
+ }
1355
+ break;
1356
+ case "session.compacted":
1357
+ if (event.properties.sessionID === state.sessionId) {
1358
+ events.push({
1359
+ type: "timeline",
1360
+ provider: "opencode",
1361
+ item: createCompactionTimelineItem("completed"),
1362
+ });
1363
+ }
1364
+ break;
1365
+ case "session.idle":
1366
+ if (event.properties.sessionID === state.sessionId) {
1367
+ resetOpenCodeTurnTrackingState(state);
1368
+ events.push({ type: "turn_completed", provider: "opencode", usage: undefined });
1369
+ }
1370
+ break;
1371
+ case "session.error":
1372
+ appendOpenCodeSessionError(event, state, events);
1373
+ break;
1374
+ case "session.status":
1375
+ appendOpenCodeSessionStatus(event, state, events);
1376
+ break;
1377
+ }
1378
+ return events;
1379
+ }
1380
+ function resetOpenCodeTurnTrackingState(state) {
1381
+ state.streamedPartKeys.clear();
1382
+ state.partTypes.clear();
1383
+ state.compactionSummaryMessageIds.clear();
1384
+ state.emittedCompactionPartIds.clear();
1385
+ if (state.suppressAssistantMessagesUntilIdle) {
1386
+ state.suppressAssistantMessagesUntilIdle.active = false;
1387
+ }
1388
+ }
1389
+ function getOpenCodeSubAgentMaps(state) {
1390
+ state.subAgentsByCallId ?? (state.subAgentsByCallId = new Map());
1391
+ state.subAgentCallIdByChildSessionId ?? (state.subAgentCallIdByChildSessionId = new Map());
1392
+ state.pendingChildToolPartsBySessionId ?? (state.pendingChildToolPartsBySessionId = new Map());
1393
+ return {
1394
+ byCallId: state.subAgentsByCallId,
1395
+ callIdByChildSessionId: state.subAgentCallIdByChildSessionId,
1396
+ pendingChildToolPartsBySessionId: state.pendingChildToolPartsBySessionId,
1397
+ };
1398
+ }
1399
+ function isOpenCodeSessionTrackedByParent(sessionId, state) {
1400
+ return (sessionId === state.sessionId || state.subAgentCallIdByChildSessionId?.has(sessionId) === true);
1401
+ }
1402
+ function getOpenCodeSubAgentState(callId, state, toolCall) {
1403
+ const maps = getOpenCodeSubAgentMaps(state);
1404
+ const existing = maps.byCallId.get(callId);
1405
+ if (existing) {
1406
+ existing.toolCall = toolCall;
1407
+ return existing;
1408
+ }
1409
+ const created = {
1410
+ toolCall,
1411
+ actions: [],
1412
+ actionIndexByKey: new Map(),
1413
+ nextActionIndex: 1,
1414
+ };
1415
+ maps.byCallId.set(callId, created);
1416
+ return created;
1417
+ }
1418
+ function linkOpenCodeSubAgentChildSession(activity, childSessionId, state) {
1419
+ activity.childSessionId = childSessionId;
1420
+ const maps = getOpenCodeSubAgentMaps(state);
1421
+ maps.callIdByChildSessionId.set(childSessionId, activity.toolCall.callId);
1422
+ }
1423
+ function buildOpenCodeSubAgentLog(detail, activity) {
1424
+ const actionLog = activity.actions
1425
+ .map((action) => action.summary ? `[${action.toolName}] ${action.summary}` : `[${action.toolName}]`)
1426
+ .join("\n");
1427
+ const parts = [actionLog, detail.log].filter((part) => part.trim().length > 0);
1428
+ return parts.join("\n\n");
1429
+ }
1430
+ function buildOpenCodeSubAgentTimelineItem(activity) {
1431
+ const toolCall = activity.toolCall;
1432
+ if (toolCall.detail.type !== "sub_agent") {
1433
+ return toolCall;
1434
+ }
1435
+ const childSessionId = activity.childSessionId ?? toolCall.detail.childSessionId;
1436
+ return {
1437
+ ...toolCall,
1438
+ detail: {
1439
+ ...toolCall.detail,
1440
+ ...(childSessionId ? { childSessionId } : {}),
1441
+ log: buildOpenCodeSubAgentLog(toolCall.detail, activity),
1442
+ },
1443
+ };
1444
+ }
1445
+ function registerOpenCodeSubAgentToolCall(item, state) {
1446
+ if (item.detail.type !== "sub_agent") {
1447
+ return item;
1448
+ }
1449
+ const activity = getOpenCodeSubAgentState(item.callId, state, item);
1450
+ if (item.detail.childSessionId) {
1451
+ linkOpenCodeSubAgentChildSession(activity, item.detail.childSessionId, state);
1452
+ }
1453
+ return buildOpenCodeSubAgentTimelineItem(activity);
1454
+ }
1455
+ function bufferOpenCodeSubAgentChildToolPart(part, state) {
1456
+ const maps = getOpenCodeSubAgentMaps(state);
1457
+ if (maps.byCallId.size === 0) {
1458
+ return;
1459
+ }
1460
+ const totalPending = [...maps.pendingChildToolPartsBySessionId.values()].reduce((total, parts) => total + parts.length, 0);
1461
+ if (totalPending >= MAX_OPENCODE_PENDING_CHILD_TOOL_PARTS) {
1462
+ return;
1463
+ }
1464
+ const pending = maps.pendingChildToolPartsBySessionId.get(part.sessionID) ?? [];
1465
+ pending.push(part);
1466
+ maps.pendingChildToolPartsBySessionId.set(part.sessionID, pending);
1467
+ }
1468
+ function flushOpenCodeSubAgentChildToolParts(childSessionId, state, events) {
1469
+ const maps = getOpenCodeSubAgentMaps(state);
1470
+ const pending = maps.pendingChildToolPartsBySessionId.get(childSessionId);
1471
+ if (!pending || pending.length === 0) {
1472
+ return;
1473
+ }
1474
+ maps.pendingChildToolPartsBySessionId.delete(childSessionId);
1475
+ for (const part of pending) {
1476
+ appendOpenCodeSubAgentChildToolPart(part, state, events);
1477
+ }
1478
+ }
1479
+ function findOnlyOpenCodeSubAgentWaitingForChild(state) {
1480
+ const maps = getOpenCodeSubAgentMaps(state);
1481
+ const candidates = [...maps.byCallId.values()].filter((activity) => activity.toolCall.status === "running" &&
1482
+ activity.toolCall.detail.type === "sub_agent" &&
1483
+ !activity.childSessionId);
1484
+ return candidates.length === 1 ? (candidates[0] ?? null) : null;
1485
+ }
1486
+ function summarizeOpenCodeSubAgentAction(item, cwd) {
1487
+ const display = buildToolCallDisplayModel({
1488
+ name: item.name,
1489
+ status: item.status,
1490
+ error: item.error,
1491
+ metadata: item.metadata,
1492
+ detail: item.detail,
1493
+ cwd,
1494
+ });
1495
+ return display.summary ?? display.errorText;
1496
+ }
1497
+ function appendOpenCodeSubAgentAction(activity, item, cwd) {
1498
+ const key = item.callId || `${item.name}:${activity.actions.length}`;
1499
+ const existingIndex = activity.actionIndexByKey.get(key);
1500
+ const summary = summarizeOpenCodeSubAgentAction(item, cwd);
1501
+ if (existingIndex !== undefined) {
1502
+ const action = activity.actions[existingIndex];
1503
+ if (!action) {
1504
+ return false;
1505
+ }
1506
+ const changed = action.toolName !== item.name || action.summary !== summary;
1507
+ action.toolName = item.name;
1508
+ if (summary) {
1509
+ action.summary = summary;
1510
+ }
1511
+ else {
1512
+ delete action.summary;
1513
+ }
1514
+ return changed;
1515
+ }
1516
+ if (activity.actions.length >= MAX_OPENCODE_SUB_AGENT_ACTIONS) {
1517
+ return false;
1518
+ }
1519
+ activity.actionIndexByKey.set(key, activity.actions.length);
1520
+ activity.actions.push({
1521
+ index: activity.nextActionIndex,
1522
+ key,
1523
+ toolName: item.name,
1524
+ ...(summary ? { summary } : {}),
1525
+ });
1526
+ activity.nextActionIndex += 1;
1527
+ return true;
1528
+ }
1529
+ function appendOpenCodeToolCallTimelineItem(item, state, events) {
1530
+ const timelineItem = registerOpenCodeSubAgentToolCall(item, state);
1531
+ events.push({
1532
+ type: "timeline",
1533
+ provider: "opencode",
1534
+ item: timelineItem,
1535
+ });
1536
+ if (timelineItem.detail.type === "sub_agent" && timelineItem.detail.childSessionId) {
1537
+ flushOpenCodeSubAgentChildToolParts(timelineItem.detail.childSessionId, state, events);
1538
+ }
1539
+ }
1540
+ function appendOpenCodeSubAgentChildSessionLinked(childSessionId, state, events) {
1541
+ const activity = findOnlyOpenCodeSubAgentWaitingForChild(state);
1542
+ if (!activity) {
1543
+ return;
1544
+ }
1545
+ linkOpenCodeSubAgentChildSession(activity, childSessionId, state);
1546
+ events.push({
1547
+ type: "timeline",
1548
+ provider: "opencode",
1549
+ item: buildOpenCodeSubAgentTimelineItem(activity),
1550
+ });
1551
+ flushOpenCodeSubAgentChildToolParts(childSessionId, state, events);
1552
+ }
1553
+ function appendOpenCodeSubAgentChildToolPart(part, state, events) {
1554
+ const maps = getOpenCodeSubAgentMaps(state);
1555
+ const parentCallId = maps.callIdByChildSessionId.get(part.sessionID);
1556
+ if (!parentCallId) {
1557
+ bufferOpenCodeSubAgentChildToolPart(part, state);
1558
+ return;
1559
+ }
1560
+ const activity = maps.byCallId.get(parentCallId);
1561
+ if (!activity) {
1562
+ return;
1563
+ }
1564
+ const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
1565
+ if (!parsedToolPart.success || !parsedToolPart.data) {
1566
+ return;
1567
+ }
1568
+ if (!appendOpenCodeSubAgentAction(activity, parsedToolPart.data, state.cwd)) {
1569
+ return;
1570
+ }
1571
+ events.push({
1572
+ type: "timeline",
1573
+ provider: "opencode",
1574
+ item: buildOpenCodeSubAgentTimelineItem(activity),
1575
+ });
1576
+ }
1577
+ function appendOpenCodeSessionCreatedOrUpdated(event, state, events) {
1578
+ const info = readOpenCodeRecord(event.properties.info);
1579
+ if (event.properties.info.id === state.sessionId) {
1580
+ const sessionCost = readPositiveFiniteNumber(info?.cost);
1581
+ if (sessionCost !== undefined) {
1582
+ state.sessionTotalCostUsd = maxFiniteNumber(state.sessionTotalCostUsd, sessionCost);
1583
+ state.accumulatedUsage.totalCostUsd = state.sessionTotalCostUsd;
1584
+ }
1585
+ events.push({
1586
+ type: "thread_started",
1587
+ sessionId: state.sessionId,
1588
+ provider: "opencode",
1589
+ });
1590
+ return;
1591
+ }
1592
+ const parentSessionId = readNonEmptyString(info?.parentID) ?? readNonEmptyString(info?.parentId);
1593
+ if (parentSessionId === state.sessionId) {
1594
+ appendOpenCodeSubAgentChildSessionLinked(event.properties.info.id, state, events);
1595
+ }
1596
+ }
1597
+ function appendOpenCodeMessageUpdated(event, state, events) {
1598
+ const info = event.properties.info;
1599
+ if (info.sessionID !== state.sessionId) {
1600
+ return;
1601
+ }
1602
+ state.messageRoles.set(info.id, info.role);
1603
+ if (info.role === "user") {
1604
+ appendOpenCodeUserMessageUpdated(info, state, events);
1605
+ return;
1606
+ }
1607
+ if (info.role !== "assistant") {
1608
+ return;
1609
+ }
1610
+ if (state.suppressAssistantMessagesUntilIdle?.active) {
1611
+ state.compactionSummaryMessageIds.add(info.id);
1612
+ return;
1613
+ }
1614
+ if (isOpenCodeCompactionSummaryMessage(info)) {
1615
+ state.compactionSummaryMessageIds.add(info.id);
1616
+ return;
1617
+ }
1618
+ const modelLookupKey = resolveOpenCodeModelLookupKeyFromAssistantMessage(info);
1619
+ if (modelLookupKey) {
1620
+ const contextWindowMaxTokens = state.modelContextWindowsByModelKey?.get(modelLookupKey);
1621
+ if (contextWindowMaxTokens !== undefined) {
1622
+ state.onAssistantModelContextWindowResolved?.(contextWindowMaxTokens);
1623
+ }
1624
+ }
1625
+ if (state.emittedStructuredMessageIds.has(info.id) || info.time?.completed === undefined) {
1626
+ return;
1627
+ }
1628
+ const text = stringifyStructuredAssistantMessage(info.structured);
1629
+ if (!text) {
1630
+ return;
1631
+ }
1632
+ state.emittedStructuredMessageIds.add(info.id);
1633
+ events.push({
1634
+ type: "timeline",
1635
+ provider: "opencode",
1636
+ item: { type: "assistant_message", text },
1637
+ });
1638
+ }
1639
+ function appendOpenCodeUserMessageUpdated(info, state, events) {
1640
+ const text = state.pendingUserMessageText;
1641
+ if (!text || text.trim().length === 0 || state.emittedUserMessageIds?.has(info.id)) {
1642
+ return;
1643
+ }
1644
+ state.emittedUserMessageIds?.add(info.id);
1645
+ events.push({
1646
+ type: "timeline",
1647
+ provider: "opencode",
1648
+ item: { type: "user_message", text, messageId: info.id },
1649
+ });
1650
+ }
1651
+ function appendOpenCodeMessagePartUpdated(event, state, events) {
1652
+ const part = event.properties.part;
1653
+ if (part.type === "tool" && isOpenCodeTodoWriteToolPart(part)) {
1654
+ return;
1655
+ }
1656
+ if (part.sessionID !== state.sessionId) {
1657
+ if (part.type === "tool") {
1658
+ appendOpenCodeSubAgentChildToolPart(part, state, events);
1659
+ }
1660
+ return;
1661
+ }
1662
+ const messageRole = state.messageRoles.get(part.messageID);
1663
+ state.partTypes.set(part.id, part.type);
1664
+ if (state.compactionSummaryMessageIds.has(part.messageID)) {
1665
+ return;
1666
+ }
1667
+ if (shouldSuppressOpenCodeAssistantPart(part, messageRole, state)) {
1668
+ state.compactionSummaryMessageIds.add(part.messageID);
1669
+ return;
1670
+ }
1671
+ if (part.type === "text") {
1672
+ appendOpenCodeTextPart(part, messageRole, state, events);
1673
+ return;
1674
+ }
1675
+ if (part.type === "reasoning") {
1676
+ appendOpenCodeReasoningPart(part, state, events);
1677
+ return;
1678
+ }
1679
+ if (part.type === "tool") {
1680
+ const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
1681
+ if (parsedToolPart.success && parsedToolPart.data) {
1682
+ appendOpenCodeToolCallTimelineItem(parsedToolPart.data, state, events);
1683
+ }
1684
+ return;
1685
+ }
1686
+ if (part.type === "compaction") {
1687
+ if (state.emittedCompactionPartIds.has(part.id)) {
1688
+ return;
1689
+ }
1690
+ state.emittedCompactionPartIds.add(part.id);
1691
+ events.push({
1692
+ type: "timeline",
1693
+ provider: "opencode",
1694
+ item: createCompactionTimelineItem("loading", part.auto ? "auto" : "manual"),
1695
+ });
1696
+ return;
1697
+ }
1698
+ if (part.type === "step-finish") {
1699
+ const stepCost = readPositiveFiniteNumber(part.cost);
1700
+ if (stepCost !== undefined) {
1701
+ state.sessionTotalCostUsd = (state.sessionTotalCostUsd ?? 0) + stepCost;
1702
+ }
1703
+ mergeOpenCodeStepFinishUsage(state.accumulatedUsage, part, {
1704
+ totalCostUsd: state.sessionTotalCostUsd,
1705
+ });
1706
+ if (hasNormalizedOpenCodeUsage(state.accumulatedUsage)) {
1707
+ events.push({
1708
+ type: "usage_updated",
1709
+ provider: "opencode",
1710
+ usage: { ...state.accumulatedUsage },
1711
+ });
1712
+ }
1713
+ }
1714
+ }
1715
+ function shouldSuppressOpenCodeAssistantPart(part, messageRole, state) {
1716
+ return (state.suppressAssistantMessagesUntilIdle?.active === true &&
1717
+ part.type === "text" &&
1718
+ messageRole !== "user");
1719
+ }
1720
+ function appendOpenCodeTextPart(part, messageRole, state, events) {
1721
+ if (messageRole === "user") {
1722
+ return;
1723
+ }
1724
+ if (!part.time?.end) {
1725
+ return;
1726
+ }
1727
+ const partKey = resolvePartDedupeKey(part, "text");
1728
+ if (partKey && state.streamedPartKeys.delete(partKey)) {
1729
+ return;
1730
+ }
1731
+ if (part.text) {
1732
+ events.push({
1733
+ type: "timeline",
1734
+ provider: "opencode",
1735
+ item: { type: "assistant_message", text: part.text },
1736
+ });
1737
+ }
1738
+ }
1739
+ function appendOpenCodeReasoningPart(part, state, events) {
1740
+ if (!part.time.end) {
1741
+ return;
1742
+ }
1743
+ const partKey = resolvePartDedupeKey(part, "reasoning");
1744
+ if (partKey && state.streamedPartKeys.delete(partKey)) {
1745
+ return;
1746
+ }
1747
+ if (part.text) {
1748
+ events.push({
1749
+ type: "timeline",
1750
+ provider: "opencode",
1751
+ item: { type: "reasoning", text: part.text },
1752
+ });
1753
+ }
1754
+ }
1755
+ function appendOpenCodeMessagePartDelta(event, state, events) {
1756
+ const { sessionID, messageID, partID, field, delta } = event.properties;
1757
+ if (sessionID !== state.sessionId) {
1758
+ return;
1759
+ }
1760
+ if (!delta || !field) {
1761
+ return;
1762
+ }
1763
+ const messageRole = messageID ? state.messageRoles.get(messageID) : undefined;
1764
+ const knownPartType = partID ? state.partTypes.get(partID) : undefined;
1765
+ const isReasoning = knownPartType === "reasoning" || field === "reasoning";
1766
+ if (messageID && state.compactionSummaryMessageIds.has(messageID)) {
1767
+ return;
1768
+ }
1769
+ if (isReasoning) {
1770
+ if (partID) {
1771
+ state.streamedPartKeys.add(`reasoning:${partID}`);
1772
+ }
1773
+ events.push({
1774
+ type: "timeline",
1775
+ provider: "opencode",
1776
+ item: { type: "reasoning", text: delta },
1777
+ });
1778
+ return;
1779
+ }
1780
+ if (field !== "text") {
1781
+ return;
1782
+ }
1783
+ if (messageRole === "user") {
1784
+ return;
1785
+ }
1786
+ if (messageID && state.suppressAssistantMessagesUntilIdle?.active === true) {
1787
+ state.compactionSummaryMessageIds.add(messageID);
1788
+ return;
1789
+ }
1790
+ if (partID) {
1791
+ state.streamedPartKeys.add(`text:${partID}`);
1792
+ }
1793
+ events.push({
1794
+ type: "timeline",
1795
+ provider: "opencode",
1796
+ item: { type: "assistant_message", text: delta },
1797
+ });
1798
+ }
1799
+ function appendOpenCodePermissionAsked(event, state, events) {
1800
+ if (!isOpenCodeSessionTrackedByParent(event.properties.sessionID, state)) {
1801
+ return;
1802
+ }
1803
+ const metadata = readOpenCodeRecord(event.properties.metadata);
1804
+ const tool = readOpenCodeRecord(event.properties.tool);
1805
+ const patterns = Array.isArray(event.properties.patterns)
1806
+ ? event.properties.patterns.filter((value) => typeof value === "string")
1807
+ : [];
1808
+ const command = readPermissionField(metadata, PERMISSION_COMMAND_KEYS);
1809
+ const cwd = readPermissionField(metadata, PERMISSION_CWD_KEYS);
1810
+ const reason = readPermissionField(metadata, PERMISSION_REASON_KEYS);
1811
+ const input = buildOpenCodePermissionInput({ patterns, metadata, tool, command });
1812
+ const detail = buildOpenCodePermissionDetail({
1813
+ permission: event.properties.permission,
1814
+ input,
1815
+ command,
1816
+ cwd,
1817
+ });
1818
+ const description = buildOpenCodePermissionDescription({ reason, patterns });
1819
+ events.push({
1820
+ type: "permission_requested",
1821
+ provider: "opencode",
1822
+ request: {
1823
+ id: event.properties.id,
1824
+ provider: "opencode",
1825
+ name: event.properties.permission,
1826
+ kind: "tool",
1827
+ title: toHumanReadablePermissionTitle(event.properties.permission),
1828
+ ...(description ? { description } : {}),
1829
+ input,
1830
+ detail,
1831
+ actions: buildOpenCodePermissionActions(),
1832
+ },
1833
+ });
1834
+ }
1835
+ function appendOpenCodeQuestionAsked(event, state, events) {
1836
+ if (event.properties.sessionID !== state.sessionId) {
1837
+ return;
1838
+ }
1839
+ const questions = event.properties.questions.flatMap((q) => {
1840
+ if (!q.question || !q.header) {
1841
+ return [];
1842
+ }
1843
+ const options = q.options?.map((o) => ({
1844
+ label: o.label,
1845
+ ...(o.description ? { description: o.description } : {}),
1846
+ })) ?? [];
1847
+ return [
1848
+ {
1849
+ question: q.question,
1850
+ header: q.header,
1851
+ options,
1852
+ ...(q.multiple === true ? { multiSelect: true } : {}),
1853
+ allowOther: true,
1854
+ },
1855
+ ];
1856
+ });
1857
+ if (questions.length === 0) {
1858
+ return;
1859
+ }
1860
+ events.push({
1861
+ type: "permission_requested",
1862
+ provider: "opencode",
1863
+ request: {
1864
+ id: event.properties.id,
1865
+ provider: "opencode",
1866
+ name: "question",
1867
+ kind: "question",
1868
+ title: "Question",
1869
+ input: { questions },
1870
+ metadata: {
1871
+ source: "opencode_question",
1872
+ ...event.properties.tool,
1873
+ },
1874
+ },
1875
+ });
1876
+ }
1877
+ function appendOpenCodeSessionError(event, state, events) {
1878
+ if (event.properties.sessionID !== state.sessionId) {
1879
+ return;
1880
+ }
1881
+ resetOpenCodeTurnTrackingState(state);
1882
+ const error = event.properties.error;
1883
+ if (typeof error === "object" &&
1884
+ error !== null &&
1885
+ "name" in error &&
1886
+ error.name === "MessageAbortedError") {
1887
+ events.push({
1888
+ type: "turn_canceled",
1889
+ provider: "opencode",
1890
+ reason: "interrupted",
1891
+ });
1892
+ }
1893
+ else {
1894
+ events.push({
1895
+ type: "turn_failed",
1896
+ provider: "opencode",
1897
+ error: toDiagnosticErrorMessage(error),
1898
+ });
1899
+ }
1900
+ }
1901
+ function appendOpenCodeSessionStatus(event, state, events) {
1902
+ if (event.properties.sessionID !== state.sessionId) {
1903
+ return;
1904
+ }
1905
+ const { status } = event.properties;
1906
+ if (status.type === "idle") {
1907
+ resetOpenCodeTurnTrackingState(state);
1908
+ events.push({ type: "turn_completed", provider: "opencode", usage: undefined });
1909
+ return;
1910
+ }
1911
+ if (status.type === "retry") {
1912
+ // Mirror what opencode's TUI shows: retry attempts are visible activity, not
1913
+ // terminal. opencode itself never gives up — it backs off and tries again
1914
+ // forever. If we silently swallow these the user sees a spinner with no
1915
+ // explanation. Forwarding as a timeline error item is a no-op for old
1916
+ // clients (the schema already supports it).
1917
+ const message = typeof status.message === "string" ? status.message.trim() : "";
1918
+ const text = message
1919
+ ? `Provider retry (attempt ${status.attempt}): ${message}`
1920
+ : `Provider retry (attempt ${status.attempt})`;
1921
+ events.push({
1922
+ type: "timeline",
1923
+ provider: "opencode",
1924
+ item: { type: "error", message: text },
1925
+ });
1926
+ return;
1927
+ }
1928
+ // "busy" is transient — no terminal event, no surfaced activity.
1929
+ }
1930
+ function createDeferred() {
1931
+ let resolve;
1932
+ let reject;
1933
+ const promise = new Promise((res, rej) => {
1934
+ resolve = res;
1935
+ reject = rej;
1936
+ });
1937
+ return { promise, resolve, reject };
1938
+ }
1939
+ function unwrapOpenCodeGlobalEvent(event) {
1940
+ const record = readOpenCodeRecord(event);
1941
+ if (!record) {
1942
+ return null;
1943
+ }
1944
+ const payload = readOpenCodeRecord(record.payload);
1945
+ if (typeof payload?.type === "string") {
1946
+ return payload;
1947
+ }
1948
+ if (typeof record.type === "string") {
1949
+ return record;
1950
+ }
1951
+ return null;
1952
+ }
1953
+ function isOpenCodeUserMessageEvent(event, sessionId) {
1954
+ return (event.type === "message.updated" &&
1955
+ event.properties.info.sessionID === sessionId &&
1956
+ event.properties.info.role === "user");
1957
+ }
1958
+ function isOpenCodeTerminalEvent(event, sessionId) {
1959
+ if (event.type === "session.idle" || event.type === "session.error") {
1960
+ return event.properties.sessionID === sessionId;
1961
+ }
1962
+ return (event.type === "session.status" &&
1963
+ event.properties.sessionID === sessionId &&
1964
+ event.properties.status.type === "idle");
1965
+ }
1966
+ class OpenCodeAgentSession {
1967
+ constructor(config, client, sessionId, logger, modelContextWindowsByModelKey = new Map(), releaseServer, persistSession = true, agentId) {
1968
+ this.agentId = agentId;
1969
+ this.provider = "opencode";
1970
+ this.capabilities = OPENCODE_CAPABILITIES;
1971
+ this.currentMode = "default";
1972
+ this.autoAcceptEnabled = false;
1973
+ this.pendingPermissions = new Map();
1974
+ this.abortController = null;
1975
+ this.pendingAbortPromise = null;
1976
+ this.accumulatedUsage = {};
1977
+ this.mcpConfigured = false;
1978
+ this.mcpSetupPromise = null;
1979
+ /** Tracks the role of each message by ID to distinguish user from assistant messages */
1980
+ this.messageRoles = new Map();
1981
+ this.pendingUserMessageText = null;
1982
+ this.emittedUserMessageIds = new Set();
1983
+ /** Tracks streamed textual part IDs to suppress final full-text echoes from OpenCode. */
1984
+ this.streamedPartKeys = new Set();
1985
+ /** Tracks assistant messages already emitted from structured payloads. */
1986
+ this.emittedStructuredMessageIds = new Set();
1987
+ this.compactionSummaryMessageIds = new Set();
1988
+ this.emittedCompactionPartIds = new Set();
1989
+ this.suppressAssistantMessagesUntilIdle = { active: false };
1990
+ /** Tracks the type of each part by ID, learned from message.part.updated events. */
1991
+ this.partTypes = new Map();
1992
+ this.availableModesCache = null;
1993
+ this.subscribers = new Set();
1994
+ this.nextTurnOrdinal = 0;
1995
+ this.activeForegroundTurnId = null;
1996
+ this.runningToolCalls = new Map();
1997
+ this.subAgentsByCallId = new Map();
1998
+ this.subAgentCallIdByChildSessionId = new Map();
1999
+ this.pendingChildToolPartsBySessionId = new Map();
2000
+ this.eventStreamAbortController = null;
2001
+ this.eventStreamReady = null;
2002
+ this.suppressTerminalUntilNextUserMessage = false;
2003
+ this.closed = false;
2004
+ this.deletedFromProvider = false;
2005
+ this.config = config;
2006
+ this.client = client;
2007
+ this.sessionId = sessionId;
2008
+ this.logger = logger.child({ agentId: this.agentId });
2009
+ this.modelContextWindowsByModelKey = modelContextWindowsByModelKey;
2010
+ this.currentMode = normalizeOpenCodeModeId(config.modeId);
2011
+ this.autoAcceptEnabled = isOpenCodeAutoAcceptEnabled(config);
2012
+ this.releaseServer = releaseServer ?? null;
2013
+ this.persistSession = persistSession;
2014
+ this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(config.model);
2015
+ this.startEventStream();
2016
+ }
2017
+ get id() {
2018
+ return this.sessionId;
2019
+ }
2020
+ get features() {
2021
+ return [buildOpenCodeAutoAcceptFeature(this.config)];
2022
+ }
2023
+ async getRuntimeInfo() {
2024
+ return {
2025
+ provider: "opencode",
2026
+ sessionId: this.sessionId,
2027
+ model: this.config.model ?? null,
2028
+ modeId: this.currentMode,
2029
+ };
2030
+ }
2031
+ async setModel(modelId) {
2032
+ const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId : null;
2033
+ this.config.model = normalizedModelId ?? undefined;
2034
+ this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(this.config.model);
2035
+ }
2036
+ async setThinkingOption(thinkingOptionId) {
2037
+ const normalizedThinkingOptionId = typeof thinkingOptionId === "string" && thinkingOptionId.trim().length > 0
2038
+ ? thinkingOptionId
2039
+ : null;
2040
+ this.config.thinkingOptionId = normalizedThinkingOptionId ?? undefined;
2041
+ }
2042
+ async run(prompt, options) {
2043
+ return runProviderTurn({
2044
+ prompt,
2045
+ runOptions: options,
2046
+ startTurn: (p, o) => this.startTurn(p, o),
2047
+ subscribe: (callback) => this.subscribe(callback),
2048
+ getSessionId: () => this.sessionId,
2049
+ });
2050
+ }
2051
+ async interrupt() {
2052
+ const turnId = this.activeForegroundTurnId;
2053
+ const turnAbortController = this.abortController;
2054
+ turnAbortController?.abort();
2055
+ // COMPAT(opencodeSlowAbort): OpenCode 1.14.42+ blocks session.abort until
2056
+ // the running tool actually stops, which can be tens of seconds for
2057
+ // long-running tools. Cap the wait so the user-visible cancel lands
2058
+ // quickly while still giving OpenCode a chance to confirm the abort
2059
+ // cleanly. Drop the timeout once upstream returns abort acknowledgement
2060
+ // before tool teardown.
2061
+ const abortPromise = this.beginSessionAbort(turnId, "interrupt");
2062
+ await withTimeout(abortPromise, 2000, "OpenCode session.abort").catch((error) => {
2063
+ this.logger.warn({ err: error, sessionId: this.sessionId, turnId }, "OpenCode session.abort exceeded the cancel cap; proceeding with local cancel");
2064
+ });
2065
+ if (turnId) {
2066
+ this.suppressTerminalUntilNextUserMessage = true;
2067
+ this.finishForegroundTurn({ type: "turn_canceled", provider: "opencode", reason: "interrupted" }, turnId);
2068
+ }
2069
+ }
2070
+ async revertBoth(input) {
2071
+ await revertOpenCodeConversationAndFiles({
2072
+ client: this.client,
2073
+ sessionId: this.sessionId,
2074
+ cwd: this.config.cwd,
2075
+ messageId: input.messageId,
2076
+ });
2077
+ }
2078
+ beginSessionAbort(turnId, reason) {
2079
+ const abortPromise = this.client.session
2080
+ .abort({
2081
+ sessionID: this.sessionId,
2082
+ directory: this.config.cwd,
2083
+ })
2084
+ .then(() => undefined)
2085
+ .catch((error) => {
2086
+ this.logger.warn({ err: error, sessionId: this.sessionId, turnId, reason }, "OpenCode session.abort rejected");
2087
+ });
2088
+ const trackedAbortPromise = abortPromise.finally(() => {
2089
+ if (this.pendingAbortPromise === trackedAbortPromise) {
2090
+ this.pendingAbortPromise = null;
2091
+ }
2092
+ });
2093
+ this.pendingAbortPromise = trackedAbortPromise;
2094
+ return trackedAbortPromise;
2095
+ }
2096
+ async awaitPendingAbortBeforeStartingTurn() {
2097
+ const pendingAbortPromise = this.pendingAbortPromise;
2098
+ if (!pendingAbortPromise) {
2099
+ return;
2100
+ }
2101
+ await withTimeout(pendingAbortPromise, OPENCODE_PENDING_ABORT_START_TIMEOUT_MS, "OpenCode pending session.abort").catch((error) => {
2102
+ this.logger.warn({ err: error, sessionId: this.sessionId }, "OpenCode session.abort was still pending before starting the next turn");
2103
+ });
2104
+ }
2105
+ async startTurn(prompt, options) {
2106
+ if (this.activeForegroundTurnId) {
2107
+ throw new Error("A foreground turn is already active");
2108
+ }
2109
+ await this.awaitPendingAbortBeforeStartingTurn();
2110
+ this.runningToolCalls.clear();
2111
+ this.subAgentsByCallId.clear();
2112
+ this.subAgentCallIdByChildSessionId.clear();
2113
+ this.pendingChildToolPartsBySessionId.clear();
2114
+ const turnAbortController = new AbortController();
2115
+ this.abortController = turnAbortController;
2116
+ await this.ensureMcpServersConfigured();
2117
+ const contextWindowMaxTokens = this.resolveSelectedModelContextWindowMaxTokens();
2118
+ this.accumulatedUsage = contextWindowMaxTokens !== undefined ? { contextWindowMaxTokens } : {};
2119
+ const parts = buildOpenCodePromptParts(prompt);
2120
+ this.pendingUserMessageText = buildOpenCodeUserTimelineText(prompt);
2121
+ this.suppressAssistantMessagesUntilIdle.active = false;
2122
+ const model = this.parseModel(this.config.model);
2123
+ const thinkingOptionId = this.config.thinkingOptionId;
2124
+ const effectiveVariant = thinkingOptionId ?? undefined;
2125
+ const effectiveMode = resolveOpenCodeRuntimeAgentId(this.currentMode);
2126
+ try {
2127
+ await this.ensureEventStreamReady();
2128
+ }
2129
+ catch (error) {
2130
+ if (this.abortController === turnAbortController) {
2131
+ this.abortController = null;
2132
+ }
2133
+ throw error;
2134
+ }
2135
+ const turnId = this.createTurnId();
2136
+ this.activeForegroundTurnId = turnId;
2137
+ this.notifySubscribers({ type: "turn_started", provider: "opencode" }, turnId);
2138
+ const slashCommand = await this.resolveSlashCommandInvocation(prompt);
2139
+ if (slashCommand) {
2140
+ if (slashCommand.commandName === "compact" || slashCommand.commandName === "summarize") {
2141
+ this.suppressAssistantMessagesUntilIdle.active = true;
2142
+ void this.client.session
2143
+ .summarize({
2144
+ sessionID: this.sessionId,
2145
+ directory: this.config.cwd,
2146
+ ...(model ? { providerID: model.providerID, modelID: model.modelID } : {}),
2147
+ })
2148
+ .then((response) => {
2149
+ if (response.error) {
2150
+ this.suppressAssistantMessagesUntilIdle.active = false;
2151
+ this.finishForegroundTurn({
2152
+ type: "turn_failed",
2153
+ provider: "opencode",
2154
+ error: toDiagnosticErrorMessage(response.error),
2155
+ }, turnId);
2156
+ }
2157
+ return;
2158
+ })
2159
+ .catch((error) => {
2160
+ this.suppressAssistantMessagesUntilIdle.active = false;
2161
+ this.finishForegroundTurn({
2162
+ type: "turn_failed",
2163
+ provider: "opencode",
2164
+ error: toDiagnosticErrorMessage(error),
2165
+ }, turnId);
2166
+ });
2167
+ return { turnId };
2168
+ }
2169
+ // command() is only dispatch acknowledgement. OpenCode session events are
2170
+ // the source of truth for when the command turn becomes idle or fails.
2171
+ void this.client.session
2172
+ .command({
2173
+ sessionID: this.sessionId,
2174
+ directory: this.config.cwd,
2175
+ command: slashCommand.commandName,
2176
+ arguments: slashCommand.args ?? "",
2177
+ ...(this.config.model ? { model: this.config.model } : {}),
2178
+ ...(effectiveMode ? { agent: effectiveMode } : {}),
2179
+ ...(effectiveVariant ? { variant: effectiveVariant } : {}),
2180
+ })
2181
+ .then((response) => {
2182
+ if (response.error) {
2183
+ if (isOpenCodeHeadersTimeoutFailure(response.error)) {
2184
+ this.logger.warn({
2185
+ err: response.error,
2186
+ commandName: slashCommand.commandName,
2187
+ turnId,
2188
+ }, "OpenCode slash command hit a header timeout; waiting for SSE terminal event");
2189
+ return;
2190
+ }
2191
+ const errorMsg = toDiagnosticErrorMessage(response.error);
2192
+ this.finishForegroundTurn({ type: "turn_failed", provider: "opencode", error: errorMsg }, turnId);
2193
+ }
2194
+ return;
2195
+ })
2196
+ .catch((err) => {
2197
+ if (isOpenCodeHeadersTimeoutFailure(err)) {
2198
+ this.logger.warn({
2199
+ err,
2200
+ commandName: slashCommand.commandName,
2201
+ turnId,
2202
+ }, "OpenCode slash command hit a header timeout; waiting for SSE terminal event");
2203
+ return;
2204
+ }
2205
+ this.finishForegroundTurn({ type: "turn_failed", provider: "opencode", error: toDiagnosticErrorMessage(err) }, turnId);
2206
+ });
2207
+ }
2208
+ else {
2209
+ // Wrap in an async IIFE so a synchronous throw from promptAsync (e.g.
2210
+ // SDK input validation) is caught alongside async rejections. A plain
2211
+ // `.then().catch()` chain would let a sync throw escape unhandled.
2212
+ void (async () => {
2213
+ this.traceOpenCode("provider.opencode.prompt_async.start", {
2214
+ turnId,
2215
+ sessionId: this.sessionId,
2216
+ model,
2217
+ effectiveMode,
2218
+ effectiveVariant,
2219
+ partTypes: parts.map((p) => p.type),
2220
+ });
2221
+ try {
2222
+ const systemPrompt = composeSystemPromptParts(this.config.systemPrompt, this.config.daemonAppendSystemPrompt);
2223
+ const promptResponse = await this.client.session.promptAsync({
2224
+ sessionID: this.sessionId,
2225
+ directory: this.config.cwd,
2226
+ parts,
2227
+ ...(options?.outputSchema
2228
+ ? {
2229
+ format: {
2230
+ type: "json_schema",
2231
+ schema: options.outputSchema,
2232
+ },
2233
+ }
2234
+ : {}),
2235
+ ...(systemPrompt ? { system: systemPrompt } : {}),
2236
+ ...(model ? { model } : {}),
2237
+ ...(effectiveMode ? { agent: effectiveMode } : {}),
2238
+ ...(effectiveVariant ? { variant: effectiveVariant } : {}),
2239
+ });
2240
+ this.traceOpenCode("provider.opencode.prompt_async.response", {
2241
+ turnId,
2242
+ hasError: promptResponse.error !== undefined,
2243
+ error: promptResponse.error,
2244
+ data: promptResponse.data,
2245
+ });
2246
+ if (promptResponse.error) {
2247
+ this.finishForegroundTurn({
2248
+ type: "turn_failed",
2249
+ provider: "opencode",
2250
+ error: toDiagnosticErrorMessage(promptResponse.error),
2251
+ }, turnId);
2252
+ }
2253
+ }
2254
+ catch (error) {
2255
+ this.traceOpenCode("provider.opencode.prompt_async.throw", {
2256
+ turnId,
2257
+ error: error instanceof Error
2258
+ ? { name: error.name, message: error.message, stack: error.stack }
2259
+ : String(error),
2260
+ });
2261
+ this.finishForegroundTurn({
2262
+ type: "turn_failed",
2263
+ provider: "opencode",
2264
+ error: toDiagnosticErrorMessage(error),
2265
+ }, turnId);
2266
+ }
2267
+ })();
2268
+ }
2269
+ return { turnId };
2270
+ }
2271
+ subscribe(callback) {
2272
+ this.subscribers.add(callback);
2273
+ return () => {
2274
+ this.subscribers.delete(callback);
2275
+ };
2276
+ }
2277
+ startEventStream() {
2278
+ void this.ensureEventStreamReady().catch((error) => {
2279
+ this.logger.warn({ err: error, sessionId: this.sessionId }, "OpenCode event stream failed");
2280
+ });
2281
+ }
2282
+ ensureEventStreamReady() {
2283
+ if (this.eventStreamReady) {
2284
+ return this.eventStreamReady.promise;
2285
+ }
2286
+ const eventStreamAbortController = new AbortController();
2287
+ const eventStreamReady = createDeferred();
2288
+ this.eventStreamAbortController = eventStreamAbortController;
2289
+ this.eventStreamReady = eventStreamReady;
2290
+ void this.consumeEventStream(eventStreamAbortController, eventStreamReady).finally(() => {
2291
+ if (this.eventStreamAbortController === eventStreamAbortController) {
2292
+ this.eventStreamAbortController = null;
2293
+ this.eventStreamReady = null;
2294
+ }
2295
+ });
2296
+ return eventStreamReady.promise;
2297
+ }
2298
+ async consumeEventStream(eventStreamAbortController, eventStreamReady) {
2299
+ this.traceOpenCode("provider.opencode.subscribe.start", {
2300
+ sessionId: this.sessionId,
2301
+ cwd: this.config.cwd,
2302
+ });
2303
+ let eventStreamReadyResolved = false;
2304
+ try {
2305
+ const result = await this.client.global.event({
2306
+ signal: eventStreamAbortController.signal,
2307
+ sseMaxRetryAttempts: 0,
2308
+ });
2309
+ eventStreamReadyResolved = true;
2310
+ this.traceOpenCode("provider.opencode.subscribe.ready", {
2311
+ sessionId: this.sessionId,
2312
+ });
2313
+ eventStreamReady.resolve();
2314
+ let eventCount = 0;
2315
+ for await (const rawEvent of result.stream) {
2316
+ eventCount += 1;
2317
+ await this.consumeOpenCodeStreamEvent({ rawEvent, eventCount });
2318
+ }
2319
+ this.traceOpenCode("provider.opencode.stream.eof", {
2320
+ eventCount,
2321
+ aborted: eventStreamAbortController.signal.aborted,
2322
+ activeTurnId: this.activeForegroundTurnId,
2323
+ });
2324
+ if (!eventStreamAbortController.signal.aborted) {
2325
+ if (!eventStreamReadyResolved) {
2326
+ eventStreamReady.reject(new Error("OpenCode event stream ended before it became ready"));
2327
+ }
2328
+ const activeTurnId = this.activeForegroundTurnId;
2329
+ if (activeTurnId) {
2330
+ this.traceOpenCode("provider.opencode.turn.fail_eof", {
2331
+ turnId: activeTurnId,
2332
+ eventCount,
2333
+ });
2334
+ this.finishForegroundTurn({
2335
+ type: "turn_failed",
2336
+ provider: "opencode",
2337
+ error: "OpenCode event stream ended before the turn reached a terminal state",
2338
+ }, activeTurnId);
2339
+ }
2340
+ }
2341
+ }
2342
+ catch (error) {
2343
+ this.traceOpenCode("provider.opencode.subscribe.error", {
2344
+ turnId: this.activeForegroundTurnId ?? undefined,
2345
+ error: error instanceof Error ? { name: error.name, message: error.message } : String(error),
2346
+ });
2347
+ if (!eventStreamReadyResolved) {
2348
+ eventStreamReady.reject(error);
2349
+ }
2350
+ const activeTurnId = this.activeForegroundTurnId;
2351
+ if (!eventStreamAbortController.signal.aborted && activeTurnId) {
2352
+ this.finishForegroundTurn({
2353
+ type: "turn_failed",
2354
+ provider: "opencode",
2355
+ error: toDiagnosticErrorMessage(error),
2356
+ }, activeTurnId);
2357
+ }
2358
+ }
2359
+ }
2360
+ async consumeOpenCodeStreamEvent(params) {
2361
+ const { rawEvent, eventCount } = params;
2362
+ const turnId = this.activeForegroundTurnId;
2363
+ const event = unwrapOpenCodeGlobalEvent(rawEvent);
2364
+ this.traceOpenCode("provider.opencode.raw_event", {
2365
+ turnId: turnId ?? undefined,
2366
+ n: eventCount,
2367
+ type: event?.type,
2368
+ rawType: readOpenCodeRecord(rawEvent)?.type,
2369
+ directory: readOpenCodeRecord(rawEvent)?.directory,
2370
+ rawEvent,
2371
+ properties: event?.properties,
2372
+ });
2373
+ if (!event) {
2374
+ return;
2375
+ }
2376
+ if (!turnId) {
2377
+ this.traceOpenCode("provider.opencode.event.skip", {
2378
+ n: eventCount,
2379
+ reason: "no_active_turn",
2380
+ type: event.type,
2381
+ });
2382
+ return;
2383
+ }
2384
+ if (this.suppressTerminalUntilNextUserMessage) {
2385
+ if (isOpenCodeUserMessageEvent(event, this.sessionId)) {
2386
+ this.suppressTerminalUntilNextUserMessage = false;
2387
+ }
2388
+ else if (isOpenCodeTerminalEvent(event, this.sessionId)) {
2389
+ this.traceOpenCode("provider.opencode.event.skip", {
2390
+ n: eventCount,
2391
+ reason: "stale_interrupt_terminal",
2392
+ type: event.type,
2393
+ });
2394
+ return;
2395
+ }
2396
+ }
2397
+ const translated = await this.translateEvent(event);
2398
+ this.traceOpenCode("provider.opencode.parsed_event", {
2399
+ turnId,
2400
+ n: eventCount,
2401
+ count: translated.length,
2402
+ types: translated.map((t) => t.type),
2403
+ events: translated,
2404
+ });
2405
+ for (const e of translated) {
2406
+ if (this.activeForegroundTurnId !== turnId) {
2407
+ this.traceOpenCode("provider.opencode.parsed_event.skip_active", { turnId, type: e.type });
2408
+ return;
2409
+ }
2410
+ if (e.type === "timeline" && e.item.type === "tool_call") {
2411
+ this.trackToolCall(e.item);
2412
+ }
2413
+ const terminalEvent = toTerminalTurnEvent(e);
2414
+ if (terminalEvent) {
2415
+ this.traceOpenCode("provider.opencode.event.terminal", {
2416
+ turnId,
2417
+ type: terminalEvent.type,
2418
+ });
2419
+ this.finishForegroundTurn(terminalEvent, turnId);
2420
+ return;
2421
+ }
2422
+ this.notifySubscribers(e, turnId);
2423
+ }
2424
+ }
2425
+ finishForegroundTurn(event, turnId) {
2426
+ this.traceOpenCode("provider.opencode.finish_foreground_turn", {
2427
+ turnId,
2428
+ activeTurnId: this.activeForegroundTurnId,
2429
+ type: event.type,
2430
+ error: event.type === "turn_failed" ? event.error : undefined,
2431
+ reason: event.type === "turn_canceled" ? event.reason : undefined,
2432
+ });
2433
+ if (this.activeForegroundTurnId !== turnId) {
2434
+ return;
2435
+ }
2436
+ if (event.type === "turn_canceled" || event.type === "turn_failed") {
2437
+ this.synthesizeInterruptedToolCalls(turnId);
2438
+ }
2439
+ else {
2440
+ this.runningToolCalls.clear();
2441
+ }
2442
+ this.pendingUserMessageText = null;
2443
+ this.activeForegroundTurnId = null;
2444
+ this.abortController = null;
2445
+ this.notifySubscribers(event, turnId);
2446
+ }
2447
+ trackToolCall(item) {
2448
+ if (item.status === "running") {
2449
+ this.runningToolCalls.set(item.callId, item);
2450
+ return;
2451
+ }
2452
+ this.runningToolCalls.delete(item.callId);
2453
+ }
2454
+ synthesizeInterruptedToolCalls(turnId) {
2455
+ for (const item of this.runningToolCalls.values()) {
2456
+ const error = { message: "Tool execution aborted" };
2457
+ this.notifySubscribers({
2458
+ type: "timeline",
2459
+ provider: "opencode",
2460
+ item: {
2461
+ ...item,
2462
+ status: "failed",
2463
+ error,
2464
+ detail: item.detail.type === "sub_agent"
2465
+ ? {
2466
+ ...item.detail,
2467
+ log: [item.detail.log, error.message]
2468
+ .filter((entry) => entry.trim().length > 0)
2469
+ .join("\n"),
2470
+ }
2471
+ : item.detail,
2472
+ },
2473
+ }, turnId);
2474
+ }
2475
+ this.runningToolCalls.clear();
2476
+ }
2477
+ notifySubscribers(event, turnIdOverride) {
2478
+ if (this.closed) {
2479
+ return;
2480
+ }
2481
+ const turnId = turnIdOverride ?? this.activeForegroundTurnId;
2482
+ const tagged = turnId ? { ...event, turnId } : event;
2483
+ this.traceOpenCode("provider.opencode.event_emit", {
2484
+ turnId: getAgentStreamEventTurnId(tagged),
2485
+ event: tagged,
2486
+ });
2487
+ for (const callback of this.subscribers) {
2488
+ try {
2489
+ callback(tagged);
2490
+ }
2491
+ catch {
2492
+ // Subscriber callback error isolation
2493
+ }
2494
+ }
2495
+ }
2496
+ createTurnId() {
2497
+ return `opencode-turn-${this.nextTurnOrdinal++}`;
2498
+ }
2499
+ traceOpenCode(msg, data = {}) {
2500
+ this.logger.trace({
2501
+ agentId: this.agentId,
2502
+ provider: "opencode",
2503
+ sessionId: this.sessionId,
2504
+ turnId: data.turnId ?? this.activeForegroundTurnId ?? undefined,
2505
+ ...data,
2506
+ }, msg);
2507
+ }
2508
+ async *streamHistory() {
2509
+ const sessionResponse = await this.client.session.get({
2510
+ sessionID: this.sessionId,
2511
+ directory: this.config.cwd,
2512
+ });
2513
+ const response = await this.client.session.messages({
2514
+ sessionID: this.sessionId,
2515
+ directory: this.config.cwd,
2516
+ });
2517
+ if (response.error || !response.data) {
2518
+ return;
2519
+ }
2520
+ const messages = filterOpenCodeRevertedMessages(response.data, sessionResponse.error ? null : sessionResponse.data?.revert);
2521
+ for (const message of messages) {
2522
+ for (const event of buildOpenCodeReplayTimelineEvents(message)) {
2523
+ yield event;
2524
+ }
2525
+ }
2526
+ }
2527
+ async getAvailableModes() {
2528
+ if (this.availableModesCache) {
2529
+ return this.availableModesCache;
2530
+ }
2531
+ const response = await openCodeMetadataLimit(() => this.client.app.agents({
2532
+ directory: this.config.cwd,
2533
+ }));
2534
+ const agents = response.error || !response.data ? [] : response.data;
2535
+ const discoveredModes = agents.filter(isSelectableOpenCodeAgent).map(mapOpenCodeAgentToMode);
2536
+ this.availableModesCache = mergeOpenCodeModes(discoveredModes);
2537
+ return this.availableModesCache;
2538
+ }
2539
+ async getCurrentMode() {
2540
+ return this.currentMode;
2541
+ }
2542
+ async listCommands() {
2543
+ return await listOpenCodeCommandsFromSdk(this.client, this.config.cwd);
2544
+ }
2545
+ async setMode(modeId) {
2546
+ const normalizedModeId = normalizeOpenCodeModeId(modeId);
2547
+ if (normalizedModeId === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID) {
2548
+ this.currentMode = OPENCODE_BUILD_MODE_ID;
2549
+ await this.setFeature(OPENCODE_AUTO_ACCEPT_FEATURE_ID, true);
2550
+ return;
2551
+ }
2552
+ this.currentMode = normalizedModeId;
2553
+ this.config.modeId = normalizedModeId;
2554
+ }
2555
+ async setFeature(featureId, value) {
2556
+ if (featureId !== OPENCODE_AUTO_ACCEPT_FEATURE_ID) {
2557
+ throw new Error(`Unsupported OpenCode feature '${featureId}'`);
2558
+ }
2559
+ const enabled = value === true;
2560
+ this.autoAcceptEnabled = enabled;
2561
+ this.config.featureValues = {
2562
+ ...this.config.featureValues,
2563
+ [OPENCODE_AUTO_ACCEPT_FEATURE_ID]: enabled,
2564
+ };
2565
+ }
2566
+ getPendingPermissions() {
2567
+ return Array.from(this.pendingPermissions.values());
2568
+ }
2569
+ async respondToPermission(requestId, response) {
2570
+ const pending = this.pendingPermissions.get(requestId);
2571
+ if (!pending) {
2572
+ throw new Error(`No pending permission request with id '${requestId}'`);
2573
+ }
2574
+ if (pending.kind === "question") {
2575
+ if (response.behavior === "deny") {
2576
+ await this.client.question.reject({
2577
+ requestID: requestId,
2578
+ directory: this.config.cwd,
2579
+ });
2580
+ }
2581
+ else {
2582
+ const answersRecord = readOpenCodeRecord(response.updatedInput?.answers);
2583
+ const questions = Array.isArray(pending.input?.questions) ? pending.input.questions : [];
2584
+ const answers = questions.map((item) => {
2585
+ const header = readNonEmptyString(readOpenCodeRecord(item)?.header);
2586
+ const rawAnswer = header ? readNonEmptyString(answersRecord?.[header]) : null;
2587
+ if (!rawAnswer) {
2588
+ return [];
2589
+ }
2590
+ return rawAnswer
2591
+ .split(",")
2592
+ .map((entry) => entry.trim())
2593
+ .filter((entry) => entry.length > 0);
2594
+ });
2595
+ await this.client.question.reply({
2596
+ requestID: requestId,
2597
+ directory: this.config.cwd,
2598
+ answers,
2599
+ });
2600
+ }
2601
+ this.pendingPermissions.delete(requestId);
2602
+ return;
2603
+ }
2604
+ const reply = resolveOpenCodePermissionReply(response);
2605
+ await this.client.permission.reply({
2606
+ requestID: requestId,
2607
+ directory: this.config.cwd,
2608
+ reply,
2609
+ message: response.behavior === "deny" ? response.message : undefined,
2610
+ });
2611
+ this.pendingPermissions.delete(requestId);
2612
+ }
2613
+ describePersistence() {
2614
+ return {
2615
+ provider: "opencode",
2616
+ sessionId: this.sessionId,
2617
+ nativeHandle: this.sessionId,
2618
+ metadata: {
2619
+ cwd: this.config.cwd,
2620
+ ...(this.config.modeId ? { modeId: this.config.modeId } : {}),
2621
+ ...(this.config.model ? { model: this.config.model } : {}),
2622
+ },
2623
+ };
2624
+ }
2625
+ async close() {
2626
+ try {
2627
+ // Flip closed before clearing subscribers so any event the SDK delivers
2628
+ // after the abort (between here and subscribers.clear) is swallowed by
2629
+ // notifySubscribers instead of bubbling through provider-runner as an
2630
+ // unhandled rejection in whichever test the daemon hops to next.
2631
+ this.closed = true;
2632
+ this.abortController?.abort();
2633
+ this.eventStreamAbortController?.abort();
2634
+ this.eventStreamAbortController = null;
2635
+ this.eventStreamReady = null;
2636
+ this.subscribers.clear();
2637
+ await reconcileOpenCodeSessionClose({
2638
+ client: this.client,
2639
+ sessionId: this.sessionId,
2640
+ directory: this.config.cwd,
2641
+ logger: this.logger,
2642
+ });
2643
+ await this.deleteProviderSessionIfEphemeral();
2644
+ this.activeForegroundTurnId = null;
2645
+ }
2646
+ finally {
2647
+ this.releaseServer?.();
2648
+ this.releaseServer = null;
2649
+ }
2650
+ }
2651
+ async deleteProviderSessionIfEphemeral() {
2652
+ if (this.persistSession || this.deletedFromProvider) {
2653
+ return;
2654
+ }
2655
+ this.deletedFromProvider = true;
2656
+ try {
2657
+ const response = await this.client.session.delete({
2658
+ sessionID: this.sessionId,
2659
+ directory: this.config.cwd,
2660
+ });
2661
+ if (response.error) {
2662
+ throw new Error(`OpenCode session.delete failed: ${JSON.stringify(response.error)}`);
2663
+ }
2664
+ }
2665
+ catch (error) {
2666
+ this.logger.debug({ err: error, sessionId: this.sessionId }, "Failed to delete non-persistent OpenCode session");
2667
+ }
2668
+ }
2669
+ parseSlashCommandInput(text) {
2670
+ const trimmed = text.trim();
2671
+ if (!trimmed.startsWith("/") || trimmed.length <= 1) {
2672
+ return null;
2673
+ }
2674
+ const withoutPrefix = trimmed.slice(1);
2675
+ const firstWhitespaceIdx = withoutPrefix.search(/\s/);
2676
+ const commandName = firstWhitespaceIdx === -1 ? withoutPrefix : withoutPrefix.slice(0, firstWhitespaceIdx);
2677
+ if (!commandName || commandName.includes("/")) {
2678
+ return null;
2679
+ }
2680
+ const rawArgs = firstWhitespaceIdx === -1 ? "" : withoutPrefix.slice(firstWhitespaceIdx + 1).trim();
2681
+ return rawArgs.length > 0 ? { commandName, args: rawArgs } : { commandName };
2682
+ }
2683
+ async resolveSlashCommandInvocation(prompt) {
2684
+ if (typeof prompt !== "string") {
2685
+ return null;
2686
+ }
2687
+ const parsed = this.parseSlashCommandInput(prompt);
2688
+ if (!parsed) {
2689
+ return null;
2690
+ }
2691
+ try {
2692
+ const commands = await this.listCommands();
2693
+ return commands.some((command) => command.name === parsed.commandName) ? parsed : null;
2694
+ }
2695
+ catch (error) {
2696
+ this.logger.warn({ err: error, commandName: parsed.commandName }, "Failed to resolve slash command; falling back to plain prompt input");
2697
+ return null;
2698
+ }
2699
+ }
2700
+ parseModel(model) {
2701
+ if (!model) {
2702
+ return undefined;
2703
+ }
2704
+ const parts = model.split("/");
2705
+ if (parts.length >= 2) {
2706
+ return { providerID: parts[0], modelID: parts.slice(1).join("/") };
2707
+ }
2708
+ return { providerID: "opencode", modelID: model };
2709
+ }
2710
+ async ensureMcpServersConfigured() {
2711
+ if (this.mcpConfigured) {
2712
+ return;
2713
+ }
2714
+ const mcpServers = this.config.mcpServers;
2715
+ if (!mcpServers || Object.keys(mcpServers).length === 0) {
2716
+ this.mcpConfigured = true;
2717
+ return;
2718
+ }
2719
+ if (!this.mcpSetupPromise) {
2720
+ this.mcpSetupPromise = this.configureMcpServers(mcpServers);
2721
+ }
2722
+ try {
2723
+ await this.mcpSetupPromise;
2724
+ this.mcpConfigured = true;
2725
+ }
2726
+ catch (error) {
2727
+ this.mcpSetupPromise = null;
2728
+ throw error;
2729
+ }
2730
+ }
2731
+ async configureMcpServers(mcpServers) {
2732
+ await Promise.all(Object.entries(mcpServers).map(([name, serverConfig]) => this.registerMcpServer(name, toOpenCodeMcpConfig(serverConfig))));
2733
+ }
2734
+ async registerMcpServer(name, config) {
2735
+ await this.runMcpOperation("add", name, () => this.client.mcp.add({
2736
+ directory: this.config.cwd,
2737
+ name,
2738
+ config,
2739
+ }));
2740
+ }
2741
+ async runMcpOperation(operation, name, run) {
2742
+ const response = await run();
2743
+ const error = response.error ?? readOpenCodeMcpOperationError(response.data, name);
2744
+ if (!error) {
2745
+ return;
2746
+ }
2747
+ if (isAlreadyPresentMcpError(error)) {
2748
+ return;
2749
+ }
2750
+ throw new Error(`Failed to ${operation} OpenCode MCP server '${name}': ${toDiagnosticErrorMessage(error)}`);
2751
+ }
2752
+ async translateEvent(event) {
2753
+ const translated = translateOpenCodeEvent(event, {
2754
+ sessionId: this.sessionId,
2755
+ cwd: this.config.cwd,
2756
+ messageRoles: this.messageRoles,
2757
+ pendingUserMessageText: this.pendingUserMessageText,
2758
+ emittedUserMessageIds: this.emittedUserMessageIds,
2759
+ accumulatedUsage: this.accumulatedUsage,
2760
+ sessionTotalCostUsd: this.sessionTotalCostUsd,
2761
+ streamedPartKeys: this.streamedPartKeys,
2762
+ emittedStructuredMessageIds: this.emittedStructuredMessageIds,
2763
+ compactionSummaryMessageIds: this.compactionSummaryMessageIds,
2764
+ emittedCompactionPartIds: this.emittedCompactionPartIds,
2765
+ suppressAssistantMessagesUntilIdle: this.suppressAssistantMessagesUntilIdle,
2766
+ partTypes: this.partTypes,
2767
+ subAgentsByCallId: this.subAgentsByCallId,
2768
+ subAgentCallIdByChildSessionId: this.subAgentCallIdByChildSessionId,
2769
+ pendingChildToolPartsBySessionId: this.pendingChildToolPartsBySessionId,
2770
+ modelContextWindowsByModelKey: this.modelContextWindowsByModelKey,
2771
+ onAssistantModelContextWindowResolved: (contextWindowMaxTokens) => {
2772
+ this.accumulatedUsage.contextWindowMaxTokens = contextWindowMaxTokens;
2773
+ if (!this.config.model) {
2774
+ this.selectedModelContextWindowMaxTokens = contextWindowMaxTokens;
2775
+ }
2776
+ },
2777
+ });
2778
+ const events = [];
2779
+ if (typeof this.accumulatedUsage.totalCostUsd === "number") {
2780
+ this.sessionTotalCostUsd = maxFiniteNumber(this.sessionTotalCostUsd, this.accumulatedUsage.totalCostUsd);
2781
+ }
2782
+ for (const translatedEvent of translated) {
2783
+ if (translatedEvent.type === "permission_requested") {
2784
+ const autoApproved = await this.tryAutoApproveToolPermission(translatedEvent.request);
2785
+ if (autoApproved) {
2786
+ continue;
2787
+ }
2788
+ this.pendingPermissions.set(translatedEvent.request.id, translatedEvent.request);
2789
+ }
2790
+ if (translatedEvent.type === "turn_completed") {
2791
+ if (hasNormalizedOpenCodeUsage(this.accumulatedUsage)) {
2792
+ translatedEvent.usage = this.accumulatedUsage;
2793
+ }
2794
+ const contextWindowMaxTokens = this.resolveSelectedModelContextWindowMaxTokens();
2795
+ this.accumulatedUsage =
2796
+ contextWindowMaxTokens !== undefined ? { contextWindowMaxTokens } : {};
2797
+ }
2798
+ events.push(translatedEvent);
2799
+ }
2800
+ return events;
2801
+ }
2802
+ async tryAutoApproveToolPermission(request) {
2803
+ if (!this.autoAcceptEnabled || request.kind !== "tool") {
2804
+ return false;
2805
+ }
2806
+ try {
2807
+ await this.client.permission.reply({
2808
+ requestID: request.id,
2809
+ directory: this.config.cwd,
2810
+ reply: "once",
2811
+ });
2812
+ return true;
2813
+ }
2814
+ catch (error) {
2815
+ this.logger.warn({ err: error, requestId: request.id }, "Failed to auto-approve OpenCode tool permission");
2816
+ return false;
2817
+ }
2818
+ }
2819
+ resolveSelectedModelContextWindowMaxTokens() {
2820
+ return this.selectedModelContextWindowMaxTokens;
2821
+ }
2822
+ resolveConfiguredModelContextWindowMaxTokens(modelId) {
2823
+ const modelLookupKey = parseOpenCodeModelLookupKey(modelId);
2824
+ if (!modelLookupKey) {
2825
+ return undefined;
2826
+ }
2827
+ return this.modelContextWindowsByModelKey.get(modelLookupKey);
2828
+ }
2829
+ }
2830
+ //# sourceMappingURL=opencode-agent.js.map