import { type Agent, type AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction"; import type { AssistantMessage, ImageContent, Model, Usage, UsageReport } from "@oh-my-pi/pi-ai"; import type { Component, EditorTheme, KeyId } from "@oh-my-pi/pi-tui"; import { Container, Loader, type TUI } from "@oh-my-pi/pi-tui"; import type { DescribeContext, NativeNode } from "@oh-my-pi/pi-tui/native/node"; import { type TrackSegment } from "@oh-my-pi/pi-tui/chrome/segment-track"; import type { TspText } from "@oh-my-pi/pi-wire"; import type { CollabGuestLink } from "../collab/guest.js"; import { CollabController } from "../collab/controller.js"; import type { CollabHost } from "../collab/host.js"; import { KeybindingsManager } from "@oh-my-pi/pi-tui/app-keybindings"; import { Settings } from "../config/settings.js"; import type { AutocompleteProviderFactory, ContextUsage, ExtensionCustomOptions, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions/index.js"; import type { CompactOptions, ExtensionLiveSession, ExtensionLiveStartOptions, ExtensionSessionModes, PlanReviewChoice, PlanReviewRequestedEvent } from "../extensibility/extensions/types.js"; import type { Skill } from "../extensibility/skills.js"; import type { FileSlashCommand } from "../extensibility/slash-commands.js"; import type { MCPManager } from "../mcp/index.js"; import { type PlanApprovalDetails } from "../plan-mode/approved-plan.js"; import { type AgentSession, type DroppedPrompt } from "../session/agent-session.js"; import type { AgentViewSession } from "../session/agent-session-handle.js"; import type { CompactMode } from "../session/compact-modes.js"; import type { ForeignSessionSource } from "../session/foreign-session-store.js"; import { HistoryStorage } from "../session/history-storage.js"; import { type SessionContext } from "../session/session-context.js"; import type { SessionManager } from "../session/session-manager.js"; import type { ShakeMode } from "../session/shake-types.js"; import { type DictationTarget } from "../stt/index.js"; import type { SpaceHoldHandler } from "@oh-my-pi/pi-tui/space-hold"; import { type ConfiguredThinkingLevel } from "@oh-my-pi/pi-tui/thinking"; import type { LspStartupServerInfo } from "../tools/index.js"; import { PresentedBlock } from "@oh-my-pi/pi-tui/chat/transcript-presentation"; import { type StartupChangelogSelection } from "../utils/changelog.js"; import type { EventBus } from "../utils/event-bus.js"; import type { TokenRateMeter } from "../utils/token-rate.js"; import { AssistantMessageComponent } from "@oh-my-pi/pi-tui/chat/assistant-message"; import { StatusNotice } from "@oh-my-pi/pi-tui/chrome/status-notice"; import type { BashExecutionComponent } from "@oh-my-pi/pi-tui/chat/bash-execution"; import { CustomEditor } from "@oh-my-pi/pi-tui/prompt/custom-editor"; import type { VersionUpdate } from "@oh-my-pi/pi-tui/prompt/welcome"; import type { EvalExecutionComponent } from "@oh-my-pi/pi-tui/chat/eval-execution"; import type { HookEditorComponent } from "@oh-my-pi/pi-tui/overlays/hook-editor"; import type { HookInputComponent } from "@oh-my-pi/pi-tui/overlays/hook-input"; import type { HookSelectorComponent, HookSelectorSlider } from "@oh-my-pi/pi-tui/overlays/hook-selector"; import { type PlanReviewAnnotationState } from "@oh-my-pi/pi-tui/overlays/plan-review-overlay"; import { ServedModelTracker } from "@oh-my-pi/pi-tui/chat/served-model-marker"; import { StatusLineComponent } from "@oh-my-pi/pi-tui/status-line"; import { type ToolExecutionHandle } from "@oh-my-pi/pi-tui/chat/tool-execution"; import { TranscriptContainer } from "@oh-my-pi/pi-tui/chrome/transcript-container"; import { Composer } from "@oh-my-pi/pi-tui/prompt/composer"; import { EventController } from "./controllers/event-controller.js"; import type { LoopConditionConfig, LoopLimitRuntime } from "@oh-my-pi/pi-tui/status-line/loop"; import { OAuthManualInputManager } from "./oauth-manual-input.js"; import { type ObservableSession } from "@oh-my-pi/pi-tui/overlays/session-observer-registry"; import type { Theme } from "@oh-my-pi/pi-tui/theme"; import type { AgentHubOpenOptions, CompactionQueuedMessage, InteractiveModeContext, InteractiveModeInitOptions, InteractiveSelectorDialogOptions, RenderInitialMessagesOptions, RenderSessionContextOptions, ResumeSessionOptions, SessionPickHandler, ShowStatusOptions, SelectOnlyModelSelectorOptions, SubmittedUserInput, YieldCompaction } from "./types.js"; import type { TodoItem, TodoPhase } from "@oh-my-pi/pi-tui/tools/todo"; import type { ConversationPosition } from "./utils/conversation-position.js"; /** * Editor max-height cap for a terminal of `terminalRows` rows. * * Roomy terminals get the comfortable [6, 18] band. Small terminals shrink the * cap so the editor leaves at least EDITOR_MIN_CHROME_ROWS rows for the * transcript + status line. The editor is bordered, so it never renders fewer * than EDITOR_MIN_RENDERED_ROWS rows; once the terminal is too small for both * (terminalRows < EDITOR_MIN_RENDERED_ROWS + EDITOR_MIN_CHROME_ROWS) the cap is * pinned to that floor — returning a smaller number would not shrink the editor * any further, it would only misreport the rows it actually occupies. */ export declare function computeEditorMaxHeight(terminalRows: number): number; export declare function shouldEnterPlanModeOnStartup(sessionManager: Pick, sessionSettings: Settings): boolean; /** Options for creating an InteractiveMode instance (for future API use) */ export interface InteractiveModeOptions { /** Providers that were migrated during startup */ migratedProviders?: string[]; /** Warning message if model fallback occurred */ modelFallbackMessage?: string; /** Initial message to send */ initialMessage?: string; /** Initial images to include with the message */ initialImages?: ImageContent[]; /** Additional initial messages to queue */ initialMessages?: string[]; } export declare const TODO_COMPACT_TERMINAL_ROWS_THRESHOLD = 18; /** * Anchored subagent HUD block with its visible session order, so click-to-focus * can map a rendered row back to its agent. Row 0 is the leading blank, row 1 * the title; item rows follow in `order`; the overflow summary maps nowhere. * The expander row (when `layoutPinnedHud` shows one) resolves to the toggle * sentinel, which the click router handles before any registry lookup. * Rendering delegates to the same `Text` mount as before, so output bytes are * unchanged — only the row map is new. Long rows wrap inside `Text` (content * is two cells narrower than the terminal), so the map is built on the first * click after rendering at a new width or width configuration: continuation * rows belong to the agent (or toggle) whose logical row started them. */ export declare class SubagentHudComponent implements Component { #private; constructor(lines: readonly string[], order: readonly string[], toggleRow?: number); /** Repaint in place with a new view; the click map rebuilds lazily. */ update(lines: readonly string[], order: readonly string[], toggleRow: number | undefined): void; render(width: number): readonly string[]; getClickAgentAtRow(row: number): string | undefined; } /** Pinned jump-list layout: painted item rows plus the expander row. */ export interface PinnedHudLayout { /** Item rows painted, in registry order. */ itemRows: number; /** Expander direction, or undefined when the list fits without one. */ toggle: "expand" | "collapse" | undefined; /** Viewport row of the expander within HUD lines (2 header rows + items). */ toggleRow: number | undefined; } /** * Pinned jump-list layout for `runningTotal` live agents. Collapsed shows a * few rows plus an expander; expanded shows every row plus a collapse row. * Single source of truth for the renderer and the click row map, so painted * rows and hit-testing can never disagree. */ export declare function layoutPinnedHud(runningTotal: number, expanded: boolean): PinnedHudLayout; /** * Delay until the live preview next needs a repaint with no progress event to * trigger it: when the first listed in-flight call crosses the elapsed-marker * threshold, then once a second while any marker is showing. Undefined when no * listed agent is mid-call, so an idle or thinking agent never arms a timer. */ export declare function nextSubagentPreviewTickMs(sessions: readonly ObservableSession[], now: number): number | undefined; /** * Build the anchored subagent HUD block: a bold accent "Subagents · N running" * header (with the agent-hub key right-aligned when `hubKey` is given) plus * a bounded set of running-agent rows in the same `Id ⟨role⟩: description` * shape the inline task rows use (muted assignment preview when no * description was given). Layout mirrors the Todos HUD exactly: unindented * header, then `renderTreeList` rows (dim connectors) shifted right by one space. * Every active subagent is listed — detached background spawns and sync task * calls alike — so the pinned block doubles as a click jump list. * With `livePreview` (`display.subagentLivePreview`), a row that has called a * tool carries a second physical row showing that call; both rows share one * returned line (joined by a newline) so line N still maps to agent N - 2. * Returns an empty array when nothing is running so the container can clear. */ export declare function renderSubagentHudLines(sessions: ObservableSession[], columns: number, expanded?: boolean, livePreview?: boolean, hubKey?: string): string[]; export declare class InteractiveMode implements InteractiveModeContext { #private; session: AgentSession; sessionManager: SessionManager; settings: Settings; keybindings: KeybindingsManager; agent: Agent; historyStorage?: HistoryStorage; /** Canonical composer shared by cold prepaint and the session-aware runtime. */ readonly composer: Composer; ui: TUI; chatContainer: TranscriptContainer; /** `!`/`$` runs started while the agent streams; {@link flushPendingBashComponents} moves them into the transcript. */ pendingShellContainer: Container; pendingMessagesContainer: Container; /** Judge-batch and automatic-download progress rows above the working line. */ progressHudContainer: Container; statusContainer: Container; /** Whether {@link statusContainer} rendered lines in the latest frame; the band composer's editor top gap collapses only then. */ statusRowOccupied: boolean; todoContainer: Container; subagentContainer: Container; btwContainer: Container; omfgContainer: Container; cleanseContainer: Container; errorBannerContainer: Container; modelCycleContainer: Container; deferredCommandContainer: Container; /** The docked `/changelog`-style command report, just above the editor; Esc clears it. */ reportContainer: Container; editor: CustomEditor; editorContainer: Container; /** Composer attachment band (chip cards) rendered directly above the prompt box. */ attachmentChipsContainer: Container; /** The margin above the editor: its top gap, which tracks the status row, or a spacer over hook widgets. */ editorTopGapContainer: Container; hookWidgetContainerAbove: Container; hookWidgetContainerBelow: Container; statusLine: StatusLineComponent; isInitialized: boolean; initialChatRendered: boolean; isBashMode: boolean; toolOutputExpanded: boolean; hideToolActivity: boolean; todoExpanded: boolean; planModeEnabled: boolean; planModePaused: boolean; goalModeEnabled: boolean; goalModePaused: boolean; vibeModeEnabled: boolean; planModePlanFilePath: string | undefined; loopModeEnabled: boolean; loopModePaused: boolean; loopPrompt: string | undefined; loopLimit: LoopLimitRuntime | undefined; loopCondition: LoopConditionConfig | undefined; readonly sessionModes: ExtensionSessionModes; todoPhases: TodoPhase[]; hideThinkingBlock: boolean; /** Whether the visible session has produced thinking content the user can reveal. */ get hasDisplayableThinkingContent(): boolean; /** Record received reasoning content so Ctrl+T can reveal it even when model metadata says thinking is off. */ noteDisplayableThinkingContent(message: AgentMessage): boolean; /** * Effective thinking-block visibility: hidden when the user's setting is on, * or while thinking is "off" before the session has actually produced * displayable thinking content. Some providers return thinking blocks without * advertising reasoning support, so observed content unlocks the visibility * toggle. */ get effectiveHideThinkingBlock(): boolean; proseOnlyThinking: boolean; expandThinkingBlocks: boolean; compactionQueuedMessages: CompactionQueuedMessage[]; yieldCompaction: YieldCompaction | undefined; compactionQueueOrigin: ConversationPosition | undefined; pendingTools: Map; transcriptMessageComponents: WeakMap; pendingBashComponents: PresentedBlock[]; bashComponent: BashExecutionComponent | undefined; pendingPythonComponents: PresentedBlock[]; pythonComponent: EvalExecutionComponent | undefined; isPythonMode: boolean; streamingComponent: AssistantMessageComponent | undefined; streamingMessage: AssistantMessage | undefined; lastAssistantUsage: Usage | undefined; servedModelTracker: ServedModelTracker; loadingAnimation: Loader | undefined; autoCompactionLoader: Loader | undefined; retryLoader: Loader | undefined; /** Live gen tok/s for the working row: the viewed session's own meter, so a * focused subagent shows its own reading and the main session's survives * focus round-trips. */ get tokenRate(): TokenRateMeter; /** Idle stand-in for the working row: the last tok/s reading and the * band-mode title stay readable between turns, docked where the loader's * trailer was. */ renderIdleStatusHud(width: number): readonly string[] | undefined; /** The key that interrupts (the working row's stop control), or undefined when Esc would not cancel. */ maintenanceInterruptKey(): KeyId | undefined; /** The stop control's click: the same handler as the interrupt key. */ interruptFromPointer(): void; /** * The native HUD pills row: one running-work counter (`omp.hud.pill`) with * an agents segment, then a jobs segment, each present only while its * count is non-zero (the agents segment stays as a muted icon while the * session holds finished subagents); the row is hidden while both are absent. */ describeHudPills(): NativeNode; /** * Native activity line (`omp.hud.activity`): the status rows (the working, * retry and compaction loaders describe themselves as the working row; * other rows describe themselves) in an `omp.hud.status` column, then the * todo HUD, so the todo holds its place as the turn starts and ends. * Nothing to show describes the empty HUD. */ describeStatusHud(children: readonly Component[], cx: DescribeContext): NativeNode; unsubscribe?: () => void; onInputCallback?: (input: SubmittedUserInput) => void; optimisticUserMessageSignature: string | undefined; locallySubmittedUserSignatures: Set; /** True while an optimistically-rendered `/skill:` row awaits its canonical * `message_start`. Read by the event controller to reconcile the row. */ optimisticSkillMessagePending: boolean; lastSigintTime: number; lastEscapeTime: number; /** Owns Esc for every `/mcp test` that is active or whose cancellation hint may still be visible. */ mcpTestEscapeHandlers: Set<() => void>; lastLeftTapTime: number; shutdownRequested: boolean; /** True once a graceful `shutdown()` teardown failed at the memoized * dispose stage. Surfaced to the input controller so the next single * Ctrl+C skips the double-tap gate and runs `shutdown()` — which * force-quits — instead of merely clearing the editor (#12238). */ get teardownFailed(): boolean; /** True once `shutdown()` has begun teardown. Surfaced to the input * controller so a Ctrl+C arriving while teardown is in flight can hard- * abort the remaining work instead of stacking another no-op call. */ get isShuttingDown(): boolean; hookSelector: HookSelectorComponent | undefined; hookInput: HookInputComponent | undefined; hookEditor: HookEditorComponent | undefined; lastStatus: StatusNotice | undefined; fileSlashCommands: Set; skillCommands: Map; oauthManualInput: OAuthManualInputManager; /** Owns hosting: manual `/collab`, `collab.autoStart`, and room rotation on session switch. */ readonly collabController: CollabController; /** Owned room; use {@link collabController}.host for current-session reuse and links. */ collabHost?: CollabHost; /** Set with {@link setCollabGuest}. */ get collabGuest(): CollabGuestLink | undefined; /** * A guest's transcript is the host's and draws stock cards. Joining replays the host's next, so * the local blocks are left as drawn; leaving restored the local transcript while the guest still * held it, so its blocks are asked again. */ setCollabGuest(guest: CollabGuestLink | undefined): void; readonly lspServers: LspStartupServerInfo[] | undefined; mcpManager?: MCPManager; get eventController(): EventController; get eventBus(): EventBus | undefined; syncIdleMaintenanceView(target?: AgentViewSession | null): void; get viewSession(): AgentViewSession; get assistantImagesVisible(): boolean; get tableChartsVisible(): boolean; resolveAssistantMessageLinkHrefs(hrefs: readonly string[]): Promise>; approvalOwnsComposer: boolean; get focusedAgentId(): string | undefined; get sessionName(): string | undefined; focusAgentSession(id: string): Promise; focusParentSession(): Promise; unfocusSession(): Promise; invalidatePendingFocus(): void; resolveViewportClickCandidates(index: number): string[]; /** Flip the pinned jump list between its collapsed few and the full list, overriding the setting. */ togglePinnedHudExpanded(): void; /** Rebuild the pinned jump list for a `display.pinnedAgents` change. */ applyPinnedAgentsSetting(): void; setClickHoverId(id: string | undefined): void; clearTransientSessionUi(options?: { viewOnly?: boolean; }): void; /** Root-scoped bus carrying this session tree's `task:subagent:*` frames. */ get subagentEventBus(): EventBus | undefined; constructor(session: AgentSession, version: string, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, lspServers?: LspStartupServerInfo[] | undefined, mcpManager?: MCPManager, eventBus?: EventBus, composer?: Composer, subagentEventBus?: EventBus); playWelcomeIntro(): void; init(options?: InteractiveModeInitOptions): Promise; /** Reload the title-generation system prompt override for the provided working * directory and stash it on the session so first-input titling * ({@link input-controller}) and replan-driven refresh * ({@link AgentSession.#refreshTitleAfterReplan}) share one source * ({@link discoverTitleSystemPromptFile}; issue #3734). */ refreshTitleSystemPrompt(cwd?: string): Promise; /** Reload session skills and the `/skill:` command list. */ refreshSkillState(): Promise; /** Reload slash commands and autocomplete for the provided working directory. */ refreshSlashCommandState(cwd?: string, preloaded?: ReadonlyArray): Promise; /** Stack extension autocomplete behavior on top of the built-in editor provider (#4919). */ addAutocompleteProvider(factory: AutocompleteProviderFactory): void; /** * Re-point the process and every cwd-derived cache at `newCwd` after the * active session's working directory changed (`/move` relocation or resuming * a session from another project). The SessionManager's cwd MUST already * reflect `newCwd` before this is called. */ applyCwdChange(newCwd: string): Promise; getUserInput(): Promise; cancelGoalContinuation(): void; disableGoalMode(message?: string): void; disableLoopMode(message?: string): void; setLoopPrompt(prompt: string): void; /** * Schedule the next auto-resubmit for a submission that never reached * {@link getUserInput}. A `/skill:` command dispatches inline from the * submit handler, leaving `onInputCallback` unresolved, so the run loop * never re-enters `getUserInput` to arm the following iteration. */ armLoopAutoSubmit(): void; /** * Pause the loop without exiting it: drops the captured prompt and any * pending auto-resubmit. Loop mode stays enabled — the next prompt the * user submits becomes the new loop prompt and resumes iteration. */ pauseLoop(): void; handleLoopCommand(args?: string): Promise; recordLocalSubmission(text: string, imageCount?: number): () => void; withLocalSubmission(text: string, fn: () => Promise, options?: { imageCount?: number; }): Promise; clearOptimisticUserMessage(): void; replaceOptimisticUserMessage(message: AgentMessage, options?: { imageLinks?: readonly (string | undefined)[]; }): void; /** * Optimistically render a user-invoked `/skill:` row before its awaited * dispatch so a slow preflight (memory recall, `before_agent_start` hooks, * auto-thinking classification, pre-prompt compaction) does not leave the * submission invisible — normal prompts paint their row via * {@link startPendingSubmission} the same way (issue #8895). The canonical * skill `message_start` swaps this row in place via * {@link reconcileOptimisticSkillMessage}; a failed or bailed dispatch drops * it via {@link clearOptimisticSkillMessage}. */ renderOptimisticSkillMessage(message: AgentMessage, options?: { imageLinks?: readonly (string | undefined)[]; }): void; /** * Reconcile the optimistic `/skill:` row against the canonical message emitted * by the session (mirrors {@link replaceOptimisticUserMessage} for skills). * * The row is adopted in place — finalized, with the append skipped — only when * it is still on screen but no longer removable: it retired into native * scrollback before the canonical `message_start` arrived, so appending would * trail it with a second identical card (issue #11217). Otherwise the canonical * message is appended after clearing the tracked row, covering both a still-live * row (clean replace) and a row a transcript rebuild already detached from the * container (e.g. a display-setting toggle calling {@link rebuildChatFromMessages}), * whose card would otherwise vanish. */ reconcileOptimisticSkillMessage(message: AgentMessage): void; /** Drop the optimistic `/skill:` row when dispatch fails or bails before the * message reaches the agent (aborted preflight, streaming-race requeue). */ clearOptimisticSkillMessage(): void; startPendingSubmission(input: { text: string; images?: ImageContent[]; imageLinks?: (string | undefined)[]; customType?: string; display?: boolean; streamingBehavior?: "steer" | "followUp"; followUps?: string[]; }, options?: { preserveDraft?: boolean; clearEditor?: boolean; }): SubmittedUserInput; cancelPendingSubmission(): boolean; /** * Hands back a prompt the session dropped before dispatch (an Esc abort, * usage preflight denial or the cost cap raced turn setup). The message was * never persisted, so the tree/branch selectors cannot offer it — remove the * optimistic transcript row and put the typed text back in the editor for editing. * A prompt with unsent follow-ups comes back as one `=>` list. */ restoreDroppedPrompt(prompt: DroppedPrompt): void; markPendingSubmissionStarted(input: SubmittedUserInput): boolean; finishPendingSubmission(input: SubmittedUserInput): void; syncComposerShape(): void; syncExtensionLinks(): void; activateViewportLink(index: number, col: number): boolean; startLinkHints(): boolean; get linkHintsActive(): boolean; typeLinkHint(letter: string): void; cancelLinkHints(): void; openLinkBrowser(): boolean; closeLinkBrowser(): void; rerenderInLinkBrowser(timestamp: number): void; rebuildTranscriptScrollback(): void; refreshTranscriptPresentation(ids?: ReadonlySet): void; redrawTranscriptBlocks(blocks: readonly Component[], redraw: (block: Component) => boolean): void; updateEditorBorderColor(): void; /** * Refresh the running-subagents status badge from the active local or collab * registry, and the cost segment's subagent-tree spend (local sessions only: * a collab guest's registry mirrors host transcripts outside this root). */ syncRunningSubagentBadge(options?: { requestRender?: boolean; }): boolean; rebuildChatFromMessages(options?: { reuseSettledComponents?: boolean; }): void; /** * Render the ctrl+p model-role cycle chip track into its own anchored * container (just above the editor), mirroring the todo HUD: the container is * cleared and rebuilt in place on every cycle, so rapid presses or concurrent * chat activity can never stack duplicate tracks into the scrollback. */ showModelCycleTrack(segments: readonly TrackSegment[], activeIndex: number): void; isCompactTodoMode(): boolean; renderCompactStatusLine(width: number, childLines: readonly string[]): readonly string[]; /** Apply any deferred model switch after the current stream ends. */ flushPendingModelSwitch(): Promise; showPlanReview(planContent: string, title: string, options: string[], dialogOptions?: { helpText?: string; disabledIndices?: number[]; onExternalEditor?: () => void; onPlanEdited?: (content: string) => void; onFeedbackChange?: (feedback: string) => void; annotationState?: PlanReviewAnnotationState; onAnnotationStateChange?: (state: PlanReviewAnnotationState) => void; initialIndex?: number; /** Called once, with the settled choice, when the review overlay is hidden. */ onClose?: (choice: string | undefined) => void; }, extra?: { slider?: HookSelectorSlider; }): Promise; /** The open plan review awaiting an answer, as `plan_review_requested` reported it: backs `ctx.getPendingPlanReview`. */ getPendingPlanReview(): PlanReviewRequestedEvent | undefined; /** * Answers the open plan review as if the operator picked `choice`: backs `ctx.resolvePlanReview`. * Refine feedback is sent verbatim; it only has to be non-blank. `input.role` moves an * approval's execution-tier selection before the pick; it is refused when it names no tier * or accompanies a non-approval choice. */ resolvePlanReview(reviewId: string, choice: PlanReviewChoice, input?: { feedback?: string; role?: string; }): boolean; applySessionPin(sessionId: string, pinned: boolean): void; handlePlanModeCommand(initialPrompt?: string, input?: Pick): Promise; /** * `/vibe` toggle. Entering installs the ephemeral vibe tools, strips the * active toolset down to `read`, optional parent-owned `todo`, plus those * tools, and injects the director context. Exiting unregisters them, restores * the previous toolset, and kills every worker session so workers cannot * outlive the mode that directs them. */ handleVibeModeCommand(initialPrompt?: string, input?: Pick): Promise; handleGoalModeCommand(rest?: string, input?: Pick): Promise; handleGuidedGoalCommand(rest?: string, input?: Pick): Promise; /** Enter through the same goal activation path as `/goal set`, then start its first turn. */ startGoalAtStartup(objective: string): Promise; /** End the `working` hold of `init({ startupRun })` once the command-line prompts are dispatched. */ settleStartupRun(): void; /** Manually (re-)open the plan-review overlay — bound to `/plan-review`. Lets * the operator pull the review back up after dismissing it, or review a plan * the agent wrote without dispatching approval. There is no fixed plan filename: * `getPlanReferencePath()` is empty until a plan is actually approved (and does * not survive a restart), so this drives off the newest `local://-plan.md` * the agent wrote — the files persist in the session artifacts dir, so the scan * works before any review and across restarts. * * `fromExtension` backs `ctx.openPlanReview`: an extension never interrupts work * it did not start, so a busy session refuses instead of aborting the turn (an * extension tool awaiting this would otherwise wait on its own abort), an open * review is kept rather than replaced, and refusals are silent. Resolves once a * review is awaiting an answer, not when it is answered. */ openPlanReview(options?: { fromExtension?: boolean; }): Promise; /** * `abortTurn: false` skips the turn abort for callers that checked the session is * idle; the review is then refused if a turn started while the plan was read. * `onOpened` runs once the overlay and pending request exist. */ handlePlanApproval(details: PlanApprovalDetails, options?: { abortTurn?: boolean; onOpened?: () => void; }): Promise; stop(): void; shutdown(): Promise; /** * Tear down like {@link shutdown}, then relaunch the CLI with the original * launch argv (session-source flags and positional prompts stripped, see * {@link restartArgv}), restoring this session (`--restore`) when it exists on * disk: subagents the teardown cut off mid-turn go on in the new process. * * On POSIX the relaunch is a true `execvp(3)` image replacement: same PID, * same terminal, no lingering parent. Postmortem cleanups and stdout are * flushed first because nothing in this process runs after a successful * exec. On Windows (no exec semantics) or on exec failure, falls back to * spawning the replacement and lingering only to forward its exit code. */ restart(): Promise; requestShutdown(): void; /** True while a foreground submission is accepted but not yet handed to the session. */ hasPendingSubmission(): boolean; checkShutdownRequested(): Promise; setToolUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void; initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void; setEditorComponent(factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => CustomEditor) | undefined): void; getEditorComponent(): ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => CustomEditor) | undefined; present(content: Component | readonly Component[]): void; /** * Defer transcript command panels while the agent is streaming, then mount * them at the next settle, terminal or not. A non-terminal settle is only a * scheduling pause, so resumed streaming can still land below a panel * flushed there. That is preferred over leaving it queued behind a command * the user runs during the pause, which mounts immediately and would put the * older panel out of order. * * The deferral is acknowledged in {@link deferredCommandContainer}, an * anchored container above the editor. Nothing is mounted into the * transcript: a mid-turn mount changes the active frame while streaming, * which is why the earlier `showStatus` acknowledgment was reverted. An * anchored container is cleared and rebuilt in place without adding history * rows — the same reason the ctrl+p role-cycle track lives there. * * A Tern Surface Protocol surface has no append-only scrollback to * duplicate into, so there the panel mounts in the transcript at once. */ presentCommandOutput(content: Component | readonly Component[]): void; showSessionInfo(info: string, context?: ContextUsage): void; /** * The jobs pill's sheet: live background jobs in a dismissable overlay. * Unlike `/jobs` it adds nothing to the transcript, so a click mid-turn * leaves no deferred command preview above the editor. * * It lists the viewed session's jobs and its subagents' as a tree. A focused * subagent view opens it without the Cancel action: the view only watches * that agent, as `/jobs kill` stays main-session only. */ showJobsSheet(): void; /** Mount every command panel queued for the current session while the agent was streaming. */ flushPendingCommandOutput(): void; resetTranscript(): void; showStatus(message: string, options?: ShowStatusOptions): void; showError(message: string): void; showPinnedError(message: string): void; clearPinnedError(): void; showWarning(message: string, options?: { hideWithToolActivity?: boolean; }): void; ensureLoadingAnimation(): void; /** * Set the working row's label; no argument restores the default. A function * label is live — re-read on every spinner tick — and is kept to one row. */ setWorkingMessage(message?: string | (() => string)): void; applyPendingWorkingMessage(): void; showNewVersionNotification(newVersion: string): void; /** Show (or with `null`, clear) background auto-update progress after the welcome banner's version. */ showVersionUpdate(update: VersionUpdate | null): void; /** * Add this launch's release notes below the welcome header. Startup calls it * after the first session-bound frame; quiet startup and hidden mode show nothing. */ showStartupChangelog(selection: StartupChangelogSelection): void; clearEditor(): void; updatePendingMessagesDisplay(): void; queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[], options?: { preserveDraft?: boolean; }): void; flushCompactionQueue(options?: { willRetry?: boolean; }): Promise; runYieldCompaction(): void; flushPendingBashComponents(): void; isKnownSlashCommand(text: string): boolean; addMessageToChat(message: AgentMessage, options?: { imageLinks?: readonly (string | undefined)[]; reuseSettledComponent?: boolean; }): Component[]; renderSessionContext(sessionContext: SessionContext, options?: RenderSessionContextOptions): void; /** Build a session context in bounded chunks so terminal input runs between event-loop turns. */ renderSessionContextIncrementally(sessionContext: SessionContext, options: RenderSessionContextOptions, renderChunk?: () => void): Promise; renderInitialMessages(options?: RenderInitialMessagesOptions): Promise; /** * Reconcile the idle "F5 to Retry" status row with the transcript tail: * mount it when the last turn died on a tool call (Esc mid-execution, * stream failure) and the status row is free, remove it when stale. Runs at * turn end and after every transcript replay (startup resume, `/tree`, * session switch). The advertised key is the live `app.retry` binding, * dispatched by the editor to `InputController.handleRetry`. */ syncRetryHintRow(): void; truncateTranscriptFromMessage(message: AgentMessage): boolean; findLastAssistantMessage(): AssistantMessage | undefined; extractAssistantText(message: AssistantMessage): string; handleExportCommand(text: string): Promise; handleTraceCommand(): Promise; handleDumpCommand(): Promise; handleDumpAllCommand(): Promise; handleDumpAnonCommand(): Promise; handleAdvisorDumpCommand(isRaw?: boolean): void; handleDebugTranscriptCommand(): Promise; handleShareCommand(): Promise; handleTodoCommand(args: string): Promise; handleSessionCommand(): Promise; handleAdvisorStatusCommand(): Promise; handleJobsCommand(options?: { full?: boolean; }): Promise; handleUsageCommand(reports?: UsageReport[] | null): Promise; handleChangelogCommand(args?: string): Promise; handleHotkeysCommand(): void; handleToolsCommand(): void; handleContextCommand(): void; prepareSessionSwitch(): Promise; handleClearCommand(): Promise; /** * User-initiated `/new` (slash command and `app.session.new`): starts a new * session like {@link handleClearCommand}, then honors `plan.defaultOnStartup` * as launch does. Internal resets keep calling handleClearCommand directly. */ handleNewCommand(): Promise; handleFreshCommand(): Promise; handleResetContextCommand(): Promise; handleDeleteCommand(): Promise; handleForkCommand(placement?: "pane" | "window"): Promise; handleMoveCommand(targetPath?: string): Promise; handleWorktreeCommand(branch?: string, options?: { keepChanges?: boolean; }): Promise; /** * Whether {@link maybeAutoCreateWorktree} may stop to ask the user (`worktree.onStart: ask`). * It answers before the async git check, so it also says yes in a folder where the question * never shows. */ mayAskForStartupWorktree(): boolean; /** * Apply `worktree.onStart` to a fresh launch: optionally move the session into * a new worktree forked from clean `HEAD`. Silent no-op outside git checkouts; * failures become a warning so startup continues. */ maybeAutoCreateWorktree(): Promise; withBtwSessionMove(operation: () => Promise): Promise; handleRenameCommand(title: string): Promise; handleMemoryCommand(text: string): Promise; handleSTTToggle(): Promise; dictationSpaceHold(target: DictationTarget): SpaceHoldHandler; /** Start a `/record` screen capture, or stop the running one and report where it was saved. */ toggleRecording(): Promise; /** Start or stop the Codex-backed realtime voice surface. */ handleLiveCommand(): Promise; /** Start a live session whose audio runs on an extension's own WebRTC peer. */ startRemoteLive(options: ExtensionLiveStartOptions): Promise; showDebugSelector(): Promise; showAgentHub(options?: AgentHubOpenOptions): void; resetObserverRegistry(): void; handleBashCommand(command: string, excludeFromContext?: boolean): Promise; handlePythonCommand(code: string, excludeFromContext?: boolean): Promise; handleMCPCommand(text: string): Promise; handleSSHCommand(text: string): Promise; handleCompactCommand(customInstructions?: string, mode?: CompactMode, beforeFlush?: (outcome: CompactionOutcome) => void | Promise, options?: Pick): Promise; handleHandoffCommand(customInstructions?: string): Promise; handleShakeCommand(mode: ShakeMode): Promise; executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise; openInBrowser(urlOrPath: string): void; showSettingsSelector(): void; showUsageDashboard(reports: UsageReport[]): void; showAdvisorConfigure(): void; showHistorySearch(): void; showExtensionsDashboard(): void; showAgentsDashboard(): void; showGitUi(revision?: string): void; showModelSelector(options?: { temporaryOnly?: boolean; selectOnly?: SelectOnlyModelSelectorOptions; }): void; switchSessionModel(model: Model, thinkingLevel?: ConfiguredThinkingLevel): Promise; showPluginSelector(mode?: "install" | "uninstall"): void; showUserMessageSelector(startAt?: string): void; showCopySelector(): void; showTreeSelector(): void; showThinkingSelector(): void; showSessionSelector(source?: ForeignSessionSource, onSelect?: SessionPickHandler): void; handleResumeSession(sessionPath: string, options?: ResumeSessionOptions): Promise; handleSessionDeleteCommand(): Promise; showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise; showSessionPinSelector(): Promise; showResetUsageSelector(): Promise; showProviderSetup(): Promise; showHookConfirm(title: string, message: string, dialogOptions?: InteractiveSelectorDialogOptions): Promise; handleCtrlC(): void; handleCtrlD(): void; handleCtrlZ(): void; resetDisplayAfterAppearanceRefresh(): void; handleDequeue(): void; restoreCompactionQueueToEditor(status?: string): void; waitForSubmissionDispatches(): Promise; handleImagePaste(): Promise; handleImagePathPaste(path: string): Promise; /** Queue slash-command input behind the active turn. */ handleQueueCommand(message: string, detached?: Pick): Promise; handleBtwCommand(question: string, images?: ImageContent[], title?: string): Promise; handleTanCommand(work: string, images?: ImageContent[]): Promise; hasActiveBtw(): boolean; handleBtwEscape(): boolean; canBranchBtw(): boolean; /** Reserves plain `b` only after /btw has a completed branch action to handle. */ handlesBtwBranchKey(): boolean; handleBtwBranchKey(): Promise; canCopyBtw(): boolean; isGuidedGoalInterviewActive(): boolean; handleBtwCopyKey(): Promise; canFollowUpBtw(): boolean; handleBtwFollowUpKey(): boolean; handleBtwInjectKey(): boolean; handleBtwBranch(question: string, assistantMessage: AssistantMessage, leafId: string, sessionId: string, images?: ImageContent[]): Promise; handleOmfgCommand(complaint: string): Promise; hasActiveOmfg(): boolean; handleOmfgEscape(): boolean; handleCleanseCommand(args: string): Promise; hasActiveCleanse(): boolean; handleCleanseEscape(): boolean; showCommandReport(options: { title: string; head?: TspText; body: Component; }): void; dismissCommandReport(): boolean; commandReportRows(): number | undefined; cycleThinkingLevel(): void; cycleRoleModel(direction?: "forward" | "backward"): Promise; toggleToolOutputExpansion(): void; setToolsExpanded(expanded: boolean): void; toggleThinkingBlockVisibility(): void; toggleTodoExpansion(): void; setTodoExpanded(expanded: boolean): void; setTodos(todos: TodoItem[] | TodoPhase[]): void; reloadTodos(source?: AgentViewSession): Promise; openExternalEditor(): void; registerExtensionShortcuts(): void; initHooksAndCustomTools(): Promise; getToolUIContext(): ExtensionUIContext | undefined; emitCustomToolSessionEvent(reason: "start" | "switch" | "branch" | "tree" | "shutdown", previousSessionFile?: string): Promise; setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void; setHookStatus(key: string, text: string | undefined): void; showHookSelector(title: string, options: ExtensionUISelectItem[], dialogOptions?: InteractiveSelectorDialogOptions, extra?: { slider?: HookSelectorSlider; }): Promise; hideHookSelector(): void; showHookInput(title: string, placeholder?: string): Promise; hideHookInput(): void; showHookEditor(title: string, prefill?: string, dialogOptions?: ExtensionUIDialogOptions, editorOptions?: { promptStyle?: boolean; }): Promise; hideHookEditor(): void; showHookNotify(message: string, type?: "info" | "warning" | "error"): void; showHookCustom(factory: (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T) => void) => (Component & { dispose?(): void; }) | Promise, options?: ExtensionCustomOptions): Promise; showExtensionError(extensionPath: string, error: string): void; showToolError(toolName: string, error: string): void; }