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
package/README.md CHANGED
@@ -6,9 +6,11 @@
6
6
  <a href="https://github.com/vercel-labs/subharness/blob/main/LICENSE"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache--2.0-blue.svg?style=for-the-badge&amp;labelColor=000000" height="28"></a>
7
7
  </p>
8
8
 
9
- Stay with your favorite coding agent. Ask it to delegate tasks across Codex, Claude Code, and fx, and bring results back to one conversation. The main agent uses the local `subharness` CLI to start independent native sessions, check their status, read complete responses, and report the results.
9
+ Stay with your favorite coding agent. Ask it to delegate tasks across Codex, Claude Code, fx, OpenCode, GitHub Copilot, and Cursor, and bring results back to one conversation. The main agent uses the local `subharness` CLI to start independent native sessions, check their status, read complete responses, and report the results.
10
10
 
11
- TypeScript definitions are optional. Add them when your coding agent needs reusable specialists with shared instructions, tools, or harness alternatives. Codex and Claude Code use eligible native subscription logins or explicitly configured API access; fx uses an explicitly configured Vercel AI Gateway connection.
11
+ TypeScript definitions are optional. Add them when your coding agent needs reusable specialists with shared instructions, tools, or harness alternatives. Codex and Claude Code use eligible native subscription logins or explicitly configured API access. fx, OpenCode, and GitHub Copilot accept explicitly configured native logins, direct API keys, or Vercel AI Gateway access. Cursor uses an explicitly configured native CLI login or Cursor API key.
12
+
13
+ Use the Node.js execution SDK to build an agent-running UI or expose a specialist as a tool in an existing chat. The application owns its runner and can observe task states, approval requests, and complete responses without invoking the Subharness CLI.
12
14
 
13
15
  The library coordinates sessions, queues, and subagents. Harnesses own model execution, native tools, and context. You supply the working directory and any sandbox or worktree isolation.
14
16
 
@@ -56,10 +58,13 @@ You can also invoke a harness manually:
56
58
  subharness run claude "Review the current diff."
57
59
  subharness run codex --cwd /path/to/worktree "Implement the documented feature."
58
60
  subharness run fx --model "provider/model" "Compare the proposed implementations."
61
+ subharness run opencode --model "creator/model" "Inspect the current implementation."
62
+ subharness run copilot --model "creator/model" "Review the current diff."
63
+ subharness run cursor --model "CURSOR_MODEL_ID" "Implement the documented change."
59
64
  subharness run claude --help
