subharness 0.0.4 → 0.0.7

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 (303) hide show
  1. package/README.md +95 -9
  2. package/dist/adapters/claude-process.d.ts +3 -0
  3. package/dist/adapters/claude-process.js +19 -2
  4. package/dist/adapters/claude-process.js.map +1 -1
  5. package/dist/adapters/claude-result.d.ts +2 -0
  6. package/dist/adapters/claude-result.js +22 -0
  7. package/dist/adapters/claude-result.js.map +1 -0
  8. package/dist/adapters/claude-tools.d.ts +6 -2
  9. package/dist/adapters/claude-tools.js +14 -12
  10. package/dist/adapters/claude-tools.js.map +1 -1
  11. package/dist/adapters/claude-worker-client.d.ts +49 -0
  12. package/dist/adapters/claude-worker-client.js +359 -0
  13. package/dist/adapters/claude-worker-client.js.map +1 -0
  14. package/dist/adapters/claude-worker-process.d.ts +20 -0
  15. package/dist/adapters/claude-worker-process.js +76 -0
  16. package/dist/adapters/claude-worker-process.js.map +1 -0
  17. package/dist/adapters/claude-worker-protocol.d.ts +38 -0
  18. package/dist/adapters/claude-worker-protocol.js +2 -0
  19. package/dist/adapters/claude-worker-protocol.js.map +1 -0
  20. package/dist/adapters/claude-worker.d.ts +1 -0
  21. package/dist/adapters/claude-worker.js +126 -0
  22. package/dist/adapters/claude-worker.js.map +1 -0
  23. package/dist/adapters/claude.d.ts +16 -6
  24. package/dist/adapters/claude.js +164 -75
  25. package/dist/adapters/claude.js.map +1 -1
  26. package/dist/adapters/codex.js +4 -1
  27. package/dist/adapters/codex.js.map +1 -1
  28. package/dist/adapters/copilot-permissions.d.ts +26 -0
  29. package/dist/adapters/copilot-permissions.js +121 -0
  30. package/dist/adapters/copilot-permissions.js.map +1 -0
  31. package/dist/adapters/copilot-tools.d.ts +12 -0
  32. package/dist/adapters/copilot-tools.js +61 -0
  33. package/dist/adapters/copilot-tools.js.map +1 -0
  34. package/dist/adapters/copilot.d.ts +108 -0
  35. package/dist/adapters/copilot.js +819 -0
  36. package/dist/adapters/copilot.js.map +1 -0
  37. package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
  38. package/dist/adapters/cursor-cli-approvals.js +83 -0
  39. package/dist/adapters/cursor-cli-approvals.js.map +1 -0
  40. package/dist/adapters/cursor-cli-model.d.ts +4 -0
  41. package/dist/adapters/cursor-cli-model.js +64 -0
  42. package/dist/adapters/cursor-cli-model.js.map +1 -0
  43. package/dist/adapters/cursor-cli-session.d.ts +32 -0
  44. package/dist/adapters/cursor-cli-session.js +316 -0
  45. package/dist/adapters/cursor-cli-session.js.map +1 -0
  46. package/dist/adapters/cursor-cli-tools.d.ts +20 -0
  47. package/dist/adapters/cursor-cli-tools.js +181 -0
  48. package/dist/adapters/cursor-cli-tools.js.map +1 -0
  49. package/dist/adapters/cursor-cli.d.ts +2 -0
  50. package/dist/adapters/cursor-cli.js +306 -0
  51. package/dist/adapters/cursor-cli.js.map +1 -0
  52. package/dist/adapters/cursor-model.d.ts +7 -0
  53. package/dist/adapters/cursor-model.js +67 -0
  54. package/dist/adapters/cursor-model.js.map +1 -0
  55. package/dist/adapters/cursor-rpc.d.ts +42 -0
  56. package/dist/adapters/cursor-rpc.js +271 -0
  57. package/dist/adapters/cursor-rpc.js.map +1 -0
  58. package/dist/adapters/cursor-session.d.ts +4 -0
  59. package/dist/adapters/cursor-session.js +327 -0
  60. package/dist/adapters/cursor-session.js.map +1 -0
  61. package/dist/adapters/cursor-startup.d.ts +16 -0
  62. package/dist/adapters/cursor-startup.js +74 -0
  63. package/dist/adapters/cursor-startup.js.map +1 -0
  64. package/dist/adapters/cursor-tools.d.ts +11 -0
  65. package/dist/adapters/cursor-tools.js +49 -0
  66. package/dist/adapters/cursor-tools.js.map +1 -0
  67. package/dist/adapters/cursor.d.ts +6 -0
  68. package/dist/adapters/cursor.js +17 -0
  69. package/dist/adapters/cursor.js.map +1 -0
  70. package/dist/adapters/fx-auth.d.ts +12 -2
  71. package/dist/adapters/fx-auth.js +51 -61
  72. package/dist/adapters/fx-auth.js.map +1 -1
  73. package/dist/adapters/fx-profile.d.ts +8 -0
  74. package/dist/adapters/fx-profile.js +101 -0
  75. package/dist/adapters/fx-profile.js.map +1 -0
  76. package/dist/adapters/fx-rpc.d.ts +2 -0
  77. package/dist/adapters/fx-rpc.js +30 -4
  78. package/dist/adapters/fx-rpc.js.map +1 -1
  79. package/dist/adapters/fx-status.d.ts +2 -0
  80. package/dist/adapters/fx-status.js +96 -0
  81. package/dist/adapters/fx-status.js.map +1 -0
  82. package/dist/adapters/fx.js +28 -12
  83. package/dist/adapters/fx.js.map +1 -1
  84. package/dist/adapters/opencode-access.d.ts +13 -0
  85. package/dist/adapters/opencode-access.js +76 -0
  86. package/dist/adapters/opencode-access.js.map +1 -0
  87. package/dist/adapters/opencode-config.d.ts +11 -0
  88. package/dist/adapters/opencode-config.js +238 -0
  89. package/dist/adapters/opencode-config.js.map +1 -0
  90. package/dist/adapters/opencode-http.d.ts +28 -0
  91. package/dist/adapters/opencode-http.js +297 -0
  92. package/dist/adapters/opencode-http.js.map +1 -0
  93. package/dist/adapters/opencode-tools.d.ts +19 -0
  94. package/dist/adapters/opencode-tools.js +127 -0
  95. package/dist/adapters/opencode-tools.js.map +1 -0
  96. package/dist/adapters/opencode.d.ts +2 -0
  97. package/dist/adapters/opencode.js +569 -0
  98. package/dist/adapters/opencode.js.map +1 -0
  99. package/dist/adapters/rpc.d.ts +3 -0
  100. package/dist/adapters/rpc.js +45 -4
  101. package/dist/adapters/rpc.js.map +1 -1
  102. package/dist/adapters/types.d.ts +5 -0
  103. package/dist/adapters/types.js.map +1 -1
  104. package/dist/approvals/types.d.ts +1 -1
  105. package/dist/approvals/types.js.map +1 -1
  106. package/dist/cli/args.d.ts +1 -1
  107. package/dist/cli/args.js +4 -1
  108. package/dist/cli/args.js.map +1 -1
  109. package/dist/cli/catalog-worker.js.map +1 -1
  110. package/dist/cli/dashboard-client.js +20 -31
  111. package/dist/cli/dashboard-client.js.map +1 -1
  112. package/dist/cli/dashboard-controller.d.ts +7 -0
  113. package/dist/cli/dashboard-controller.js +27 -0
  114. package/dist/cli/dashboard-controller.js.map +1 -0
  115. package/dist/cli/dashboard-detail-view.d.ts +1 -1
  116. package/dist/cli/dashboard-detail-view.js +3 -4
  117. package/dist/cli/dashboard-detail-view.js.map +1 -1
  118. package/dist/cli/dashboard-history-view.js.map +1 -1
  119. package/dist/cli/dashboard-input.d.ts +1 -1
  120. package/dist/cli/dashboard-input.js +11 -1
  121. package/dist/cli/dashboard-input.js.map +1 -1
  122. package/dist/cli/dashboard-layout.js +3 -3
  123. package/dist/cli/dashboard-layout.js.map +1 -1
  124. package/dist/cli/dashboard-renderer.js.map +1 -1
  125. package/dist/cli/dashboard-style.js +2 -2
  126. package/dist/cli/dashboard-style.js.map +1 -1
  127. package/dist/cli/dashboard.d.ts +2 -2
  128. package/dist/cli/dashboard.js +7 -32
  129. package/dist/cli/dashboard.js.map +1 -1
  130. package/dist/cli/help.d.ts +1 -1
  131. package/dist/cli/help.js +30 -12
  132. package/dist/cli/help.js.map +1 -1
  133. package/dist/cli/main.js +55 -43
  134. package/dist/cli/main.js.map +1 -1
  135. package/dist/config/access-provenance.d.ts +10 -0
  136. package/dist/config/access-provenance.js +31 -0
  137. package/dist/config/access-provenance.js.map +1 -0
  138. package/dist/config/access.d.ts +12 -2
  139. package/dist/config/access.js +125 -27
  140. package/dist/config/access.js.map +1 -1
  141. package/dist/config/loader.js +1 -0
  142. package/dist/config/loader.js.map +1 -1
  143. package/dist/config/oidc.js +4 -2
  144. package/dist/config/oidc.js.map +1 -1
  145. package/dist/config/project.js +2 -1
  146. package/dist/config/project.js.map +1 -1
  147. package/dist/config/resolve-access.js +24 -10
  148. package/dist/config/resolve-access.js.map +1 -1
  149. package/dist/errors.d.ts +2 -2
  150. package/dist/errors.js.map +1 -1
  151. package/dist/index.d.ts +8 -2
  152. package/dist/index.js +4 -1
  153. package/dist/index.js.map +1 -1
  154. package/dist/process-diagnostics.d.ts +2 -0
  155. package/dist/process-diagnostics.js +11 -0
  156. package/dist/process-diagnostics.js.map +1 -0
  157. package/dist/runtime/access-errors.d.ts +4 -0
  158. package/dist/runtime/access-errors.js +25 -0
  159. package/dist/runtime/access-errors.js.map +1 -0
  160. package/dist/runtime/approval-registry.d.ts +9 -0
  161. package/dist/runtime/approval-registry.js +144 -5
  162. package/dist/runtime/approval-registry.js.map +1 -1
  163. package/dist/runtime/capture.d.ts +22 -0
  164. package/dist/runtime/capture.js +320 -0
  165. package/dist/runtime/capture.js.map +1 -0
  166. package/dist/runtime/catalog.d.ts +39 -0
  167. package/dist/runtime/catalog.js +84 -0
  168. package/dist/runtime/catalog.js.map +1 -0
  169. package/dist/runtime/client.d.ts +2 -1
  170. package/dist/runtime/client.js +74 -47
  171. package/dist/runtime/client.js.map +1 -1
  172. package/dist/runtime/coordinator.d.ts +44 -7
  173. package/dist/runtime/coordinator.js +265 -31
  174. package/dist/runtime/coordinator.js.map +1 -1
  175. package/dist/runtime/daemon.js +12 -6
  176. package/dist/runtime/daemon.js.map +1 -1
  177. package/dist/runtime/dashboard-sanitize.d.ts +2 -0
  178. package/dist/runtime/dashboard-sanitize.js +7 -0
  179. package/dist/runtime/dashboard-sanitize.js.map +1 -0
  180. package/dist/runtime/dashboard-workspace.js +5 -1
  181. package/dist/runtime/dashboard-workspace.js.map +1 -1
  182. package/dist/runtime/dashboard.d.ts +1 -1
  183. package/dist/runtime/dashboard.js +2 -3
  184. package/dist/runtime/dashboard.js.map +1 -1
  185. package/dist/runtime/definition.d.ts +6 -1
  186. package/dist/runtime/definition.js +42 -2
  187. package/dist/runtime/definition.js.map +1 -1
  188. package/dist/runtime/native-owner.js +7 -1
  189. package/dist/runtime/native-owner.js.map +1 -1
  190. package/dist/runtime/pagination.d.ts +9 -0
  191. package/dist/runtime/pagination.js +36 -0
  192. package/dist/runtime/pagination.js.map +1 -0
  193. package/dist/runtime/sdk-observation.d.ts +8 -0
  194. package/dist/runtime/sdk-observation.js +75 -0
  195. package/dist/runtime/sdk-observation.js.map +1 -0
  196. package/dist/runtime/sdk-projection.d.ts +8 -0
  197. package/dist/runtime/sdk-projection.js +19 -0
  198. package/dist/runtime/sdk-projection.js.map +1 -0
  199. package/dist/runtime/sdk-service.d.ts +11 -0
  200. package/dist/runtime/sdk-service.js +109 -0
  201. package/dist/runtime/sdk-service.js.map +1 -0
  202. package/dist/runtime/select-native.d.ts +4 -1
  203. package/dist/runtime/select-native.js +28 -13
  204. package/dist/runtime/select-native.js.map +1 -1
  205. package/dist/runtime/service.d.ts +11 -1
  206. package/dist/runtime/service.js +178 -3
  207. package/dist/runtime/service.js.map +1 -1
  208. package/dist/runtime/session-launcher.js +1 -1
  209. package/dist/runtime/session-launcher.js.map +1 -1
  210. package/dist/runtime/snapshots.d.ts +103 -0
  211. package/dist/runtime/snapshots.js +124 -0
  212. package/dist/runtime/snapshots.js.map +1 -0
  213. package/dist/runtime/state.d.ts +26 -0
  214. package/dist/runtime/state.js +86 -17
  215. package/dist/runtime/state.js.map +1 -1
  216. package/dist/runtime/task-data.d.ts +38 -0
  217. package/dist/runtime/task-data.js +2 -0
  218. package/dist/runtime/task-data.js.map +1 -0
  219. package/dist/runtime/transport.d.ts +13 -0
  220. package/dist/runtime/transport.js +166 -0
  221. package/dist/runtime/transport.js.map +1 -0
  222. package/dist/runtime/types.d.ts +28 -9
  223. package/dist/runtime/types.js.map +1 -1
  224. package/dist/runtime/worker-client.js +40 -13
  225. package/dist/runtime/worker-client.js.map +1 -1
  226. package/dist/runtime/worker-server.js +105 -24
  227. package/dist/runtime/worker-server.js.map +1 -1
  228. package/dist/sdk/connected-types.d.ts +42 -0
  229. package/dist/sdk/connected-types.js +2 -0
  230. package/dist/sdk/connected-types.js.map +1 -0
  231. package/dist/sdk/connected.d.ts +3 -0
  232. package/dist/sdk/connected.js +232 -0
  233. package/dist/sdk/connected.js.map +1 -0
  234. package/dist/sdk/definitions.d.ts +4 -1
  235. package/dist/sdk/definitions.js +16 -2
  236. package/dist/sdk/definitions.js.map +1 -1
  237. package/dist/sdk/execution-definition.d.ts +3 -0
  238. package/dist/sdk/execution-definition.js +59 -0
  239. package/dist/sdk/execution-definition.js.map +1 -0
  240. package/dist/sdk/execution-driver.d.ts +6 -0
  241. package/dist/sdk/execution-driver.js +86 -0
  242. package/dist/sdk/execution-driver.js.map +1 -0
  243. package/dist/sdk/execution-observation.d.ts +3 -0
  244. package/dist/sdk/execution-observation.js +35 -0
  245. package/dist/sdk/execution-observation.js.map +1 -0
  246. package/dist/sdk/execution-options.d.ts +17 -0
  247. package/dist/sdk/execution-options.js +115 -0
  248. package/dist/sdk/execution-options.js.map +1 -0
  249. package/dist/sdk/execution-types.d.ts +72 -0
  250. package/dist/sdk/execution-types.js +2 -0
  251. package/dist/sdk/execution-types.js.map +1 -0
  252. package/dist/sdk/execution.d.ts +3 -0
  253. package/dist/sdk/execution.js +3 -0
  254. package/dist/sdk/execution.js.map +1 -0
  255. package/dist/sdk/hosted-error.d.ts +3 -0
  256. package/dist/sdk/hosted-error.js +14 -0
  257. package/dist/sdk/hosted-error.js.map +1 -0
  258. package/dist/sdk/hosted.d.ts +23 -0
  259. package/dist/sdk/hosted.js +204 -0
  260. package/dist/sdk/hosted.js.map +1 -0
  261. package/dist/sdk/permission-validation.js +10 -1
  262. package/dist/sdk/permission-validation.js.map +1 -1
  263. package/dist/sdk/runner.d.ts +5 -0
  264. package/dist/sdk/runner.js +107 -0
  265. package/dist/sdk/runner.js.map +1 -0
  266. package/dist/sdk/tools.js +4 -1
  267. package/dist/sdk/tools.js.map +1 -1
  268. package/dist/sdk/types.d.ts +29 -1
  269. package/dist/sdk/types.js.map +1 -1
  270. package/package.json +6 -3
  271. package/sdk/access-config.md +60 -4
  272. package/sdk/adapter-contract.md +13 -3
  273. package/sdk/additional-harnesses.md +43 -0
  274. package/sdk/agent-skill.md +2 -2
  275. package/sdk/agent.md +6 -4
  276. package/sdk/approvals.md +5 -1
  277. package/sdk/authentication.md +1 -1
  278. package/sdk/cli/dashboard-design.md +6 -0
  279. package/sdk/cli/index.md +9 -6
  280. package/sdk/cli/output.md +3 -1
  281. package/sdk/completion-notifications.md +2 -0
  282. package/sdk/config.md +1 -1
  283. package/sdk/copilot.md +53 -0
  284. package/sdk/cursor.md +68 -0
  285. package/sdk/diagnostics.md +29 -0
  286. package/sdk/distribution.md +5 -3
  287. package/sdk/evals.md +1 -1
  288. package/sdk/examples/chat-tool.ts +126 -0
  289. package/sdk/execution.md +743 -0
  290. package/sdk/fx.md +50 -5
  291. package/sdk/harnesses.md +13 -13
  292. package/sdk/index.md +45 -20
  293. package/sdk/message-delivery.md +2 -0
  294. package/sdk/opencode.md +57 -0
  295. package/sdk/permissions.md +6 -2
  296. package/sdk/plugins/sub-agents.md +4 -2
  297. package/sdk/project-team.md +25 -1
  298. package/sdk/sessions.md +2 -0
  299. package/sdk/tools.md +4 -2
  300. package/sdk/v1-runtime.md +8 -6
  301. package/dist/cli/catalog.d.ts +0 -26
  302. package/dist/cli/catalog.js +0 -41
  303. package/dist/cli/catalog.js.map +0 -1
