export interface TmuxExecResult { stdout: string; stderr: string; status: number; } export type TmuxExec = (args: string[]) => TmuxExecResult; /** Default exec: forks real tmux via spawnSync. Tests inject a mock exec to avoid touching the real tmux server. */ export declare const defaultTmuxExec: TmuxExec; export interface NewSessionOptions { /** tmux session name, e.g. "cortex-claude-" */ name: string; /** Command argv to run inside the session, e.g. ['claude', '--session-id', '...'] */ command: string[]; /** Working directory for the spawned shell/command (tmux -c) */ cwd: string; /** Extra environment variables surfaced to the child via tmux -e (each entry becomes "-e KEY=VAL") */ env?: Record; /** Pane columns (tmux -x). Default 200. */ cols?: number; /** Pane rows (tmux -y). Default 50. */ rows?: number; } /** * Stateless tmux command wrapper. All side effects flow through the injected exec — tests inject mocks, * production uses {@link defaultTmuxExec}. No internal state means safe concurrent use. * * @see DR-0012 §3.1 (architecture) — this is the bottom-most utility used by adapter-tui. */ export declare class TmuxControl { private readonly exec; constructor(exec?: TmuxExec); /** Returns true iff `tmux has-session -t ` exits 0. */ hasSession(name: string): boolean; /** * Create a detached tmux session running the given command in the given cwd. * Throws on tmux non-zero exit (e.g. duplicate session name). * * Env + command are NOT placed on the tmux command line. tmux packs the whole new-session * invocation (argv + every `-e KEY=VAL`) into a single control-socket imsg with a ~16KB ceiling; * the agent-server's full environment (~8KB+) plus claude's long `--append-system-prompt` / * `--settings` arguments blow past it ("command too long"). Instead we stage a launcher script * that exports the env and `exec`s the command, so the tmux command line stays a few dozen bytes. * @see DR-0012 — TUI command-length fix. */ newSession(opts: NewSessionOptions): void; /** Kill a tmux session. Idempotent: missing session is not an error (matches tmux's own semantics for our use case). */ killSession(name: string): void; /** * Send one or more tmux key tokens to the session. Tokens are passed verbatim to tmux, * so callers can use 'Enter', 'Escape', 'C-u', etc. No-op when no keys supplied. */ sendKeys(name: string, ...keys: string[]): void; /** * Paste arbitrary text (including multi-line + non-ASCII + shell metachars) into the session's * input area without submitting it. Internally: write text to a tempfile → tmux load-buffer (named * buffer to avoid concurrent collisions) → tmux paste-buffer -d (free buffer after paste) → unlink tempfile. * * Does NOT send Enter — caller decides whether to submit (typically `sendKeys(name, 'Enter')` after). * * Why a named buffer + -d: tmux has a global anonymous paste buffer; concurrent sessions on the same * tmux server would race. Named buffers + -d (auto-delete after paste) avoid that. * * Why -p (bracketed paste): Claude's Ink TUI only registers input wrapped in bracketed-paste escape * sequences (ESC[200~ … ESC[201~) as a paste. Without -p (Claude ≥2.1.162) the text lands but the * prompt buffer never accepts it, so the follow-up Enter submits nothing and no jsonl is written — * the turn then dies on the first-event watchdog. Verified: with -p, paste→Enter submits and Claude * responds; without it the pane goes blank and the transcript only contains session-init lines. * * @see DR-0012 spike §4 — paste-buffer is more reliable than send-keys -l for special chars. */ pasteText(name: string, text: string): void; /** Read the current visible pane content. Used only for diagnostics — production paths rely on jsonl. */ capturePane(name: string): string; /** * List all tmux session names on the server, optionally filtered by prefix. * Returns [] if no tmux server is running (status != 0) — graceful for the "agent-server startup * with no prior tmux state" case. * * @see DR-0012 §3.6 — used at agent-server startup to discover orphan TUI sessions for re-adoption. */ listSessions(prefix?: string): string[]; } /** * Write a self-deleting bash launcher that exports `env` then `exec`s `command`, returning its path. * On Linux the open file descriptor keeps the script readable after `rm "$0"`, so bash finishes * reading the remaining lines from the still-open inode (verified). The caller runs it via * `tmux new-session -- bash `; on a failed spawn the caller unlinks it instead. */ export declare function writeLauncherScript(env: Record, command: string[]): string;