60
65
  ```
61
66
 
62
- Replace `provider/model` with an available Gateway model identifier. fx requires an explicit Gateway connection as described under Access below. Without `--model`, a direct target uses a verifiable native default for the authorized connection and retains that model for follow-ups. If the default cannot be resolved, the command asks for an explicit model. `--effort` uses the selected harness's supported values. These options do not override a TypeScript specialist.
67
+ Replace `provider/model` or `creator/model` with an available Gateway model identifier. fx, OpenCode, and Copilot require an explicit compatible connection as described under Access below. Non-Gateway routes use their documented native model identifiers. Cursor requires explicit subscription or Cursor API-key access. OpenCode, Copilot, and Cursor require `--model` and reject `--effort`. Codex, Claude Code, and fx use a verifiable native default when possible and accept their documented effort values. These options do not override a TypeScript specialist.
63
68
 
64
69
  Prompts can be one quoted argument, `--prompt`, or a UTF-8 file. `--prompt-file -` reads piped stdin explicitly. Exactly one prompt source is accepted, with a 1 MiB limit.
65
70
 
@@ -70,6 +75,60 @@ subharness run codex --prompt-file - < ./task.txt
70
75
 
71
76
  Use `subharness dashboard` in a terminal to watch agents managed by the local coordinator. The live view groups agents by repository/worktree, pins the checkout containing the launch directory first, and orders other groups by their latest agent update. Each group shows current agents followed by finished runs, with harness, task title, elapsed time, and status. Finished history is retained for the coordinator’s lifetime. Use Up/Down to select a run and Enter to open its agent session’s request history. Queued requests appear first, followed by remaining requests newest first. Enter expands a request’s prompt and response; Escape returns to the overview. Completed statuses are green, and a compact Sub-agents footer summarizes direct child agents. The overview has no application banner or footer. Ctrl+C closes the view without stopping agents.
72
77
 
78
+ ## Run agents from application code
79
+
80
+ Install `subharness` as an application dependency:
81
+
82
+ ```sh
83
+ npm install subharness
84
+ ```
85
+
86
+ Create an application-owned runner and pass an in-memory definition:
87
+
88
+ ```ts
89
+ import { agent, codex, createRunner } from "subharness";
90
+
91
+ const reviewer = agent({
92
+ name: "reviewer",
93
+ description: "Reviews the requested changes.",
94
+ instructions: "Report concrete findings with file references.",
95
+ harness: codex({ model: "CODEX_MODEL_ID" }),
96
+ });
97
+ const runner = createRunner();
98
+ try {
99
+ const session = await runner.createSession(reviewer, { cwd: "/absolute/project/path" });
100
+ const { task } = await session.prompt("Review the current diff.");
101
+ // A separate UI handler must answer any pending approvals.
102
+ console.log((await task.result).text);
103
+ // Reuse session.prompt(...) for follow-ups in the same conversation.
104
+ } finally {
105
+ await runner.close();
106
+ }
107
+ ```
108
+
109
+ Replace the model placeholder with an available native model. A UI can consume `task.watch({ signal })` for current and changed snapshots. Aborting observation leaves execution running; a Stop action calls `task.cancel()`. Keep the runner for the chat/job lifetime and `await runner.close()` when that scope ends. Native harnesses still need installation and authorized access.
110
+
111
+ Tool functions can close over application services. Embedded sessions keep them in the host process, with its unchanged cwd and environment. Declared subagents use hosted tools instead of CLI launchers. An existing chat app can register a tool whose `execute` calls `session.prompt`, while retaining its own model loop, authorization, and UI transport.
112
+
113
+ To connect a UI backend to agents started by the CLI, use the shared coordinator:
114
+
115
+ ```ts
116
+ import { connect } from "subharness";
117
+
118
+ const client = await connect(); // Connects or starts an empty coordinator.
119
+ try {
120
+ const { items: sessions } = await client.sessions.list();
121
+ console.log(sessions);
122
+ // client.sessions.get(id) retrieves the same conversation used by the CLI.
123
+ } finally {
124
+ await client.disconnect(); // Execution continues in the shared coordinator.
125
+ }
126
+ ```
127
+
128
+ Use `connect({ start: false })` for strict attachment. Approval requests advertise actions; submit a chosen action with `client.approvals.respond(request.id, { actionId: action.id })`, adding explicitly selected grants when required. Applications can render the supplied labels without provider-specific branches.
129
+
130
+ The SDK exposes complete responses, task states, and approvals, without token deltas or native tool-progress events. History lasts for the owning coordinator's lifetime; restart recovery is not provided. See the [execution API](sdk/execution.md) and [chat-tool example](sdk/examples/chat-tool.ts) for lifecycle and integration details. Embedded access is explicit through `createRunner({ access, env })`; connected sessions use the CLI's existing project access settings.
131
+
73
132
  ## Define a reusable specialist
74
133
 
75
134
  Install the SDK in the project before adding definitions:
@@ -94,7 +153,7 @@ export default agent({
94
153
  });
95
154
  ```
96
155
 
97
- Replace the model placeholders with identifiers available through your native harnesses. Each file default-exports one definition. Global definitions live in `~/.subharness/agents/`; repository and global names never silently override one another. The bare targets `codex`, `claude`, and `fx` are reserved for harnesses; a specialist with one of those names requires a scope qualifier such as `repo:claude`.
156
+ Replace the model placeholders with identifiers available through your native harnesses. Each file default-exports one definition. Global definitions live in `~/.subharness/agents/`; repository and global names never silently override one another. The bare targets `codex`, `claude`, `fx`, `opencode`, `copilot`, and `cursor` are reserved for harnesses; a specialist with one of those names requires a scope qualifier such as `repo:claude`.
98
157
 
99
158
  ## Run and follow up
100
159
 
@@ -114,7 +173,7 @@ Long prompts can use `--prompt-file`. Output is compact text by default; `--form
114
173
 
115
174
  ## Access
116
175
 
117
- No project configuration is needed when an eligible native subscription login is available. Login remains with the native harness. Ambient API keys do not silently enable paid fallback.
176
+ Codex and Claude Code need no personal access configuration when an eligible native subscription login is available. Other harnesses require an explicit connection. Login remains with the native harness. Ambient API keys do not silently enable paid fallback.
118
177
 
119
178
  Personal access preferences can be placed in the ignored `.subharness/agents.local.json`. Linked worktrees read that file from the main checkout, while their agent definitions come from the selected worktree.
120
179
 
@@ -129,7 +188,7 @@ Personal access preferences can be placed in the ignored `.subharness/agents.loc
129
188
 
130
189
  Only credential references belong in that file. [Access configuration](sdk/access-config.md) also describes direct API keys, project OIDC, explicit connection fallback, and token-lifetime limits.
131
190
 
132
- For [fx](sdk/fx.md), select a Gateway connection explicitly:
191
+ For [fx](sdk/fx.md), select a connection explicitly. This example uses Gateway:
133
192
 
