/** * Environment FORCED onto every `claude` this cockpit spawns, whatever the * parent process happened to carry. Both transports apply it; keep them in * step through this constant rather than by copying the string. * * Why it has to be asserted and not merely un-poisoned: shadok-ai reads all * content from the `.jsonl` transcript (`src/tail.ts`), and uses the screen * only for control. An agent that writes no transcript therefore runs, does * the work, and says NOTHING — silent in the web chat, silent in Telegram, * empty on reload. It is the same silent-loss class as invariant 7. * * Claude Code turns transcript writing off when it inherits * CLAUDE_CODE_CHILD_SESSION, and it sets that marker itself on the environment * it hands to its own tool subprocesses — so an agent that shells out to * `claude` passes it on without anyone choosing to. * * Stripping the marker at spawn (below, and in TmuxPilot) still happens and * still matters. This is the other half: subtraction assumes we have listed * every name that can suppress a transcript, and it stops working in silence * the day a new one appears. The assertion holds either way. */ export declare const FORCED_CLAUDE_ENV: Record; export interface PilotOptions { /** Path to the claude executable (default: "claude" from PATH). */ claudePath?: string; /** Arguments passed to the CLI (e.g. ["--resume", ""]). */ args?: string[]; /** Working directory of the session. */ cwd?: string; cols?: number; rows?: number; env?: Record; } export interface WaitOptions { timeoutMs?: number; } /** The stability window for this poll: a fixed value, or whatever the getter * says right now. */ export declare function windowMs(v: number | (() => number)): number; export interface WaitIdleOptions extends WaitOptions { /** * How long the screen must stay unchanged to be considered "idle". * * A GETTER is accepted because the bar can change mid-wait: an explicit * interrupt arrives while we are already waiting, and from that moment the * end state is known rather than guessed. Read on every poll, so it takes * effect on the next one instead of at the next turn. */ stableMs?: number | (() => number); } /** * Drives an interactive `claude` session (the TUI) through a pseudo-terminal. * * The output stream is replayed into a headless virtual terminal * (@xterm/headless), which lets us read the screen as a human would see it * instead of parsing the raw ANSI escape stream. */ export declare class PtyPilot { private readonly opts; private proc; private term; private dataListeners; private exitListeners; private lastDataAt; private exited; constructor(options?: PilotOptions); /** Spawns the `claude` process inside a PTY. */ start(): void; /** Raw stream (with ANSI sequences), useful for a "mirror" mode. */ onData(cb: (chunk: string) => void): () => void; onExit(cb: (code: number) => void): () => void; get hasExited(): boolean; /** The currently visible screen (the last rendered `rows` lines). */ screen(): string; /** The whole terminal content, scrollback included. */ fullBuffer(): string; /** Writes text as-is into the TUI (without submitting). */ write(text: string): void; /** * Types a prompt then presses Enter. * * The TUI flushes stdin received before its keyboard handler is ready: * we paste the text (bracketed paste) and retry until it shows up on * screen, clearing partial input (Ctrl-U) between attempts. Then we * verify the message actually went through (spinner visible or "❯ …" * echo in the transcript), with retries. */ submit(text: string): Promise; /** A concise client-facing error; the full screen goes to the server log only. */ private submitError; press(key: "enter" | "escape" | "up" | "down" | "left" | "right" | "tab" | "ctrl-c"): void; /** True when the TUI indicates that Claude is working. */ isWorking(): boolean; /** Waits until a predicate on the screen becomes true. */ waitFor(predicate: (screen: string) => boolean, { timeoutMs }?: WaitOptions): Promise; /** * Waits until Claude is idle: no working marker on screen and the screen * stable for `stableMs`. */ waitForIdle({ stableMs, timeoutMs, }?: WaitIdleOptions): Promise; /** Waits for the process to exit (after /exit for example). */ waitForExit({ timeoutMs }?: WaitOptions): Promise; /** Clean shutdown: /exit, then kill if the process does not leave. */ stop(): Promise; kill(): void; }