@getpaseo/server 0.1.63 → 0.1.66

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 (265) hide show
  1. package/dist/server/client/daemon-client-transport-types.d.ts +2 -0
  2. package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client-websocket-transport.d.ts +2 -1
  4. package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -1
  5. package/dist/server/client/daemon-client-websocket-transport.js +4 -4
  6. package/dist/server/client/daemon-client-websocket-transport.js.map +1 -1
  7. package/dist/server/client/daemon-client.d.ts +30 -20
  8. package/dist/server/client/daemon-client.d.ts.map +1 -1
  9. package/dist/server/client/daemon-client.js +206 -98
  10. package/dist/server/client/daemon-client.js.map +1 -1
  11. package/dist/server/client/terminal-stream-router.d.ts +24 -0
  12. package/dist/server/client/terminal-stream-router.d.ts.map +1 -0
  13. package/dist/server/client/terminal-stream-router.js +100 -0
  14. package/dist/server/client/terminal-stream-router.js.map +1 -0
  15. package/dist/server/server/agent/activity-curator.d.ts +6 -3
  16. package/dist/server/server/agent/activity-curator.d.ts.map +1 -1
  17. package/dist/server/server/agent/activity-curator.js +45 -138
  18. package/dist/server/server/agent/activity-curator.js.map +1 -1
  19. package/dist/server/server/agent/agent-manager.d.ts +3 -14
  20. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  21. package/dist/server/server/agent/agent-manager.js +75 -140
  22. package/dist/server/server/agent/agent-manager.js.map +1 -1
  23. package/dist/server/server/agent/agent-metadata-generator.d.ts +0 -5
  24. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
  25. package/dist/server/server/agent/agent-metadata-generator.js +3 -93
  26. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  27. package/dist/server/server/agent/agent-sdk-types.d.ts +15 -1
  28. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  29. package/dist/server/server/agent/agent-stream-coalescer.d.ts +1 -1
  30. package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -1
  31. package/dist/server/server/agent/agent-stream-coalescer.js +1 -1
  32. package/dist/server/server/agent/agent-stream-coalescer.js.map +1 -1
  33. package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -1
  34. package/dist/server/server/agent/agent-timeline-store.js +29 -32
  35. package/dist/server/server/agent/agent-timeline-store.js.map +1 -1
  36. package/dist/server/server/agent/foreground-run-state.d.ts +50 -0
  37. package/dist/server/server/agent/foreground-run-state.d.ts.map +1 -0
  38. package/dist/server/server/agent/foreground-run-state.js +162 -0
  39. package/dist/server/server/agent/foreground-run-state.js.map +1 -0
  40. package/dist/server/server/agent/mcp-server.d.ts +5 -3
  41. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  42. package/dist/server/server/agent/mcp-server.js +110 -99
  43. package/dist/server/server/agent/mcp-server.js.map +1 -1
  44. package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
  45. package/dist/server/server/agent/mcp-shared.js +7 -1
  46. package/dist/server/server/agent/mcp-shared.js.map +1 -1
  47. package/dist/server/server/agent/prompt-attachments.d.ts +4 -3
  48. package/dist/server/server/agent/prompt-attachments.d.ts.map +1 -1
  49. package/dist/server/server/agent/prompt-attachments.js +43 -4
  50. package/dist/server/server/agent/prompt-attachments.js.map +1 -1
  51. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  52. package/dist/server/server/agent/provider-manifest.js +7 -0
  53. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  54. package/dist/server/server/agent/providers/acp-agent.d.ts +38 -1
  55. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  56. package/dist/server/server/agent/providers/acp-agent.js +257 -140
  57. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  58. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts.map +1 -1
  59. package/dist/server/server/agent/providers/claude/sidechain-tracker.js +1 -1
  60. package/dist/server/server/agent/providers/claude/sidechain-tracker.js.map +1 -1
  61. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
  62. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +68 -198
  63. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  64. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  65. package/dist/server/server/agent/providers/claude-agent.js +52 -102
  66. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  67. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  68. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +125 -141
  69. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  70. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +36 -6
  71. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  72. package/dist/server/server/agent/providers/codex-app-server-agent.js +374 -219
  73. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  74. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +6 -2
  75. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -1
  76. package/dist/server/server/agent/providers/mock-load-test-agent.js +294 -113
  77. package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -1
  78. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +1 -1
  79. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -1
  80. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +94 -2
  81. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  82. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -1
  83. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +24 -115
  84. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -1
  85. package/dist/server/server/agent/providers/opencode-agent.d.ts +102 -1
  86. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  87. package/dist/server/server/agent/providers/opencode-agent.js +416 -158
  88. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  89. package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
  90. package/dist/server/server/agent/providers/provider-runner.d.ts.map +1 -0
  91. package/dist/server/server/agent/providers/provider-runner.js +80 -0
  92. package/dist/server/server/agent/providers/provider-runner.js.map +1 -0
  93. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +6 -3
  94. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  95. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +2 -0
  96. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -1
  97. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +31 -0
  98. package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -1
  99. package/dist/server/server/agent/timeline-projection.d.ts +21 -5
  100. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
  101. package/dist/server/server/agent/timeline-projection.js +59 -9
  102. package/dist/server/server/agent/timeline-projection.js.map +1 -1
  103. package/dist/server/server/auth.d.ts +25 -0
  104. package/dist/server/server/auth.d.ts.map +1 -0
  105. package/dist/server/server/auth.js +93 -0
  106. package/dist/server/server/auth.js.map +1 -0
  107. package/dist/server/server/bootstrap.d.ts +3 -1
  108. package/dist/server/server/bootstrap.d.ts.map +1 -1
  109. package/dist/server/server/bootstrap.js +87 -30
  110. package/dist/server/server/bootstrap.js.map +1 -1
  111. package/dist/server/server/config.d.ts.map +1 -1
  112. package/dist/server/server/config.js +11 -0
  113. package/dist/server/server/config.js.map +1 -1
  114. package/dist/server/server/exports.d.ts +7 -1
  115. package/dist/server/server/exports.d.ts.map +1 -1
  116. package/dist/server/server/exports.js +6 -1
  117. package/dist/server/server/exports.js.map +1 -1
  118. package/dist/server/server/file-explorer/service.d.ts +10 -0
  119. package/dist/server/server/file-explorer/service.d.ts.map +1 -1
  120. package/dist/server/server/file-explorer/service.js +38 -4
  121. package/dist/server/server/file-explorer/service.js.map +1 -1
  122. package/dist/server/server/index.js +9 -6
  123. package/dist/server/server/index.js.map +1 -1
  124. package/dist/server/server/logger.d.ts.map +1 -1
  125. package/dist/server/server/logger.js +15 -1
  126. package/dist/server/server/logger.js.map +1 -1
  127. package/dist/server/server/pagination/cursor.d.ts +16 -0
  128. package/dist/server/server/pagination/cursor.d.ts.map +1 -0
  129. package/dist/server/server/pagination/cursor.js +62 -0
  130. package/dist/server/server/pagination/cursor.js.map +1 -0
  131. package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
  132. package/dist/server/server/pagination/sortable-pager.d.ts.map +1 -0
  133. package/dist/server/server/pagination/sortable-pager.js +68 -0
  134. package/dist/server/server/pagination/sortable-pager.js.map +1 -0
  135. package/dist/server/server/paseo-worktree-archive-service.d.ts +3 -1
  136. package/dist/server/server/paseo-worktree-archive-service.d.ts.map +1 -1
  137. package/dist/server/server/paseo-worktree-archive-service.js +61 -53
  138. package/dist/server/server/paseo-worktree-archive-service.js.map +1 -1
  139. package/dist/server/server/paseo-worktree-service.d.ts +13 -0
  140. package/dist/server/server/paseo-worktree-service.d.ts.map +1 -1
  141. package/dist/server/server/paseo-worktree-service.js +72 -3
  142. package/dist/server/server/paseo-worktree-service.js.map +1 -1
  143. package/dist/server/server/persisted-config.d.ts +25 -0
  144. package/dist/server/server/persisted-config.d.ts.map +1 -1
  145. package/dist/server/server/persisted-config.js +9 -0
  146. package/dist/server/server/persisted-config.js.map +1 -1
  147. package/dist/server/server/relay-transport.d.ts.map +1 -1
  148. package/dist/server/server/relay-transport.js +16 -4
  149. package/dist/server/server/relay-transport.js.map +1 -1
  150. package/dist/server/server/resolve-worktree-creation-intent.d.ts +0 -10
  151. package/dist/server/server/resolve-worktree-creation-intent.d.ts.map +1 -1
  152. package/dist/server/server/resolve-worktree-creation-intent.js +1 -45
  153. package/dist/server/server/resolve-worktree-creation-intent.js.map +1 -1
  154. package/dist/server/server/script-status-projection.d.ts +6 -1
  155. package/dist/server/server/script-status-projection.d.ts.map +1 -1
  156. package/dist/server/server/script-status-projection.js +12 -3
  157. package/dist/server/server/script-status-projection.js.map +1 -1
  158. package/dist/server/server/session.d.ts +19 -51
  159. package/dist/server/server/session.d.ts.map +1 -1
  160. package/dist/server/server/session.js +354 -1069
  161. package/dist/server/server/session.js.map +1 -1
  162. package/dist/server/server/websocket-server.d.ts +4 -2
  163. package/dist/server/server/websocket-server.d.ts.map +1 -1
  164. package/dist/server/server/websocket-server.js +65 -12
  165. package/dist/server/server/websocket-server.js.map +1 -1
  166. package/dist/server/server/workspace-directory.d.ts +69 -0
  167. package/dist/server/server/workspace-directory.d.ts.map +1 -0
  168. package/dist/server/server/workspace-directory.js +229 -0
  169. package/dist/server/server/workspace-directory.js.map +1 -0
  170. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
  171. package/dist/server/server/worktree-bootstrap.js +6 -2
  172. package/dist/server/server/worktree-bootstrap.js.map +1 -1
  173. package/dist/server/server/worktree-core.d.ts +2 -0
  174. package/dist/server/server/worktree-core.d.ts.map +1 -1
  175. package/dist/server/server/worktree-core.js.map +1 -1
  176. package/dist/server/server/worktree-errors.d.ts +1 -1
  177. package/dist/server/server/worktree-errors.d.ts.map +1 -1
  178. package/dist/server/server/worktree-errors.js +1 -4
  179. package/dist/server/server/worktree-errors.js.map +1 -1
  180. package/dist/server/server/worktree-session.d.ts +47 -20
  181. package/dist/server/server/worktree-session.d.ts.map +1 -1
  182. package/dist/server/server/worktree-session.js +68 -25
  183. package/dist/server/server/worktree-session.js.map +1 -1
  184. package/dist/server/shared/binary-frames/file-transfer.d.ts +56 -0
  185. package/dist/server/shared/binary-frames/file-transfer.d.ts.map +1 -0
  186. package/dist/server/shared/binary-frames/file-transfer.js +108 -0
  187. package/dist/server/shared/binary-frames/file-transfer.js.map +1 -0
  188. package/dist/server/shared/binary-frames/index.d.ts +3 -0
  189. package/dist/server/shared/binary-frames/index.d.ts.map +1 -0
  190. package/dist/server/shared/binary-frames/index.js +3 -0
  191. package/dist/server/shared/binary-frames/index.js.map +1 -0
  192. package/dist/server/shared/{terminal-stream-protocol.d.ts → binary-frames/terminal.d.ts} +2 -2
  193. package/dist/server/shared/binary-frames/terminal.d.ts.map +1 -0
  194. package/dist/server/shared/{terminal-stream-protocol.js → binary-frames/terminal.js} +2 -2
  195. package/dist/server/shared/binary-frames/terminal.js.map +1 -0
  196. package/dist/server/shared/client-capabilities.d.ts +5 -0
  197. package/dist/server/shared/client-capabilities.d.ts.map +1 -0
  198. package/dist/server/shared/client-capabilities.js +4 -0
  199. package/dist/server/shared/client-capabilities.js.map +1 -0
  200. package/dist/server/shared/connection-offer.d.ts +8 -0
  201. package/dist/server/shared/connection-offer.d.ts.map +1 -1
  202. package/dist/server/shared/connection-offer.js +35 -0
  203. package/dist/server/shared/connection-offer.js.map +1 -1
  204. package/dist/server/shared/daemon-endpoints.d.ts +16 -1
  205. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
  206. package/dist/server/shared/daemon-endpoints.js +65 -6
  207. package/dist/server/shared/daemon-endpoints.js.map +1 -1
  208. package/dist/server/shared/host-connection-schema.d.ts +23 -0
  209. package/dist/server/shared/host-connection-schema.d.ts.map +1 -0
  210. package/dist/server/shared/host-connection-schema.js +9 -0
  211. package/dist/server/shared/host-connection-schema.js.map +1 -0
  212. package/dist/server/shared/messages.d.ts +3242 -535
  213. package/dist/server/shared/messages.d.ts.map +1 -1
  214. package/dist/server/shared/messages.js +73 -34
  215. package/dist/server/shared/messages.js.map +1 -1
  216. package/dist/server/terminal/terminal-manager-factory.d.ts +7 -0
  217. package/dist/server/terminal/terminal-manager-factory.d.ts.map +1 -0
  218. package/dist/server/terminal/terminal-manager-factory.js +13 -0
  219. package/dist/server/terminal/terminal-manager-factory.js.map +1 -0
  220. package/dist/server/terminal/terminal-manager.d.ts +7 -1
  221. package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
  222. package/dist/server/terminal/terminal-manager.js +14 -1
  223. package/dist/server/terminal/terminal-manager.js.map +1 -1
  224. package/dist/server/terminal/terminal-session-controller.d.ts +63 -0
  225. package/dist/server/terminal/terminal-session-controller.d.ts.map +1 -0
  226. package/dist/server/terminal/terminal-session-controller.js +615 -0
  227. package/dist/server/terminal/terminal-session-controller.js.map +1 -0
  228. package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
  229. package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
  230. package/dist/server/terminal/terminal-worker-process.d.ts.map +1 -0
  231. package/dist/server/terminal/terminal-worker-process.js +221 -0
  232. package/dist/server/terminal/terminal-worker-process.js.map +1 -0
  233. package/dist/server/terminal/terminal-worker-protocol.d.ts +113 -0
  234. package/dist/server/terminal/terminal-worker-protocol.d.ts.map +1 -0
  235. package/dist/server/terminal/terminal-worker-protocol.js +2 -0
  236. package/dist/server/terminal/terminal-worker-protocol.js.map +1 -0
  237. package/dist/server/terminal/terminal.d.ts +7 -0
  238. package/dist/server/terminal/terminal.d.ts.map +1 -1
  239. package/dist/server/terminal/terminal.js +45 -9
  240. package/dist/server/terminal/terminal.js.map +1 -1
  241. package/dist/server/terminal/worker-terminal-manager.d.ts +19 -0
  242. package/dist/server/terminal/worker-terminal-manager.d.ts.map +1 -0
  243. package/dist/server/terminal/worker-terminal-manager.js +466 -0
  244. package/dist/server/terminal/worker-terminal-manager.js.map +1 -0
  245. package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
  246. package/dist/server/utils/directory-suggestions.js +10 -1
  247. package/dist/server/utils/directory-suggestions.js.map +1 -1
  248. package/dist/server/utils/process-tree.d.ts +25 -0
  249. package/dist/server/utils/process-tree.d.ts.map +1 -0
  250. package/dist/server/utils/process-tree.js +96 -0
  251. package/dist/server/utils/process-tree.js.map +1 -0
  252. package/dist/server/utils/windows-command.d.ts.map +1 -1
  253. package/dist/server/utils/windows-command.js +5 -1
  254. package/dist/server/utils/windows-command.js.map +1 -1
  255. package/dist/server/utils/worktree-metadata.d.ts +44 -0
  256. package/dist/server/utils/worktree-metadata.d.ts.map +1 -1
  257. package/dist/server/utils/worktree-metadata.js +58 -0
  258. package/dist/server/utils/worktree-metadata.js.map +1 -1
  259. package/dist/server/utils/worktree.d.ts +14 -2
  260. package/dist/server/utils/worktree.d.ts.map +1 -1
  261. package/dist/server/utils/worktree.js +22 -13
  262. package/dist/server/utils/worktree.js.map +1 -1
  263. package/package.json +5 -4
  264. package/dist/server/shared/terminal-stream-protocol.d.ts.map +0 -1
  265. package/dist/server/shared/terminal-stream-protocol.js.map +0 -1