134
193
  ```json
135
194
  {
@@ -141,6 +200,33 @@ For [fx](sdk/fx.md), select a Gateway connection explicitly:
141
200
 
142
201
  Use `fx({ model: "google/gemini-3.8-flash" })` in the agent's `harness` field. Native fx must select the supplied environment credential instead of a saved login. Gateway model and provider restrictions remain effective; catalog availability alone does not establish access for a particular team.
143
202
 
203
+ OpenCode and Copilot use the same explicit Gateway connection types under their own access keys. Cursor accepts an explicit `subscription` connection for the current `cursor-agent login`, or an `api-key` connection that defaults to `CURSOR_API_KEY`. This example selects the local login:
204
+
205
+ ```json
206
+ {
207
+ "access": {
208
+ "opencode": [{ "type": "vercel-api-key" }],
209
+ "copilot": [{ "type": "vercel-oidc", "project": ".", "envFile": ".env.local" }],
210
+ "cursor": [{ "type": "subscription" }]
211
+ }
212
+ }
213
+ ```
214
+
215
+ Gateway is optional for OpenCode and Copilot. For example, select direct Anthropic access in OpenCode and a native GitHub login in Copilot:
216
+
217
+ ```json
218
+ {
219
+ "access": {
220
+ "opencode": [{ "type": "api-key", "provider": "anthropic", "env": "ANTHROPIC_API_KEY" }],
221
+ "copilot": [{ "type": "subscription" }]
222
+ }
223
+ }
224
+ ```
225
+
226
+ Copilot also supports explicit GitHub tokens and OpenAI, Anthropic, or Azure BYOK keys. fx supports its own saved Vercel, Codex, or Grok login and keys for native named Chat Completions connections. Model identifiers and required setup depend on the selected route.
227
+
228
+ See the [OpenCode](sdk/opencode.md), [Copilot](sdk/copilot.md), and [Cursor](sdk/cursor.md) adapter contracts for native behavior.
229
+
144
230
  ## Tools and delegation
145
231
 
146
232
  Agents can expose validated [custom tools](sdk/tools.md) and declare [subagents](sdk/plugins/sub-agents.md). A managed parent invokes its child with the same CLI:
@@ -151,7 +237,7 @@ subharness run subagent:reviewer --cwd /path/to/worktree --prompt "Review the im
151
237
 
152
238
  The library supplies the parent context. Declaring `subagents` authorizes their invocation. Claude Code receives permissions scoped to its session launcher, declared children, and coordination commands; native approval rules and sandbox restrictions still apply. Children receive explicit task context rather than a copy of the parent's transcript. Cancellation propagates through delegated descendants; a parent task completes after child results and its own continuation are handled.
153
239
 
154
- This checkout includes six [repository roles](sdk/project-team.md): architecture and design with Astra, implementation with Sol, independent review with Opus, research with Gemini, the integrator with Sol, and visual and shader work with Fable 5.1 through fx. Reusable techniques live in `.agents/skills/` and are read only when the task needs them; a new technique does not require another agent definition. Discover the roles with `subharness list` and invoke them through `repo:<name>`. Skill loading, native capability limits, and the image-task execution path are documented with the team.
240
+ This checkout includes nine [repository roles](sdk/project-team.md): the Astra `architect` and `planner`; Sol `developer` and `integrator`; Luna `api-researcher` and `systems-researcher`; Opus `reviewer`; Gemini `researcher`; and Fable 5.1 `visual-engineer` through fx. Reusable techniques live in `.agents/skills/` and are read only when the task needs them; a new technique does not require another agent definition. Discover the roles with `subharness list` and invoke them through `repo:<name>`. Skill loading, native capability limits, and the image-task execution path are documented with the team.
155
241
 
156
242
  ## Boundaries
157
243
 
@@ -184,8 +270,8 @@ pnpm run check
184
270
  pnpm run check:package
185
271
  ```
186
272
 
187
- `pnpm run check` builds the current SDK first, then runs TypeScript checking and behavioral tests. Building first lets repository agent definitions import the package's current exports on a clean checkout and prevents tests from exercising stale build output. Tests cover native protocol boundaries, credential selection, worktrees, task queues, cancellation races, nested CLI execution, and process lifetime.
273
+ `pnpm run check` builds the current SDK first, then runs TypeScript checking and behavioral tests. Building first lets repository agent definitions import the package's current exports on a clean checkout and prevents tests from exercising stale build output. Tests cover native protocol boundaries, credential selection, worktrees, task queues, cancellation races, nested CLI execution, and process lifetime. The default suite runs two test files at a time to keep subprocess-heavy protocol fixtures within their timing budgets.
188
274
 
