/**
 * Spawn helper for Codex CLI.
 *
 * Codex CLI reads stdin even when the prompt is supplied as a positional
 * argument — without an explicit EOF it hangs indefinitely. Node's
 * `execFile` / `exec` don't close stdin for us, so this helper uses
 * `spawn` and explicitly calls `child.stdin.end()` immediately.
 *
 * Empirically observed 2026-05-15: without this fix, oneShotCompletion
 * hangs for the full timeout window (30-60s) and returns empty. With it,
 * a Reply-with-PONGXYZ smoke call completes in ~4-5 seconds.
 *
 * Used by all transport primitives that spawn `codex exec` (one-shot,
 * structured one-shot). The agenticSessionHeadless primitive spawns
 * codex inside tmux, where tmux owns stdin — that path is unaffected.
 */
import { spawn } from 'node:child_process';
import { type Stats } from 'node:fs';
export interface SpawnResult {
    exitCode: number | null;
    stdout: string;
    stderr: string;
}
export interface BuildCodexChildEnvOptions {
    /**
     * Explicit OPENAI_API_KEY to pass through to the child process.
     *
     * @deprecated Phase A only. The API-key path is forbidden as a routine
     * path per Spec 12 Rule 1. Phase B refuses construction when this is set.
     * Use ChatGPT subscription OAuth in `~/.codex/auth.json` instead.
     * @internal
     */
    apiKey?: string;
    /** Override for CODEX_HOME (caller-provided). */
    codexHome?: string;
}
/**
 * Build the env for a `codex` child process per Rule 1a.
 *
 * Constructs an explicit allowlist (NOT a blocklist) — variables not listed
 * are dropped. `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID` are
 * defensively hard-deleted even if accidentally allowlisted in the future.
 *
 * Exceptions to the strict allowlist:
 *   - `OPENAI_BASE_URL` from the boot-time snapshot is passed through (Spec 12
 *     § "Scope clarification — what 'Codex traffic' means": user-installed
 *     proxies are user-owned compatibility).
 *   - When `INSTAR_DISABLE_RULE1_OPENAI=1` is set in the parent env, the
 *     `OPENAI_API_KEY` from parent env is passed through (Spec 12 § "Escape-hatch
 *     interaction with Rule 1a"). Sunset-date enforcement and audit-log codes
 *     are layered in by the credential validation cycle.
 *   - When the caller passes `options.apiKey`, that value is set on the child
 *     env regardless of the parent env's `OPENAI_API_KEY`. This is the
 *     deprecated explicit-config path; staged for removal at Phase B.
 */
