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.
- package/README.md +95 -9
- package/dist/adapters/claude-process.d.ts +3 -0
- package/dist/adapters/claude-process.js +19 -2
- package/dist/adapters/claude-process.js.map +1 -1
- package/dist/adapters/claude-result.d.ts +2 -0
- package/dist/adapters/claude-result.js +22 -0
- package/dist/adapters/claude-result.js.map +1 -0
- package/dist/adapters/claude-tools.d.ts +6 -2
- package/dist/adapters/claude-tools.js +14 -12
- package/dist/adapters/claude-tools.js.map +1 -1
- package/dist/adapters/claude-worker-client.d.ts +49 -0
- package/dist/adapters/claude-worker-client.js +359 -0
- package/dist/adapters/claude-worker-client.js.map +1 -0
- package/dist/adapters/claude-worker-process.d.ts +20 -0
- package/dist/adapters/claude-worker-process.js +76 -0
- package/dist/adapters/claude-worker-process.js.map +1 -0
- package/dist/adapters/claude-worker-protocol.d.ts +38 -0
- package/dist/adapters/claude-worker-protocol.js +2 -0
- package/dist/adapters/claude-worker-protocol.js.map +1 -0
- package/dist/adapters/claude-worker.d.ts +1 -0
- package/dist/adapters/claude-worker.js +126 -0
- package/dist/adapters/claude-worker.js.map +1 -0
- package/dist/adapters/claude.d.ts +16 -6
- package/dist/adapters/claude.js +164 -75
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex.js +4 -1
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/copilot-permissions.d.ts +26 -0
- package/dist/adapters/copilot-permissions.js +121 -0
- package/dist/adapters/copilot-permissions.js.map +1 -0
- package/dist/adapters/copilot-tools.d.ts +12 -0
- package/dist/adapters/copilot-tools.js +61 -0
- package/dist/adapters/copilot-tools.js.map +1 -0
- package/dist/adapters/copilot.d.ts +108 -0
- package/dist/adapters/copilot.js +819 -0
- package/dist/adapters/copilot.js.map +1 -0
- package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
- package/dist/adapters/cursor-cli-approvals.js +83 -0
- package/dist/adapters/cursor-cli-approvals.js.map +1 -0
- package/dist/adapters/cursor-cli-model.d.ts +4 -0
- package/dist/adapters/cursor-cli-model.js +64 -0
- package/dist/adapters/cursor-cli-model.js.map +1 -0
- package/dist/adapters/cursor-cli-session.d.ts +32 -0
- package/dist/adapters/cursor-cli-session.js +316 -0
- package/dist/adapters/cursor-cli-session.js.map +1 -0
- package/dist/adapters/cursor-cli-tools.d.ts +20 -0
- package/dist/adapters/cursor-cli-tools.js +181 -0
- package/dist/adapters/cursor-cli-tools.js.map +1 -0
- package/dist/adapters/cursor-cli.d.ts +2 -0
- package/dist/adapters/cursor-cli.js +306 -0
- package/dist/adapters/cursor-cli.js.map +1 -0
- package/dist/adapters/cursor-model.d.ts +7 -0
- package/dist/adapters/cursor-model.js +67 -0
- package/dist/adapters/cursor-model.js.map +1 -0
- package/dist/adapters/cursor-rpc.d.ts +42 -0
- package/dist/adapters/cursor-rpc.js +271 -0
- package/dist/adapters/cursor-rpc.js.map +1 -0
- package/dist/adapters/cursor-session.d.ts +4 -0
- package/dist/adapters/cursor-session.js +327 -0
- package/dist/adapters/cursor-session.js.map +1 -0
- package/dist/adapters/cursor-startup.d.ts +16 -0
- package/dist/adapters/cursor-startup.js +74 -0
- package/dist/adapters/cursor-startup.js.map +1 -0
- package/dist/adapters/cursor-tools.d.ts +11 -0
- package/dist/adapters/cursor-tools.js +49 -0
- package/dist/adapters/cursor-tools.js.map +1 -0
- package/dist/adapters/cursor.d.ts +6 -0
- package/dist/adapters/cursor.js +17 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/fx-auth.d.ts +12 -2
- package/dist/adapters/fx-auth.js +51 -61
- package/dist/adapters/fx-auth.js.map +1 -1
- package/dist/adapters/fx-profile.d.ts +8 -0
- package/dist/adapters/fx-profile.js +101 -0
- package/dist/adapters/fx-profile.js.map +1 -0
- package/dist/adapters/fx-rpc.d.ts +2 -0
- package/dist/adapters/fx-rpc.js +30 -4
- package/dist/adapters/fx-rpc.js.map +1 -1
- package/dist/adapters/fx-status.d.ts +2 -0
- package/dist/adapters/fx-status.js +96 -0
- package/dist/adapters/fx-status.js.map +1 -0
- package/dist/adapters/fx.js +28 -12
- package/dist/adapters/fx.js.map +1 -1
- package/dist/adapters/opencode-access.d.ts +13 -0
- package/dist/adapters/opencode-access.js +76 -0
- package/dist/adapters/opencode-access.js.map +1 -0
- package/dist/adapters/opencode-config.d.ts +11 -0
- package/dist/adapters/opencode-config.js +238 -0
- package/dist/adapters/opencode-config.js.map +1 -0
- package/dist/adapters/opencode-http.d.ts +28 -0
- package/dist/adapters/opencode-http.js +297 -0
- package/dist/adapters/opencode-http.js.map +1 -0
- package/dist/adapters/opencode-tools.d.ts +19 -0
- package/dist/adapters/opencode-tools.js +127 -0
- package/dist/adapters/opencode-tools.js.map +1 -0
- package/dist/adapters/opencode.d.ts +2 -0
- package/dist/adapters/opencode.js +569 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/adapters/rpc.d.ts +3 -0
- package/dist/adapters/rpc.js +45 -4
- package/dist/adapters/rpc.js.map +1 -1
- package/dist/adapters/types.d.ts +5 -0
- package/dist/adapters/types.js.map +1 -1
- package/dist/approvals/types.d.ts +1 -1
- package/dist/approvals/types.js.map +1 -1
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +4 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/catalog-worker.js.map +1 -1
- package/dist/cli/dashboard-client.js +20 -31
- package/dist/cli/dashboard-client.js.map +1 -1
- package/dist/cli/dashboard-controller.d.ts +7 -0
- package/dist/cli/dashboard-controller.js +27 -0
- package/dist/cli/dashboard-controller.js.map +1 -0
- package/dist/cli/dashboard-detail-view.d.ts +1 -1
- package/dist/cli/dashboard-detail-view.js +3 -4
- package/dist/cli/dashboard-detail-view.js.map +1 -1
- package/dist/cli/dashboard-history-view.js.map +1 -1
- package/dist/cli/dashboard-input.d.ts +1 -1
- package/dist/cli/dashboard-input.js +11 -1
- package/dist/cli/dashboard-input.js.map +1 -1
- package/dist/cli/dashboard-layout.js +3 -3
- package/dist/cli/dashboard-layout.js.map +1 -1
- package/dist/cli/dashboard-renderer.js.map +1 -1
- package/dist/cli/dashboard-style.js +2 -2
- package/dist/cli/dashboard-style.js.map +1 -1
- package/dist/cli/dashboard.d.ts +2 -2
- package/dist/cli/dashboard.js +7 -32
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +30 -12
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +55 -43
- package/dist/cli/main.js.map +1 -1
- package/dist/config/access-provenance.d.ts +10 -0
- package/dist/config/access-provenance.js +31 -0
- package/dist/config/access-provenance.js.map +1 -0
- package/dist/config/access.d.ts +12 -2
- package/dist/config/access.js +125 -27
- package/dist/config/access.js.map +1 -1
- package/dist/config/loader.js +1 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/oidc.js +4 -2
- package/dist/config/oidc.js.map +1 -1
- package/dist/config/project.js +2 -1
- package/dist/config/project.js.map +1 -1
- package/dist/config/resolve-access.js +24 -10
- package/dist/config/resolve-access.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/process-diagnostics.d.ts +2 -0
- package/dist/process-diagnostics.js +11 -0
- package/dist/process-diagnostics.js.map +1 -0
- package/dist/runtime/access-errors.d.ts +4 -0
- package/dist/runtime/access-errors.js +25 -0
- package/dist/runtime/access-errors.js.map +1 -0
- package/dist/runtime/approval-registry.d.ts +9 -0
- package/dist/runtime/approval-registry.js +144 -5
- package/dist/runtime/approval-registry.js.map +1 -1
- package/dist/runtime/capture.d.ts +22 -0
- package/dist/runtime/capture.js +320 -0
- package/dist/runtime/capture.js.map +1 -0
- package/dist/runtime/catalog.d.ts +39 -0
- package/dist/runtime/catalog.js +84 -0
- package/dist/runtime/catalog.js.map +1 -0
- package/dist/runtime/client.d.ts +2 -1
- package/dist/runtime/client.js +74 -47
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +44 -7
- package/dist/runtime/coordinator.js +265 -31
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/daemon.js +12 -6
- package/dist/runtime/daemon.js.map +1 -1
- package/dist/runtime/dashboard-sanitize.d.ts +2 -0
- package/dist/runtime/dashboard-sanitize.js +7 -0
- package/dist/runtime/dashboard-sanitize.js.map +1 -0
- package/dist/runtime/dashboard-workspace.js +5 -1
- package/dist/runtime/dashboard-workspace.js.map +1 -1
- package/dist/runtime/dashboard.d.ts +1 -1
- package/dist/runtime/dashboard.js +2 -3
- package/dist/runtime/dashboard.js.map +1 -1
- package/dist/runtime/definition.d.ts +6 -1
- package/dist/runtime/definition.js +42 -2
- package/dist/runtime/definition.js.map +1 -1
- package/dist/runtime/native-owner.js +7 -1
- package/dist/runtime/native-owner.js.map +1 -1
- package/dist/runtime/pagination.d.ts +9 -0
- package/dist/runtime/pagination.js +36 -0
- package/dist/runtime/pagination.js.map +1 -0
- package/dist/runtime/sdk-observation.d.ts +8 -0
- package/dist/runtime/sdk-observation.js +75 -0
- package/dist/runtime/sdk-observation.js.map +1 -0
- package/dist/runtime/sdk-projection.d.ts +8 -0
- package/dist/runtime/sdk-projection.js +19 -0
- package/dist/runtime/sdk-projection.js.map +1 -0
- package/dist/runtime/sdk-service.d.ts +11 -0
- package/dist/runtime/sdk-service.js +109 -0
- package/dist/runtime/sdk-service.js.map +1 -0
- package/dist/runtime/select-native.d.ts +4 -1
- package/dist/runtime/select-native.js +28 -13
- package/dist/runtime/select-native.js.map +1 -1
- package/dist/runtime/service.d.ts +11 -1
- package/dist/runtime/service.js +178 -3
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/session-launcher.js +1 -1
- package/dist/runtime/session-launcher.js.map +1 -1
- package/dist/runtime/snapshots.d.ts +103 -0
- package/dist/runtime/snapshots.js +124 -0
- package/dist/runtime/snapshots.js.map +1 -0
- package/dist/runtime/state.d.ts +26 -0
- package/dist/runtime/state.js +86 -17
- package/dist/runtime/state.js.map +1 -1
- package/dist/runtime/task-data.d.ts +38 -0
- package/dist/runtime/task-data.js +2 -0
- package/dist/runtime/task-data.js.map +1 -0
- package/dist/runtime/transport.d.ts +13 -0
- package/dist/runtime/transport.js +166 -0
- package/dist/runtime/transport.js.map +1 -0
- package/dist/runtime/types.d.ts +28 -9
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/worker-client.js +40 -13
- package/dist/runtime/worker-client.js.map +1 -1
- package/dist/runtime/worker-server.js +105 -24
- package/dist/runtime/worker-server.js.map +1 -1
- package/dist/sdk/connected-types.d.ts +42 -0
- package/dist/sdk/connected-types.js +2 -0
- package/dist/sdk/connected-types.js.map +1 -0
- package/dist/sdk/connected.d.ts +3 -0
- package/dist/sdk/connected.js +232 -0
- package/dist/sdk/connected.js.map +1 -0
- package/dist/sdk/definitions.d.ts +4 -1
- package/dist/sdk/definitions.js +16 -2
- package/dist/sdk/definitions.js.map +1 -1
- package/dist/sdk/execution-definition.d.ts +3 -0
- package/dist/sdk/execution-definition.js +59 -0
- package/dist/sdk/execution-definition.js.map +1 -0
- package/dist/sdk/execution-driver.d.ts +6 -0
- package/dist/sdk/execution-driver.js +86 -0
- package/dist/sdk/execution-driver.js.map +1 -0
- package/dist/sdk/execution-observation.d.ts +3 -0
- package/dist/sdk/execution-observation.js +35 -0
- package/dist/sdk/execution-observation.js.map +1 -0
- package/dist/sdk/execution-options.d.ts +17 -0
- package/dist/sdk/execution-options.js +115 -0
- package/dist/sdk/execution-options.js.map +1 -0
- package/dist/sdk/execution-types.d.ts +72 -0
- package/dist/sdk/execution-types.js +2 -0
- package/dist/sdk/execution-types.js.map +1 -0
- package/dist/sdk/execution.d.ts +3 -0
- package/dist/sdk/execution.js +3 -0
- package/dist/sdk/execution.js.map +1 -0
- package/dist/sdk/hosted-error.d.ts +3 -0
- package/dist/sdk/hosted-error.js +14 -0
- package/dist/sdk/hosted-error.js.map +1 -0
- package/dist/sdk/hosted.d.ts +23 -0
- package/dist/sdk/hosted.js +204 -0
- package/dist/sdk/hosted.js.map +1 -0
- package/dist/sdk/permission-validation.js +10 -1
- package/dist/sdk/permission-validation.js.map +1 -1
- package/dist/sdk/runner.d.ts +5 -0
- package/dist/sdk/runner.js +107 -0
- package/dist/sdk/runner.js.map +1 -0
- package/dist/sdk/tools.js +4 -1
- package/dist/sdk/tools.js.map +1 -1
- package/dist/sdk/types.d.ts +29 -1
- package/dist/sdk/types.js.map +1 -1
- package/package.json +6 -3
- package/sdk/access-config.md +60 -4
- package/sdk/adapter-contract.md +13 -3
- package/sdk/additional-harnesses.md +43 -0
- package/sdk/agent-skill.md +2 -2
- package/sdk/agent.md +6 -4
- package/sdk/approvals.md +5 -1
- package/sdk/authentication.md +1 -1
- package/sdk/cli/dashboard-design.md +6 -0
- package/sdk/cli/index.md +9 -6
- package/sdk/cli/output.md +3 -1
- package/sdk/completion-notifications.md +2 -0
- package/sdk/config.md +1 -1
- package/sdk/copilot.md +53 -0
- package/sdk/cursor.md +68 -0
- package/sdk/diagnostics.md +29 -0
- package/sdk/distribution.md +5 -3
- package/sdk/evals.md +1 -1
- package/sdk/examples/chat-tool.ts +126 -0
- package/sdk/execution.md +743 -0
- package/sdk/fx.md +50 -5
- package/sdk/harnesses.md +13 -13
- package/sdk/index.md +45 -20
- package/sdk/message-delivery.md +2 -0
- package/sdk/opencode.md +57 -0
- package/sdk/permissions.md +6 -2
- package/sdk/plugins/sub-agents.md +4 -2
- package/sdk/project-team.md +25 -1
- package/sdk/sessions.md +2 -0
- package/sdk/tools.md +4 -2
- package/sdk/v1-runtime.md +8 -6
- package/dist/cli/catalog.d.ts +0 -26
- package/dist/cli/catalog.js +0 -41
- 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&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
|
|
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
|
|
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
|
|
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 `
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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', () =>
|
|
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
|
-
|
|
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;
|
|
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,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 {
|
|
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
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
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;
|
|
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
|
+
}
|