@@ -4,10 +4,13 @@ import net from "node:net";
4
4
  import { z } from "zod";
5
5
  import { createProviderEnvSpec, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
6
6
  import { findExecutable, isCommandAvailable } from "../../../utils/executable.js";
7
+ import { terminateProcessTree } from "../../../utils/process-tree.js";
7
8
  import { withTimeout } from "../../../utils/promise-timeout.js";
8
9
  import { spawnProcess } from "../../../utils/spawn.js";
10
+ import { buildToolCallDisplayModel } from "../../../shared/tool-call-display.js";
9
11
  import { mapOpencodeToolCall } from "./opencode/tool-call-mapper.js";
10
12
  import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, resolveBinaryVersion, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
13
+ import { runProviderTurn } from "./provider-runner.js";
11
14
  import { renderPromptAttachmentAsText } from "../prompt-attachments.js";
12
15
  const OPENCODE_CAPABILITIES = {
13
16
  supportsStreaming: true,
@@ -17,9 +20,11 @@ const OPENCODE_CAPABILITIES = {
17
20
  supportsReasoningStream: true,
18
21
  supportsToolInvocations: true,
19
22
  };
23
+ const OPENCODE_BUILD_MODE_ID = "build";
24
+ const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
20
25
  const DEFAULT_MODES = [
21
26
  {
22
- id: "build",
27
+ id: OPENCODE_BUILD_MODE_ID,
23
28
  label: "Build",
24
29
  description: "Allows edits and tool execution for implementation work",
25
30
  },
@@ -28,9 +33,20 @@ const DEFAULT_MODES = [
28
33
  label: "Plan",
29
34
  description: "Read-only planning mode that avoids file edits",
30
35
  },
36
+ {
37
+ id: OPENCODE_FULL_ACCESS_MODE_ID,
38
+ label: "Full Access",
39
+ description: "Automatically approves all tool permission prompts for the session",
40
+ },
31
41
  ];
32
42
  const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
33
43
  const OPENCODE_PROVIDER_LIST_TIMEOUT_MS = 30000;
44
+ const OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5000;
45
+ const OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS = 1000;
46
+ const OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS = [
47
+ { name: "compact", description: "Compact the current session", argumentHint: "" },
48
+ { name: "summarize", description: "Compact the current session", argumentHint: "" },
49
+ ];
34
50
  const OPENCODE_FATAL_RETRY_MESSAGE_TOKENS = [
35
51
  "insufficient balance",
36
52
  "no resource package",
@@ -268,10 +284,26 @@ function resolvePartDedupeKey(part, partType) {
268
284
  function normalizeOpenCodeModeId(modeId) {
269
285
  const trimmed = typeof modeId === "string" ? modeId.trim() : "";
270
286
  if (!trimmed || trimmed === "default") {
271
- return "build";
287
+ return OPENCODE_BUILD_MODE_ID;
272
288
  }
273
289
  return trimmed;
274
290
  }
291
+ function resolveOpenCodeRuntimeAgentId(modeId) {
292
+ const normalizedModeId = normalizeOpenCodeModeId(modeId);
293
+ return normalizedModeId === OPENCODE_FULL_ACCESS_MODE_ID
294
+ ? OPENCODE_BUILD_MODE_ID
295
+ : normalizedModeId;
296
+ }
297
+ function isSelectableOpenCodeAgent(agent) {
298
+ return (agent.mode === "primary" || agent.mode === "all") && agent.hidden !== true;
299
+ }
300
+ function mergeOpenCodeModes(discoveredModes) {
301
+ const modesById = new Map(DEFAULT_MODES.map((mode) => [mode.id, mode]));
302
+ for (const mode of discoveredModes) {
303
+ modesById.set(mode.id, mode);
304
+ }
305
+ return sortOpenCodeModes(Array.from(modesById.values()));
306
+ }
275
307
  function sortOpenCodeModes(modes) {
276
308
  const order = new Map(DEFAULT_MODES.map((mode, index) => [mode.id, index]));
277
309
  return [...modes].sort((left, right) => {
@@ -457,18 +489,18 @@ function buildOpenCodePromptParts(prompt) {
457
489
  output.push({ type: "text", text: part.text });
458
490
  continue;
459
491
  }
460
- if (part.type === "github_pr" || part.type === "github_issue") {
461
- output.push({ type: "text", text: renderPromptAttachmentAsText(part) });
492
+ if (part.type === "image") {
493
+ attachmentOrdinal += 1;
494
+ const normalized = toOpenCodeDataUrl(part.mimeType, part.data);
495
+ output.push({
496
+ type: "file",
497
+ mime: normalized.mimeType,
498
+ filename: `attachment-${attachmentOrdinal}.${getOpenCodeAttachmentExtension(normalized.mimeType)}`,
499
+ url: normalized.url,
500
+ });
462
501
  continue;
463
502
  }
464
- attachmentOrdinal += 1;
465
- const normalized = toOpenCodeDataUrl(part.mimeType, part.data);
466
- output.push({
467
- type: "file",
468
- mime: normalized.mimeType,
469
- filename: `attachment-${attachmentOrdinal}.${getOpenCodeAttachmentExtension(normalized.mimeType)}`,
470
- url: normalized.url,
471
- });
503
+ output.push({ type: "text", text: renderPromptAttachmentAsText(part) });
472
504
  }
473
505
  return output;
474
506
  }
@@ -484,6 +516,10 @@ export const __openCodeInternals = {
484
516
  reconcileOpenCodeSessionClose,
485
517
  resolveOpenCodeModelLookupKeyFromAssistantMessage,
486
518
  resolveOpenCodeSelectedModelContextWindow,
519
+ isSelectableOpenCodeAgent,
520
+ get OpenCodeAgentSession() {
521
+ return OpenCodeAgentSession;
522
+ },
487
523
  };
488
524
  export class OpenCodeServerManager {
489
525
  constructor(logger, runtimeSettings) {
@@ -600,6 +636,7 @@ export class OpenCodeServerManager {
600
636
  const launchPrefix = await resolveProviderCommandPrefix(this.runtimeSettings?.command, resolveOpenCodeBinary);
601
637
  return new Promise((resolve, reject) => {
602
638
  const serverProcess = spawnProcess(launchPrefix.command, [...launchPrefix.args, "serve", "--port", String(port)], {
639
+ detached: process.platform !== "win32",
603
640
  stdio: ["ignore", "pipe", "pipe"],
604
641
  ...createProviderEnvSpec({ runtimeSettings: this.runtimeSettings }),
605
642
  });
@@ -693,25 +730,16 @@ export class OpenCodeServerManager {
693
730
  if (server.process.killed) {
694
731
  return;
695
732
  }
696
- await new Promise((resolve) => {
697
- let pendingResolve = resolve;
698
- const settle = () => {
699
- if (!pendingResolve)
700
- return;
701
- const fn = pendingResolve;
702
- pendingResolve = null;
703
- fn();
704
- };
705
- const timeout = setTimeout(() => {
706
- server.process.kill("SIGKILL");
707
- settle();
708
- }, 5000);
709
- server.process.on("exit", () => {
710
- clearTimeout(timeout);
711
- settle();
712
- });
713
- server.process.kill("SIGTERM");
733
+ const result = await terminateProcessTree(server.process, {
734
+ gracefulTimeoutMs: OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS,
735
+ forceTimeoutMs: OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS,
736
+ onForceSignal: () => {
737
+ this.logger.warn({ timeoutMs: OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS }, "OpenCode server did not exit after SIGTERM; sending SIGKILL");
738
+ },
714
739
  });
740
+ if (result === "kill-timeout") {
741
+ this.logger.warn({ timeoutMs: OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS }, "OpenCode server did not report exit after SIGKILL");
742
+ }
715
743
  }
716
744
  }
717
745
  OpenCodeServerManager.instance = null;
@@ -832,16 +860,14 @@ export class OpenCodeAgentClient {
832
860
  if (response.error || !response.data) {
833
861
  return DEFAULT_MODES;
834
862
  }
835
- const discovered = response.data
836
- .filter((agent) => agent.mode === "primary" && agent.hidden !== true)
837
- .map((agent) => ({
863
+ const discovered = response.data.filter(isSelectableOpenCodeAgent).map((agent) => ({
838
864
  id: agent.name,
839
865
  label: agent.name.charAt(0).toUpperCase() + agent.name.slice(1),
840
866
  description: typeof agent.description === "string" && agent.description.trim().length > 0
841
867
  ? agent.description.trim()
842
868
  : DEFAULT_MODES.find((mode) => mode.id === agent.name)?.description,
843
869
  }));
844
- return discovered.length > 0 ? sortOpenCodeModes(discovered) : DEFAULT_MODES;
870
+ return mergeOpenCodeModes(discovered);
845
871
  }
846
872
  finally {
847
873
  acquisition.release();
@@ -936,6 +962,8 @@ export class OpenCodeAgentClient {
936
962
  }
937
963
  }
938
964
  }
965
+ const MAX_OPENCODE_SUB_AGENT_ACTIONS = 200;
966
+ const MAX_OPENCODE_PENDING_CHILD_TOOL_PARTS = 200;
939
967
  function stringifyStructuredAssistantMessage(value) {
940
968
  if (value === undefined) {
941
969
  return null;
@@ -1132,6 +1160,191 @@ function resetOpenCodeTurnTrackingState(state) {
1132
1160
  state.streamedPartKeys.clear();
1133
1161
  state.partTypes.clear();
1134
1162
  }
1163
+ function getOpenCodeSubAgentMaps(state) {
1164
+ state.subAgentsByCallId ?? (state.subAgentsByCallId = new Map());
1165
+ state.subAgentCallIdByChildSessionId ?? (state.subAgentCallIdByChildSessionId = new Map());
1166
+ state.pendingChildToolPartsBySessionId ?? (state.pendingChildToolPartsBySessionId = new Map());
1167
+ return {
1168
+ byCallId: state.subAgentsByCallId,
1169
+ callIdByChildSessionId: state.subAgentCallIdByChildSessionId,
1170
+ pendingChildToolPartsBySessionId: state.pendingChildToolPartsBySessionId,
1171
+ };
1172
+ }
1173
+ function getOpenCodeSubAgentState(callId, state, toolCall) {
1174
+ const maps = getOpenCodeSubAgentMaps(state);
1175
+ const existing = maps.byCallId.get(callId);
1176
+ if (existing) {
1177
+ existing.toolCall = toolCall;
1178
+ return existing;
1179
+ }
1180
+ const created = {
1181
+ toolCall,
1182
+ actions: [],
1183
+ actionIndexByKey: new Map(),
1184
+ nextActionIndex: 1,
1185
+ };
1186
+ maps.byCallId.set(callId, created);
1187
+ return created;
1188
+ }
1189
+ function linkOpenCodeSubAgentChildSession(activity, childSessionId, state) {
1190
+ activity.childSessionId = childSessionId;
1191
+ const maps = getOpenCodeSubAgentMaps(state);
1192
+ maps.callIdByChildSessionId.set(childSessionId, activity.toolCall.callId);
1193
+ }
1194
+ function buildOpenCodeSubAgentLog(detail, activity) {
1195
+ const actionLog = activity.actions
1196
+ .map((action) => action.summary ? `[${action.toolName}] ${action.summary}` : `[${action.toolName}]`)
1197
+ .join("\n");
1198
+ const parts = [actionLog, detail.log].filter((part) => part.trim().length > 0);
1199
+ return parts.join("\n\n");
1200
+ }
1201
+ function buildOpenCodeSubAgentTimelineItem(activity) {
1202
+ const toolCall = activity.toolCall;
1203
+ if (toolCall.detail.type !== "sub_agent") {
1204
+ return toolCall;
1205
+ }
1206
+ const childSessionId = activity.childSessionId ?? toolCall.detail.childSessionId;
1207
+ return {
1208
+ ...toolCall,
1209
+ detail: {
1210
+ ...toolCall.detail,
1211
+ ...(childSessionId ? { childSessionId } : {}),
1212
+ log: buildOpenCodeSubAgentLog(toolCall.detail, activity),
1213
+ },
1214
+ };
1215
+ }
1216
+ function registerOpenCodeSubAgentToolCall(item, state) {
1217
+ if (item.detail.type !== "sub_agent") {
1218
+ return item;
1219
+ }
1220
+ const activity = getOpenCodeSubAgentState(item.callId, state, item);
1221
+ if (item.detail.childSessionId) {
1222
+ linkOpenCodeSubAgentChildSession(activity, item.detail.childSessionId, state);
1223
+ }
1224
+ return buildOpenCodeSubAgentTimelineItem(activity);
1225
+ }
1226
+ function bufferOpenCodeSubAgentChildToolPart(part, state) {
1227
+ const maps = getOpenCodeSubAgentMaps(state);
1228
+ if (maps.byCallId.size === 0) {
1229
+ return;
1230
+ }
1231
+ const totalPending = [...maps.pendingChildToolPartsBySessionId.values()].reduce((total, parts) => total + parts.length, 0);
1232
+ if (totalPending >= MAX_OPENCODE_PENDING_CHILD_TOOL_PARTS) {
1233
+ return;
1234
+ }
1235
+ const pending = maps.pendingChildToolPartsBySessionId.get(part.sessionID) ?? [];
1236
+ pending.push(part);
1237
+ maps.pendingChildToolPartsBySessionId.set(part.sessionID, pending);
1238
+ }
1239
+ function flushOpenCodeSubAgentChildToolParts(childSessionId, state, events) {
1240
+ const maps = getOpenCodeSubAgentMaps(state);
1241
+ const pending = maps.pendingChildToolPartsBySessionId.get(childSessionId);
1242
+ if (!pending || pending.length === 0) {
1243
+ return;
1244
+ }
1245
+ maps.pendingChildToolPartsBySessionId.delete(childSessionId);
1246
+ for (const part of pending) {
1247
+ appendOpenCodeSubAgentChildToolPart(part, state, events);
1248
+ }
1249
+ }
1250
+ function findOnlyOpenCodeSubAgentWaitingForChild(state) {
1251
+ const maps = getOpenCodeSubAgentMaps(state);
1252
+ const candidates = [...maps.byCallId.values()].filter((activity) => activity.toolCall.status === "running" &&
1253
+ activity.toolCall.detail.type === "sub_agent" &&
1254
+ !activity.childSessionId);
1255
+ return candidates.length === 1 ? (candidates[0] ?? null) : null;
1256
+ }
1257
+ function summarizeOpenCodeSubAgentAction(item, cwd) {
1258
+ const display = buildToolCallDisplayModel({
1259
+ name: item.name,
1260
+ status: item.status,
1261
+ error: item.error,
1262
+ metadata: item.metadata,
1263
+ detail: item.detail,
1264
+ cwd,
1265
+ });
1266
+ return display.summary ?? display.errorText;
1267
+ }
1268
+ function appendOpenCodeSubAgentAction(activity, item, cwd) {
1269
+ const key = item.callId || `${item.name}:${activity.actions.length}`;
1270
+ const existingIndex = activity.actionIndexByKey.get(key);
1271
+ const summary = summarizeOpenCodeSubAgentAction(item, cwd);
1272
+ if (existingIndex !== undefined) {
1273
+ const action = activity.actions[existingIndex];
1274
+ if (!action) {
1275
+ return false;
1276
+ }
1277
+ const changed = action.toolName !== item.name || action.summary !== summary;
1278
+ action.toolName = item.name;
1279
+ if (summary) {
1280
+ action.summary = summary;
1281
+ }
1282
+ else {
1283
+ delete action.summary;
1284
+ }
1285
+ return changed;
1286
+ }
1287
+ if (activity.actions.length >= MAX_OPENCODE_SUB_AGENT_ACTIONS) {
1288
+ return false;
1289
+ }
1290
+ activity.actionIndexByKey.set(key, activity.actions.length);
1291
+ activity.actions.push({
1292
+ index: activity.nextActionIndex,
1293
+ key,
1294
+ toolName: item.name,
1295
+ ...(summary ? { summary } : {}),
1296
+ });
1297
+ activity.nextActionIndex += 1;
1298
+ return true;
1299
+ }
1300
+ function appendOpenCodeToolCallTimelineItem(item, state, events) {
1301
+ const timelineItem = registerOpenCodeSubAgentToolCall(item, state);
1302
+ events.push({
1303
+ type: "timeline",
1304
+ provider: "opencode",
1305
+ item: timelineItem,
1306
+ });
1307
+ if (timelineItem.detail.type === "sub_agent" && timelineItem.detail.childSessionId) {
1308
+ flushOpenCodeSubAgentChildToolParts(timelineItem.detail.childSessionId, state, events);
1309
+ }
1310
+ }
1311
+ function appendOpenCodeSubAgentChildSessionLinked(childSessionId, state, events) {
1312
+ const activity = findOnlyOpenCodeSubAgentWaitingForChild(state);
1313
+ if (!activity) {
1314
+ return;
1315
+ }
1316
+ linkOpenCodeSubAgentChildSession(activity, childSessionId, state);
1317
+ events.push({
1318
+ type: "timeline",
1319
+ provider: "opencode",
1320
+ item: buildOpenCodeSubAgentTimelineItem(activity),
1321
+ });
1322
+ flushOpenCodeSubAgentChildToolParts(childSessionId, state, events);
1323
+ }
1324
+ function appendOpenCodeSubAgentChildToolPart(part, state, events) {
1325
+ const maps = getOpenCodeSubAgentMaps(state);
1326
+ const parentCallId = maps.callIdByChildSessionId.get(part.sessionID);
1327
+ if (!parentCallId) {
1328
+ bufferOpenCodeSubAgentChildToolPart(part, state);
1329
+ return;
1330
+ }
1331
+ const activity = maps.byCallId.get(parentCallId);
1332
+ if (!activity) {
1333
+ return;
1334
+ }
1335
+ const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
1336
+ if (!parsedToolPart.success || !parsedToolPart.data) {
1337
+ return;
1338
+ }
1339
+ if (!appendOpenCodeSubAgentAction(activity, parsedToolPart.data, state.cwd)) {
1340
+ return;
1341
+ }
1342
+ events.push({
1343
+ type: "timeline",
1344
+ provider: "opencode",
1345
+ item: buildOpenCodeSubAgentTimelineItem(activity),
1346
+ });
1347
+ }
1135
1348
  function appendOpenCodeSessionCreatedOrUpdated(event, state, events) {
1136
1349
  if (event.properties.info.id === state.sessionId) {
1137
1350
  events.push({
@@ -1139,6 +1352,12 @@ function appendOpenCodeSessionCreatedOrUpdated(event, state, events) {
1139
1352
  sessionId: state.sessionId,
1140
1353
  provider: "opencode",
1141
1354
  });
1355
+ return;
1356
+ }
1357
+ const info = readOpenCodeRecord(event.properties.info);
1358
+ const parentSessionId = readNonEmptyString(info?.parentID) ?? readNonEmptyString(info?.parentId);
1359
+ if (parentSessionId === state.sessionId) {
1360
+ appendOpenCodeSubAgentChildSessionLinked(event.properties.info.id, state, events);
1142
1361
  }
1143
1362
  }
1144
1363
  function appendOpenCodeMessageUpdated(event, state, events) {
@@ -1174,6 +1393,9 @@ function appendOpenCodeMessageUpdated(event, state, events) {
1174
1393
  function appendOpenCodeMessagePartUpdated(event, state, events) {
1175
1394
  const part = event.properties.part;
1176
1395
  if (part.sessionID !== state.sessionId) {
1396
+ if (part.type === "tool") {
1397
+ appendOpenCodeSubAgentChildToolPart(part, state, events);
1398
+ }
1177
1399
  return;
1178
1400
  }
1179
1401
  const messageRole = state.messageRoles.get(part.messageID);
@@ -1189,7 +1411,7 @@ function appendOpenCodeMessagePartUpdated(event, state, events) {
1189
1411
  if (part.type === "tool") {
1190
1412
  const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
1191
1413
  if (parsedToolPart.success && parsedToolPart.data) {
1192
- events.push({ type: "timeline", provider: "opencode", item: parsedToolPart.data });
1414
+ appendOpenCodeToolCallTimelineItem(parsedToolPart.data, state, events);
1193
1415
  }
1194
1416
  return;
1195
1417
  }
@@ -1380,6 +1602,15 @@ function appendOpenCodeSessionStatus(event, state, events) {
1380
1602
  }
1381
1603
  // "retry" and "busy" are transient — no terminal event.
1382
1604
  }
1605
+ function createDeferred() {
1606
+ let resolve;
1607
+ let reject;
1608
+ const promise = new Promise((res, rej) => {
1609
+ resolve = res;
1610
+ reject = rej;
1611
+ });
1612
+ return { promise, resolve, reject };
1613
+ }
1383
1614
  class OpenCodeAgentSession {
1384
1615
  constructor(config, client, sessionId, logger, modelContextWindowsByModelKey = new Map(), releaseServer) {
1385
1616
  this.provider = "opencode";
@@ -1403,6 +1634,9 @@ class OpenCodeAgentSession {
1403
1634
  this.nextTurnOrdinal = 0;
1404
1635
  this.activeForegroundTurnId = null;
1405
1636
  this.runningToolCalls = new Map();
1637
+ this.subAgentsByCallId = new Map();
1638
+ this.subAgentCallIdByChildSessionId = new Map();
1639
+ this.pendingChildToolPartsBySessionId = new Map();
1406
1640
  this.config = config;
1407
1641
  this.client = client;
1408
1642
  this.sessionId = sessionId;
@@ -1435,75 +1669,13 @@ class OpenCodeAgentSession {
1435
1669
  this.config.thinkingOptionId = normalizedThinkingOptionId ?? undefined;
1436
1670
  }
1437
1671
  async run(prompt, options) {
1438
- const timeline = [];
1439
- let finalText = "";
1440
- let usage;
1441
- let turnId = null;
1442
- const bufferedEvents = [];
1443
- let settled = false;
1444
- let resolveCompletion;
1445
- let rejectCompletion;
1446
- const processEvent = (event) => {
1447
- if (settled) {
1448
- return;
1449
- }
1450
- const eventTurnId = event.turnId;
1451
- if (turnId && eventTurnId && eventTurnId !== turnId) {
1452
- return;
1453
- }
1454
- if (event.type === "timeline") {
1455
- timeline.push(event.item);
1456
- if (event.item.type === "assistant_message") {
1457
- finalText = event.item.text;
1458
- }
1459
- return;
1460
- }
1461
- if (event.type === "turn_completed") {
1462
- usage = event.usage;
1463
- settled = true;
1464
- resolveCompletion();
1465
- return;
1466
- }
1467
- if (event.type === "turn_failed") {
1468
- settled = true;
1469
- rejectCompletion(new Error(event.error));
1470
- return;
1471
- }
1472
- if (event.type === "turn_canceled") {
1473
- settled = true;
1474
- resolveCompletion();
1475
- }
1476
- };
1477
- const completion = new Promise((resolve, reject) => {
1478
- resolveCompletion = resolve;
1479
- rejectCompletion = reject;
1480
- });
1481
- const unsubscribe = this.subscribe((event) => {
1482
- if (!turnId) {
1483
- bufferedEvents.push(event);
1484
- return;
1485
- }
1486
- processEvent(event);
1672
+ return runProviderTurn({
1673
+ prompt,
1674
+ runOptions: options,
1675
+ startTurn: (p, o) => this.startTurn(p, o),
1676
+ subscribe: (callback) => this.subscribe(callback),
1677
+ getSessionId: () => this.sessionId,
1487
1678
  });
1488
- try {
1489
- const result = await this.startTurn(prompt, options);
1490
- turnId = result.turnId;
1491
- for (const event of bufferedEvents) {
1492
- processEvent(event);
1493
- }
1494
- if (!settled) {
1495
- await completion;
1496
- }
1497
- }
1498
- finally {
1499
- unsubscribe();
1500
- }
1501
- return {
1502
- sessionId: this.sessionId,
1503
- finalText,
1504
- usage,
1505
- timeline,
1506
- };
1507
1679
  }
1508
1680
  async interrupt() {
1509
1681
  const turnId = this.activeForegroundTurnId;
@@ -1522,6 +1694,9 @@ class OpenCodeAgentSession {
1522
1694
  throw new Error("A foreground turn is already active");
1523
1695
  }
1524
1696
  this.runningToolCalls.clear();
1697
+ this.subAgentsByCallId.clear();
1698
+ this.subAgentCallIdByChildSessionId.clear();
1699
+ this.pendingChildToolPartsBySessionId.clear();
1525
1700
  const turnAbortController = new AbortController();
1526
1701
  this.abortController = turnAbortController;
1527
1702
  await this.ensureMcpServersConfigured();
@@ -1531,12 +1706,54 @@ class OpenCodeAgentSession {
1531
1706
  const model = this.parseModel(this.config.model);
1532
1707
  const thinkingOptionId = this.config.thinkingOptionId;
1533
1708
  const effectiveVariant = thinkingOptionId ?? undefined;
1534
- const effectiveMode = normalizeOpenCodeModeId(this.currentMode);
1709
+ const effectiveMode = resolveOpenCodeRuntimeAgentId(this.currentMode);
1535
1710
  const turnId = this.createTurnId();
1536
1711
  this.activeForegroundTurnId = turnId;
1537
- void this.consumeEventStream(turnId, turnAbortController);
1712
+ // OpenCode's /event SSE endpoint does NOT replay past events. If we send
1713
+ // the prompt before our reader is connected, terminal events fired early
1714
+ // by the server (e.g. session.error / session.idle for invalid model or
1715
+ // mode) are missed and the turn hangs forever. Wait for the subscription
1716
+ // to be established before sending anything.
1717
+ const subscriptionReady = createDeferred();
1718
+ void this.consumeEventStream(turnId, turnAbortController, subscriptionReady);
1719
+ try {
1720
+ await subscriptionReady.promise;
1721
+ }
1722
+ catch {
1723
+ // consumeEventStream already finished the turn with the subscription error.
1724
+ return { turnId };
1725
+ }
1538
1726
  const slashCommand = await this.resolveSlashCommandInvocation(prompt);
1539
1727
  if (slashCommand) {
1728
+ if (slashCommand.commandName === "compact" || slashCommand.commandName === "summarize") {
1729
+ void this.client.session
1730
+ .summarize({
1731
+ sessionID: this.sessionId,
1732
+ directory: this.config.cwd,
1733
+ ...(model ? { providerID: model.providerID, modelID: model.modelID } : {}),
1734
+ })
1735
+ .then((response) => {
1736
+ if (response.error) {
1737
+ this.finishForegroundTurn({
1738
+ type: "turn_failed",
1739
+ provider: "opencode",
1740
+ error: toDiagnosticErrorMessage(response.error),
1741
+ }, turnId);
1742
+ }
1743
+ else {
1744
+ this.finishForegroundTurn({ type: "turn_completed", provider: "opencode", usage: undefined }, turnId);
1745
+ }
1746
+ return;
1747
+ })
1748
+ .catch((error) => {
1749
+ this.finishForegroundTurn({
1750
+ type: "turn_failed",
1751
+ provider: "opencode",
1752
+ error: toDiagnosticErrorMessage(error),
1753
+ }, turnId);
1754
+ });
1755
+ return { turnId };
1756
+ }
1540
1757
  // command() blocks until the server finishes processing. OpenCode's SSE
1541
1758
  // endpoint does NOT replay past events, so if the command completes before
1542
1759
  // our SSE reader connects, we miss `session.idle` and the turn hangs.
@@ -1584,41 +1801,44 @@ class OpenCodeAgentSession {
1584
1801
  });
1585
1802
  }
1586
1803
  else {
1587
- void this.client.session
1588
- .promptAsync({
1589
- sessionID: this.sessionId,
1590
- directory: this.config.cwd,
1591
- parts,
1592
- ...(options?.outputSchema
1593
- ? {
1594
- format: {
1595
- type: "json_schema",
1596
- schema: options.outputSchema,
1597
- },
1804
+ // Wrap in an async IIFE so a synchronous throw from promptAsync (e.g.
1805
+ // SDK input validation) is caught alongside async rejections. A plain
1806
+ // `.then().catch()` chain would let a sync throw escape unhandled.
1807
+ void (async () => {
1808
+ try {
1809
+ const promptResponse = await this.client.session.promptAsync({
1810
+ sessionID: this.sessionId,
1811
+ directory: this.config.cwd,
1812
+ parts,
1813
+ ...(options?.outputSchema
1814
+ ? {
1815
+ format: {
1816
+ type: "json_schema",
1817
+ schema: options.outputSchema,
1818
+ },
1819
+ }
1820
+ : {}),
1821
+ ...(this.config.systemPrompt ? { system: this.config.systemPrompt } : {}),
1822
+ ...(model ? { model } : {}),
1823
+ ...(effectiveMode ? { agent: effectiveMode } : {}),
1824
+ ...(effectiveVariant ? { variant: effectiveVariant } : {}),
1825
+ });
1826
+ if (promptResponse.error) {
1827
+ this.finishForegroundTurn({
1828
+ type: "turn_failed",
1829
+ provider: "opencode",
1830
+ error: toDiagnosticErrorMessage(promptResponse.error),
1831
+ }, turnId);
1598
1832
  }
1599
- : {}),
1600
- ...(this.config.systemPrompt ? { system: this.config.systemPrompt } : {}),
1601
- ...(model ? { model } : {}),
1602
- ...(effectiveMode ? { agent: effectiveMode } : {}),
1603
- ...(effectiveVariant ? { variant: effectiveVariant } : {}),
1604
- })
1605
- .then((promptResponse) => {
1606
- if (promptResponse.error) {
1833
+ }
1834
+ catch (error) {
1607
1835
  this.finishForegroundTurn({
1608
1836
  type: "turn_failed",
1609
1837
  provider: "opencode",
1610
- error: toDiagnosticErrorMessage(promptResponse.error),
1838
+ error: toDiagnosticErrorMessage(error),
1611
1839
  }, turnId);
1612
1840
  }
1613
- return;
1614
- })
1615
- .catch((error) => {
1616
- this.finishForegroundTurn({
1617
- type: "turn_failed",
1618
- provider: "opencode",
1619
- error: toDiagnosticErrorMessage(error),
1620
- }, turnId);
1621
- });
1841
+ })();
1622
1842
  }
1623
1843
  return { turnId };
1624
1844
  }
@@ -1628,14 +1848,15 @@ class OpenCodeAgentSession {
1628
1848
  this.subscribers.delete(callback);
1629
1849
  };
1630
1850
  }
1631
- async consumeEventStream(turnId, turnAbortController) {
1851
+ async consumeEventStream(turnId, turnAbortController, subscriptionReady) {
1632
1852
  try {
1633
1853
  const result = await this.client.event.subscribe({ directory: this.config.cwd }, { signal: turnAbortController.signal, sseMaxRetryAttempts: 0 });
1854
+ subscriptionReady.resolve();
1634
1855
  for await (const event of result.stream) {
1635
1856
  if (turnAbortController.signal.aborted || this.activeForegroundTurnId !== turnId) {
1636
1857
  break;
1637
1858
  }
1638
- const translated = this.translateEvent(event);
1859
+ const translated = await this.translateEvent(event);
1639
1860
  for (const e of translated) {
1640
1861
  if (this.activeForegroundTurnId !== turnId) {
1641
1862
  return;
@@ -1660,6 +1881,7 @@ class OpenCodeAgentSession {
1660
1881
  }
1661
1882
  }
1662
1883
  catch (error) {
1884
+ subscriptionReady.reject(error);
1663
1885
  if (!turnAbortController.signal.aborted && this.activeForegroundTurnId === turnId) {
1664
1886
  this.finishForegroundTurn({
1665
1887
  type: "turn_failed",
@@ -1706,13 +1928,22 @@ class OpenCodeAgentSession {
1706
1928
  }
1707
1929
  synthesizeInterruptedToolCalls(turnId) {
1708
1930
  for (const item of this.runningToolCalls.values()) {
1931
+ const error = { message: "Tool execution aborted" };
1709
1932
  this.notifySubscribers({
1710
1933
  type: "timeline",
1711
1934
  provider: "opencode",
1712
1935
  item: {
1713
1936
  ...item,
1714
1937
  status: "failed",
1715
- error: { message: "Tool execution aborted" },
1938
+ error,
1939
+ detail: item.detail.type === "sub_agent"
1940
+ ? {
1941
+ ...item.detail,
1942
+ log: [item.detail.log, error.message]
1943
+ .filter((entry) => entry.trim().length > 0)
1944
+ .join("\n"),
1945
+ }
1946
+ : item.detail,
1716
1947
  },
1717
1948
  }, turnId);
1718
1949
  }
@@ -1807,19 +2038,15 @@ class OpenCodeAgentSession {
1807
2038
  const response = await this.client.app.agents({
1808
2039
  directory: this.config.cwd,
1809
2040
  });
1810
- const discoveredModes = response.error || !response.data
1811
- ? []
1812
- : response.data
1813
- .filter((agent) => agent.mode === "primary" && agent.hidden !== true)
1814
- .map((agent) => ({
1815
- id: agent.name,
1816
- label: agent.name.charAt(0).toUpperCase() + agent.name.slice(1),
1817
- description: typeof agent.description === "string" && agent.description.trim().length > 0
1818
- ? agent.description.trim()
1819
- : DEFAULT_MODES.find((mode) => mode.id === agent.name)?.description,
1820
- }));
1821
- this.availableModesCache =
1822
- discoveredModes.length > 0 ? sortOpenCodeModes(discoveredModes) : DEFAULT_MODES;
2041
+ const agents = response.error || !response.data ? [] : response.data;
2042
+ const discoveredModes = agents.filter(isSelectableOpenCodeAgent).map((agent) => ({
2043
+ id: agent.name,
2044
+ label: agent.name.charAt(0).toUpperCase() + agent.name.slice(1),
2045
+ description: typeof agent.description === "string" && agent.description.trim().length > 0
2046
+ ? agent.description.trim()
2047
+ : DEFAULT_MODES.find((mode) => mode.id === agent.name)?.description,
2048
+ }));
2049
+ this.availableModesCache = mergeOpenCodeModes(discoveredModes);
1823
2050
  return this.availableModesCache;
1824
2051
  }
1825
2052
  async getCurrentMode() {
@@ -1829,14 +2056,18 @@ class OpenCodeAgentSession {
1829
2056
  const result = await this.client.command.list({
1830
2057
  directory: this.config.cwd,
1831
2058
  });
2059
+ const commandsByName = new Map(OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS.map((command) => [command.name, command]));
1832
2060
  if (result.error || !result.data) {
1833
- return [];
2061
+ return Array.from(commandsByName.values());
1834
2062
  }
1835
- return result.data.map((cmd) => ({
1836
- name: cmd.name,
1837
- description: cmd.description ?? "",
1838
- argumentHint: cmd.hints?.length ? cmd.hints.join(" ") : "",
1839
- }));
2063
+ for (const cmd of result.data) {
2064
+ commandsByName.set(cmd.name, {
2065
+ name: cmd.name,
2066
+ description: cmd.description ?? "",
2067
+ argumentHint: cmd.hints?.length ? cmd.hints.join(" ") : "",
2068
+ });
2069
+ }
2070
+ return Array.from(commandsByName.values());
1840
2071
  }
1841
2072
  async setMode(modeId) {
1842
2073
  this.currentMode = normalizeOpenCodeModeId(modeId);
@@ -2002,14 +2233,18 @@ class OpenCodeAgentSession {
2002
2233
  }
2003
2234
  throw new Error(`Failed to ${operation} OpenCode MCP server '${name}': ${toDiagnosticErrorMessage(error)}`);
2004
2235
  }
2005
- translateEvent(event) {
2236
+ async translateEvent(event) {
2006
2237
  const translated = translateOpenCodeEvent(event, {
2007
2238
  sessionId: this.sessionId,
2239
+ cwd: this.config.cwd,
2008
2240
  messageRoles: this.messageRoles,
2009
2241
  accumulatedUsage: this.accumulatedUsage,
2010
2242
  streamedPartKeys: this.streamedPartKeys,
2011
2243
  emittedStructuredMessageIds: this.emittedStructuredMessageIds,
2012
2244
  partTypes: this.partTypes,
2245
+ subAgentsByCallId: this.subAgentsByCallId,
2246
+ subAgentCallIdByChildSessionId: this.subAgentCallIdByChildSessionId,
2247
+ pendingChildToolPartsBySessionId: this.pendingChildToolPartsBySessionId,
2013
2248
  modelContextWindowsByModelKey: this.modelContextWindowsByModelKey,
2014
2249
  onAssistantModelContextWindowResolved: (contextWindowMaxTokens) => {
2015
2250
  this.accumulatedUsage.contextWindowMaxTokens = contextWindowMaxTokens;
@@ -2018,8 +2253,13 @@ class OpenCodeAgentSession {
2018
2253
  }
2019
2254
  },
2020
2255
  });
2256
+ const events = [];
2021
2257
  for (const translatedEvent of translated) {
2022
2258
  if (translatedEvent.type === "permission_requested") {
2259
+ const autoApproved = await this.tryAutoApproveToolPermission(translatedEvent.request);
2260
+ if (autoApproved) {
2261
+ continue;
2262
+ }
2023
2263
  this.pendingPermissions.set(translatedEvent.request.id, translatedEvent.request);
2024
2264
  }
2025
2265
  if (translatedEvent.type === "turn_completed") {
@@ -2030,8 +2270,26 @@ class OpenCodeAgentSession {
2030
2270
  this.accumulatedUsage =
2031
2271
  contextWindowMaxTokens !== undefined ? { contextWindowMaxTokens } : {};
2032
2272
  }
2273
+ events.push(translatedEvent);
2274
+ }
2275
+ return events;
2276
+ }
2277
+ async tryAutoApproveToolPermission(request) {
2278
+ if (this.currentMode !== OPENCODE_FULL_ACCESS_MODE_ID || request.kind !== "tool") {
2279
+ return false;
2280
+ }
2281
+ try {
2282
+ await this.client.permission.reply({
2283
+ requestID: request.id,
2284
+ directory: this.config.cwd,
2285
+ reply: "once",
2286
+ });
2287
+ return true;
2288
+ }
2289
+ catch (error) {
2290
+ this.logger.warn({ err: error, requestId: request.id }, "Failed to auto-approve OpenCode tool permission");
2291
+ return false;
2033
2292
  }
2034
- return translated;
2035
2293
  }
2036
2294
  resolveSelectedModelContextWindowMaxTokens() {
2037
2295
  return this.selectedModelContextWindowMaxTokens;