189
- `pnpm run check:package` packs the SDK and verifies installation, imports, and CLI agent discovery in a temporary consumer project. It downloads dependencies from the public npm registry with install lifecycle scripts disabled and does not run a coding model.
275
+ `pnpm run check:package` packs the SDK and verifies installation, public types, embedded execution, shared CLI/SDK sessions, the chat-tool example, and CLI agent discovery in a temporary consumer project. It downloads dependencies from the public npm registry with install lifecycle scripts disabled and uses a fake native harness without calling a coding model.
190
276
 
191
277
  The [SDK documentation](sdk/index.md) describes the final contracts. [AGENTS.md](AGENTS.md) defines the project's documentation, TDD, and review workflow. Comparative research and review evidence are kept in the ignored `.context` directory.
@@ -4,7 +4,10 @@ export declare class ClaudeProcessOwner {
4
4
  private child?;
5
5
  private exited?;
6
6
  private closing?;
7
+ private exitCode;
8
+ private exitSignal;
7
9
  readonly spawn: (options: SpawnOptions) => SpawnedProcess;
10
+ exitDescription(): string;
8
11
  close(): Promise<void>;
9
12
  private stop;
10
13
  }
@@ -1,5 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { AgentError } from '../errors.js';
3
+ import { processExitDescription } from '../process-diagnostics.js';
3
4
  const gracefulExitMs = 2_000;
4
5
  const terminatedExitMs = 5_000;
5
6
  const killedExitMs = 1_000;