export declare function buildCodexChildEnv(options?: BuildCodexChildEnvOptions): NodeJS.ProcessEnv;
export interface BuildCodexTmuxSessionEnvOptions {
    sessionId: string;
    /** tmux session name — exposed as INSTAR_SESSION_NAME for Threadline binding. */
    sessionName?: string;
    codexHome?: string;
    extraEnv?: Readonly<Record<string, string>>;
}
export declare function buildCodexTmuxSessionEnv(options: BuildCodexTmuxSessionEnvOptions): Array<[string, string]>;
export declare function spawnCodexAndWait(binary: string, args: string[], options: {
    timeoutMs: number;
    env: NodeJS.ProcessEnv;
    signal?: AbortSignal;
}): Promise<SpawnResult>;
/** Carry-buffer cap for event-line assembly (2 MB). */
export declare const EXEC_JSON_CARRY_CAP_BYTES: number;
export interface SpawnCodexExecJsonOptions {
    timeoutMs: number;
    env: NodeJS.ProcessEnv;
    /**
     * Prompt text written to the child's stdin (then stdin is ended). The
     * prompt moves OFF argv in --json mode: process argv is world-readable
     * (`ps`), cartographer-sweep-class prompts embed repo content, and macOS
     * ARG_MAX (~1 MB) would hard-fail large prompts.
     */
    prompt: string;
    /** Complete event lines, called BEFORE the promise settles. Must not throw. */
    onLine: (line: string) => void;
    /** An over-cap line was discarded unparsed (counted for the drift tripwire). */
    onOversizedLine?: () => void;
    /** Test seams. */
    spawnImpl?: typeof spawn;
    /** Bounded post-exit grace before forced settlement (default 5000 ms). */
    postExitGraceMs?: number;
    /** SIGTERM → SIGKILL grace (house standard 2000 ms). */
    sigkillGraceMs?: number;
}
export interface ExecJsonResult {
    exitCode: number | null;
    /** Rolling tail (last 600 chars) of the child's stderr. */
    stderrTail: string;
}
export declare class CodexExecJsonTimeoutError extends Error {
    readonly stderrTail: string;
    readonly killed = true;
    readonly signal = "SIGTERM";
    constructor(timeoutMs: number, stderrTail: string);
}
/**
 * Spawn `codex exec --json ...` and stream its event lines to `onLine`.
 *
 * Settlement contract (the spec's "settlement ordering"): the timeout timer
 * only INITIATES the kill sequence (SIGTERM → sigkillGraceMs → SIGKILL); the
 * promise settles from the `close` handler — after the carry flush — or at a
 * bounded post-`exit` grace when `close` is deferred by a held fd. Either
 * way, every onLine callback (including the final post-SIGTERM flush) has
 * completed before the promise settles, exactly once.
 *
 * Resolution: `{ exitCode, stderrTail }` for ANY exit (the caller decides
 * what a non-zero exit means). Rejection: spawn error, or timeout
 * (CodexExecJsonTimeoutError — after the final flush, so already-parsed
 * usage is never lost to the reject path).
 */
export declare function spawnCodexExecJson(binary: string, args: string[], options: SpawnCodexExecJsonOptions): Promise<ExecJsonResult>;
export declare function createCodexOutDir(): string;
/**
 * Remove a per-call out-dir. Pinned nesting per spec:
 * `try { safeRm(dir) } finally { set.delete(dir) }` — a throwing deletion
 * (EPERM, sandbox-revoked tmp access) cannot leak the in-flight entry.
 * Never throws into the call path.
 */
export declare function cleanupCodexOutDir(dir: string): void;
/** Sweep brakes (P19 — every one unit-tested). */
export declare const CODEX_OUT_SWEEP_INTERVAL_MS: number;
export declare const CODEX_OUT_SWEEP_AGE_MS: number;
export declare const CODEX_OUT_SWEEP_MAX_DELETIONS = 20;
export declare const CODEX_OUT_SWEEP_MAX_CANDIDATES = 200;
export interface SweepStaleCodexOutDirsOptions {
    /** Test seams. */
    now?: () => number;
    tmpDirOverride?: string;
    lstatImpl?: (p: string) => Promise<Stats>;
}
/**
 * Opportunistic, bounded sweep of stale `instar-codex-out-*` dirs left by
 * CRASHED processes (the in-process `finally` covers everything else).
 *
 * Fire-and-forget async — NEVER awaited by evaluate() (os.tmpdir() population
 * scales with the whole machine; a synchronous readdir+lstat pass inside a
 * judgment call would block the single server event loop on foreign state).
 * `lastSweepAt` is set BEFORE the pass starts so concurrent calls cannot
 * double-trigger. os.tmpdir() is hostile on Linux (world-writable /tmp): each
 * candidate is lstat-verified — a directory, NOT a symlink, owned by this
 * uid, mtime older than the threshold — and EPERM/ENOENT during deletion is
 * swallowed, never thrown into the call path.
 *
 * Returns the sweep promise when a pass started (for tests), null when
 * rate-floored.
 */
export declare function maybeSweepStaleCodexOutDirs(options?: SweepStaleCodexOutDirsOptions): Promise<void> | null;
/** Test-only internals. */
export declare const _codexOutDirInternals: {
    inFlightOutDirs: Set<string>;
    resetSweepClock(): void;
};
//# sourceMappingURL=codexSpawn.d.ts.map