import type { PtyHandle, RunnerSubmissionDisposition } from './types.js'; import type { CodexAppTurnInput, TrustedCaller } from '../../types.js'; /** * Shared stdin-injection path for the "runner" CLI adapters (codex-app, mira). * * These adapters don't drive a TUI — they spawn a small Node runner that reads * its stdin raw, byte-by-byte, and enqueues a message only when it sees a * trailing newline (see codex-app-runner.ts / mira-runner.ts). botmux hands the * runner one control line per message: * * ::botmux-:\n * * The naive implementation wrote the WHOLE line in a single * `tmux send-keys -l -- `. For a large message (e.g. a Code Review * webhook whose full MR JSON is embedded — ~16-21KB after base64) that single * injection overruns the pane pty's input buffer (N_TTY's ~4KB read buffer): * tmux's write blocks until the reader drains, which takes longer than * execFileSync's 5s timeout, so Botmux can no longer prove whether the * keystroke landed — yet the old writeInput still reported `submitted: true`, * potentially wedging the session "busy" forever. (Compare claude-code, which throttles * its send-keys for exactly this reason; codex-app/mira were the only naive * single-shot writers.) * * Fix: split the line into small chunks and inject them with a short throttle * between writes, so no single send-keys overruns the buffer and the reader * keeps draining. Crucially we never inject a newline between chunks — the * runner accumulates the partial line in its own buffer and only acts on the * final Enter — so splitting mid-line is safe. The control line is pure ASCII * (marker + base64 alphabet), so 1 char == 1 byte and slicing by code unit is * a clean byte split. */ /** Max bytes per send-keys chunk. Well under the ~4KB N_TTY input buffer so a * single chunk always drains before the next, even if the reader is briefly * busy. */ export declare const RUNNER_INPUT_CHUNK_BYTES = 1024; /** Throttle between chunks — gives the runner's event loop time to drain the * pane pty between writes. */ export declare const RUNNER_INPUT_THROTTLE_MS = 20; export declare function encodeRunnerInput(content: string, codexAppInput?: CodexAppTurnInput, replyTurnId?: string, codexAppSteerable?: boolean, trustedCaller?: TrustedCaller): string; /** Split an ASCII string into <=maxBytes pieces. Safe because the caller only * ever passes `marker + base64`, which is single-byte throughout. */ export declare function chunkAscii(line: string, maxBytes: number): string[]; /** * Write one control line to a runner adapter's stdin, chunked + throttled. * * Returns `{ submitted: false }` when any chunk (or the final Enter) cannot be * confirmed. A tmux timeout is ambiguous: bytes may still have reached the * pane. `submissionDisposition` therefore tells the worker whether the new * frame was provably untouched or the runner generation must be fenced. * * Buffer-hygiene contract (the runner only clears its stdin buffer on a newline, * see handleInput in codex-app-runner.ts / mira-runner.ts — a half-written * control line with no trailing Enter lingers and would PREPEND to the next * message, corrupting both into one un-parseable blob): * - Pre-flush: emit one Enter before writing, terminating any partial line a * prior failed write may have left behind (runner discards the fragment as * bad input; an empty buffer just ignores the blank line). * - On an unconfirmed chunk: attempt a flush Enter so a partial frame is less * likely to merge with the next message, then report an ambiguous dirty * generation; the flush cannot make delivery proof retroactive. * - Submit Enter is retried — a single unconfirmed Enter could otherwise leave a * COMPLETE but unsubmitted line in the buffer. */ export declare function writeRunnerInput(pty: PtyHandle, markerPrefix: string, content: string, codexAppInput?: CodexAppTurnInput, replyTurnId?: string, codexAppSteerable?: boolean, trustedCaller?: TrustedCaller): Promise<{ submitted: boolean; submissionDisposition: RunnerSubmissionDisposition; }>; //# sourceMappingURL=runner-input.d.ts.map