/** * The OpenAI Codex CLI as a gateway provider (task 188, ADR 0007). * * Everything Codex-specific lives here so the turn pipeline in `gateway.ts` * owns nothing but "resolve the model, hand over the prompt, persist what came * back". Every function is pure except `resolveCodexCli` (filesystem) and * `spawnCodex` (the process). * * Measured against codex-cli 0.153.4 (2026-09-06): * - `codex exec --json -` prints one JSON event per line and reads the prompt * from stdin, which sidesteps ARG_MAX for a prompt carrying retrieved context. * - Text arrives per completed `agent_message` item, not per token. * - MCP servers are configured with `-c mcp_servers..=`; * a tool call is refused unless approvals are bypassed, which is the Codex * equivalent of the `bypassPermissions` mode every Claude turn runs under. */ import { type ChildProcess } from 'child_process'; import type { StreamSegment } from './gateway.js'; /** * Resolve the full path to the `codex` binary. Mirrors `resolveClaudeCli`: a * daemon started by systemd or launchd has a minimal PATH. */ export declare function resolveCodexCli(): string; /** The reasoning levels the Codex CLI accepts — the same five the thread `effort` field carries. */ export declare const CODEX_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max"]; export type CodexEffort = (typeof CODEX_EFFORTS)[number]; /** Map a thread's `effort` onto Codex's `model_reasoning_effort`; unknown or absent → high, as the Claude path defaults. */ export declare function codexReasoningEffort(effort?: string): CodexEffort; /** Shape of one MCP server entry as `buildMcpConfig` produces it (matches Claude's mcp-config JSON). */ export interface CodexMcpServerEntry { command: string; args?: string[]; env?: Record; } /** * Translate the gateway's MCP config object into Codex `-c` overrides. * * One `-c` per field, e.g. * -c mcp_servers.cumulus-history.command="/usr/bin/node" * -c mcp_servers.cumulus-history.args=["/…/dist/mcp/index.js"] * -c mcp_servers.cumulus-history.env={CUMULUS_THREAD_PATH="…",CUMULUS_THREAD_NAME="…"} * * `-c` parses the value as TOML, so every string goes through `tomlString` and * env keys through `tomlKey`. The returned array is argv fragments, NOT a shell * string: nothing here is shell-quoted because nothing here goes through a shell. */ export declare function mcpConfigToCodexArgs(config: { mcpServers: Record; }): string[]; /** * The full argv for a Codex turn. `-` is LAST: it tells Codex to read the prompt * from stdin, and a positional after it would be taken as a second prompt. * * `--dangerously-bypass-approvals-and-sandbox` is the Codex equivalent of the * `--permission-mode bypassPermissions` every Claude turn runs under (task 188 * measured that an MCP tool call is refused without it). `--ephemeral` because * the gateway never resumes a Codex session: the RLM prompt is the memory. * * `model_reasoning_summary` is what keeps a long turn from looking dead (task * 189). Codex emits NO `reasoning` items unless it is asked for summaries — * measured on 0.153.4 by running one prompt twice, identical but for this * override — so the minutes a turn spends thinking between tool calls produce * nothing at all on the wire: no token, no segment, no chip. The parser already * turns `reasoning` items into `thinking` segments; this flag is what makes * that code reachable. */ export declare function codexSpawnArgs(params: { model: string; effort?: string; cwd: string; mcpArgs?: string[]; images?: string[]; }): string[]; /** * The argv for a gateway-owned one-shot call (stall classifier, worker): no * tools, read-only sandbox, a scratch cwd, the prompt on stdin. */ export declare function codexOneShotArgs(params: { model: string; effort?: string; cwd: string; }): string[]; /** What one line of `codex exec --json` output means to the turn pipeline. */ export interface CodexEvent { /** Assistant text to append to the response (a completed `agent_message`). */ text?: string; /** Structured activity for the widget's activity chips. */ segments: StreamSegment[]; /** Set on `turn.completed`. */ usage?: Record; /** Set when the CLI reported a failure (`turn.failed`, a top-level `error`). */ error?: string; } /** * Parse one JSONL line from `codex exec --json`. Returns null for a blank or * unparseable line and for the housekeeping events that carry nothing * (`thread.started`, `turn.started`). */ export declare function parseCodexEvent(line: string): CodexEvent | null; /** How a Codex process ended, after every stdout line has been consumed. */ export interface CodexExit { code: number | null; signal: NodeJS.Signals | null; /** A clean process exit without turn.completed is still an interrupted turn. */ interrupted: boolean; /** The CLI's own failure message, if it reported one on stdout. */ cliError?: string; /** The last ~2KB of stderr, for an error message when there was no answer. */ stderrTail: string; } /** * Spawn `codex` with the prompt on stdin and deliver each parsed event to * `onEvent` as it arrives. `exit` settles only after the last line has been * consumed, so a caller that awaits it has seen every event; it rejects when the * process could not be started at all (ENOENT and friends). */ export declare function spawnCodex(opts: { bin: string; args: string[]; cwd: string; env: NodeJS.ProcessEnv; prompt: string; onEvent: (evt: CodexEvent) => void; }): { child: ChildProcess; exit: Promise; }; //# sourceMappingURL=codex-cli.d.ts.map