import type { DebugStopData } from "../runtime/debugger.js"; import { Screen, type Element, type Frame } from "../tui/index.js"; import type { Checkpoint } from "../runtime/state/checkpointStore.js"; import type { FunctionParameter } from "../types.js"; import { type BusyEntry, type DebuggerCommand, type DebuggerIO, type DebuggerMode, type Parties } from "./types.js"; import type { Hello } from "./attach/protocol.js"; import { UIState } from "./uiState.js"; import type { GuardTripData } from "./guardAnswer.js"; import type { PendingPreview } from "./edits/rows.js"; import type { InterruptResponse } from "../runtime/interrupts.js"; type PaneSlot = { name: string; color: string; label: string; content: () => string; }; export declare class DebuggerUI implements DebuggerIO { private screen; state: UIState; prevState: UIState | null; private focusIndex; private zoomedPane; /** The thread the threads pane shows; the active one when unset. */ private threadDisplayId; private scrollOffsets; /** The row under the cursor in each pane that lists rows. */ private cursor; /** The composer under the thread: open takes typing; the draft stays * when it is closed. */ private composer; private spinnerInterval; private spinnerText; private commandBarOverride; private inputPrompt; private inputValue; /** Set when this UI drives a program in another process * (`agency debug --attach`): what the run said about itself, for the * status line. */ private attached; /** The redraw that ticks the connecting figure, from the handshake to * the first stop. */ private connectingInterval; private quitListeners; private pauseListeners; /** Set from the pause key until the next stop, for the bar. */ private pausing; private pendingKey; private personWaiter; /** Set by `withdraw` while the request it is for is between reads. */ private withdrawn; /** Who else is on the run, attached; shown on the stats line. */ private parties; private heldKeys; private waitingListeners; lastFrame: Frame | null; /** The clock the elapsed figures count on; a test injects its own. */ private readonly now; constructor(screen: Screen, opts?: { now?: () => number; }); private formatKeyVal; private getPanes; private buildSourceContent; /** Attached, and the run has not yet said where it is: no stop and no * wait open. The source pane has nothing to show until it does. A wait * that opened and closed before the first stop puts the pane back in * this state, so it is read on every draw rather than kept. */ private connecting; /** The source pane between the handshake and the first stop. The socket * is open; the program reports its next statement when it reaches one, * which can take a while inside a long model call, so the pane says so * rather than showing a source it does not have yet. */ private buildConnectingContent; /** The open waits in `moduleId` whose line the UI knows, the one that * began last at the end. */ private busyLines; /** What the program is waiting on, as one line: the wait that began * last, and how many others are open. Empty when nothing is. */ private busyBarText; private buildThreadsContent; /** The composer, under the thread's messages: the draft with a * cursor while open, the draft or an invitation while closed; none * before a stop. */ private composerLine; /** The pane's lines with the cursor's row marked, while the pane is * focused and has a stop to act on. */ private withCursor; private cursorIndex; /** The pane under focus, by name. */ focusedPaneName(): string | undefined; private buildLocalsContent; private buildGlobalsContent; private buildCallStackContent; /** One line per branch: its status, its label, and the owner it runs * under; the branch being viewed is marked. */ private buildBranchesContent; /** The tool loop's round and removed tools, then one line per guard: * `research ยท cost $0.42 of $0.50`. */ private buildGuardsContent; private buildActivityContent; private buildStdoutContent; private buildStatsContent; /** What the program is waiting on, while it runs and at a stop with a * wait still open (an `async` call in a model request, for one), as * a row of its own under the figures: beside them it was clipped at * 80 columns, and the renderer clips a row rather than wrapping it. * The command bar keeps the keys, so the person can see how to * pause. Empty when nothing is open. */ private buildWaitingContent; private renderPane; private buildCommandBar; private buildStatsBar; /** The edits waiting at the stop, and the keys the focused row takes, * from the same tables that handle them. Empty before a stop. */ private buildEditsLine; /** The one value `action` asks for on `row`, typed in the command bar: * JSON or text, text, or a guard amount, which is parsed with the * row's dimension and asked again with the reason when it does not * parse. Null when the person escapes. */ private askValue; /** The palette: every edit every row offers, filtered by typing. The * entry picked is run the way its key would be, after the cursor has * moved to its row, so the panes show what is being changed. */ private openPalette; /** What the key tables need to know right now. */ private keyContext; /** Carry out what a key table said, returning the command to send * when there is one. */ private carryOut; /** * Build the top two rows of the standard layout: * row 1: source (+ threads if present) * row 2: locals, globals, call stack */ buildTopRows(panes: PaneSlot[], focusedName: string | undefined): Element[]; /** Branches and guards get a row of their own when present, so the * second row keeps its widths and each gets room for a line like * `> search [interrupted] under llm()`. The activity and stdout row * gives up the height. Not part of `buildTopRows`, which the overlays * draw above themselves: a third row there would leave a form no * room on a short terminal. */ private buildExtrasRow; private buildElementTree; private renderUI; /** Show thread `id`. Picking the active thread means following it, * so the pane moves with the program again. */ private showThread; /** This UI now drives the program the run described. While attached, * only `q` quits (`escape` would detach by accident), and quitting * tells `onQuit` listeners first. */ attachedTo(hello: Hello): void; private stopConnectingTicks; onQuit(listener: () => void): void; onPause(listener: () => void): void; /** Another party answered the request this UI is answering: the read * the person is in rejects, which closes an open picker or form and * ends the command loop's wait, and the activity line already says * who. A request can be between reads for a moment (the rewind * picker loads a checkpoint before its first read and between * moves), so with no read in progress the withdrawal is kept for the * request's next read, and each request starts with it cleared. */ withdraw(): void; /** The start of a request that reads keys: a withdrawal left over from * the one before does not apply to it. */ private beginRequest; setParties(parties: Parties): void; /** Resolves the next time this UI waits on the person for a key: a * command at a stop, a text input, or an overlay. A test feeds a key * and waits on this to know the UI has taken it and come back for * the next; the reader that runs while the program runs does not * count, since the person is not asked for anything then. */ waitForPerson(): Promise; /** The next key for the person: a held one first, else the next the * screen gives. The one place the UI reads a key for someone. */ private readKey; /** Make sure one `nextKey()` is in flight whenever someone wants a * key: the person, or the pause key while the program runs. */ private armReader; private dispatch; log(line: string): void; setMode(mode: DebuggerMode): void; setPending(preview: PendingPreview): void; setBusy(entries: BusyEntry[]): void; render(checkpoint?: Checkpoint, stop?: DebugStopData, interruptId?: string): Promise; private static SPINNER_FRAMES; /** While the program runs, the bar says so and names the keys that * work then: space to pause, `q` to quit or detach. It is redrawn every * 80ms, which also ticks the wait's time in the stats bar; the frame * character is the sign that the screen is alive. */ startSpinner(): void; stopSpinner(): void; cleanup(error?: string): void; waitForCommand(): Promise; /** The viewed frame's value, so print agrees with the locals pane * after `b`. Answered here, not by the driver: the frame being shown * is the UI's, and a driver in another process could not see it. */ private printVariable; private handleInteractiveKey; private scrollPane; /** Scroll `paneName` just enough that the cursor's row is in view: a * row above the viewport becomes its first line, one below it its * last. The rows are the pane's lines in order, so the row index is * the line index. */ private scrollToCursor; /** The lines `paneName` can show, from its most recently rendered * frame: the frame's height less the border's two rows. Zero before * the first render. */ private viewportHeight; private enterTextInput; /** Ask until the person answers: escape clears the prompt and asks * again. A withdrawn read rejects out of here like any other. */ promptForInput(prompt: string): Promise; promptForNodeArgs(parameters: FunctionParameter[]): Promise; appendStdout(text: string): void; renderActivityOnly(): void; private overlayContext; showRewindSelector(checkpoints: Checkpoint[]): Promise; answerGuardTrip(data: GuardTripData): Promise; showCheckpointsPanel(checkpoints: Checkpoint[]): Promise; destroy(): void; } export {};