@hydraharness/harness-subagent-claude-code 0.1.1-rc.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # @hydraharness/harness-subagent-claude-code
2
+
3
+ This package registers a Profile-named Claude Code subagent provider whose default name is `claude-code`. Each accepted run invokes the official Claude Agent SDK in the delegating Session's workspace, lets the pinned SDK select its installed platform CLI, submits one self-contained text task, and returns either the strict final answer or a separate safe failure diagnostic through the shared [`@hydraharness/harness-subagent`](../subagent/README.md) result contract.
4
+
5
+ ## Start and ownership
6
+
7
+ `start(request)` accepts only a non-empty sequence of text blocks and derives the child cwd from the parent Session. It creates one private `AbortController`, calls the official SDK `query()`, and publishes the run only after the SDK's `spawnClaudeCodeProcess` hook has supplied a live CLI handle owned by [`@hydraharness/harness-subprocess`](../../subprocess/subprocess/README.md). A failure or cancellation before publication closes the query, terminates any acquired process tree, waits for it to exit, and rejects `start()`.
8
+
9
+ The SDK receives the exact concatenated text task. The provider iterates the complete SDK message stream and accepts only a `result` message with `subtype: "success"`, `is_error: false`, and a nonblank `result`, followed by normal iterator completion. Every failure still maps to `error`: the four error subtypes in Agent SDK 0.3.220 retain their exact category, an error-marked or blank success becomes `invalid-success`, a missing result becomes `missing-result`, an unclassified query failure becomes `unknown`, and an early CLI exit becomes `process-exit`. The diagnostic also names the current `query-start`, `query-run`, `process`, or `teardown` stage and independently includes an observed exit code and signal. The provider produces neither `max-tokens` nor `refusal`.
10
+
11
+ Local cancellation wins the result race and maps to `aborted` without a failure diagnostic. `dispose()` is idempotent: it aborts the run, asks the SDK query to close, invokes the shared process-tree termination escalation, and waits for whole-tree exit. SDK graceful close expresses protocol intent; the subprocess handle remains the authority for process quiescence. Startup and teardown rejections expose the same fixed safe stage and process facts through their Error message, while the original product or Host error remains on the internal cause chain and in the Provider's Host log. Result failure and independent teardown failure remain separate.
12
+
13
+ ## Native settings and interaction
14
+
15
+ The provider deliberately omits the SDK `settingSources` option. The official SDK therefore reads the host's normal user, project, and local Claude settings relative to the parent Session cwd, including native account state and product configuration. The provider neither copies nor filters those files and does not create or modify login state. The Profile-selected `permissionMode` is the one query-level override: Claude Code still owns its settings and sandbox, while the selected native mode decides how this unattended query handles permission checks.
16
+
17
+ Each query sets `persistSession: false` and disables `AskUserQuestion`. Except in bypass mode, `canUseTool` immediately denies requests that still require human approval. Plan mode also places `ExitPlanMode` in the SDK's `disallowedTools`, so native settings cannot pre-approve a transition back to execution and the model must return the completed plan as its final answer. MCP elicitation is declined, the known refusal fallback dialog is cancelled, and undeclared dialog kinds use the SDK's no-dialog failure behavior. These decisions never wait for a user interface. When both facts contribute to a failed run, `SubagentResult.diagnostic` contains the structured failure line first and the latest safe permission decision second; the shared result boundary limits the complete text to 4096 UTF-8 bytes. Successful and locally cancelled runs expose neither captured fact.
18
+
19
+ ## Capabilities and context
20
+
21
+ The provider advertises no optional start-time capabilities and reports `inheritsParentContext: false`. Claude Code receives the standalone text task and the parent Session cwd, but not the parent conversation, persona, tool filter, depth policy, or structured-output contract. Every run has an independent SDK query, cancellation controller, CLI process, and non-persisted product session.
22
+
23
+ ## Configuration
24
+
25
+ | Key | Default | Meaning |
26
+ |---|---|---|
27
+ | `providerName` | `claude-code` | Non-empty registry name on `ctx.subagents`; each mounted instance needs a unique value. |
28
+ | `env` | `{}` | Explicit SDK/CLI environment layered over the shared credential-scrubbed parent environment. |
29
+ | `permissionMode` | `dontAsk` | Native non-interactive permission policy fixed for every run from this Provider instance. |
30
+ | `disposeGraceMs` | `3000` | Positive finite grace in milliseconds, no greater than [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.md), between the shared process-tree owner's termination tiers; disposal then waits for whole-tree exit. |
31
+
32
+ | `permissionMode` value | Native behavior |
33
+ |---|---|
34
+ | `dontAsk` | Deny operations that are not already authorized instead of prompting. |
35
+ | `acceptEdits` | Accept file edits; any remaining permission prompt is denied by the unattended callback. |
36
+ | `auto` | Let Claude Code's native classifier allow or deny permission requests. |
37
+ | `plan` | Run in native planning mode, deny execution approval, and return the completed plan as the final answer. |
38
+ | `bypassPermissions` | Explicitly set the SDK's dangerous confirmation and bypass permission checks. |
39
+
40
+ Production omits `pathToClaudeCodeExecutable`, so Agent SDK 0.3.220 selects the matching native `claude` or `claude.exe` from its own platform package and passes that absolute command through the custom-spawn hook to `@hydraharness/harness-subprocess`. The provider does not inspect `PATH`, implement platform selection, or fall back to a host `claude`. Native settings and authentication remain authoritative, while `permissionMode` is the only query-level policy override. The plugin does not select a model, create a product home, log in, or probe an account. Credential-shaped ambient variables are removed before the explicit `env` overlay is applied, so an API key or token intended for the child must be supplied there. Non-credential endpoint variables such as `ANTHROPIC_BASE_URL`, along with ordinary ambient values such as `PATH` and `HOME`, remain inherited unless overridden; `PATH` does not choose the Claude executable.
41
+
42
+ This package is an optional Profile Bundle. Install it into the target Profile, then restart that Profile; installation brings the pinned Agent SDK and one compatible platform CLI payload into that Profile, while the declared `cordis.patch.yml` layer registers only the dormant `claude-code` Host provider and starts no Claude process. Removing the package withdraws that provider and its private runtime closure on the next Profile start.
43
+
44
+ ```sh
45
+ hydra plugin --profile <name> add @hydraharness/harness-subagent-claude-code
46
+ hydra plugin --profile <name> remove @hydraharness/harness-subagent-claude-code
47
+ hydra --profile <name>
48
+ ```
49
+
50
+ Installation controls Host availability, not model permission. The Bundle supplies the dormant default `claude-code` row; the Profile may replace that row's complete config or mount additional rows with distinct `providerName`, `permissionMode`, and `env` values. Loading an instance starts no Claude process until a bound tool calls it. Each `@hydraharness/harness-tool-subagent` row names one provider and needs its own `toolName`, so the model sees static tools rather than a dynamic provider selector. Full Agent Presets carry a matching default product tool row with `disabled: true`; copy a preset and remove that field to expose `subagent_claude_code` only to agents composed from the copy. Its `one-shot` policy keeps omitted or `false` `run_in_background` calls in the foreground, while explicit `true` returns a parent-owned Job id for `job_output` or `job_kill`. The base host and full presets already provide the generic Job registry and controls.
51
+
52
+ The standalone composition below shows the complete explicit capability. A Profile based on `@hydraharness/harness-base` keeps its existing Job rows, adds the product provider and tool rows, and does not mount duplicate Job services.
53
+
54
+ ```yaml
55
+ - id: subagent-claude-safe
56
+ name: '@hydraharness/harness-subagent-claude-code'
57
+ config:
58
+ providerName: claude-safe
59
+ permissionMode: dontAsk
60
+ env:
61
+ ANTHROPIC_API_KEY: !!js process.env.ANTHROPIC_API_KEY
62
+
63
+ - id: subagent-claude-bypass
64
+ name: '@hydraharness/harness-subagent-claude-code'
65
+ config:
66
+ providerName: claude-bypass
67
+ permissionMode: bypassPermissions
68
+ env:
69
+ ANTHROPIC_API_KEY: !!js process.env.ANTHROPIC_API_KEY
70
+ ```
71
+
72
+ ```yaml
73
+ - id: jobs
74
+ name: '@hydraharness/harness-jobs-local'
75
+
76
+ - id: tool-jobs
77
+ name: '@hydraharness/harness-tool-jobs'
78
+
79
+ - id: tool-subagent-claude-safe
80
+ name: '@hydraharness/harness-tool-subagent'
81
+ disabled: true
82
+ config:
83
+ provider: claude-safe
84
+ toolName: subagent_claude_safe
85
+ backgroundMode: one-shot
86
+ maxDepth: provider-managed
87
+
88
+ - id: tool-subagent-claude-bypass
89
+ name: '@hydraharness/harness-tool-subagent'
90
+ config:
91
+ provider: claude-bypass
92
+ toolName: subagent_claude_bypass
93
+ backgroundMode: one-shot
94
+ maxDepth: provider-managed
95
+ ```
96
+
97
+ ## Product compatibility and evidence
98
+
99
+ The runtime dependency is pinned to `@anthropic-ai/claude-agent-sdk@0.3.220`, whose eight platform packages carry Claude Code 2.1.220. A normal install selects one payload for the current OS, CPU, and Linux libc. For the current darwin-arm64 payload, `npm pack --dry-run --json` reports 74,858,812 packed bytes and 256,908,856 unpacked bytes; other platforms may differ, and these values are disclosure rather than an installation threshold. The keyless real-product test runs the SDK-selected CLI against a loopback Messages fixture and asserts that the shared subprocess argv begins with that platform package's native executable. Loader composition proves that installing the Bundle registers only the dormant Claude Code provider and starts no product process.
100
+
101
+ Installing with optional dependencies omitted, using an unsupported platform, or losing the selected payload leaves provider registration dormant but makes the first delegation fail at the SDK startup boundary. The caller receives the safe `query-start` / `unknown` failure fact; the native payload error remains only on the internal cause chain and in the Provider's Host log. The provider neither probes a host CLI nor retries with one.
102
+
103
+ Loader composition proves that the Bundle default, two additional named Claude instances, and the existing Codex package coexist without starting either product.
104
+
105
+ The project owner's identity-scoped distribution authorization covers the official SDK and the official CLI/platform payloads declared by each SDK version. [`THIRD_PARTY_NOTICES.md`](../../../THIRD_PARTY_NOTICES.md) discloses the current optional payload closure without classifying its declared terms as permissive; unrelated non-permissive runtime dependencies continue to fail the notices gate.
106
+
107
+ ## Model Experience
108
+
109
+ ### Child request
110
+
111
+ #### What the model sees
112
+
113
+ The Claude Code child receives the standalone text task as one fresh SDK query. Its workspace is the parent Session cwd; its model, system instructions, tools, sandbox, and authentication come from native Claude settings, the selected Provider instance's Profile configuration fixes the query's environment and non-interactive permission mode, and the executable version comes from the Bundle's pinned SDK platform payload.
114
+
115
+ #### Token effect
116
+
117
+ The child pays for an independent Claude Code context and query. Child tokens do not enter the parent's context.
118
+
119
+ #### KV Cache effect
120
+
121
+ Independent of the parent request cache. Reuse depends only on Claude Code's own model, instructions, tools, native settings, and fresh query.
122
+
123
+ ### Parent scheduling and results, indirectly
124
+
125
+ #### What the model sees
126
+
127
+ Through `@hydraharness/harness-tool-subagent`, a foreground call gives the parent the strict final Claude Code answer or an error containing the stop reason and optional safe diagnostic for a non-completed result. That diagnostic can distinguish the fixed SDK error category, lifecycle stage, and observed process outcome without copying raw product text. A background call first returns a Job id; the generic job controls later deliver a completion notice, expose the same final answer or failed status detail through `job_output`, and let `job_kill` request cancellation. Claude Code reasoning, tool activity, intermediate messages, stderr, workspace diffs, usage, product ids, tool inputs, and raw protocol payloads are not copied into the parent Session.
128
+
129
+ #### Token effect
130
+
131
+ Foreground input grows by the retained final answer or error. Background input also includes the start acknowledgement, completion notice, and any `job_output`, `job_kill`, or later status results; child tokens still do not enter the parent context. This provider adds no parent tool schema by itself.
132
+
133
+ #### KV Cache effect
134
+
135
+ Append-only: foreground adds one result after the reusable parent prefix, while background appends the Job acknowledgement, notice, and later control or collection results. Background scheduling can add a notice-driven turn, but none of these messages rewrites the earlier prefix.
136
+
137
+ ## Known Limitations and Deferred Work
138
+
139
+ - **One fresh query and process per run** — there is no continuation, resume, pooling, progress stream, or product-session persistence.
140
+ - **Static instance selection** — Profile rows fix provider names and tool bindings; calls cannot choose a provider dynamically, and every exposed tool needs a unique `toolName`.
141
+ - **Host settings are intentionally authoritative** — project and user settings can change model, tools, and behavior; the provider does not provide a filtered or hermetic production mode.
142
+ - **Authentication and account state remain native** — the Bundle supplies the CLI but does not create an account, log in, or rewrite Claude settings; configuration and authentication failures surface with their lifecycle stage and the safe `unknown` fallback rather than a separate public classification.
143
+ - **The SDK platform payload is required at delegation time** — installs that omit optional dependencies, unsupported platforms, and missing or damaged payloads fail at the first query; there is no host-CLI fallback.
144
+ - **No human interaction path** — `AskUserQuestion` is disabled, permission prompts are denied, MCP elicitation is declined, and blocking dialogs fail closed instead of suspending.
145
+ - **Assistant payload is final text only** — a failed run may additionally expose the separate safe diagnostic; reasoning, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain product-local, while generic Job ids, notices, and status come from the shared job runtime.
146
+ - **No optional shared capabilities** — output schemas, child personas, tool filtering, and harness depth enforcement are rejected by the shared service for this provider.
147
+ - **No wall-clock timeout or side-effect rollback** — the caller cancels long work, and files or external systems changed before cancellation are not restored.
@@ -0,0 +1,6 @@
1
+ # This optional Profile layer registers the dormant Claude Code provider. Agent
2
+ # presets separately decide whether one session receives its delegation tool.
3
+
4
+ - insert:
5
+ - id: subagent-claude-code
6
+ name: '@hydraharness/harness-subagent-claude-code'
package/lib/index.js ADDED
@@ -0,0 +1,563 @@
1
+ import z from "@hydraharness/schemastery";
2
+ import { MAX_TIMER_DELAY_MS } from "@hydraharness/harness-timeout";
3
+ import { NO_START_CAPABILITIES, assertPositiveFinite, resolveChildCwd, settleRunResult, subprocessRunHandle } from "@hydraharness/harness-subagent";
4
+ import { randomUUID } from "node:crypto";
5
+ import { query } from "@anthropic-ai/claude-agent-sdk";
6
+ import { SessionId } from "@hydraharness/harness-session";
7
+ import { scrubbedParentEnv } from "@hydraharness/harness-subprocess";
8
+ import { EventEmitter } from "node:events";
9
+ //#region lib/types/process.js
10
+ /**
11
+ * Projection from the shared managed-process handle to the official Claude
12
+ * Agent SDK's custom-spawn process interface.
13
+ *
14
+ * @module @hydraharness/harness-subagent-claude-code/process
15
+ */
16
+ function thrown$1(value) {
17
+ /* v8 ignore next -- the subprocess seam rejects with Error. */
18
+ return value instanceof Error ? value : new Error(String(value));
19
+ }
20
+ /**
21
+ * Encode the SDK's complete child environment as a subprocess overlay.
22
+ * @param env - SDK-composed child environment after its removals and replacements.
23
+ * @returns explicit values plus tombstones for surviving ambient names the SDK removed.
24
+ */
25
+ function sdkEnvironmentOverlay(env) {
26
+ const overlay = { ...env };
27
+ for (const name of Object.keys(scrubbedParentEnv())) if (!(name in env)) overlay[name] = void 0;
28
+ return overlay;
29
+ }
30
+ /**
31
+ * Translate one official SDK spawn request to the shared process owner.
32
+ * @param options - command, arguments, workspace, environment, and forwarded signal from the SDK.
33
+ * @param graceMs - process-tree termination grace.
34
+ * @returns the fully explicit shared subprocess request.
35
+ */
36
+ function claudeSpawnSpec(options, graceMs) {
37
+ if (options.cwd === void 0 || options.cwd.length === 0) throw new Error("subagent-claude-code: SDK spawn request omitted its workspace");
38
+ return {
39
+ argv: [options.command, ...options.args],
40
+ cwd: options.cwd,
41
+ stdio: {
42
+ stdin: "pipe",
43
+ stdout: "pipe",
44
+ stderr: "inherit"
45
+ },
46
+ graceMs,
47
+ signal: options.signal,
48
+ env: sdkEnvironmentOverlay(options.env)
49
+ };
50
+ }
51
+ /**
52
+ * SDK-facing view of one shared managed process. Protocol transport remains
53
+ * in the official SDK; this adapter only projects streams and exit events.
54
+ */
55
+ var ManagedClaudeCodeProcess = class {
56
+ child;
57
+ stdin;
58
+ stdout;
59
+ events = new EventEmitter();
60
+ outcomeValue;
61
+ killRequested = false;
62
+ /**
63
+ * Project a managed process with piped stdin and stdout.
64
+ * @param child - shared handle that remains the process-tree authority.
65
+ */
66
+ constructor(child) {
67
+ this.child = child;
68
+ this.stdin = child.stdin;
69
+ this.stdout = child.stdout;
70
+ this.events.on("error", () => {});
71
+ child.done.then((outcome) => {
72
+ this.outcomeValue = outcome;
73
+ this.events.emit("exit", outcome.exitCode, outcome.signal);
74
+ }, (error) => {
75
+ this.events.emit("error", thrown$1(error));
76
+ });
77
+ }
78
+ /** Whether the SDK has requested managed tree termination. */
79
+ get killed() {
80
+ return this.killRequested;
81
+ }
82
+ /** Direct-child exit code, or null while running or after signal exit. */
83
+ get exitCode() {
84
+ return this.outcomeValue?.exitCode ?? null;
85
+ }
86
+ /** Direct-child terminating signal, if any. */
87
+ get signalCode() {
88
+ return this.outcomeValue?.signal ?? null;
89
+ }
90
+ /** Exact managed-process outcome after exit, or undefined while running. */
91
+ get outcome() {
92
+ return this.outcomeValue;
93
+ }
94
+ /**
95
+ * Route the SDK's termination request to the tree-scoped process owner.
96
+ * @param _signal - SDK-selected signal; the shared seam owns its escalation ladder.
97
+ * @returns false only after exit or a previous termination request.
98
+ */
99
+ kill(_signal) {
100
+ if (this.killRequested || this.outcomeValue !== void 0) return false;
101
+ this.killRequested = true;
102
+ this.child.terminate();
103
+ return true;
104
+ }
105
+ /** Register a persistent process lifecycle listener. */
106
+ on(event, listener) {
107
+ this.events.on(event, listener);
108
+ }
109
+ /** Register a one-shot process lifecycle listener. */
110
+ once(event, listener) {
111
+ this.events.once(event, listener);
112
+ }
113
+ /** Remove a process lifecycle listener. */
114
+ off(event, listener) {
115
+ this.events.off(event, listener);
116
+ }
117
+ };
118
+ //#endregion
119
+ //#region lib/types/run.js
120
+ /**
121
+ * One-shot Claude Code lifecycle: invoke the official Agent SDK, place its
122
+ * real CLI process under the shared subprocess owner, map only strict SDK
123
+ * success to completion, and dispose to whole-tree quiescence.
124
+ *
125
+ * @module @hydraharness/harness-subagent-claude-code/run
126
+ */
127
+ /** Default POSIX grace between subprocess termination tiers. */
128
+ const DEFAULT_DISPOSE_GRACE_MS = 3e3;
129
+ /** Claude Code permission modes that cannot wait for a human response. */
130
+ const CLAUDE_CODE_PERMISSION_MODES = [
131
+ "dontAsk",
132
+ "acceptEdits",
133
+ "auto",
134
+ "plan",
135
+ "bypassPermissions"
136
+ ];
137
+ /** Safe default for unattended Claude Code runs. */
138
+ const DEFAULT_CLAUDE_CODE_PERMISSION_MODE = "dontAsk";
139
+ const SUPPORTED_UNATTENDED_DIALOG_KINDS = ["refusal_fallback_prompt"];
140
+ function failureDiagnostic(facts) {
141
+ const fields = [
142
+ "product: Claude Code",
143
+ `stage: ${facts.stage}`,
144
+ `category: ${facts.category}`
145
+ ];
146
+ const exitCode = facts.outcome?.exitCode;
147
+ if (exitCode !== null && exitCode !== void 0) fields.push(`exit code: ${exitCode}`);
148
+ const signal = facts.outcome?.signal;
149
+ if (signal !== null && signal !== void 0) fields.push(`signal: ${signal}`);
150
+ return `Product subagent failure (${fields.join("; ")})`;
151
+ }
152
+ var ClaudeCodeFailure = class extends Error {
153
+ facts;
154
+ constructor(facts, cause) {
155
+ super(`subagent-claude-code: ${failureDiagnostic(facts)}`, cause === void 0 ? void 0 : { cause });
156
+ this.facts = facts;
157
+ this.name = "ClaudeCodeFailure";
158
+ }
159
+ };
160
+ function sdkFailureCategory(subtype) {
161
+ switch (subtype) {
162
+ case "error_during_execution":
163
+ case "error_max_turns":
164
+ case "error_max_budget_usd":
165
+ case "error_max_structured_output_retries": return subtype;
166
+ default: return "unknown";
167
+ }
168
+ }
169
+ /**
170
+ * Hide an unpublished product startup failure behind fixed safe facts.
171
+ * @param cause - original host-side failure retained only on the Error cause chain.
172
+ * @returns a rejection safe to expose through the subagent start boundary.
173
+ */
174
+ function claudeCodeStartupFailure(cause) {
175
+ return new ClaudeCodeFailure({
176
+ stage: "query-start",
177
+ category: "unknown"
178
+ }, cause);
179
+ }
180
+ function unattendedDiagnostic(mode, request, decision, reason) {
181
+ return `Claude Code unattended decision (mode: ${mode}; request: ${request}; decision: ${decision}): ${reason}`;
182
+ }
183
+ function thrown(value) {
184
+ /* v8 ignore next -- typed SDK and subprocess failures reject with Error. */
185
+ return value instanceof Error ? value : new Error(String(value));
186
+ }
187
+ /** Read live request cancellation across awaited startup cleanup. */
188
+ function isAborted(signal) {
189
+ return signal.aborted;
190
+ }
191
+ /**
192
+ * Validate and preserve the one-shot task before crossing the SDK boundary.
193
+ * @param prompt - task content accepted from the shared subagent service.
194
+ * @returns the exact text sequence as one SDK prompt.
195
+ */
196
+ function textTask(prompt) {
197
+ if (prompt.length === 0) throw new Error("subagent-claude-code: the one-shot task must contain only text blocks");
198
+ const texts = [];
199
+ for (const block of prompt) {
200
+ if (block.type !== "text") throw new Error("subagent-claude-code: the one-shot task must contain only text blocks");
201
+ texts.push(block.text);
202
+ }
203
+ if (texts.every((text) => text.trim().length === 0)) throw new Error("subagent-claude-code: the one-shot task must not be empty");
204
+ return texts.join("");
205
+ }
206
+ /**
207
+ * Strictly derive the only SDK result that can complete a shared run.
208
+ * @param message - an official discriminated result union.
209
+ * @returns exact final text for a successful, non-error result.
210
+ */
211
+ function successfulResult(message) {
212
+ if (message.subtype !== "success") {
213
+ const category = sdkFailureCategory(message.subtype);
214
+ const detail = category === "unknown" ? void 0 : message.errors.join("; ");
215
+ throw new ClaudeCodeFailure({
216
+ stage: "query-run",
217
+ category
218
+ }, detail === void 0 || detail.length === 0 ? void 0 : new Error(detail));
219
+ }
220
+ if (message.is_error || message.result.trim().length === 0) throw new ClaudeCodeFailure({
221
+ stage: "query-run",
222
+ category: "invalid-success"
223
+ });
224
+ return message.result;
225
+ }
226
+ /**
227
+ * Consume the complete SDK stream and require one strict success plus normal
228
+ * iterator completion.
229
+ * @param query - published official SDK query.
230
+ * @param onPermissionDenied - records a safe fact when the SDK reports native denial.
231
+ * @param onResult - records that the SDK supplied a terminal result message.
232
+ * @returns the completed shared result.
233
+ */
234
+ async function consumeClaudeQuery(query, onPermissionDenied, onResult) {
235
+ let answer;
236
+ for await (const message of query) {
237
+ if (message.type === "system" && message.subtype === "permission_denied") {
238
+ onPermissionDenied?.();
239
+ continue;
240
+ }
241
+ if (message.type !== "result") continue;
242
+ onResult?.();
243
+ answer = successfulResult(message);
244
+ }
245
+ if (answer === void 0) throw new ClaudeCodeFailure({
246
+ stage: "query-run",
247
+ category: "missing-result"
248
+ });
249
+ return {
250
+ output: [{
251
+ type: "text",
252
+ text: answer
253
+ }],
254
+ stopReason: "completed"
255
+ };
256
+ }
257
+ /**
258
+ * Close the official query, terminate the managed process tree, and wait for
259
+ * the subprocess owner to prove it is gone.
260
+ * @param query - official SDK query, when creation reached that point.
261
+ * @param child - live shared-service handle that owns the CLI process tree;
262
+ * spawn-failed handles settle at the startup boundary instead.
263
+ */
264
+ async function disposeClaudeCodeChild(query, child) {
265
+ const failures = [];
266
+ try {
267
+ query?.close();
268
+ } catch (error) {
269
+ failures.push(thrown(error));
270
+ }
271
+ child.terminate();
272
+ try {
273
+ await child.waitForExit();
274
+ } catch (error) {
275
+ failures.push(thrown(error));
276
+ }
277
+ const outcome = await child.done;
278
+ const firstFailure = failures[0];
279
+ if (firstFailure !== void 0) throw new ClaudeCodeFailure({
280
+ stage: "teardown",
281
+ category: "unknown",
282
+ outcome
283
+ }, failures.length === 1 ? firstFailure : new AggregateError(failures, "Claude Code teardown failures"));
284
+ }
285
+ /**
286
+ * Build the fixed official SDK options for one one-shot provider run.
287
+ * @param spec - Workspace, environment, process service, and disposal policy.
288
+ * @param controller - per-run cancellation owner.
289
+ * @param capture - receives the shared child and SDK-facing process synchronously.
290
+ * @param captureDiagnostic - receives safe facts from unattended interaction callbacks.
291
+ * @returns options that inherit native settings while disabling persistence and user questions.
292
+ */
293
+ function claudeQueryOptions(spec, controller, capture, captureDiagnostic) {
294
+ return {
295
+ abortController: controller,
296
+ cwd: spec.cwd,
297
+ env: {
298
+ ...scrubbedParentEnv(),
299
+ ...spec.env
300
+ },
301
+ persistSession: false,
302
+ disallowedTools: spec.permissionMode === "plan" ? ["AskUserQuestion", "ExitPlanMode"] : ["AskUserQuestion"],
303
+ permissionMode: spec.permissionMode,
304
+ ...spec.permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : { canUseTool: () => {
305
+ captureDiagnostic(unattendedDiagnostic(spec.permissionMode, "tool permission", "denied", "the provider does not request human approval"));
306
+ return Promise.resolve({
307
+ behavior: "deny",
308
+ message: "This unattended Claude Code subagent cannot request human approval."
309
+ });
310
+ } },
311
+ onElicitation: () => {
312
+ captureDiagnostic(unattendedDiagnostic(spec.permissionMode, "MCP elicitation", "declined", "the provider does not collect interactive MCP input"));
313
+ return Promise.resolve({ action: "decline" });
314
+ },
315
+ onUserDialog: () => {
316
+ captureDiagnostic(unattendedDiagnostic(spec.permissionMode, "user dialog", "cancelled", "the provider does not render blocking dialogs"));
317
+ return Promise.resolve({ behavior: "cancelled" });
318
+ },
319
+ supportedDialogKinds: SUPPORTED_UNATTENDED_DIALOG_KINDS,
320
+ spawnClaudeCodeProcess: (options) => {
321
+ const child = spec.spawn(claudeSpawnSpec(options, spec.disposeGraceMs));
322
+ const process = new ManagedClaudeCodeProcess(child);
323
+ capture(child, process);
324
+ return process;
325
+ }
326
+ };
327
+ }
328
+ /**
329
+ * Start one official Claude Agent SDK query and publish its one-shot run.
330
+ * @param request - resolved shared subagent request.
331
+ * @param spec - Workspace, environment, process service, and diagnostic policy.
332
+ * @returns the published run after both Query and real CLI handle exist.
333
+ */
334
+ async function startClaudeCodeRun(request, spec) {
335
+ const prompt = textTask(request.prompt);
336
+ if (request.signal.aborted) throw new Error("subagent-claude-code: request was aborted before SDK startup");
337
+ const controller = new AbortController();
338
+ const requestCancel = () => {
339
+ if (!controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("subagent-claude-code: run cancelled locally"));
340
+ };
341
+ const onAbort = () => {
342
+ requestCancel();
343
+ };
344
+ request.signal.addEventListener("abort", onAbort, { once: true });
345
+ const reportFailure = (error) => {
346
+ try {
347
+ spec.onError?.(error, "error");
348
+ } catch {}
349
+ };
350
+ let child;
351
+ let query$1;
352
+ let managedProcess;
353
+ let diagnostic;
354
+ const capturePermissionDiagnostic = (value) => {
355
+ diagnostic = value;
356
+ };
357
+ const prependFailureDiagnostic = (facts) => {
358
+ const failure = failureDiagnostic(facts);
359
+ diagnostic = diagnostic === void 0 ? failure : `${failure}\n${diagnostic}`;
360
+ };
361
+ const captureChild = (captured, process) => {
362
+ child = captured;
363
+ managedProcess = process;
364
+ };
365
+ try {
366
+ query$1 = query({
367
+ prompt,
368
+ options: claudeQueryOptions(spec, controller, captureChild, capturePermissionDiagnostic)
369
+ });
370
+ if (child === void 0 || child.pid <= 0) throw new Error("subagent-claude-code: official SDK did not publish a controllable Claude Code process");
371
+ if (controller.signal.aborted) throw new Error("subagent-claude-code: request was aborted before SDK startup");
372
+ } catch (error) {
373
+ request.signal.removeEventListener("abort", onAbort);
374
+ const cancelledBeforeCleanup = controller.signal.aborted;
375
+ await Promise.resolve();
376
+ const startupFacts = {
377
+ stage: "query-start",
378
+ category: "unknown",
379
+ outcome: managedProcess?.outcome
380
+ };
381
+ const startupFailure = (cause = error) => new ClaudeCodeFailure(startupFacts, thrown(cause));
382
+ requestCancel();
383
+ if (child !== void 0 && child.pid <= 0) {
384
+ let closeError;
385
+ try {
386
+ query$1?.close();
387
+ } catch (disposeError) {
388
+ closeError = thrown(disposeError);
389
+ }
390
+ let spawnError = thrown(error);
391
+ try {
392
+ await child.done;
393
+ } catch (childError) {
394
+ spawnError = thrown(childError);
395
+ }
396
+ if (closeError !== void 0) {
397
+ const failure = startupFailure(spawnError);
398
+ const cleanupFailure = new ClaudeCodeFailure({
399
+ stage: "teardown",
400
+ category: "unknown"
401
+ }, closeError);
402
+ const aggregate = new AggregateError([failure, cleanupFailure], `${failure.message}; ${cleanupFailure.message}`);
403
+ reportFailure(aggregate);
404
+ throw aggregate;
405
+ }
406
+ if (cancelledBeforeCleanup || isAborted(request.signal)) throw new Error("subagent-claude-code: request was aborted before SDK startup");
407
+ const failure = startupFailure(spawnError);
408
+ reportFailure(failure);
409
+ throw failure;
410
+ }
411
+ if (child !== void 0) try {
412
+ await disposeClaudeCodeChild(query$1, child);
413
+ } catch (disposeError) {
414
+ const failure = startupFailure();
415
+ const cleanupFailure = thrown(disposeError);
416
+ const aggregate = new AggregateError([failure, cleanupFailure], `${failure.message}; ${cleanupFailure.message}`);
417
+ reportFailure(aggregate);
418
+ throw aggregate;
419
+ }
420
+ else if (query$1 !== void 0) try {
421
+ query$1.close();
422
+ } catch (disposeError) {
423
+ const failure = startupFailure();
424
+ const cleanupFailure = new ClaudeCodeFailure({
425
+ stage: "teardown",
426
+ category: "unknown"
427
+ }, thrown(disposeError));
428
+ const aggregate = new AggregateError([failure, cleanupFailure], `${failure.message}; ${cleanupFailure.message}`);
429
+ reportFailure(aggregate);
430
+ throw aggregate;
431
+ }
432
+ if (cancelledBeforeCleanup || isAborted(request.signal)) throw new Error("subagent-claude-code: request was aborted before SDK startup");
433
+ const failure = startupFailure();
434
+ reportFailure(failure);
435
+ throw failure;
436
+ }
437
+ const publishedQuery = query$1;
438
+ const publishedChild = child;
439
+ let receivedResult = false;
440
+ const result = settleRunResult({
441
+ attempt: async () => {
442
+ try {
443
+ return await consumeClaudeQuery(publishedQuery, () => {
444
+ capturePermissionDiagnostic(unattendedDiagnostic(spec.permissionMode, "tool permission", "denied", "Claude Code denied the request before an interactive prompt"));
445
+ }, () => {
446
+ receivedResult = true;
447
+ });
448
+ } catch (error) {
449
+ const processOutcome = managedProcess?.outcome;
450
+ let facts;
451
+ if (error instanceof ClaudeCodeFailure) facts = {
452
+ ...error.facts,
453
+ outcome: processOutcome
454
+ };
455
+ else if (processOutcome !== void 0 && !receivedResult) facts = {
456
+ stage: "process",
457
+ category: "process-exit",
458
+ outcome: processOutcome
459
+ };
460
+ else facts = {
461
+ stage: "query-run",
462
+ category: "unknown",
463
+ outcome: processOutcome
464
+ };
465
+ prependFailureDiagnostic(facts);
466
+ throw error instanceof ClaudeCodeFailure ? error : new ClaudeCodeFailure(facts, thrown(error));
467
+ }
468
+ },
469
+ collectOutput: () => [],
470
+ collectDiagnostic: () => diagnostic,
471
+ cancelled: () => controller.signal.aborted,
472
+ onError: spec.onError,
473
+ signal: request.signal,
474
+ onAbort
475
+ });
476
+ return subprocessRunHandle({
477
+ id: SessionId(randomUUID()),
478
+ result,
479
+ signal: request.signal,
480
+ onAbort,
481
+ requestCancel,
482
+ teardown: async () => {
483
+ try {
484
+ await disposeClaudeCodeChild(publishedQuery, publishedChild);
485
+ } catch (error) {
486
+ const failure = thrown(error);
487
+ reportFailure(failure);
488
+ throw failure;
489
+ }
490
+ }
491
+ });
492
+ }
493
+ //#endregion
494
+ //#region lib/types/index.js
495
+ /**
496
+ * Profile-named Claude Code one-shot subagent provider. Every accepted run
497
+ * invokes the official Agent SDK in the delegating Session's workspace and
498
+ * places the SDK-spawned real CLI under the shared subprocess owner.
499
+ *
500
+ * @module @hydraharness/harness-subagent-claude-code
501
+ */
502
+ const name = "subagent-claude-code";
503
+ const inject = ["subagents", "subprocess"];
504
+ const DEFAULT_PROVIDER_NAME = "claude-code";
505
+ const Config = z.object({
506
+ providerName: z.string().min(1).default(DEFAULT_PROVIDER_NAME),
507
+ env: z.dict(z.string()).default({}),
508
+ permissionMode: z.union([...CLAUDE_CODE_PERMISSION_MODES]).default(DEFAULT_CLAUDE_CODE_PERMISSION_MODE),
509
+ disposeGraceMs: z.number().default(DEFAULT_DISPOSE_GRACE_MS)
510
+ });
511
+ var ClaudeCodeProvider = class {
512
+ name;
513
+ ctx;
514
+ config;
515
+ capabilities = NO_START_CAPABILITIES;
516
+ inheritsParentContext = false;
517
+ constructor(name, ctx, config) {
518
+ this.name = name;
519
+ this.ctx = ctx;
520
+ this.config = config;
521
+ }
522
+ async start(request) {
523
+ const parentCwd = request.parent.session.header.cwd;
524
+ if (parentCwd === void 0) throw new Error("subagent-claude-code: no working directory for the child — delegate from a parent session that has one");
525
+ let cwd;
526
+ try {
527
+ cwd = resolveChildCwd("subagent-claude-code", void 0, parentCwd);
528
+ } catch (error) {
529
+ if (request.signal.aborted) throw new Error("subagent-claude-code: request was aborted before SDK startup");
530
+ const failure = claudeCodeStartupFailure(error);
531
+ this.ctx.logger.warn(`subagent-claude-code "${this.name}": child start failed: %o`, failure);
532
+ throw failure;
533
+ }
534
+ return startClaudeCodeRun(request, {
535
+ cwd,
536
+ permissionMode: this.config.permissionMode,
537
+ env: this.config.env,
538
+ disposeGraceMs: this.config.disposeGraceMs,
539
+ spawn: (spawnSpec) => this.ctx.subprocess.spawn(spawnSpec),
540
+ onError: (error, stopReason) => {
541
+ this.ctx.logger.warn(`subagent-claude-code "${this.name}": child run failed (${stopReason}): %o`, error);
542
+ }
543
+ });
544
+ }
545
+ };
546
+ /**
547
+ * Register one Profile-named Claude Code provider.
548
+ * @param ctx - context carrying shared subagent and subprocess services.
549
+ * @param config - registry name, permission mode, child environment, and disposal grace.
550
+ */
551
+ function apply(ctx, config) {
552
+ const resolved = {
553
+ providerName: config.providerName ?? DEFAULT_PROVIDER_NAME,
554
+ env: config.env,
555
+ permissionMode: config.permissionMode ?? "dontAsk",
556
+ disposeGraceMs: config.disposeGraceMs
557
+ };
558
+ assertPositiveFinite("subagent-claude-code", "disposeGraceMs", resolved.disposeGraceMs);
559
+ if (resolved.disposeGraceMs > MAX_TIMER_DELAY_MS) throw new Error(`subagent-claude-code: disposeGraceMs must be no greater than ${MAX_TIMER_DELAY_MS}`);
560
+ ctx.subagents.registerProvider(new ClaudeCodeProvider(resolved.providerName, ctx, resolved));
561
+ }
562
+ //#endregion
563
+ export { Config, apply, inject, name };
@@ -0,0 +1,24 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for
4
+ * `@hydraharness/harness-subagent-claude-code`.
5
+ * @module @hydraharness/harness-subagent-claude-code/invariant
6
+ */
7
+ const PACKAGE_NAME = "@hydraharness/harness-subagent-claude-code";
8
+ /** Cordis companion plugin name. */
9
+ const name = "subagent-claude-code-invariant";
10
+ /** Service required before the companion can reserve package ownership. */
11
+ const inject = ["invariants"];
12
+ /**
13
+ * No runtime invariant: lifecycle pairing belongs to the shared subagent
14
+ * service and process-tree ownership belongs to the subprocess service.
15
+ */
16
+ const install = () => {};
17
+ /**
18
+ * Register this package's invariant companion.
19
+ * @param ctx - plugin context carrying the invariant registry.
20
+ * @returns the installed registration's disposer.
21
+ */
22
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
23
+ //#endregion
24
+ export { apply, inject, name };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Profile-named Claude Code one-shot subagent provider. Every accepted run
3
+ * invokes the official Agent SDK in the delegating Session's workspace and
4
+ * places the SDK-spawned real CLI under the shared subprocess owner.
5
+ *
6
+ * @module @hydraharness/harness-subagent-claude-code
7
+ */
8
+ import type { Context } from '@hydraharness/cordis';
9
+ import z from '@hydraharness/schemastery';
10
+ import { type ClaudeCodePermissionMode } from './run.ts';
11
+ export declare const name = "subagent-claude-code";
12
+ export declare const inject: string[];
13
+ /** Deployment-owned permission, environment, and process-release settings. */
14
+ export interface Config {
15
+ /** Provider name on `ctx.subagents` (default `claude-code`). */
16
+ providerName?: string;
17
+ /**
18
+ * Explicit environment entries layered over the subprocess seam's
19
+ * credential-scrubbed parent environment.
20
+ */
21
+ env?: Record<string, string>;
22
+ /**
23
+ * Native non-interactive mode fixed for this Provider instance. Defaults to
24
+ * `dontAsk`; `acceptEdits` accepts edits, `auto` uses the native classifier,
25
+ * `plan` returns a plan without approving execution, and
26
+ * `bypassPermissions` explicitly skips permission checks.
27
+ */
28
+ permissionMode?: ClaudeCodePermissionMode;
29
+ /** Grace in milliseconds for Claude Code process-tree termination. */
30
+ disposeGraceMs?: number;
31
+ }
32
+ export declare const Config: z<Config>;
33
+ /**
34
+ * Register one Profile-named Claude Code provider.
35
+ * @param ctx - context carrying shared subagent and subprocess services.
36
+ * @param config - registry name, permission mode, child environment, and disposal grace.
37
+ */
38
+ export declare function apply(ctx: Context, config: Config): void;
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Package-owned invariant companion for
3
+ * `@hydraharness/harness-subagent-claude-code`.
4
+ * @module @hydraharness/harness-subagent-claude-code/invariant
5
+ */
6
+ import type { Context } from '@hydraharness/cordis';
7
+ /** Cordis companion plugin name. */
8
+ export declare const name = "subagent-claude-code-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ export declare const inject: string[];
11
+ /**
12
+ * Register this package's invariant companion.
13
+ * @param ctx - plugin context carrying the invariant registry.
14
+ * @returns the installed registration's disposer.
15
+ */
16
+ export declare const apply: (ctx: Context) => Promise<() => void>;
17
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Projection from the shared managed-process handle to the official Claude
3
+ * Agent SDK's custom-spawn process interface.
4
+ *
5
+ * @module @hydraharness/harness-subagent-claude-code/process
6
+ */
7
+ import type { SpawnedProcess, SpawnOptions } from '@anthropic-ai/claude-agent-sdk';
8
+ import { type SubprocessHandle, type SubprocessOutcome, type SubprocessSpawnSpec } from '@hydraharness/harness-subprocess';
9
+ /**
10
+ * Encode the SDK's complete child environment as a subprocess overlay.
11
+ * @param env - SDK-composed child environment after its removals and replacements.
12
+ * @returns explicit values plus tombstones for surviving ambient names the SDK removed.
13
+ */
14
+ export declare function sdkEnvironmentOverlay(env: SpawnOptions['env']): NodeJS.ProcessEnv;
15
+ /**
16
+ * Translate one official SDK spawn request to the shared process owner.
17
+ * @param options - command, arguments, workspace, environment, and forwarded signal from the SDK.
18
+ * @param graceMs - process-tree termination grace.
19
+ * @returns the fully explicit shared subprocess request.
20
+ */
21
+ export declare function claudeSpawnSpec(options: SpawnOptions, graceMs: number): SubprocessSpawnSpec;
22
+ /**
23
+ * SDK-facing view of one shared managed process. Protocol transport remains
24
+ * in the official SDK; this adapter only projects streams and exit events.
25
+ */
26
+ export declare class ManagedClaudeCodeProcess implements SpawnedProcess {
27
+ private readonly child;
28
+ readonly stdin: import("stream").Writable;
29
+ readonly stdout: import("stream").Readable;
30
+ private readonly events;
31
+ private outcomeValue;
32
+ private killRequested;
33
+ /**
34
+ * Project a managed process with piped stdin and stdout.
35
+ * @param child - shared handle that remains the process-tree authority.
36
+ */
37
+ constructor(child: SubprocessHandle);
38
+ /** Whether the SDK has requested managed tree termination. */
39
+ get killed(): boolean;
40
+ /** Direct-child exit code, or null while running or after signal exit. */
41
+ get exitCode(): number | null;
42
+ /** Direct-child terminating signal, if any. */
43
+ get signalCode(): NodeJS.Signals | null;
44
+ /** Exact managed-process outcome after exit, or undefined while running. */
45
+ get outcome(): SubprocessOutcome | undefined;
46
+ /**
47
+ * Route the SDK's termination request to the tree-scoped process owner.
48
+ * @param _signal - SDK-selected signal; the shared seam owns its escalation ladder.
49
+ * @returns false only after exit or a previous termination request.
50
+ */
51
+ kill(_signal: NodeJS.Signals): boolean;
52
+ /** Register a persistent process lifecycle listener. */
53
+ on(event: 'exit' | 'error', listener: ((code: number | null, signal: NodeJS.Signals | null) => void) | ((error: Error) => void)): void;
54
+ /** Register a one-shot process lifecycle listener. */
55
+ once(event: 'exit' | 'error', listener: ((code: number | null, signal: NodeJS.Signals | null) => void) | ((error: Error) => void)): void;
56
+ /** Remove a process lifecycle listener. */
57
+ off(event: 'exit' | 'error', listener: ((code: number | null, signal: NodeJS.Signals | null) => void) | ((error: Error) => void)): void;
58
+ }
59
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1,87 @@
1
+ /**
2
+ * One-shot Claude Code lifecycle: invoke the official Agent SDK, place its
3
+ * real CLI process under the shared subprocess owner, map only strict SDK
4
+ * success to completion, and dispose to whole-tree quiescence.
5
+ *
6
+ * @module @hydraharness/harness-subagent-claude-code/run
7
+ */
8
+ import { type Options, type Query, type SDKMessage, type SDKResultMessage } from '@anthropic-ai/claude-agent-sdk';
9
+ import type { ContentBlock } from '@hydraharness/harness-llm';
10
+ import { type SubagentResult, type SubagentRun, type SubagentStartRequest, type SubagentStopReason } from '@hydraharness/harness-subagent';
11
+ import { type SubprocessHandle, type SubprocessSpawnSpec } from '@hydraharness/harness-subprocess';
12
+ import { ManagedClaudeCodeProcess } from './process.ts';
13
+ /** Default POSIX grace between subprocess termination tiers. */
14
+ export declare const DEFAULT_DISPOSE_GRACE_MS = 3000;
15
+ /** Claude Code permission modes that cannot wait for a human response. */
16
+ export declare const CLAUDE_CODE_PERMISSION_MODES: readonly ["dontAsk", "acceptEdits", "auto", "plan", "bypassPermissions"];
17
+ /** Profile-selectable non-interactive Claude Code permission mode. */
18
+ export type ClaudeCodePermissionMode = typeof CLAUDE_CODE_PERMISSION_MODES[number];
19
+ /** Safe default for unattended Claude Code runs. */
20
+ export declare const DEFAULT_CLAUDE_CODE_PERMISSION_MODE: ClaudeCodePermissionMode;
21
+ /**
22
+ * Hide an unpublished product startup failure behind fixed safe facts.
23
+ * @param cause - original host-side failure retained only on the Error cause chain.
24
+ * @returns a rejection safe to expose through the subagent start boundary.
25
+ */
26
+ export declare function claudeCodeStartupFailure(cause: unknown): Error;
27
+ /** Fully resolved inputs for one official Claude Agent SDK query. */
28
+ export interface ClaudeCodeRunSpec {
29
+ /** Parent Session workspace supplied to the SDK and real CLI. */
30
+ readonly cwd: string;
31
+ /** Profile-selected native non-interactive permission mode. */
32
+ readonly permissionMode: ClaudeCodePermissionMode;
33
+ /** Explicit deployment/test environment layered after shared scrubbing. */
34
+ readonly env: Record<string, string>;
35
+ /** Subprocess termination grace passed to the shared process-tree owner. */
36
+ readonly disposeGraceMs: number;
37
+ /** Shared subprocess service spawn operation. */
38
+ readonly spawn: (spec: SubprocessSpawnSpec) => SubprocessHandle;
39
+ /** Host diagnostic sink for a product failure kept outside model-visible text. */
40
+ readonly onError?: (error: Error, stopReason: SubagentStopReason) => void;
41
+ }
42
+ /**
43
+ * Validate and preserve the one-shot task before crossing the SDK boundary.
44
+ * @param prompt - task content accepted from the shared subagent service.
45
+ * @returns the exact text sequence as one SDK prompt.
46
+ */
47
+ export declare function textTask(prompt: readonly ContentBlock[]): string;
48
+ /**
49
+ * Strictly derive the only SDK result that can complete a shared run.
50
+ * @param message - an official discriminated result union.
51
+ * @returns exact final text for a successful, non-error result.
52
+ */
53
+ export declare function successfulResult(message: SDKResultMessage): string;
54
+ /**
55
+ * Consume the complete SDK stream and require one strict success plus normal
56
+ * iterator completion.
57
+ * @param query - published official SDK query.
58
+ * @param onPermissionDenied - records a safe fact when the SDK reports native denial.
59
+ * @param onResult - records that the SDK supplied a terminal result message.
60
+ * @returns the completed shared result.
61
+ */
62
+ export declare function consumeClaudeQuery(query: AsyncIterable<SDKMessage>, onPermissionDenied?: () => void, onResult?: () => void): Promise<SubagentResult>;
63
+ /**
64
+ * Close the official query, terminate the managed process tree, and wait for
65
+ * the subprocess owner to prove it is gone.
66
+ * @param query - official SDK query, when creation reached that point.
67
+ * @param child - live shared-service handle that owns the CLI process tree;
68
+ * spawn-failed handles settle at the startup boundary instead.
69
+ */
70
+ export declare function disposeClaudeCodeChild(query: Pick<Query, 'close'> | undefined, child: SubprocessHandle): Promise<void>;
71
+ /**
72
+ * Build the fixed official SDK options for one one-shot provider run.
73
+ * @param spec - Workspace, environment, process service, and disposal policy.
74
+ * @param controller - per-run cancellation owner.
75
+ * @param capture - receives the shared child and SDK-facing process synchronously.
76
+ * @param captureDiagnostic - receives safe facts from unattended interaction callbacks.
77
+ * @returns options that inherit native settings while disabling persistence and user questions.
78
+ */
79
+ export declare function claudeQueryOptions(spec: ClaudeCodeRunSpec, controller: AbortController, capture: (child: SubprocessHandle, process: ManagedClaudeCodeProcess) => void, captureDiagnostic: (diagnostic: string) => void): Options;
80
+ /**
81
+ * Start one official Claude Agent SDK query and publish its one-shot run.
82
+ * @param request - resolved shared subagent request.
83
+ * @param spec - Workspace, environment, process service, and diagnostic policy.
84
+ * @returns the published run after both Query and real CLI handle exist.
85
+ */
86
+ export declare function startClaudeCodeRun(request: SubagentStartRequest, spec: ClaudeCodeRunSpec): Promise<SubagentRun>;
87
+ //# sourceMappingURL=run.d.ts.map
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@hydraharness/harness-subagent-claude-code",
3
+ "description": "One-shot Claude Code subagent provider over the official Agent SDK",
4
+ "version": "0.1.1-rc.6",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/MaiHongPhong1902/Hydra-Harness.git",
11
+ "directory": "packages/subagent/subagent-claude-code"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "cordis.patch.yml",
32
+ "lib/types/**/*.d.ts"
33
+ ],
34
+ "license": "MIT",
35
+ "hydra": {
36
+ "bundle": {
37
+ "patch": "./cordis.patch.yml"
38
+ }
39
+ },
40
+ "peerDependencies": {
41
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
42
+ "@hydraharness/harness-subagent": "^0.1.1-rc.6",
43
+ "@hydraharness/harness-session": "^0.1.1-rc.6",
44
+ "@hydraharness/harness-llm": "^0.1.1-rc.6",
45
+ "@hydraharness/harness-timeout": "^0.1.1-rc.6",
46
+ "@hydraharness/harness-subprocess": "^0.1.1-rc.6",
47
+ "@hydraharness/cordis": "^4.0.2"
48
+ },
49
+ "dependencies": {
50
+ "@anthropic-ai/sdk": "0.93.0",
51
+ "@anthropic-ai/claude-agent-sdk": "0.3.220",
52
+ "@modelcontextprotocol/sdk": "^1.29.0",
53
+ "zod": "^4.4.3",
54
+ "@hydraharness/schemastery": "^3.18.2"
55
+ },
56
+ "devDependencies": {
57
+ "@hydraharness/harness-agent": "^0.1.1-rc.6",
58
+ "@hydraharness/harness-llm": "^0.1.1-rc.6",
59
+ "@hydraharness/harness-loader-smoke": "^0.1.1-rc.6",
60
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
61
+ "@hydraharness/harness-session": "^0.1.1-rc.6",
62
+ "@hydraharness/harness-subagent": "^0.1.1-rc.6",
63
+ "@hydraharness/harness-timeout": "^0.1.1-rc.6",
64
+ "@hydraharness/harness-subprocess-local": "^0.1.1-rc.6",
65
+ "@hydraharness/harness-subprocess": "^0.1.1-rc.6",
66
+ "@hydraharness/cordis": "^4.0.2"
67
+ }
68
+ }