@@ -14,11 +15,14 @@ export class ClaudeProcessOwner {
14
15
  child;
15
16
  exited;
16
17
  closing;
18
+ exitCode;
19
+ exitSignal;
17
20
  spawn = (options) => {
18
21
  if (this.child)
19
22
  throw new AgentError('HARNESS_FAILED', 'Claude Code attempted to start more than one native process.');
20
23
  const child = spawn(options.command, options.args, {
21
24
  cwd: options.cwd,
25
+ // The SDK accepts an exact environment replacement; framework augmentations may incorrectly require NODE_ENV.
22
26
  env: options.env,
23
27
  signal: options.signal,
24
28
  stdio: ['pipe', 'pipe', 'ignore'],
@@ -26,14 +30,27 @@ export class ClaudeProcessOwner {
26
30
  });
27
31
  this.child = child;
28
32
  this.exited = new Promise(resolve => {
29
- child.once('exit', () => resolve());
33
+ child.once('exit', (code, signal) => {
34
+ this.exitCode = code;
35
+ this.exitSignal = signal;
36
+ resolve();
37
+ });
30
38
  child.once('error', () => { if (child.pid === undefined)
31
39
  resolve(); });
32
40
  });
33
41
  return child;
34
42
  };
43
+ exitDescription() {
44
+ return processExitDescription(this.exitCode, this.exitSignal);
45
+ }
35
46
  close() {
36
- return this.closing ??= this.stop();
47
+ if (!this.closing) {
48
+ this.closing = this.stop().catch(error => {
49
+ this.closing = undefined;
50
+ throw error;
51
+ });
52
+ }
53
+ return this.closing;
37
54
  }
38
55
  async stop() {
39
56
  const child = this.child;
@@ -1 +1 @@
1
- {"version":3,"file":"claude-process.js","sourceRoot":"","sources":["../../src/adapters/claude-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA4B,MAAM,oBAAoB,CAAC;AAGrE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,YAAY,GAAG,KAAK,CAAC;AAE3B,SAAS,WAAW,CAAC,MAAqB,EAAE,OAAe;IACzD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAC,OAAO,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,MAAM,OAAO,kBAAkB;IACrB,KAAK,CAA+C;IACpD,MAAM,CAAiB;IACvB,OAAO,CAAiB;IAEvB,KAAK,GAAG,CAAC,OAAqB,EAAkB,EAAE;QACzD,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAC,8DAA8D,CAAC,CAAC;QACtH,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAC,OAAO,CAAC,IAAI,EAAC;YAC/C,GAAG,EAAC,OAAO,CAAC,GAAG;YACf,GAAG,EAAC,OAAO,CAAC,GAAG;YACf,MAAM,EAAC,OAAO,CAAC,MAAM;YACrB,KAAK,EAAC,CAAC,MAAM,EAAC,MAAM,EAAC,QAAQ,CAAC;YAC9B,WAAW,EAAC,IAAI;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAClC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,KAAK;QACH,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO;QAC9B,IAAI,CAAC;YAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,sCAAsC,CAAC,CAAC;QAC3E,IAAI,MAAM,WAAW,CAAC,MAAM,EAAC,cAAc,CAAC;YAAE,OAAO;QACrD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,IAAI,MAAM,WAAW,CAAC,MAAM,EAAC,gBAAgB,CAAC;YAAE,OAAO;QACvD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,IAAI,MAAM,WAAW,CAAC,MAAM,EAAC,YAAY,CAAC;YAAE,OAAO;QACnD,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAC,yDAAyD,CAAC,CAAC;IACnG,CAAC;CACF","sourcesContent":["import { spawn, type ChildProcessByStdio } from 'node:child_process';\nimport type { Readable, Writable } from 'node:stream';\nimport type { SpawnedProcess, SpawnOptions } from '@anthropic-ai/claude-agent-sdk';\nimport { AgentError } from '../errors.js';\n\nconst gracefulExitMs = 2_000;\nconst terminatedExitMs = 5_000;\nconst killedExitMs = 1_000;\n\nfunction exitsWithin(exited: Promise<void>, timeout: number): Promise<boolean> {\n return new Promise(resolve => {\n const timer = setTimeout(() => resolve(false),timeout);\n exited.then(() => { clearTimeout(timer); resolve(true); });\n });\n}\n\n/** Retains the concrete SDK subprocess until its termination is confirmed. */\nexport class ClaudeProcessOwner {\n private child?: ChildProcessByStdio<Writable,Readable,null>;\n private exited?: Promise<void>;\n private closing?: Promise<void>;\n\n readonly spawn = (options: SpawnOptions): SpawnedProcess => {\n if (this.child) throw new AgentError('HARNESS_FAILED','Claude Code attempted to start more than one native process.');\n const child = spawn(options.command,options.args,{\n cwd:options.cwd,\n env:options.env,\n signal:options.signal,\n stdio:['pipe','pipe','ignore'],\n windowsHide:true,\n });\n this.child = child;\n this.exited = new Promise(resolve => {\n child.once('exit',() => resolve());\n child.once('error',() => { if (child.pid === undefined) resolve(); });\n });\n return child;\n };\n\n close(): Promise<void> {\n return this.closing ??= this.stop();\n }\n\n private async stop(): Promise<void> {\n const child = this.child;\n const exited = this.exited;\n if (!child || !exited) return;\n try { child.stdin.end(); } catch { /* Continue to bounded termination. */ }\n if (await exitsWithin(exited,gracefulExitMs)) return;\n child.kill('SIGTERM');\n if (await exitsWithin(exited,terminatedExitMs)) return;\n child.kill('SIGKILL');\n if (await exitsWithin(exited,killedExitMs)) return;\n throw new AgentError('HARNESS_FAILED','Claude Code process termination could not be confirmed.');\n }\n}\n"]}
1
+ {"version":3,"file":"claude-process.js","sourceRoot":"","sources":["../../src/adapters/claude-process.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, type ChildProcessByStdio } from 'node:child_process';\nimport type { Readable, Writable } from 'node:stream';\nimport type { SpawnedProcess, SpawnOptions } from '@anthropic-ai/claude-agent-sdk';\nimport { AgentError } from '../errors.js';\nimport { processExitDescription } from '../process-diagnostics.js';\n\nconst gracefulExitMs = 2_000;\nconst terminatedExitMs = 5_000;\nconst killedExitMs = 1_000;\n\nfunction exitsWithin(exited: Promise<void>, timeout: number): Promise<boolean> {\n return new Promise(resolve => {\n const timer = setTimeout(() => resolve(false),timeout);\n exited.then(() => { clearTimeout(timer); resolve(true); });\n });\n}\n\n/** Retains the concrete SDK subprocess until its termination is confirmed. */\nexport class ClaudeProcessOwner {\n private child?: ChildProcessByStdio<Writable,Readable,null>;\n private exited?: Promise<void>;\n private closing?: Promise<void>;\n private exitCode: unknown;\n private exitSignal: unknown;\n\n readonly spawn = (options: SpawnOptions): SpawnedProcess => {\n if (this.child) throw new AgentError('HARNESS_FAILED','Claude Code attempted to start more than one native process.');\n const child = spawn(options.command,options.args,{\n cwd:options.cwd,\n // The SDK accepts an exact environment replacement; framework augmentations may incorrectly require NODE_ENV.\n env:options.env as NodeJS.ProcessEnv,\n signal:options.signal,\n stdio:['pipe','pipe','ignore'],\n windowsHide:true,\n });\n this.child = child;\n this.exited = new Promise(resolve => {\n child.once('exit',(code,signal) => {\n this.exitCode = code;\n this.exitSignal = signal;\n resolve();\n });\n child.once('error',() => { if (child.pid === undefined) resolve(); });\n });\n return child;\n };\n\n exitDescription(): string {\n return processExitDescription(this.exitCode,this.exitSignal);\n }\n\n close(): Promise<void> {\n if (!this.closing) {\n this.closing = this.stop().catch(error => {\n this.closing = undefined;\n throw error;\n });\n }\n return this.closing;\n }\n\n private async stop(): Promise<void> {\n const child = this.child;\n const exited = this.exited;\n if (!child || !exited) return;\n try { child.stdin.end(); } catch { /* Continue to bounded termination. */ }\n if (await exitsWithin(exited,gracefulExitMs)) return;\n child.kill('SIGTERM');\n if (await exitsWithin(exited,terminatedExitMs)) return;\n child.kill('SIGKILL');\n if (await exitsWithin(exited,killedExitMs)) return;\n throw new AgentError('HARNESS_FAILED','Claude Code process termination could not be confirmed.');\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { AgentError } from "../errors.js";
2
+ export declare function claudeResultFailure(subtype: unknown): AgentError;
@@ -0,0 +1,22 @@
1
+ import { AgentError } from "../errors.js";
2
+ export function claudeResultFailure(subtype) {
3
+ let message;
4
+ switch (subtype) {
5
+ case "error_max_turns":
6
+ message = "Claude Code reached its native turn limit.";
7
+ break;
8
+ case "error_max_budget_usd":
9
+ message = "Claude Code reached its native budget limit.";
10
+ break;
11
+ case "error_max_structured_output_retries":
12
+ message = "Claude Code exhausted its native structured-output retries.";
13
+ break;
14
+ case "error_during_execution":
15
+ message = "Claude Code reported an error during execution.";
16
+ break;
17
+ default:
18
+ message = "Claude Code could not complete the turn.";
19
+ }
20
+ return new AgentError("HARNESS_ERROR", message);
21
+ }
22
+ //# sourceMappingURL=claude-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-result.js","sourceRoot":"","sources":["../../src/adapters/claude-result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,IAAI,OAAe,CAAC;IACpB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,iBAAiB;YACpB,OAAO,GAAG,4CAA4C,CAAC;YACvD,MAAM;QACR,KAAK,sBAAsB;YACzB,OAAO,GAAG,8CAA8C,CAAC;YACzD,MAAM;QACR,KAAK,qCAAqC;YACxC,OAAO,GAAG,6DAA6D,CAAC;YACxE,MAAM;QACR,KAAK,wBAAwB;YAC3B,OAAO,GAAG,iDAAiD,CAAC;YAC5D,MAAM;QACR;YACE,OAAO,GAAG,0CAA0C,CAAC;IACzD,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import { AgentError } from \"../errors.js\";\n\nexport function claudeResultFailure(subtype: unknown): AgentError {\n let message: string;\n switch (subtype) {\n case \"error_max_turns\":\n message = \"Claude Code reached its native turn limit.\";\n break;\n case \"error_max_budget_usd\":\n message = \"Claude Code reached its native budget limit.\";\n break;\n case \"error_max_structured_output_retries\":\n message = \"Claude Code exhausted its native structured-output retries.\";\n break;\n case \"error_during_execution\":\n message = \"Claude Code reported an error during execution.\";\n break;\n default:\n message = \"Claude Code could not complete the turn.\";\n }\n return new AgentError(\"HARNESS_ERROR\", message);\n}\n"]}
@@ -1,8 +1,12 @@
1
- import { type Options } from "@anthropic-ai/claude-agent-sdk";
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import type { ExecutionDefinition } from "./types.js";
3
+ import type { ClaudeToolDescriptor } from "./claude-worker-protocol.js";
3
4
  /** The native process may stop before host-side JavaScript callbacks finish. */
4
5
  export declare function claudeTools(agent: Pick<ExecutionDefinition, "tools">): {
5
- config: Pick<Options, "allowedTools" | "mcpServers">;
6
+ namespace: string;
7
+ descriptors: ClaudeToolDescriptor[];
8
+ allowedTools: string[];
9
+ call(name: string, input: unknown): Promise<CallToolResult>;
6
10
  start(): void;
7
11
  stopAdmission(): void;
8
12
  drain(): Promise<void>;
@@ -1,5 +1,4 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
3
2
  import { AgentError } from "../errors.js";
4
3
  import { executeTool, toolJsonSchema } from "../sdk/tools.js";
5
4
  import { mcpToolContent } from "./mcp-tool-content.js";
@@ -9,9 +8,19 @@ export function claudeTools(agent) {
9
8
  let accepting = false;
10
9
  const definitions = Object.entries(agent.tools ?? {});
11
10
  const namespace = `subharness_${randomUUID().replaceAll("-", "")}`;
12
- const tools = definitions.map(([name, definition]) => {
13
- toolJsonSchema(definition);
14
- return tool(name, definition.description, definition.inputSchema.shape, async (input) => {
11
+ const descriptors = definitions.map(([name, definition]) => ({
12
+ name,
13
+ description: definition.description,
14
+ inputSchema: toolJsonSchema(definition),
15
+ }));
16
+ return {
17
+ namespace,
18
+ descriptors,
19
+ allowedTools: definitions.map(([name]) => `mcp__${namespace}__${name}`),
20
+ async call(name, input) {
21
+ const definition = agent.tools?.[name];
22
+ if (!definition)
23
+ return errorResult("The custom tool is unavailable.");
15
24
  if (!accepting)
16
25
  return errorResult("The task is not accepting tool calls.");
17
26
  const execution = executeTool(definition, input);
@@ -25,14 +34,7 @@ export function claudeTools(agent) {
25
34
  finally {
26
35
  pending.delete(execution);
27
36
  }
28
- });
29
- });
30
- const config = definitions.length === 0 ? {} : {
31
- mcpServers: { [namespace]: createSdkMcpServer({ name: namespace, version: "1.0.0", tools }) },
32
- allowedTools: definitions.map(([name]) => `mcp__${namespace}__${name}`),
33
- };
34
- return {
35
- config,
37
+ },
36
38
  start() { accepting = true; },
37
39
  stopAdmission() { accepting = false; },
38
40
  async drain() {
@@ -1 +1 @@
1
- {"version":3,"file":"claude-tools.js","sourceRoot":"","sources":["../../src/adapters/claude-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAgB,MAAM,gCAAgC,CAAC;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,KAAyC;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,cAAc,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;IACnE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE;QACnD,cAAc,CAAC,UAAU,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACpF,IAAI,CAAC,SAAS;gBAAE,OAAO,WAAW,CAAC,uCAAuC,CAAC,CAAC;YAC5E,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACvB,IAAI,CAAC;gBACH,OAAO,EAAC,OAAO,EAAE,cAAc,CAAC,MAAM,SAAS,CAAC,EAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,WAAW,CAAC,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;YAC9F,CAAC;oBAAS,CAAC;gBACT,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAiD,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3F,UAAU,EAAE,EAAC,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAC,CAAC,EAAC;QACzF,YAAY,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,QAAQ,SAAS,KAAK,IAAI,EAAE,CAAC;KACxE,CAAC;IACF,OAAO;QACL,MAAM;QACN,KAAK,KAAK,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;QAC7B,aAAa,KAAK,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;QACtC,KAAK,CAAC,KAAK;YACT,SAAS,GAAG,KAAK,CAAC;YAClB,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,MAAe,EAAE,IAAI,EAAC,CAAC,EAAC,CAAC;AACnE,CAAC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createSdkMcpServer, tool, type Options } from \"@anthropic-ai/claude-agent-sdk\";\nimport { AgentError } from \"../errors.js\";\nimport { executeTool, toolJsonSchema } from \"../sdk/tools.js\";\nimport type { ExecutionDefinition } from \"./types.js\";\nimport { mcpToolContent } from \"./mcp-tool-content.js\";\n\n/** The native process may stop before host-side JavaScript callbacks finish. */\nexport function claudeTools(agent: Pick<ExecutionDefinition, \"tools\">) {\n const pending = new Set<Promise<unknown>>();\n let accepting = false;\n const definitions = Object.entries(agent.tools ?? {});\n const namespace = `subharness_${randomUUID().replaceAll(\"-\", \"\")}`;\n const tools = definitions.map(([name, definition]) => {\n toolJsonSchema(definition);\n return tool(name, definition.description, definition.inputSchema.shape, async input => {\n if (!accepting) return errorResult(\"The task is not accepting tool calls.\");\n const execution = executeTool(definition, input);\n pending.add(execution);\n try {\n return {content: mcpToolContent(await execution)};\n } catch (error) {\n return errorResult(error instanceof AgentError ? error.message : \"The custom tool failed.\");\n } finally {\n pending.delete(execution);\n }\n });\n });\n const config: Pick<Options, \"mcpServers\" | \"allowedTools\"> = definitions.length === 0 ? {} : {\n mcpServers: {[namespace]: createSdkMcpServer({name: namespace, version: \"1.0.0\", tools})},\n allowedTools: definitions.map(([name]) => `mcp__${namespace}__${name}`),\n };\n return {\n config,\n start() { accepting = true; },\n stopAdmission() { accepting = false; },\n async drain() {\n accepting = false;\n await Promise.allSettled([...pending]);\n },\n };\n}\n\nfunction errorResult(text: string) {\n return {isError: true, content: [{type: \"text\" as const, text}]};\n}\n"]}
1
+ {"version":3,"file":"claude-tools.js","sourceRoot":"","sources":["../../src/adapters/claude-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,KAAyC;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,cAAc,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;IACnE,MAAM,WAAW,GAA2B,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;QACnF,IAAI;QACJ,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,WAAW,EAAE,cAAc,CAAC,UAAU,CAAC;KACxC,CAAC,CAAC,CAAC;IACJ,OAAO;QACL,SAAS;QACT,WAAW;QACX,YAAY,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,QAAQ,SAAS,KAAK,IAAI,EAAE,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,KAAc;YACrC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,UAAU;gBAAE,OAAO,WAAW,CAAC,iCAAiC,CAAC,CAAC;YACvE,IAAI,CAAC,SAAS;gBAAE,OAAO,WAAW,CAAC,uCAAuC,CAAC,CAAC;YAC5E,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACvB,IAAI,CAAC;gBACH,OAAO,EAAC,OAAO,EAAE,cAAc,CAAC,MAAM,SAAS,CAAC,EAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,WAAW,CAAC,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;YAC9F,CAAC;oBAAS,CAAC;gBACT,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QACD,KAAK,KAAK,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;QAC7B,aAAa,KAAK,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;QACtC,KAAK,CAAC,KAAK;YACT,SAAS,GAAG,KAAK,CAAC;YAClB,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,MAAe,EAAE,IAAI,EAAC,CAAC,EAAC,CAAC;AACnE,CAAC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { AgentError } from \"../errors.js\";\nimport { executeTool, toolJsonSchema } from \"../sdk/tools.js\";\nimport type { ExecutionDefinition } from \"./types.js\";\nimport { mcpToolContent } from \"./mcp-tool-content.js\";\nimport type { ClaudeToolDescriptor } from \"./claude-worker-protocol.js\";\n\n/** The native process may stop before host-side JavaScript callbacks finish. */\nexport function claudeTools(agent: Pick<ExecutionDefinition, \"tools\">) {\n const pending = new Set<Promise<unknown>>();\n let accepting = false;\n const definitions = Object.entries(agent.tools ?? {});\n const namespace = `subharness_${randomUUID().replaceAll(\"-\", \"\")}`;\n const descriptors: ClaudeToolDescriptor[] = definitions.map(([name, definition]) => ({\n name,\n description: definition.description,\n inputSchema: toolJsonSchema(definition),\n }));\n return {\n namespace,\n descriptors,\n allowedTools: definitions.map(([name]) => `mcp__${namespace}__${name}`),\n async call(name: string, input: unknown): Promise<CallToolResult> {\n const definition = agent.tools?.[name];\n if (!definition) return errorResult(\"The custom tool is unavailable.\");\n if (!accepting) return errorResult(\"The task is not accepting tool calls.\");\n const execution = executeTool(definition, input);\n pending.add(execution);\n try {\n return {content: mcpToolContent(await execution)};\n } catch (error) {\n return errorResult(error instanceof AgentError ? error.message : \"The custom tool failed.\");\n } finally {\n pending.delete(execution);\n }\n },\n start() { accepting = true; },\n stopAdmission() { accepting = false; },\n async drain() {\n accepting = false;\n await Promise.allSettled([...pending]);\n },\n };\n}\n\nfunction errorResult(text: string): CallToolResult {\n return {isError: true, content: [{type: \"text\" as const, text}]};\n}\n"]}
@@ -0,0 +1,49 @@
1
+ import type { Writable } from "node:stream";
2
+ import type { ClaudeDependencies } from "./claude.js";
3
+ import type { ClaudeWorkerFactory } from "./claude-worker-protocol.js";
4
+ export declare class ClaudeWorkerMessageStream<T> implements AsyncIterable<T> {
5
+ private readonly values;
6
+ private readonly waiting;
7
+ private ended;
8
+ private failure?;
9
+ push(value: T): void;
10
+ end(): void;
11
+ fail(error: unknown): void;
12
+ [Symbol.asyncIterator](): AsyncIterator<T>;
13
+ }
14
+ export declare function writeClaudeProcessInput(stream: Writable, chunk: Buffer): Promise<void>;
15
+ export declare function endClaudeProcessInput(stream: Writable): Promise<void>;
16
+ export interface ClaudeWorkerClientOptions {
17
+ readonly createWorker?: ClaudeWorkerFactory;
18
+ }
19
+ export declare class ClaudeWorkerClient {
20
+ private readonly worker;
21
+ private readonly pending;
22
+ private readonly hostRequests;
23
+ private readonly messages;
24
+ private nextId;
25
+ private terminalFailure?;
26
+ private expectedExit;
27
+ private disposed?;
28
+ private bridge?;
29
+ private processOwner?;
30
+ private spawnAbort?;
31
+ private spawned?;
32
+ private canUseTool?;
33
+ private onElicitation?;
34
+ constructor(env: NodeJS.ProcessEnv, options?: ClaudeWorkerClientOptions);
35
+ readonly dependencies: ClaudeDependencies;
36
+ private forwardInput;
37
+ private queryProxy;
38
+ private request;
39
+ private postEvent;
40
+ private onMessage;
41
+ private postHostResponse;
42
+ private handleHostRequest;
43
+ private writeProcess;
44
+ private endProcessInput;
45
+ private readProcess;
46
+ private fail;
47
+ private terminate;
48
+ dispose(): Promise<void>;
49
+ }