@@ -0,0 +1,569 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { realpath } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { AgentError } from "../errors.js";
5
+ import { NativeApprovals } from "./approval-lifetime.js";
6
+ import { resolveOpencodeRoute } from "./opencode-access.js";
7
+ import { createOpencodeRuntime, gatewayBaseUrl } from "./opencode-config.js";
8
+ import { OpencodeHttp, startOpencodeProcess, supportedOpencodeVersion } from "./opencode-http.js";
9
+ import { startOpencodeTools } from "./opencode-tools.js";
10
+ export async function openOpencode(options) {
11
+ const harness = options.harness;
12
+ if (harness.kind !== "opencode")
13
+ throw new AgentError("INVALID_CONFIG", "OpenCode received configuration for a different harness.");
14
+ const model = harness.model;
15
+ const route = resolveOpencodeRoute(model, options.access);
16
+ const cwd = await realpath(options.cwd).catch(() => resolve(options.cwd));
17
+ const runtime = await createOpencodeRuntime(options, route);
18
+ const tools = await startOpencodeTools(options.agent).catch(async (error) => { await runtime.close(); throw error; });
19
+ const password = randomBytes(32).toString("base64url");
20
+ runtime.env.OPENCODE_SERVER_PASSWORD = password;
21
+ let native;
22
+ let eventController;
23
+ let sessionID;
24
+ try {
25
+ native = await startOpencodeProcess(cwd, runtime.env);
26
+ const http = new OpencodeHttp(native.url, cwd, password);
27
+ await verifyStartup(http, runtime.config, options, route);
28
+ const approvals = new NativeApprovals();
29
+ const pendingApprovals = new Set();
30
+ const approvalTasks = new Set();
31
+ let active;
32
+ let closed = false;
33
+ let first = true;
34
+ let closing;
35
+ let fatalEventError;
36
+ const abortNative = async () => {
37
+ if (!sessionID)
38
+ return;
39
+ await http.json(`/session/${encodeURIComponent(sessionID)}/abort`, { method: "POST", timeout: 1_000 }).catch(() => { });
40
+ };
41
+ const fail = (error) => {
42
+ const current = active;
43
+ if (!current) {
44
+ fatalEventError ??= error;
45
+ return;
46
+ }
47
+ current.failure ??= error;
48
+ tools.stopAdmission();
49
+ current.lifecycle.abort();
50
+ approvals.clear();
51
+ void abortNative();
52
+ };
53
+ const pendingNativePermissions = async () => {
54
+ const value = await http.json("/permission");
55
+ if (!Array.isArray(value))
56
+ protocol("OpenCode returned malformed native permission data.");
57
+ const result = new Set();
58
+ for (const item of value) {
59
+ const id = string(record(item).id);
60
+ if (!id)
61
+ protocol("OpenCode returned malformed native permission data.");
62
+ result.add(id);
63
+ }
64
+ return result;
65
+ };
66
+ const reconcilePermissions = async () => {
67
+ const nativePending = await pendingNativePermissions();
68
+ for (const id of [...pendingApprovals]) {
69
+ if (nativePending.has(id))
70
+ continue;
71
+ pendingApprovals.delete(id);
72
+ approvals.withdraw(id);
73
+ }
74
+ };
75
+ const replyPermission = async (id, decision) => {
76
+ const nativePending = await pendingNativePermissions();
77
+ if (!nativePending.has(id)) {
78
+ pendingApprovals.delete(id);
79
+ approvals.withdraw(id);
80
+ return;
81
+ }
82
+ await http.json(`/permission/${encodeURIComponent(id)}/reply`, { body: { reply: decision } });
83
+ pendingApprovals.delete(id);
84
+ approvals.withdraw(id);
85
+ if (decision === "reject")
86
+ await reconcilePermissions();
87
+ };
88
+ const handlePermission = (input) => {
89
+ const task = (async () => {
90
+ const request = record(input);
91
+ const id = string(request.id);
92
+ const nativeSession = string(request.sessionID);
93
+ const permission = string(request.permission);
94
+ if (!id || !nativeSession || !permission || !strings(request.patterns) || !strings(request.always)
95
+ || !record(request.metadata, true)) {
96
+ fail(new AgentError("INPUT_REQUIRED", "OpenCode returned an unsupported native permission request."));
97
+ return;
98
+ }
99
+ const current = active;
100
+ if (!current) {
101
+ fatalEventError ??= new AgentError("INPUT_REQUIRED", "OpenCode requested permission during startup.");
102
+ await http.json(`/permission/${encodeURIComponent(id)}/reply`, { body: { reply: "reject" } }).catch(() => { });
103
+ return;
104
+ }
105
+ if (!options.onApproval) {
106
+ fail(new AgentError("INPUT_REQUIRED", "OpenCode requested permission but no approval handler is available."));
107
+ await replyPermission(id, "reject").catch(() => { });
108
+ return;
109
+ }
110
+ const context = {
111
+ permission,
112
+ patterns: [...request.patterns],
113
+ metadata: request.metadata,
114
+ always: [...request.always],
115
+ ...(request.tool === undefined ? {} : { tool: request.tool }),
116
+ };
117
+ const approval = {
118
+ kind: "permission",
119
+ harness: "opencode",
120
+ message: permissionMessage(permission, request.patterns),
121
+ requestedSchema: {
122
+ type: "object",
123
+ properties: {
124
+ decision: { type: "string", oneOf: [
125
+ { const: "once", title: "Allow once" },
126
+ { const: "reject", title: "Deny" },
127
+ ] },
128
+ },
129
+ required: ["decision"],
130
+ additionalProperties: false,
131
+ },
132
+ context,
133
+ };
134
+ pendingApprovals.add(id);
135
+ try {
136
+ const answer = await approvals.request(id, approval, options.onApproval, current.lifecycle.signal);
137
+ if (answer.decision !== "once" && answer.decision !== "reject") {
138
+ throw new AgentError("INVALID_APPROVAL_RESPONSE", "The approval response does not match the requested schema.");
139
+ }
140
+ await replyPermission(id, answer.decision);
141
+ }
142
+ catch (error) {
143
+ if (error instanceof AgentError && error.code === "REQUEST_EXPIRED")
144
+ return;
145
+ const safe = error instanceof AgentError
146
+ ? error
147
+ : new AgentError("INPUT_REQUIRED", "The OpenCode permission response could not be completed.");
148
+ fail(safe);
149
+ await replyPermission(id, "reject").catch(() => { });
150
+ }
151
+ finally {
152
+ pendingApprovals.delete(id);
153
+ }
154
+ })();
155
+ approvalTasks.add(task);
156
+ void task.then(() => approvalTasks.delete(task), error => {
157
+ approvalTasks.delete(task);
158
+ fail(error instanceof AgentError ? error : new AgentError("PROTOCOL_ERROR", "OpenCode returned malformed native permission data."));
159
+ });
160
+ };
161
+ const handleEvent = (value) => {
162
+ const event = record(value);
163
+ if (event.type === "permission.asked") {
164
+ handlePermission(event.properties);
165
+ return;
166
+ }
167
+ if (event.type === "permission.replied") {
168
+ const properties = record(event.properties);
169
+ const id = string(properties.requestID);
170
+ if (!id) {
171
+ fail(new AgentError("PROTOCOL_ERROR", "OpenCode returned malformed native permission settlement data."));
172
+ return;
173
+ }
174
+ pendingApprovals.delete(id);
175
+ approvals.withdraw(id);
176
+ if (properties.reply === "reject")
177
+ void reconcilePermissions().catch(() => { });
178
+ return;
179
+ }
180
+ if (event.type === "question.asked") {
181
+ fail(new AgentError("INPUT_REQUIRED", "OpenCode requested unsupported interactive input."));
182
+ return;
183
+ }
184
+ if (event.type === "session.error") {
185
+ const properties = record(event.properties);
186
+ if (properties.sessionID === undefined || properties.sessionID === sessionID) {
187
+ fail(new AgentError("HARNESS_ERROR", "OpenCode reported a native execution error."));
188
+ }
189
+ }
190
+ };
191
+ eventController = new AbortController();
192
+ let eventReady;
193
+ const eventEstablished = new Promise(resolveReady => { eventReady = resolveReady; });
194
+ const eventTask = http.events(eventController.signal, handleEvent, eventReady).catch(error => {
195
+ if (!eventController.signal.aborted)
196
+ fail(error instanceof AgentError ? error : new AgentError("PROTOCOL_ERROR", "The OpenCode native event stream failed."));
197
+ });
198
+ await Promise.race([
199
+ eventEstablished,
200
+ eventTask.then(() => { throw fatalEventError ?? new AgentError("PROTOCOL_ERROR", "OpenCode could not establish its native event stream."); }),
201
+ ]);
202
+ if (fatalEventError)
203
+ throw fatalEventError;
204
+ if (tools.descriptor) {
205
+ const added = record(await http.json("/mcp", { body: tools.descriptor }));
206
+ if (record(added[tools.descriptor.name], true)?.status !== "connected") {
207
+ throw new AgentError("HARNESS_UNAVAILABLE", "OpenCode could not connect the private MCP tool endpoint.");
208
+ }
209
+ const status = record(await http.json("/mcp"));
210
+ if (record(status[tools.descriptor.name], true)?.status !== "connected") {
211
+ throw new AgentError("HARNESS_UNAVAILABLE", "OpenCode did not retain the private MCP tool connection.");
212
+ }
213
+ }
214
+ const created = record(await http.json("/session", { body: { title: "Subharness", model: { providerID: route.providerID, id: route.modelID } } }));
215
+ sessionID = string(created.id);
216
+ if (!sessionID || !samePath(string(created.directory), cwd)
217
+ || !selection(created.model, "id", route))
218
+ protocol("OpenCode returned malformed native session data.");
219
+ if (fatalEventError)
220
+ throw fatalEventError;
221
+ tools.onFailure(fail);
222
+ const session = {
223
+ async turn(prompt) {
224
+ if (closed)
225
+ throw new AgentError("SESSION_CLOSED", "The OpenCode session has closed.");
226
+ if (fatalEventError)
227
+ throw fatalEventError;
228
+ if (active)
229
+ throw new AgentError("SESSION_BUSY", "OpenCode already has an active turn.");
230
+ const includeInstructions = first && Boolean(options.agent.instructions);
231
+ const current = {
232
+ completion: undefined,
233
+ lifecycle: new AbortController(),
234
+ interrupted: false,
235
+ closing: false,
236
+ settled: false,
237
+ };
238
+ active = current;
239
+ current.completion = (async () => {
240
+ tools.beginTurn();
241
+ try {
242
+ const response = await http.json(`/session/${encodeURIComponent(sessionID)}/message`, {
243
+ timeout: false,
244
+ body: {
245
+ model: { providerID: route.providerID, modelID: route.modelID },
246
+ parts: [{ type: "text", text: prompt }],
247
+ ...(includeInstructions ? { system: options.agent.instructions } : {}),
248
+ },
249
+ });
250
+ first = false;
251
+ if (current.closing)
252
+ throw new AgentError("SESSION_CLOSED", "The OpenCode session closed during the turn.");
253
+ if (current.interrupted)
254
+ throw new AgentError("INTERRUPTED", "The OpenCode turn was interrupted.");
255
+ if (current.failure)
256
+ throw current.failure;
257
+ return terminalText(response, sessionID, route);
258
+ }
259
+ catch (error) {
260
+ if (current.closing)
261
+ throw new AgentError("SESSION_CLOSED", "The OpenCode session closed during the turn.");
262
+ if (current.interrupted)
263
+ throw new AgentError("INTERRUPTED", "The OpenCode turn was interrupted.");
264
+ if (current.failure)
265
+ throw current.failure;
266
+ if (error instanceof AgentError)
267
+ throw error;
268
+ throw new AgentError("HARNESS_ERROR", "OpenCode could not complete the native turn.");
269
+ }
270
+ finally {
271
+ current.settled = true;
272
+ current.lifecycle.abort();
273
+ approvals.clear();
274
+ tools.stopAdmission();
275
+ await Promise.allSettled([...approvalTasks]);
276
+ await tools.drain();
277
+ if (active === current)
278
+ active = undefined;
279
+ }
280
+ })();
281
+ return await current.completion;
282
+ },
283
+ async steer() { throw new AgentError("UNSUPPORTED_DELIVERY", "OpenCode does not expose verified active steering."); },
284
+ async interrupt() {
285
+ const current = active;
286
+ if (!current)
287
+ return;
288
+ current.interrupted = true;
289
+ current.lifecycle.abort();
290
+ approvals.clear();
291
+ tools.stopAdmission();
292
+ try {
293
+ const admitted = await bounded(waitForAdmission(http, sessionID, current), 5_000);
294
+ if (admitted) {
295
+ const acknowledged = await http.json(`/session/${encodeURIComponent(sessionID)}/abort`, { method: "POST", timeout: 2_000 });
296
+ if (acknowledged !== true)
297
+ throw new Error();
298
+ }
299
+ await bounded(waitForNativeStop(http, sessionID, current), 5_000);
300
+ }
301
+ catch {
302
+ throw new AgentError("CANCELLATION_FAILED", "OpenCode did not confirm native cancellation.");
303
+ }
304
+ await current.completion.catch(() => { });
305
+ },
306
+ async resume() { throw new AgentError("RECOVERY_UNSUPPORTED", "OpenCode cannot retry failed execution without replaying input."); },
307
+ async close() {
308
+ if (closing)
309
+ return await closing;
310
+ closed = true;
311
+ closing = (async () => {
312
+ const current = active;
313
+ if (current) {
314
+ current.closing = true;
315
+ current.lifecycle.abort();
316
+ }
317
+ approvals.clear();
318
+ tools.stopAdmission();
319
+ try {
320
+ const admitted = current ? await bounded(waitForAdmission(http, sessionID, current), 1_000).catch(() => false) : false;
321
+ if (current && admitted)
322
+ await http.json(`/session/${encodeURIComponent(sessionID)}/abort`, { method: "POST", timeout: 1_000 }).catch(() => { });
323
+ if (current)
324
+ await bounded(current.completion.catch(() => { }), 5_000).catch(() => { });
325
+ eventController.abort();
326
+ if (sessionID)
327
+ await http.json(`/session/${encodeURIComponent(sessionID)}`, { method: "DELETE", timeout: 1_000 }).catch(() => { });
328
+ }
329
+ finally {
330
+ const cleanupFailures = [];
331
+ for (const cleanup of [
332
+ () => native.close(),
333
+ async () => { await eventTask; },
334
+ () => tools.close(),
335
+ () => runtime.close(),
336
+ ]) {
337
+ try {
338
+ await cleanup();
339
+ }
340
+ catch (error) {
341
+ cleanupFailures.push(error);
342
+ }
343
+ }
344
+ if (cleanupFailures.length > 0)
345
+ throw cleanupFailures[0];
346
+ }
347
+ })();
348
+ return await closing;
349
+ },
350
+ };
351
+ return session;
352
+ }
353
+ catch (error) {
354
+ eventController?.abort();
355
+ if (sessionID && native) {
356
+ const http = new OpencodeHttp(native.url, cwd, password);
357
+ await http.json(`/session/${encodeURIComponent(sessionID)}`, { method: "DELETE", timeout: 1_000 }).catch(() => { });
358
+ }
359
+ await native?.close().catch(() => { });
360
+ await tools.close().catch(() => { });
361
+ await runtime.close().catch(() => { });
362
+ throw error;
363
+ }
364
+ }
365
+ async function verifyStartup(http, expected, options, route) {
366
+ const health = record(await http.json("/global/health"));
367
+ if (health.healthy !== true || typeof health.version !== "string")
368
+ protocol("OpenCode returned malformed native health data.");
369
+ if (health.version !== supportedOpencodeVersion) {
370
+ throw new AgentError("INVALID_CONFIG", "OpenCode native server version does not match the verified isolated execution profile.");
371
+ }
372
+ const config = record(await http.json("/config"));
373
+ const configuredProviders = record(config.provider, true) ?? {};
374
+ if (!onlyString(config.enabled_providers, route.providerID) || config.model !== expected.model || config.small_model !== expected.small_model
375
+ || config.share !== "disabled" || config.autoupdate !== false || !Array.isArray(config.plugin) || config.plugin.length !== 0
376
+ || config.lsp !== false || config.formatter !== false
377
+ || !sameOptionalStrings(config.instructions, expected.instructions)
378
+ || !onlyKey(record(config.permission), "*", "ask")
379
+ || config.permission?.["*"] !== "ask") {
380
+ throw new AgentError("INVALID_CONFIG", "OpenCode did not apply the isolated native configuration.");
381
+ }
382
+ if (route.kind === "subscription") {
383
+ if (Object.hasOwn(configuredProviders, route.providerID) || Object.keys(configuredProviders).length !== 0) {
384
+ throw new AgentError("INVALID_CONFIG", "OpenCode loaded provider configuration that conflicts with native subscription access.");
385
+ }
386
+ }
387
+ else {
388
+ const providerConfig = record(configuredProviders[route.providerID]);
389
+ const providerOptions = record(providerConfig.options);
390
+ const expectedOptionKeys = route.kind === "gateway"
391
+ ? options.access.type === "vercel-api-key" ? ["apiKey", "baseURL"] : ["baseURL"]
392
+ : route.baseUrl === undefined ? ["apiKey"] : ["apiKey", "baseURL"];
393
+ const expectedKey = options.access.type === "vercel-api-key" || options.access.type === "api-key" ? options.access.token : undefined;
394
+ if (Object.keys(configuredProviders).length !== 1 || providerConfig.npm !== route.configPackage
395
+ || !Array.isArray(providerConfig.env) || providerConfig.env.length !== 0
396
+ || providerOptions.apiKey !== expectedKey
397
+ || (route.kind === "gateway" ? providerOptions.baseURL !== gatewayBaseUrl : providerOptions.baseURL !== route.baseUrl)
398
+ || !sameStrings(Object.keys(providerOptions).sort(), expectedOptionKeys)) {
399
+ throw new AgentError("INVALID_CONFIG", "OpenCode did not retain the selected explicit provider configuration.");
400
+ }
401
+ }
402
+ for (const field of ["agent", "mode", "command", "tools"]) {
403
+ if (record(config[field], true) && Object.keys(config[field]).length > 0) {
404
+ throw new AgentError("INVALID_CONFIG", "OpenCode loaded excluded native configuration.");
405
+ }
406
+ }
407
+ if (record(config.mcp, true) && Object.keys(config.mcp).length > 0)
408
+ throw new AgentError("INVALID_CONFIG", "OpenCode loaded an unexpected native MCP configuration.");
409
+ const providers = record(await http.json("/provider"));
410
+ if (!Array.isArray(providers.connected) || !providers.connected.every((value) => typeof value === "string") || !Array.isArray(providers.all)) {
411
+ protocol("OpenCode returned malformed native provider data.");
412
+ }
413
+ if (route.kind === "subscription" && providers.all.length === 0 && providers.connected.length === 0) {
414
+ const defaults = record(providers.default);
415
+ if (Object.keys(defaults).length !== 0)
416
+ throw new AgentError("INVALID_CONFIG", "OpenCode returned conflicting native subscription defaults.");
417
+ throw new AgentError("ACCESS_UNAVAILABLE", "The selected OpenCode native subscription login is unavailable.");
418
+ }
419
+ if (providers.all.length !== 1 || record(providers.all[0]).id !== route.providerID) {
420
+ throw new AgentError("INVALID_CONFIG", "OpenCode enabled an unexpected native provider.");
421
+ }
422
+ const selectedProvider = providers.all.find((value) => record(value, true)?.id === route.providerID);
423
+ if (!selectedProvider)
424
+ throw new AgentError("INVALID_CONFIG", "OpenCode did not enable the selected native provider.");
425
+ const provider = record(selectedProvider);
426
+ const providerOptions = record(provider.options);
427
+ if (route.kind !== "subscription" && (provider.source !== "config"
428
+ || providerOptions.apiKey !== (options.access.type === "vercel-oidc" ? undefined : options.access.token))) {
429
+ throw new AgentError("INVALID_CONFIG", "OpenCode selected an incompatible explicit provider route.");
430
+ }
431
+ if (route.kind === "gateway" && providerOptions.baseURL !== gatewayBaseUrl) {
432
+ throw new AgentError("INVALID_CONFIG", "OpenCode selected an incompatible Gateway endpoint.");
433
+ }
434
+ if (route.kind === "direct" && route.baseUrl !== undefined && providerOptions.baseURL !== route.baseUrl) {
435
+ throw new AgentError("INVALID_CONFIG", "OpenCode did not retain the selected direct provider endpoint override.");
436
+ }
437
+ const selectedModel = record(record(provider.models)[route.modelID], true);
438
+ if (!selectedModel)
439
+ throw new AgentError("INVALID_CONFIG", "The selected model is absent from OpenCode's native catalog.");
440
+ const api = record(selectedModel.api);
441
+ const apiID = string(api.id);
442
+ if (selectedModel.id !== route.modelID || selectedModel.providerID !== route.providerID || !apiID) {
443
+ throw new AgentError("INVALID_CONFIG", "OpenCode selected incompatible native model metadata.");
444
+ }
445
+ if (route.kind === "gateway" && apiID !== route.modelID)
446
+ throw new AgentError("INVALID_CONFIG", "OpenCode changed the selected Gateway model route.");
447
+ if (!record(selectedModel.capabilities, true) || !record(selectedModel.limit, true)) {
448
+ throw new AgentError("INVALID_CONFIG", "The selected model has incomplete native metadata.");
449
+ }
450
+ if (route.kind === "subscription") {
451
+ if (providers.connected.length === 0) {
452
+ const defaults = record(providers.default);
453
+ const defaultModel = string(defaults[route.providerID]);
454
+ if (provider.source === "custom" && Object.keys(providerOptions).length === 0
455
+ && Object.keys(defaults).length === 1 && defaultModel
456
+ && route.unavailablePackages?.includes(api.npm)) {
457
+ throw new AgentError("ACCESS_UNAVAILABLE", "The selected OpenCode native subscription login is unavailable.");
458
+ }
459
+ throw new AgentError("INVALID_CONFIG", "OpenCode returned conflicting native subscription state.");
460
+ }
461
+ if (!onlyString(providers.connected, route.providerID))
462
+ throw new AgentError("INVALID_CONFIG", "OpenCode returned ambiguous native subscription state.");
463
+ if (provider.source !== "custom" || providerOptions.apiKey !== route.oauthMarker || !route.packages.includes(api.npm)) {
464
+ throw new AgentError("INVALID_CONFIG", "OpenCode could not verify the selected native OAuth subscription route.");
465
+ }
466
+ }
467
+ else if (!onlyString(providers.connected, route.providerID)) {
468
+ throw new AgentError("INVALID_CONFIG", "OpenCode did not retain the selected explicit provider route.");
469
+ }
470
+ else if (!route.packages.includes(api.npm)) {
471
+ throw new AgentError("INVALID_CONFIG", "OpenCode selected incompatible native model metadata.");
472
+ }
473
+ const agents = await http.json("/agent");
474
+ if (!Array.isArray(agents))
475
+ protocol("OpenCode returned malformed native agent data.");
476
+ for (const agent of agents) {
477
+ const entry = record(agent);
478
+ if (entry.model !== undefined && !selection(entry.model, "modelID", route)) {
479
+ throw new AgentError("INVALID_CONFIG", "OpenCode loaded a native agent with a conflicting model override.");
480
+ }
481
+ }
482
+ }
483
+ function terminalText(value, sessionID, route) {
484
+ const response = record(value);
485
+ const info = record(response.info);
486
+ if (info.role !== "assistant" || info.sessionID !== sessionID || typeof info.time?.completed !== "number" || typeof info.finish !== "string" || !Array.isArray(response.parts)) {
487
+ protocol("OpenCode returned malformed or unfinished terminal data.");
488
+ }
489
+ if (info.providerID !== route.providerID || info.modelID !== route.modelID)
490
+ throw new AgentError("INVALID_CONFIG", "OpenCode changed the requested provider or model during execution.");
491
+ if (info.error !== undefined)
492
+ throw new AgentError("HARNESS_ERROR", "OpenCode reported a native execution error.");
493
+ const text = response.parts.filter((part) => record(part, true)?.type === "text").map((part) => {
494
+ if (typeof part.text !== "string")
495
+ protocol("OpenCode returned malformed terminal text.");
496
+ return part.text;
497
+ }).join("");
498
+ if (Buffer.byteLength(text, "utf8") > 1024 * 1024)
499
+ throw new AgentError("RESPONSE_TOO_LARGE", "The OpenCode response exceeds 1 MiB.");
500
+ return text;
501
+ }
502
+ function record(value, optional = false) {
503
+ if (value && typeof value === "object" && !Array.isArray(value))
504
+ return value;
505
+ if (optional && value === undefined)
506
+ return undefined;
507
+ protocol("OpenCode returned malformed native protocol data.");
508
+ }
509
+ function string(value) { return typeof value === "string" && value.length > 0 ? value : undefined; }
510
+ function strings(value) {
511
+ return Array.isArray(value) && value.every(item => typeof item === "string") && new Set(value).size === value.length;
512
+ }
513
+ function onlyString(value, expected) { return Array.isArray(value) && value.length === 1 && value[0] === expected; }
514
+ function sameStrings(left, right) {
515
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length
516
+ && left.every((value, index) => typeof value === "string" && value === right[index]);
517
+ }
518
+ function sameOptionalStrings(left, right) {
519
+ return left === undefined && right === undefined || sameStrings(left, right);
520
+ }
521
+ function onlyKey(value, key, expected) {
522
+ return Object.keys(value).length === 1 && value[key] === expected;
523
+ }
524
+ function selection(value, modelField, route) {
525
+ const selected = record(value, true);
526
+ return Boolean(selected && selected.providerID === route.providerID && selected[modelField] === route.modelID);
527
+ }
528
+ function samePath(left, right) { return left !== undefined && resolve(left) === resolve(right); }
529
+ function permissionMessage(permission, patterns) {
530
+ const details = patterns.length === 0 ? "" : ` for ${patterns.join(", ")}`;
531
+ const message = `Allow OpenCode ${permission}${details}?`;
532
+ return message.length <= 4_096 ? message : `Allow OpenCode ${permission}?`;
533
+ }
534
+ async function waitForNativeStop(http, sessionID, current) {
535
+ for (;;) {
536
+ const statuses = record(await http.json("/session/status"));
537
+ const status = record(statuses[sessionID], true);
538
+ if (status === undefined && current.settled)
539
+ return;
540
+ await new Promise(resolveDelay => setTimeout(resolveDelay, 25));
541
+ }
542
+ }
543
+ async function waitForAdmission(http, sessionID, current) {
544
+ for (;;) {
545
+ if (current.settled)
546
+ return false;
547
+ const statuses = record(await http.json("/session/status"));
548
+ const status = record(statuses[sessionID], true);
549
+ if (status?.type === "busy" || status?.type === "retry")
550
+ return true;
551
+ await new Promise(resolveDelay => setTimeout(resolveDelay, 10));
552
+ }
553
+ }
554
+ async function bounded(promise, milliseconds) {
555
+ let timer;
556
+ const timeout = new Promise((_resolve, reject) => {
557
+ timer = setTimeout(() => reject(new AgentError("CANCELLATION_FAILED", "OpenCode did not reach a native terminal outcome.")), milliseconds);
558
+ timer.unref();
559
+ });
560
+ try {
561
+ return await Promise.race([promise, timeout]);
562
+ }
563
+ finally {
564
+ if (timer)
565
+ clearTimeout(timer);
566
+ }
567
+ }
568
+ function protocol(message) { throw new AgentError("PROTOCOL_ERROR", message); }
569
+ //# sourceMappingURL=opencode.js.map