/** * Real agent-CLI adapters. * * Exports a generic factory (`createAgentCliAdapter`) and two pre-configured * factories (`createClaudeCliAdapter`, `createCodexCliAdapter`) that spawn * the respective AI coding CLI as a child subprocess. * * Usage in CLI: * createClaudeCliAdapter({ workingDirectory: process.cwd() }) * createCodexCliAdapter({ workingDirectory: process.cwd() }) * * MCP tools and integration tests use the same factories. */ import type { MartinAdapter, MartinAdapterRequest } from "../core/index.js"; import { type AgentExecutionIntent } from "../core/index.js"; import { type SpawnLike } from "./cli-bridge.js"; /** * Given a prompt string, returns the full argv array to pass to spawn(). * Example for Claude: () => ["--output-format", "json", "--print"] * Example for Codex: () => ["exec", "--sandbox", "workspace-write", "-"] */ export type CliArgsBuilder = (prompt: string, request: MartinAdapterRequest) => string[]; export type CliStdinBuilder = (prompt: string) => string | undefined; export interface AgentCliAdapterOptions { /** The executable to spawn (e.g. "claude", "codex"). */ command: string; /** Converts a prompt string into the argv array passed to spawn(). */ argsBuilder: CliArgsBuilder; /** Optional stdin payload for CLIs that accept prompt input via stdin or `-`. */ stdinBuilder?: CliStdinBuilder; /** Adapter ID suffix. Defaults to command. */ adapterIdSuffix?: string; /** Working directory for all subprocesses. Defaults to process.cwd(). */ workingDirectory?: string; /** Timeout for the agent subprocess in ms. Defaults to 300_000 (5 min). */ timeoutMs?: number; agentExecutionIntent?: AgentExecutionIntent; providerExecutionTimeoutMs?: number; /** Timeout per verification command in ms. Defaults to 120_000 (2 min). */ verifyTimeoutMs?: number; /** Human-readable label shown in loop records. */ label?: string; /** Model name surfaced in adapter metadata (also used for cost estimation). */ model?: string; /** * Whether the CLI outputs JSON when --output-format json is passed. * Set to false for CLIs that don't support this flag (e.g. Codex). * Defaults to true for Claude. */ supportsJsonOutput?: boolean; /** * Set when `argsBuilder` requests `--output-format stream-json` (newline- * delimited JSON events) rather than single-blob `json`. Enables (a) * incremental result parsing that scans for the final `result` event, and * (b) a live cumulative-cost circuit breaker that terminates the subprocess * the moment projected spend crosses the remaining per-attempt budget, * rather than only learning about an overspend after the process exits. */ streamingUsageCap?: boolean; /** Test-only override for subprocess spawning. */ spawnImpl?: SpawnLike; } export interface ClaudeCliAdapterOptions { workingDirectory?: string; timeoutMs?: number; agentExecutionIntent?: AgentExecutionIntent; providerExecutionTimeoutMs?: number; verifyTimeoutMs?: number; label?: string; /** Override the model passed via --model flag. */ model?: string; /** Extra args appended after core args (before prompt). */ extraArgs?: string[]; spawnImpl?: SpawnLike; } export interface CodexCliAdapterOptions { /** Override the executable or absolute command path used to launch Codex. */ command?: string; workingDirectory?: string; timeoutMs?: number; agentExecutionIntent?: AgentExecutionIntent; providerExecutionTimeoutMs?: number; verifyTimeoutMs?: number; label?: string; /** Override the model passed via --model flag. */ model?: string; /** * Deprecated no-op retained for compatibility. * * Codex CLI's supported non-interactive entrypoint is `codex exec`. * MartinLoop now uses explicit sandboxing instead of the legacy * `--full-auto` compatibility path, which can exit before verifier execution. */ fullAuto?: boolean; /** Codex sandbox mode for model-generated commands. Defaults to workspace-write. */ sandbox?: "read-only" | "workspace-write" | "danger-full-access"; /** Extra args appended after core args (before prompt). */ extraArgs?: string[]; spawnImpl?: SpawnLike; } export interface GeminiCliAdapterOptions { workingDirectory?: string; timeoutMs?: number; agentExecutionIntent?: AgentExecutionIntent; providerExecutionTimeoutMs?: number; verifyTimeoutMs?: number; label?: string; /** Explicit model override passed via --model. Omitted to preserve Gemini Auto. */ model?: string; /** Approval mode for headless Gemini runs. Defaults to yolo for autonomous execution. */ approvalMode?: "default" | "auto_edit" | "yolo" | "plan"; /** Enable Gemini sandbox mode when the host is configured for it. Disabled by default. */ sandbox?: boolean; /** Extra args appended after core args. */ extraArgs?: string[]; spawnImpl?: SpawnLike; } export declare function createAgentCliAdapter(options: AgentCliAdapterOptions): MartinAdapter; /** * Spawns `claude --output-format stream-json --verbose --print "" [extraArgs]`. * * `stream-json` emits one JSON event per line — including per-turn usage on * each `assistant` message and a final `result` event carrying the same * `result`/`usage`/`total_cost_usd` fields as single-blob `json` output — so * MartinLoop can both (a) recover real token usage/cost as before, and * (b) watch cumulative spend live and self-terminate the subprocess the * moment it crosses the remaining per-attempt budget (see * `streamingUsageCap` / `createStreamingUsageInspector`), instead of only * discovering an overspend after the whole process has already exited. * * Requires the Claude Code CLI to be installed and authenticated: * https://docs.anthropic.com/claude-code */ export declare function createClaudeCliAdapter(options?: ClaudeCliAdapterOptions): MartinAdapter; /** * Spawns `codex exec --cd --sandbox [--model ] [extraArgs] -`. * * The prompt is delivered via stdin so Windows shell quoting cannot truncate or * reinterpret long MartinLoop prompts that contain paths, deny rules, or budget * context. * * Requires the Codex CLI to be installed and authenticated: * npm install -g @openai/codex */ export declare function createCodexCliAdapter(options?: CodexCliAdapterOptions): MartinAdapter; /** * Spawns `gemini [--model ] --prompt "" --approval-mode --output-format json [...]`. * * The prompt is delivered via stdin while forcing headless mode with `--prompt ""`, * which keeps large MartinLoop prompts off the command line on Windows. * * Requires the Gemini CLI to be installed and authenticated: * npm install -g @google/gemini-cli */ export declare function createGeminiCliAdapter(options?: GeminiCliAdapterOptions): MartinAdapter;