import type { ZodType } from 'zod/v4'; /** Which host CLI's headless-mode flags/output shape to use. */ export type HostCliFamily = 'claude' | 'codex'; /** * Minimal shape of a spawned child process this module needs — narrowed from * `node:child_process`'s `ChildProcess` to just the events/streams we * consume, so tests can inject a lightweight fake instead of implementing * the full `ChildProcess` interface. * * `kill` is deliberately **optional** (Phase 178 T3), not required: this * interface is imported by two test files outside this task's file boundary * (`packages/core/tests/verify/per-task.test.ts`, * `packages/core/tests/verify/json-repair.test.ts`) whose fake process * objects predate the timeout guard and do not implement `kill`. Making it * required would break those files' typecheck. Real spawned processes * (`realSpawn`, below) structurally satisfy the optional method fine via * Node's actual `ChildProcess.kill`; the timeout logic calls it defensively * (`child.kill?.(...)`) rather than assuming it exists. */ export interface SpawnedProcessLike { stdout: NodeJS.ReadableStream | null; stderr: NodeJS.ReadableStream | null; on(event: 'error', listener: (err: NodeJS.ErrnoException) => void): unknown; on(event: 'close', listener: (code: number | null) => void): unknown; kill?(signal?: NodeJS.Signals): boolean; } /** Test seam / real implementation signature: spawn `bin args…`, return the process. */ export type SpawnFn = (bin: string, args: string[]) => SpawnedProcessLike; export type HostCliErrorReason = 'not-found' | 'spawn-error' | 'nonzero-exit' | 'output-error' | 'self-invocation' | 'timeout' | 'aborted'; /** * Distinguishable error type for host-cli spawn/output failures. This is the * "clear, typed rejection" T3 (loud fallback to mock when the binary is * missing/unauthenticated) is expected to catch and pattern-match on * `reason`; T2's job is only to guarantee failures surface this way instead * of hanging or being silently swallowed — see CLAUDE.md's "Quiet Fallback" * failure mode. */ export declare class HostCliError extends Error { readonly reason: HostCliErrorReason; constructor(message: string, reason: HostCliErrorReason, options?: { cause?: unknown; }); } export interface HostCliJSONOptions { /** Host CLI binary name or path, e.g. `"claude"` or `"/usr/local/bin/codex"`. Defaults to `"claude"`. */ bin?: string; /** Explicit CLI family for flag/output-shape selection; inferred from `bin`'s basename when omitted. */ family?: HostCliFamily; /** Optional model flag; omitted entirely (host CLI uses its own default) when unset. */ model?: string; system: string; user: string; schema: ZodType; /** Test seam; defaults to a real `node:child_process.spawn` wrapper with piped stdio. */ spawnImpl?: SpawnFn; /** Test seam for the self-invocation guard; defaults to `process.env`. */ env?: NodeJS.ProcessEnv; /** * Phase 178 T3 — subprocess spawn timeout override (ms), test-injectable so * tests never sleep for the real duration. Falls back to * {@link TIMEOUT_ENV_VAR} (env or `.env`, via the existing `discoverKey` * seam) then {@link DEFAULT_TIMEOUT_MS} when unset. If the spawned host-CLI * subprocess neither closes stdout nor exits before this elapses, it is * killed and `spawnCapture` rejects with `HostCliError({ reason: 'timeout' })`. */ timeoutMs?: number; /** * Phase 184 T1 — optional external cancellation signal, e.g. one a caller * builds itself via `AbortSignal.timeout(ms)` (a web/Node-standard API; * this module never constructs one on the caller's behalf — see the phase * boundary in DESIGN.md/the DRAFT). When it fires before the subprocess * settles, the spawned child is killed and `spawnCapture` rejects with * `HostCliError({ reason: 'aborted' })` — distinct from the internal * {@link timeoutMs} guard's `'timeout'` reason. A signal that is already * aborted before the call starts is honored immediately, without spawning * a child. Omitting this keeps today's behavior byte-identical. */ signal?: AbortSignal; /** * Phase 184 T1 — optional per-call trace identifier, threaded into the * structured logger's child context (`callOnce`'s * `getLogger().child({...})`) so this call's log lines can be correlated * with a caller's own tracing. Purely observational: never sent to the * spawned subprocess and never affects behavior. Omitted from the logger * context entirely when unset, matching this file's existing * conditional-field convention. */ traceId?: string; } /** * Runs a headless host-CLI (`claude`/`codex`) subprocess and coerces its * output into a schema-valid verdict via the shared, transport-agnostic * repair-retry harness (`runWithRepair`, extracted in a prior task). Mirrors * `localChatJSON`'s shape exactly — same `system`/`user`/`schema` inputs, * same repair-retry budget — the only new transport-specific code is the * subprocess spawn/capture in this file, matching `local-client.ts`'s * fetch-based `callOnce`. * * Spawn/output failures (binary not found, non-zero exit, unparseable * output) reject with a `HostCliError` rather than being caught here — a * later task's loud mock-fallback wiring is expected to catch it. */ export declare function hostCliJSON(o: HostCliJSONOptions): Promise; //# sourceMappingURL=host-cli-client.d.ts.map