import * as readline from "readline"; /** One-line summary of a multi-line buffer: its first line + a line count. * Shared by the recall preview stored in history and the submit banner, so a * recalled paste renders identically to when it was first pasted. */ declare function summarizeMultiline(text: string): string; /** What `loadHistory` returns: the readline display list (newest-first, capped) * plus a map from a collapsed paste's preview line back to its full text. */ type LoadedHistory = { entries: string[]; expansions: Record; }; /** Read `historyFile` (a JSON array of `HistoryRecord`s, oldest first) into the * shape Node's `readline` expects: display entries newest-first, capped at * `max`, plus the preview→full-text map for collapsed pastes. JSON (rather * than one-entry-per-line) so a paste with embedded newlines round-trips * instead of splitting into bogus entries. Returns empties on any I/O or parse * error (or a non-array file) so a corrupt or missing file never breaks * startup. */ declare function loadHistory(file: string, max: number): LoadedHistory; /** Persist `history` (readline's newest-first order) to `file` as a JSON array * of `HistoryRecord`s stored oldest-first, so re-loading yields identical * chronology. An entry present in `expansions` is written as `{ preview, text }` * so a collapsed paste round-trips with its full buffer; every other entry is * written as a plain string. Creates parent dirs as needed. Swallows errors so * a read-only HOME doesn't crash the REPL on exit. */ declare function saveHistory(file: string, history: string[], max: number, expansions?: Record): void; /** Record a just-submitted `/paste` buffer into readline `history` for recall. * A multi-line buffer is stored as a one-line preview (its full text kept in * `expansions`) so readline never recalls a multi-line line buffer; a * single-line buffer is stored verbatim. */ declare function recordPasteEntry(history: string[], buffer: string, expansions: Record): void; /** Make `entry` the most-recent item in `history` (readline's newest-first * array), removing the `command` readline added for it (e.g. `/paste`, which * triggered the multi-line editor) and any earlier duplicate of `entry`. This * is how a pasted buffer reaches up-arrow recall and persistence — readline * never sees the buffer itself, only the `/paste` keystrokes. */ declare function recordHistoryEntry(history: string[], entry: string, command: string): void; /** Cursor moves that erase a footer of `rows` physical rows, leaving the * cursor at column 0 of where the footer's first row began. Empty when * there is no footer yet. */ declare function eraseRows(rows: number): string; type FrameCursor = "hide" | "show" | "keep"; type FrameSpec = { above: string | null; footerLines: string[]; prevRows: number; columns: number; cursor: FrameCursor; }; type FrameResult = { seq: string; rows: number; }; /** Build ONE atomic terminal frame: erase the previous footer, optionally * emit `above` into scrollback, then redraw the footer wrapped to width. * Pure — the exact bytes and the new physical-row count are a function of * the inputs, so the anti-flicker guarantees are unit-testable. */ declare function buildFrame(spec: FrameSpec): FrameResult; export type BottomRegion = { refresh: () => void; teardown: () => void; }; type RegionOptions = { hideCursor: boolean; }; /** Pin `render()`'s lines to the bottom of the terminal. While installed, * every other write to process.stdout is redrawn ABOVE the footer as one * atomic frame (via `buildFrame`). No-op on non-TTY. `hideCursor` scopes * cursor-hiding to callers that draw their own caret (the interrupt * widget); the spinner leaves the cursor alone. */ export declare function installBottomRegion(render: () => string[], useTTY: boolean, options?: RegionOptions): BottomRegion; type InterruptAction = "submit" | "cancel" | "exit" | "backspace" | { append: string; } | null; type KeyMeta = { name?: string; ctrl?: boolean; meta?: boolean; }; /** Map a readline keypress to an interrupt-widget action. DELIBERATELY * DIVERGES from classifyPasteKey: Ctrl+C is a hard "exit" (an approval * prompt must quit, never soft-cancel-and-continue), Escape is a soft * "cancel", and Enter is "submit" (there is no Ctrl+D submit). */ declare function classifyInterruptKey(sequence: unknown, key: KeyMeta | undefined): InterruptAction; type InterruptState = { buffer: string; notice: string; }; type InterruptOutcome = { kind: "pending"; } | { kind: "resolve"; value: string; } | { kind: "cancel"; } | { kind: "exit"; }; type InterruptConfig = { validKeys: string[]; allowFreeText: boolean; allowCancel: boolean; }; type InterruptStep = { state: InterruptState; outcome: InterruptOutcome; }; /** Pure state machine for the sticky interrupt prompt: given the current * state and a key action, return the next state and an outcome. The * imperative shell acts on the outcome; every decision lives here. */ declare function reduceInterrupt(state: InterruptState, action: InterruptAction, config: InterruptConfig): InterruptStep; /** Greedily pack `key=label` tokens into lines no wider than `width` * visible columns, joined by two spaces. A token wider than `width` gets * its own line (buildFrame wraps it at render time). */ declare function packOptions(items: { key: string; label: string; }[], width: number): string[]; type InterruptFooterInput = { title: string; body: string; items: { key: string; label: string; }[]; allowFreeText: boolean; state: InterruptState; columns: number; }; /** Render the sticky approval prompt as footer lines. Pure. The real * cursor is hidden by the widget, so the input line ends with a caret * glyph. buildFrame wraps each line to width. */ declare function renderInterruptFooter(input: InterruptFooterInput): string[]; type InterruptOpts = { title: string; body: string; items: { key: string; label: string; }[]; allowFreeText: boolean; allowCancel: boolean; }; /** The line-mode approval prompt. Renders `opts` as a pinned bottom footer * and reads a typed line terminated by Enter. All decisions come from * reduceInterrupt; this shell only wires keys → reducer → outcome and does * the I/O. Resolves with the option key or a free-text reason; rejects * with AgencyCancelledError on Escape (when allowCancel); exits 130 on * Ctrl+C. Reuses the outer REPL's readline via `_ttyWrite` — no second * readline, so none of _runPrompt's paused/raw restoration dance applies. */ declare function stickyInterruptPrompt(rl: readline.Interface, opts: InterruptOpts): Promise; /** Bridge for std::ui/cli's interruptChoice. Delegates to the sticky widget * wired to the running line-mode REPL. The Agency side gates on * _stickyInterruptAvailable first, so a missing hook is a programming * error. */ export declare function _interruptChoice(title: string, body: string, items: { key: string; label: string; }[], allowFreeText: boolean, allowCancel: boolean): Promise; /** True when a line-mode REPL is running AND both ends are real terminals, * so the pinned prompt can actually draw and read keystrokes. The hook is * installed unconditionally by `_runLineRepl`, so without the TTY checks a * non-TTY REPL would route here, draw nothing (installBottomRegion no-ops), * and hang awaiting keystrokes that never reach `_ttyWrite`. Requiring both * TTYs sends those runs to `chooseOption`'s non-TTY fallback instead * (mirrors the `/paste` guard). */ export declare function _stickyInterruptAvailable(): boolean; /** * Start a single-line "Thinking Ns" spinner as a one-line bottom region. * Returns a stop function that removes the region. The spinner is now a * client of `installBottomRegion`: outside writes (tool traces) redraw * above it automatically, and there is exactly one owner of the bottom of * the screen. It does NOT hide the cursor (default RegionOptions). No-op * on non-TTY: returns an immediate stop function so piped runs don't get * spinner frames in their logs. */ declare function startSpinner(useTTY: boolean): () => void; /** Roll back the entries the slash palette polluted — `"/"` plus any leaked * filter keystrokes, i.e. everything added since `mark` — and, when a command * was picked, record it as one clean entry. `history` is readline's * newest-first array, mutated in place. A `mark` of -1 (no trigger fired) * skips the rollback. */ declare function repairSlashHistory(history: string[], mark: number, picked: string | null): void; /** Cumulative per-model token+cost totals, keyed by model name. */ type ModelTotals = Record; type TokenSnapshot = { inputTokens: number; outputTokens: number; models: ModelTotals; }; /** Distinct models whose token count grew between two snapshots, ordered * by cost spent during the turn (descending), name as tiebreak. This is * what the footer lists after the per-turn token/time stats. */ declare function modelsUsedThisTurn(before: TokenSnapshot, after: TokenSnapshot): string[]; /** A readable footer label for a model. Hosted model IDs pass through * unchanged; a local GGUF path/filename is reduced to its model name — * `…/hf_unsloth_SmolLM2-135M-Instruct.Q4_K_M.gguf` → `SmolLM2-135M-Instruct` — * by taking the basename and dropping node-llama-cpp's `hf__` prefix, * the `.gguf` extension, and the quant suffix. */ declare function prettyModel(name: string): string; declare function fmtModels(models: string[]): string; export declare function _runLineRepl(status: unknown, onSubmit: unknown, prompt: string, historyFile: string, historyMax: number, paletteCommands: unknown): Promise; export type PasteState = { lines: string[]; current: string; }; /** Apply one input character. `\n` / `\r` commit the current line and * start a new one; anything else appends to the current line. */ declare function pasteChar(state: PasteState, ch: string): PasteState; /** Append a (possibly multi-line) chunk of text — e.g. a pasted block. * Normalizes `\r\n` / `\r` to `\n` first so a pasted line ending lands * as a single break rather than doubling up. */ declare function pasteText(state: PasteState, text: string): PasteState; /** Delete the last character of the current line. No-op at the start of * a line (v1 does not merge back into the previous line). */ declare function pasteBackspace(state: PasteState): PasteState; /** Join the buffer into the final `\n`-separated string. */ declare function pasteJoin(state: PasteState): string; type PasteAction = "submit" | "cancel" | "newline" | "backspace" | { append: string; } | null; /** Map a readline keypress to a paste-mode action. Returns `null` for * keys we ignore in this mode (arrows, function keys, other chords). */ declare function classifyPasteKey(s: unknown, key: { name?: string; ctrl?: boolean; meta?: boolean; } | undefined): PasteAction; /** Drive an inline multi-line editor by hijacking `rl._ttyWrite`. * Resolves with the joined buffer on Ctrl+D, or `null` on Ctrl+C / * Esc. Restores the previous `_ttyWrite` (e.g. the slash-trigger * wrapper) on exit. */ declare function readMultiline(rl: readline.Interface, useColor: boolean): Promise; /** Test-only surface for the `/paste` editor. Mirrors the `_internal` * convention used by other stdlib-lib modules (e.g. `layout.ts`) so * these helpers stay out of the supported `agency-lang/stdlib-lib` API * while remaining unit-testable. Not for production use. */ export declare const _internal: { EMPTY_PASTE: PasteState; pasteChar: typeof pasteChar; pasteText: typeof pasteText; pasteBackspace: typeof pasteBackspace; pasteJoin: typeof pasteJoin; classifyPasteKey: typeof classifyPasteKey; readMultiline: typeof readMultiline; modelsUsedThisTurn: typeof modelsUsedThisTurn; fmtModels: typeof fmtModels; prettyModel: typeof prettyModel; loadHistory: typeof loadHistory; saveHistory: typeof saveHistory; recordHistoryEntry: typeof recordHistoryEntry; recordPasteEntry: typeof recordPasteEntry; repairSlashHistory: typeof repairSlashHistory; summarizeMultiline: typeof summarizeMultiline; eraseRows: typeof eraseRows; buildFrame: typeof buildFrame; startSpinner: typeof startSpinner; classifyInterruptKey: typeof classifyInterruptKey; reduceInterrupt: typeof reduceInterrupt; INITIAL_INTERRUPT_STATE: InterruptState; packOptions: typeof packOptions; renderInterruptFooter: typeof renderInterruptFooter; stickyInterruptPrompt: typeof stickyInterruptPrompt; }; export declare function _clearScreen(): void; /** Clear `repl()` input history. Splits cleanly along the state boundary: * * - The persisted *file* is cleared at `path`, which the caller supplies. * Agency holds the path as a module global (`_historyFile`), so the file's * identity lives in the execution model rather than in a TS closure. Works * even with no active REPL; `saveHistory` no-ops on an empty path. * - The *live* in-session recall (`rl.history`) is a Node object owned by the * running readline, so that wipe goes through the `__agencyClearHistory` * hook `_runLineRepl` installs. A no-op outside an interactive REPL. */ export declare function _clearHistory(path: string): void; export declare function _termWidth(): number; export {};