/**
 * AgentSession - Core abstraction for agent lifecycle and session management.
 *
 * This class is shared between all run modes (interactive, print, rpc).
 * It encapsulates:
 * - Agent state access
 * - Event subscription with automatic session persistence
 * - Model and thinking level management
 * - Compaction (manual and auto)
 * - Bash execution
 * - Session switching and branching
 *
 * Modes use this class and add their own I/O layer on top.
 */
import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
import { Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, type StreamFn, ThinkingLevel, type ToolChoiceDirective } from "@oh-my-pi/pi-agent-core";
import { type CompactionResult, type ShakeConfig } from "@oh-my-pi/pi-agent-core/compaction";
import type { AssistantMessage, Context, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ResetCreditAccountStatus, ResetCreditRedeemOutcome, ResetCreditTarget, ServiceTier, ServiceTierByFamily, ServiceTierFamily, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@oh-my-pi/pi-ai";
import { Effort } from "@oh-my-pi/pi-ai";
import { postmortem } from "@oh-my-pi/pi-utils";
import { type AdvisorConfig } from "../advisor/index.js";
import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async/index.js";
import type { Rule } from "../capability/rule.js";
import type { ModelRegistry } from "../config/model-registry.js";
import { type ResolvedModelRoleValue } from "../config/model-resolver.js";
import { type PromptTemplate } from "../config/prompt-templates.js";
import type { Settings, SkillsSettings } from "../config/settings.js";
import { RawSseDebugBuffer } from "../debug/raw-sse-buffer.js";
import { type PythonResult } from "../eval/py/executor.js";
import { type BashResult } from "../exec/bash-executor.js";
import type { TtsrManager } from "../export/ttsr.js";
import type { LoadedCustomCommand } from "../extensibility/custom-commands/index.js";
import type { CustomTool } from "../extensibility/custom-tools/types.js";
import type { ExtensionRunner } from "../extensibility/extensions/index.js";
import type { CompactOptions, ContextUsage } from "../extensibility/extensions/types.js";
import type { RecoveredRetryError } from "../extensibility/shared-events.js";
import type { Skill, SkillWarning } from "../extensibility/skills.js";
import { type FileSlashCommand } from "../extensibility/slash-commands.js";
import { GoalRuntime } from "../goals/runtime.js";
import type { Goal, GoalModeState } from "../goals/state.js";
import type { HindsightSessionState } from "../hindsight/state.js";
import { type IrcMessage } from "../irc/bus.js";
import { type MnemopiSessionState } from "../mnemopi/state.js";
import type { PlanModeState } from "../plan-mode/state.js";
import { type SecretObfuscator } from "../secrets/obfuscator.js";
import { type ConfiguredThinkingLevel } from "../thinking.js";
import { type DiscoverableTool, type DiscoverableToolSearchIndex } from "../tool-discovery/tool-index.js";
import type { CheckpointState, CompletedRewindState } from "../tools/checkpoint.js";
import { type TodoItem, type TodoPhase } from "../tools/todo.js";
import type { ClientBridge } from "./client-bridge.js";
import { type CustomMessage, type CustomMessagePayload } from "./messages.js";
import type { BuildSessionContextOptions, SessionContext } from "./session-context.js";
import type { BranchSummaryEntry, NewSessionOptions } from "./session-entries.js";
import { type SessionManager } from "./session-manager.js";
import type { ShakeMode, ShakeResult } from "./shake-types.js";
import { ToolChoiceQueue } from "./tool-choice-queue.js";
import { YieldQueue } from "./yield-queue.js";
/** Session-specific events that extend the core AgentEvent */
export type AgentSessionEvent = AgentEvent | {
    type: "auto_compaction_start";
    reason: "threshold" | "overflow" | "idle" | "incomplete";
    action: "context-full" | "handoff" | "shake" | "snapcompact";
} | {
    type: "auto_compaction_end";
    action: "context-full" | "handoff" | "shake" | "snapcompact";
    result: CompactionResult | undefined;
    aborted: boolean;
    willRetry: boolean;
    errorMessage?: string;
    /** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
    skipped?: boolean;
} | {
    type: "auto_retry_start";
    attempt: number;
    maxAttempts: number;
    delayMs: number;
    errorMessage: string;
    errorId?: number;
} | {
    type: "auto_retry_end";
    success: boolean;
    attempt: number;
    finalError?: string;
    recoveredErrors?: RecoveredRetryError[];
} | {
    type: "retry_fallback_applied";
    from: string;
    to: string;
    role: string;
} | {
    type: "retry_fallback_succeeded";
    model: string;
    role: string;
} | {
    type: "ttsr_triggered";
    rules: Rule[];
} | {
    type: "todo_reminder";
    todos: TodoItem[];
    attempt: number;
    maxAttempts: number;
} | {
    type: "todo_auto_clear";
} | {
    type: "irc_message";
    message: CustomMessage;
} | {
    type: "notice";
    level: "info" | "warning" | "error";
    message: string;
    source?: string;
} | {
    type: "thinking_level_changed";
    thinkingLevel: ThinkingLevel | undefined;
    /** The user-configured selector when it differs from the effective level (e.g. `auto`). */
    configured?: ConfiguredThinkingLevel;
    /** The level `auto` resolved to this turn, once classified. */
    resolved?: Effort;
} | {
    type: "goal_updated";
    goal: Goal | null;
    state?: GoalModeState;
};
/** Listener function for agent session events */
export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
/**
 * Budget for callers on the user-visible `/quit` / `/exit` shutdown path that
 * want to cap how long they wait for `MnemopiSessionState.dispose()` to finish
 * its consolidate pass. Consolidate fires fresh LLM fact extractions, each a
 * 1–3 s round-trip, so interactive shutdown passes this budget to keep the
 * UI responsive. Callers that keep the process/session host alive must omit it
 * so dispose still awaits the full consolidate-then-close pipeline.
 */
export declare const SHUTDOWN_CONSOLIDATE_BUDGET_MS = 1500;
export interface AgentSessionDisposeOptions {
    mnemopiConsolidateTimeoutMs?: number;
    /**
     * Postmortem reason that triggered this dispose (signal/fatal teardown
     * paths). When set, the persisted `session_exit` diagnostic records it
     * instead of the generic `"dispose"` used for normal programmatic disposal
     * (`/quit`, test teardown, subagent completion).
     */
    reason?: postmortem.Reason;
}
export type CommandMetadataChangedListener = () => void | Promise<void>;
export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
export interface AsyncJobSnapshot {
    running: AsyncJobSnapshotItem[];
    recent: AsyncJobSnapshotItem[];
    delivery: AsyncJobDeliveryState;
}
export type { ShakeMode, ShakeResult };
export interface AgentSessionConfig {
    agent: Agent;
    sessionManager: SessionManager;
    settings: Settings;
    /** Whether the caller explicitly requested yolo/auto-approve behavior for this session. */
    autoApprove?: boolean;
    /** Models to cycle through with Ctrl+P (from --models flag) */
    scopedModels?: Array<{
        model: Model;
        thinkingLevel?: ThinkingLevel;
    }>;
    /** Initial session thinking selector. */
    thinkingLevel?: ConfiguredThinkingLevel;
    /** Initial per-family service tiers (OpenAI / Anthropic / Google) for the live session. */
    serviceTierByFamily?: ServiceTierByFamily;
    /** Prompt templates for expansion */
    promptTemplates?: PromptTemplate[];
    /** File-based slash commands for expansion */
    slashCommands?: FileSlashCommand[];
    /** Extension runner (created in main.ts with wrapped tools) */
    extensionRunner?: ExtensionRunner;
    /** Loaded skills (already discovered by SDK) */
    skills?: Skill[];
    /** Skill loading warnings (already captured by SDK) */
    skillWarnings?: SkillWarning[];
    /** Custom commands (TypeScript slash commands) */
    customCommands?: LoadedCustomCommand[];
    skillsSettings?: SkillsSettings;
    /** Model registry for API key resolution and model discovery */
    modelRegistry: ModelRegistry;
    /** Tool registry for LSP and settings */
    toolRegistry?: Map<string, AgentTool>;
    /** Tool names whose current registry entry is still the built-in implementation. */
    builtInToolNames?: Iterable<string>;
    /** Update tool-session predicates that render guidance from the live active tool set. */
    setActiveToolNames?: (names: Iterable<string>) => void;
    /** Current session pre-LLM message transform pipeline */
    transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => AgentMessage[] | Promise<AgentMessage[]>;
    /**
     * Per-request transform applied after `convertToLlm` and before the
     * provider call. Used for snapcompact, secret obfuscation, and image
     * clamping. When supplied via {@link createAgentSession}, the advisor agent
     * inherits this so its requests undergo the same shaping as the main turn.
     */
    transformProviderContext?: (context: Context, model: Model) => Context | Promise<Context>;
    /**
     * Stream wrapper passed to side-channel requests (`/btw`, `/omfg`, IRC
     * auto-replies, and handoff generation) so they apply the same provider
     * shaping and host-level request wrappers as normal agent turns. Defaults
     * to plain `streamSimple` when omitted.
     */
    sideStreamFn?: StreamFn;
    /**
     * Stream wrapper passed to the advisor agent so its requests apply the
     * session's `providers.openrouterVariant`, `providers.antigravityEndpoint`,
     * `providers.maxInFlightRequests`, and `model.loopGuard.*` settings —
     * keeping OpenRouter sticky-routing / response caching consistent with the
     * main agent. Defaults to plain `streamSimple` when omitted.
     */
    advisorStreamFn?: StreamFn;
    /** Hint that OpenAI Codex requests should prefer websocket transport when supported. */
    preferWebsockets?: boolean;
    /** Provider payload hook used by the active session request path */
    onPayload?: SimpleStreamOptions["onPayload"];
    /** Provider response hook used by the active session request path */
    onResponse?: SimpleStreamOptions["onResponse"];
    /** Raw SSE hook used by the active session request path */
    onSseEvent?: SimpleStreamOptions["onSseEvent"];
    /** Per-session raw SSE diagnostic buffer */
    rawSseDebugBuffer?: RawSseDebugBuffer;
    /** Current session message-to-LLM conversion pipeline */
    convertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
    /** System prompt builder that can consider tool availability. Returns ordered provider-facing blocks. */
    rebuildSystemPrompt?: (toolNames: string[], tools: Map<string, AgentTool>) => Promise<{
        systemPrompt: string[];
    }>;
    /** Local calendar date provider used by prompt-cache invalidation. Defaults to the host local date. */
    getLocalCalendarDate?: () => string;
    /** Rebuild the SSH tool from current capability discovery results. */
    reloadSshTool?: () => Promise<AgentTool | null>;
    requestedToolNames?: ReadonlySet<string>;
    /**
     * Optional accessor for live MCP server instructions. Read by the session's
     * `rebuildSystemPrompt`-skip optimization to detect server-side instruction
     * changes (e.g. an MCP server upgrade) that would otherwise pass the tool-set
     * signature comparison and silently keep a stale prompt cached.
     */
    getMcpServerInstructions?: () => Map<string, string> | undefined;
    /** Enable hidden-by-default MCP tool discovery for this session. */
    mcpDiscoveryEnabled?: boolean;
    /** MCP tool names to activate for the current session when discovery mode is enabled. */
    initialSelectedMCPToolNames?: string[];
    /** Whether constructor-provided MCP defaults should be persisted immediately. */
    persistInitialMCPToolSelection?: boolean;
    /** MCP server names whose tools should seed discovery-mode sessions whenever those servers are connected. */
    defaultSelectedMCPServerNames?: string[];
    /** MCP tool names that should seed brand-new sessions created from this AgentSession. */
    defaultSelectedMCPToolNames?: string[];
    /** TTSR manager for time-traveling stream rules */
    ttsrManager?: TtsrManager;
    /** Secret obfuscator for deobfuscating streaming edit content */
    obfuscator?: SecretObfuscator;
    /** Inherited eval executor session id from a parent agent. */
    parentEvalSessionId?: string;
    /** Logical owner for retained eval kernels created by this session. */
    evalKernelOwnerId?: string;
    /**
     * AsyncJobManager that this session installed as the process-global instance.
     * Only set for top-level sessions; subagents inherit the parent's manager and
     * **MUST NOT** dispose it on their own teardown.
     */
    ownedAsyncJobManager?: AsyncJobManager;
    /**
     * AsyncJobManager reachable by this session for scoped job actions.
     *
     * Top-level owners receive their own manager, subagents receive the inherited
     * parent manager, and secondary in-process top-level sessions receive
     * `undefined` so job snapshots and ACP drains cannot observe the primary's
     * state.
     */
    asyncJobManager?: AsyncJobManager;
    /** Agent identity (registry id like "Main" or "Alice") used for IRC routing. */
    agentId?: string;
    /** Whether this session is the top-level agent or a subagent. Drives eager-task
     *  prelude gating so a top-level session created with a custom `agentId` still
     *  receives the always-mode reminder. Defaults to "main". */
    agentKind?: "main" | "sub";
    /**
     * Override the provider-facing session ID for all API requests from this session.
     * When absent, `sessionManager.getSessionId()` is used. Needed when benchmark or
     * SDK callers issue probes / prewarming with an explicit `--provider-session-id`
     * so that credential sticky selection is consistent with the session's streaming calls.
     */
    providerSessionId?: string;
    /**
     * Full advisor toolset, pre-built in `createAgentSession` against a distinct,
     * advisor-scoped `ToolSession` (its own `-advisor` session/agent id) so the
     * advisor's tool state stays isolated from the primary. The advisor is a full
     * agent; its config `tools` selects a subset (default read/grep/glob). Undefined
     * when the advisor is disabled.
     */
    advisorTools?: AgentTool[];
    /** Preloaded watchdog prompt content for the advisor. */
    advisorWatchdogPrompt?: string;
    /** Preloaded YAML top-level `instructions` shared baseline, kept separate from
     *  `advisorWatchdogPrompt` so `/advisor configure` can swap it live. */
    advisorSharedInstructions?: string;
    /**
     * Preloaded project context files (AGENTS.md, etc.) rendered as a system-prompt
     * block for the advisor — the same standing instructions the primary agent
     * receives, so the reviewer holds the agent to them.
     */
    advisorContextPrompt?: string;
    /**
     * Advisors discovered from `WATCHDOG.yml`. Empty/undefined runs a single
     * legacy advisor on the `advisor` role (byte-for-byte the pre-config path).
     */
    advisorConfigs?: AdvisorConfig[];
    /**
     * Strip tool descriptions from provider-bound tool specs on side requests
     * (handoff). Must match the session-start value used to build the system
     * prompt so inline descriptors are not also sent through provider schemas.
     */
    pruneToolDescriptions?: boolean;
    /**
     * Disconnect this session's OWNED MCP manager on dispose. Provided only when
     * the session created the manager (top-level sessions); subagents reuse a
     * parent's manager via `options.mcpManager` and omit this so a child's
     * teardown never tears down the shared servers.
     */
    disconnectOwnedMcpManager?: () => Promise<void>;
    /**
     * Override the bundled system prompt used by automatic session-title
     * generation paths (initial title + replan refresh). Source-of-truth is
     * `TITLE_SYSTEM.md` discovered via {@link discoverTitleSystemPromptFile} and
     * resolved through {@link resolvePromptInput}; refresh after a `/move`-style
     * cwd change via {@link AgentSession.setTitleSystemPrompt}.
     */
    titleSystemPrompt?: string;
}
/** Options for AgentSession.prompt() */
export interface PromptOptions {
    /** Whether to expand file-based prompt templates (default: true) */
    expandPromptTemplates?: boolean;
    /** Image attachments */
    images?: ImageContent[];
    /** When streaming, how to queue the message: "steer" (interrupt) or "followUp" (wait). */
    streamingBehavior?: "steer" | "followUp";
    /** Optional tool choice override for the next LLM call. */
    toolChoice?: ToolChoice;
    /** Send as developer/system message instead of user. Providers that support it use the developer role; others fall back to user. */
    synthetic?: boolean;
    /** Marks this prompt as a deliberate user action (typed message, `.`/`c`
     *  continue). Clears advisor auto-resume suppression that a user interrupt set.
     *  Defaults to `!synthetic`; manual-continue is synthetic yet user-initiated, so
     *  it sets this explicitly. Agent-initiated synthetic prompts (auto-continue,
     *  plan re-prime, reminders) leave it unset and keep suppression latched. */
    userInitiated?: boolean;
    /** Explicit billing/initiator attribution for the prompt. Defaults to user prompts as `user` and synthetic prompts as `agent`. */
    attribution?: MessageAttribution;
    /** Skip pre-send compaction checks for this prompt (internal use for maintenance flows). */
    skipCompactionCheck?: boolean;
}
/** Options for AgentSession.followUp() */
export interface FollowUpOptions {
    /** Enqueue as a hidden developer message (agent-attributed by default) instead of a user follow-up. */
    synthetic?: boolean;
    /** Whether to expand file-based prompt templates (default: true). */
    expandPromptTemplates?: boolean;
    /** Explicit billing/initiator attribution. Defaults to `agent` for synthetic follow-ups. */
    attribution?: MessageAttribution;
}
/** Result from a handoff operation. */
export interface HandoffResult {
    document: string;
    savedPath?: string;
}
export interface SessionHandoffOptions {
    autoTriggered?: boolean;
    signal?: AbortSignal;
    onSwitchCancelled?: () => void;
}
/** Result from cycleModel() */
export interface ModelCycleResult {
    model: Model;
    thinkingLevel: ThinkingLevel | undefined;
    /** Whether cycling through scoped models (--models flag) or all available */
    isScoped: boolean;
}
/** Result from cycleRoleModels() */
export interface RoleModelCycleResult {
    model: Model;
    thinkingLevel: ThinkingLevel | undefined;
    role: string;
}
/** A configured role resolved to a concrete model, used by role cycling and
 *  the plan-approval model slider. */
export interface ResolvedRoleModel {
    role: string;
    model: Model;
    thinkingLevel?: ConfiguredThinkingLevel;
    explicitThinkingLevel: boolean;
}
/** The set of resolvable role models plus the index of the currently active
 *  one within {@link ResolvedRoleModel.role} order. */
export interface RoleModelCycle {
    models: ResolvedRoleModel[];
    currentIndex: number;
}
export interface ContextUsageBreakdown {
    contextWindow: number;
    anchored: boolean;
    usedTokens: number;
    systemPromptTokens: number;
    systemToolsTokens: number;
    systemContextTokens: number;
    skillsTokens: number;
    messagesTokens: number;
}
/** Session statistics for /session command */
export interface SessionStats {
    sessionFile: string | undefined;
    sessionId: string;
    userMessages: number;
    assistantMessages: number;
    toolCalls: number;
    toolResults: number;
    totalMessages: number;
    tokens: {
        input: number;
        output: number;
        reasoning: number;
        cacheRead: number;
        cacheWrite: number;
        total: number;
    };
    premiumRequests: number;
    cost: number;
    contextUsage?: ContextUsage;
}
/** Advisor statistics for /advisor status command. */
export interface AdvisorStats {
    configured: boolean;
    active: boolean;
    model?: Model;
    contextWindow: number;
    contextTokens: number;
    tokens: {
        input: number;
        output: number;
        reasoning: number;
        cacheRead: number;
        cacheWrite: number;
        total: number;
    };
    cost: number;
    messages: {
        user: number;
        assistant: number;
        total: number;
    };
    /** Per-advisor breakdown; one entry per active advisor (single-advisor sessions have one). */
    advisors: PerAdvisorStat[];
}
/** One advisor's slice of {@link AdvisorStats}, surfaced for the multi-advisor status panel. */
export interface PerAdvisorStat {
    name: string;
    model: Model;
    contextWindow: number;
    contextTokens: number;
    tokens: AdvisorStats["tokens"];
    cost: number;
    messages: AdvisorStats["messages"];
}
export interface FreshSessionResult {
    previousSessionId: string;
    sessionId: string;
    closedProviderSessions: number;
}
/** Entry returned by {@link AgentSession.clearQueue} / {@link AgentSession.popLastQueuedMessage}. */
export type RestoredQueuedMessage = {
    text: string;
    images?: ImageContent[];
};
type SessionNameTrigger = "replan";
export declare class AgentSession {
    #private;
    readonly agent: Agent;
    readonly sessionManager: SessionManager;
    readonly settings: Settings;
    readonly yieldQueue: YieldQueue;
    fileSnapshotStore?: InMemorySnapshotStore;
    readonly configWarnings: string[];
    readonly rawSseDebugBuffer: RawSseDebugBuffer;
    constructor(config: AgentSessionConfig);
    /** Model registry for API key resolution and model discovery */
    get modelRegistry(): ModelRegistry;
    get asyncJobManager(): AsyncJobManager | undefined;
    getAgentId(): string | undefined;
    /**
     * The per-turn tool-choice directive for the agent loop's `getToolChoice`. Priority:
     *   1. a HARD forced choice from the queue (genuine forces: user-force, eager-todo, …) —
     *      consuming (advances the queue generator);
     *   2. else, when a non-forcing preview is pending, a {@link SoftToolRequirement} — a
     *      PEEK (advances/pops nothing), so the agent-loop injects the reminder once per head
     *      and escalates to a forced `resolve` only if the model declines. A compliant turn
     *      pays ZERO tool_choice change (no prompt-cache messages-cache invalidation);
     *   3. else undefined.
     */
    nextToolChoiceDirective(): ToolChoiceDirective | undefined;
    /** Peek the head non-forcing pending preview invoker, for the `resolve` tool's dispatch. */
    peekPendingInvoker(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
    /** Clear stale non-forcing pending preview invokers after `resolve` proves none can run. */
    clearPendingInvokers(): void;
    /**
     * Force the next model call to target a specific active tool, then terminate
     * the agent loop. Pushes a two-step sequence [forced, "none"] so the model
     * calls exactly the forced tool once and then cannot call another.
     */
    setForcedToolChoice(toolName: string): void;
    /** The tool-choice queue: forces forthcoming tool invocations and carries handlers. */
    get toolChoiceQueue(): ToolChoiceQueue;
    /** Peek the in-flight directive's invocation handler for use by the resolve tool. */
    peekQueueInvoker(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
    peekStandingResolveHandler(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
    setStandingResolveHandler(handler: ((input: unknown) => Promise<unknown> | unknown) | null): void;
    setSessionSwitchReconciler(reconciler: (() => Promise<void>) | null): void;
    /** Provider-scoped mutable state store for transport/session caches. */
    get providerSessionState(): Map<string, ProviderSessionState>;
    /** Hint forwarded to provider calls that support websocket transport. */
    get preferWebsockets(): boolean | undefined;
    getHindsightSessionState(): HindsightSessionState | undefined;
    setHindsightSessionState(state: HindsightSessionState | undefined): HindsightSessionState | undefined;
    getMnemopiSessionState(): MnemopiSessionState | undefined;
    /** TTSR manager for time-traveling stream rules */
    get ttsrManager(): TtsrManager | undefined;
    /** Secret obfuscator, when secrets are configured; /share redaction reuses it. */
    get obfuscator(): SecretObfuscator | undefined;
    /** Whether a TTSR abort is pending (stream was aborted to inject rules) */
    get isTtsrAbortPending(): boolean;
    /** Whether an expected internal plan-mode abort is pending. Consumed by
     *  `#handleAgentEvent` to stamp `SILENT_ABORT_MARKER` on the next aborted
     *  assistant message_end; callers clear it in `finally`. */
    get isPlanInternalAbortPending(): boolean;
    /** Arm the silent-abort marker for the next aborted assistant message_end.
     *  Caller MUST clear via `clearPlanInternalAbortPending()` in a `finally`
     *  to guarantee no leak. */
    markPlanInternalAbortPending(): void;
    /** Unconditionally clear the silent-abort flag. Idempotent: safe when the
     *  flag was never set OR was already consumed by `#handleAgentEvent`. */
    clearPlanInternalAbortPending(): void;
    getAsyncJobSnapshot(options?: {
        recentLimit?: number;
    }): AsyncJobSnapshot | null;
    /**
     * Emit a UI-only notice to the session. Surfaces in interactive mode as a
     * `showWarning` / `showError` / `showStatus` line; non-interactive modes
     * receive the event through the normal subscribe stream.
     *
     * Notices are NOT added to agent state and never reach the LLM — use this
     * for out-of-band conditions the user should see but the model shouldn't
     * react to (e.g. background queue flush failures).
     */
    emitNotice(level: "info" | "warning" | "error", message: string, source?: string): void;
    /**
     * Subscribe to agent events.
     * Session persistence is handled internally (saves messages on message_end).
     * Multiple listeners can be added. Returns unsubscribe function for this listener.
     */
    subscribe(listener: AgentSessionEventListener): () => void;
    subscribeCommandMetadataChanged(listener: CommandMetadataChangedListener): () => void;
    /** True once dispose() has begun; deferred background work (e.g. the deferred
     *  MCP discovery task in sdk.ts) must not touch the session past this point. */
    get isDisposed(): boolean;
    markMovedFromEmptySessionFile(sessionFile: string): void;
    /**
     * Synchronously mark the session as disposing so new work is rejected
     * immediately: eval starts throw, queued asides are dropped, and the
     * aside provider is detached. Idempotent; `dispose()` runs it first.
     *
     * Wrappers that await other teardown before delegating to `dispose()` MUST
     * call this before their first await — otherwise work started in that async
     * gap slips past the disposal guards.
     */
    beginDispose(): void;
    dispose(options?: AgentSessionDisposeOptions): Promise<void>;
    freshSession(): FreshSessionResult | undefined;
    /** Full agent state */
    get state(): AgentState;
    /** Current model (may be undefined if not yet selected) */
    get model(): Model | undefined;
    /** Effective thinking level applied to the agent (the resolved level when `auto`). */
    get thinkingLevel(): ThinkingLevel | undefined;
    /** The selector the user configured: `auto` when auto mode is active, else the effective level. */
    configuredThinkingLevel(): ConfiguredThinkingLevel | undefined;
    /** True when `auto` thinking mode is active. */
    get isAutoThinking(): boolean;
    /** The level `auto` resolved to for the current turn (undefined until classified). */
    autoResolvedThinkingLevel(): Effort | undefined;
    /** Live per-family service tiers (OpenAI / Anthropic / Google). */
    get serviceTierByFamily(): ServiceTierByFamily;
    /** Whether agent is currently streaming a response */
    get isStreaming(): boolean;
    get isAborting(): boolean;
    /** Wait until streaming and deferred recovery work are fully settled. */
    waitForIdle(): Promise<void>;
    drainAsyncJobDeliveriesForAcp(options?: {
        timeoutMs?: number;
    }): Promise<boolean>;
    /** Most recent assistant message in agent state. */
    getLastAssistantMessage(): AssistantMessage | undefined;
    /** Current effective system prompt blocks (includes any per-turn extension modifications) */
    get systemPrompt(): string[];
    /** Current retry attempt (0 if not retrying) */
    get retryAttempt(): number;
    /**
     * Get the names of currently active tools.
     * Returns the names of tools currently set on the agent.
     */
    getActiveToolNames(): string[];
    /** Whether the edit tool is registered in this session. */
    get hasEditTool(): boolean;
    /**
     * Get a tool by name from the registry.
     */
    getToolByName(name: string): AgentTool | undefined;
    /** True when the current registry entry for `name` came from a built-in factory. */
    hasBuiltInTool(name: string): boolean;
    /**
     * Get all configured tool names (built-in via --tools or default, plus custom tools).
     */
    getAllToolNames(): string[];
    isMCPDiscoveryEnabled(): boolean;
    /**
     * Flip MCP discovery on after deferred discovery learns the real tool count.
     * UI sessions resolve `tools.discoveryMode: "auto"` before MCP servers
     * connect, so a large MCP toolset discovered later must be able to upgrade
     * the session from the force-activate path to the discovery path. One-way:
     * discovery is never downgraded mid-session.
     */
    enableMCPDiscovery(): void;
    getSelectedMCPToolNames(): string[];
    activateDiscoveredMCPTools(toolNames: string[]): Promise<string[]>;
    isToolDiscoveryEnabled(): boolean;
    getDiscoverableTools(filter?: {
        source?: DiscoverableTool["source"];
    }): DiscoverableTool[];
    getDiscoverableToolSearchIndex(): DiscoverableToolSearchIndex;
    getSelectedDiscoveredToolNames(): string[];
    activateDiscoveredTools(toolNames: string[]): Promise<string[]>;
    /**
     * Reload the SSH tool from disk-backed capability discovery and make the
     * refreshed definition visible to the next model call without restarting.
     */
    refreshSshTool(options?: {
        activateIfAvailable?: boolean;
    }): Promise<void>;
    /**
     * Set active tools by name.
     * Only tools in the registry can be enabled. Unknown tool names are ignored.
     * Also rebuilds the system prompt to reflect the new tool set.
     * Changes take effect before the next model call.
     */
    setActiveToolsByName(toolNames: string[]): Promise<void>;
    /** Rebuild the base system prompt using the current active tool set. */
    refreshBaseSystemPrompt(): Promise<void>;
    /**
     * Replace MCP tools in the registry and recompute the visible MCP tool set immediately.
     * This allows /mcp add/remove/reauth to take effect without restarting the session.
     *
     * @param mcpTools The new MCP tools to register.
     * @param options.activateAll When true, force-activates every newly registered MCP tool
     *   regardless of prior selection state. Used when an ACP client provisions MCP servers
     *   for a session where MCP discovery is disabled.
     */
    refreshMCPTools(mcpTools: CustomTool[], options?: {
        activateAll?: boolean;
    }): Promise<void>;
    /**
     * Replace RPC host-owned tools and refresh the active tool set before the next model call.
     */
    refreshRpcHostTools(rpcTools: AgentTool[]): Promise<void>;
    /** Whether auto-compaction is currently running */
    get isCompacting(): boolean;
    /**
     * Whether idle-flush tasks, auto-continuations, or other short-lived
     * post-prompt work are pending.  True in the brief window after
     * `session.prompt()` returns but before a scheduled background delivery
     * (e.g. an async-job result) has finished its own streaming turn.
     * Loop-mode and similar auto-submit paths should treat this as a block
     * to avoid racing against the delivery turn.
     */
    get hasPostPromptWork(): boolean;
    /** All messages including custom types like BashExecutionMessage */
    get messages(): AgentMessage[];
    /** Latest image attachments addressable by tools as `Image #N` or `attachment://N`. */
    getImageAttachments(): {
        label: string;
        uri: string;
        image: ImageContent;
    }[];
    buildDisplaySessionContext(): SessionContext;
    /**
     * Transcript for TUI display. Full history is kept for export/resume-style
     * callers; live chat can collapse compacted history to keep the hot render
     * surface bounded. Display-only — NEVER feed the result to
     * `agent.replaceMessages` or a provider.
     */
    buildTranscriptSessionContext(options?: Pick<BuildSessionContextOptions, "collapseCompactedHistory">): SessionContext;
    /** Convert session messages using the same pre-LLM pipeline as the active session. */
    convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]>;
    /** Apply session-level stream hooks to a direct side request. */
    prepareSimpleStreamOptions(options: SimpleStreamOptions, provider?: string): SimpleStreamOptions;
    /** Current steering mode */
    get steeringMode(): "all" | "one-at-a-time";
    /** Current follow-up mode */
    get followUpMode(): "all" | "one-at-a-time";
    /** Current interrupt mode */
    get interruptMode(): "immediate" | "wait";
    /** Current session file path, or undefined if sessions are disabled */
    get sessionFile(): string | undefined;
    /** Current session ID */
    get sessionId(): string;
    getEvalSessionId(): string | null;
    /** Current session display name, if set */
    get sessionName(): string | undefined;
    /** Scoped models for cycling (from --models flag) */
    get scopedModels(): ReadonlyArray<{
        model: Model;
        thinkingLevel?: ThinkingLevel;
    }>;
    /** Prompt templates */
    getPlanModeState(): PlanModeState | undefined;
    setPlanModeState(state: PlanModeState | undefined): void;
    getGoalModeState(): GoalModeState | undefined;
    setGoalModeState(state: GoalModeState | undefined): void;
    get goalRuntime(): GoalRuntime;
    markPlanReferenceSent(): void;
    setPlanReferencePath(path: string): void;
    getPlanReferencePath(): string;
    get clientBridge(): ClientBridge | undefined;
    setClientBridge(bridge: ClientBridge | undefined): void;
    getCheckpointState(): CheckpointState | undefined;
    getLastCompletedRewind(): CompletedRewindState | undefined;
    setCheckpointState(state: CheckpointState | undefined): void;
    /**
     * Inject the plan mode context message into the conversation history.
     */
    sendPlanModeContext(options?: {
        deliverAs?: "steer" | "followUp" | "nextTurn";
    }): Promise<void>;
    sendGoalModeContext(options?: {
        deliverAs?: "steer" | "followUp" | "nextTurn";
    }): Promise<void>;
    resolveRoleModel(role: string): Model | undefined;
    /**
     * Resolve a role to its model AND thinking level.
     * Unlike resolveRoleModel(), this preserves the thinking level suffix
     * from role configuration (e.g., "anthropic/claude-sonnet-4-5:xhigh").
     */
    resolveRoleModelWithThinking(role: string): ResolvedModelRoleValue;
    get promptTemplates(): ReadonlyArray<PromptTemplate>;
    /** Replace file-based slash commands used for prompt expansion. */
    setSlashCommands(slashCommands: FileSlashCommand[]): void;
    /** Custom commands (TypeScript slash commands and MCP prompts) */
    get customCommands(): ReadonlyArray<LoadedCustomCommand>;
    /** MCP prompt commands only, for command-list metadata. */
    get mcpPromptCommands(): ReadonlyArray<LoadedCustomCommand>;
    /** Update the MCP prompt commands list. Called when server prompts are (re)loaded. */
    setMCPPromptCommands(commands: LoadedCustomCommand[]): void;
    /**
     * Send a prompt to the agent.
     * - Handles extension commands (registered via pi.registerCommand) immediately, even during streaming
     * - Expands file-based prompt templates by default
     * - During streaming, queues via steer() or followUp() based on streamingBehavior option
     * - Validates model and API key before sending (when not streaming)
     * @throws Error if streaming and no streamingBehavior specified
     * @throws Error if no model selected or no API key available (when not streaming)
     */
    /**
     * Returns `false` when the command was fully handled locally (extension or
     * custom-TS command consumed without calling the LLM). Returns `true` when
     * the prompt was forwarded to the agent — either directly or queued as a
     * steer/follow-up. Callers that render a UI or manage turn lifecycle (e.g.
     * the ACP agent) use this to know whether to expect an `agent_end` event.
     */
    prompt(text: string, options?: PromptOptions): Promise<boolean>;
    promptCustomMessage<T = unknown>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">, options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice"> & {
        queueChipText?: string;
        queueOnly?: boolean;
    }): Promise<void>;
    /**
     * Queue a steering message to interrupt the agent mid-run.
     */
    steer(text: string, images?: ImageContent[]): Promise<void>;
    /**
     * Queue a follow-up message to process after the agent would otherwise stop.
     * Set `options.synthetic` to enqueue a hidden developer message (agent-attributed
     * by default) instead of a user-attributed follow-up; the plan-approval flow
     * uses this to land its execution directive behind a queued user turn without
     * flipping advisor auto-resume.
     */
    followUp(text: string, images?: ImageContent[], options?: FollowUpOptions): Promise<void>;
    queueDeferredMessage(message: CustomMessage): void;
    /**
     * Send a custom message to the session. Creates a CustomMessageEntry.
     *
     * Handles three cases:
     * - Streaming: queue as steer/follow-up or store for next turn
     * - Not streaming + triggerTurn: appends to state/session, starts new turn unless the client cannot own it
     * - Not streaming + no trigger: appends to state/session, no turn
     *
     * @returns true iff this call synchronously started a new turn (awaited
     * `agent.prompt`); false when the message was queued/appended without a turn
     * — including when `triggerTurn` is downgraded because the client defers
     * agent-initiated turns. Callers that must mirror the resulting `agent_end`
     * use this to avoid acting on a turn that never ran.
     */
    sendCustomMessage<T = unknown>(message: CustomMessagePayload<T>, options?: {
        triggerTurn?: boolean;
        deliverAs?: "steer" | "followUp" | "nextTurn";
        queueChipText?: string;
    }): Promise<boolean>;
    /**
     * Send a user message through the prompt flow.
     *
     * Omitted `deliverAs` starts a turn when idle and queues as a steer while streaming.
     * Explicit `deliverAs` queues without starting a turn in either state.
     */
    sendUserMessage(content: string | (TextContent | ImageContent)[], options?: {
        deliverAs?: "steer" | "followUp";
    }): Promise<void>;
    /** Clear queued messages and return the user-restorable ones (text plus any attached images).
     *  Only user-authored messages (plain user turns, `attribution:"user"` custom like `/skill`) are
     *  returned for editor restore. Other queued messages stay in the agent-core queues so a continuing
     *  stream still delivers them — EXCEPT on `forInterrupt` (Esc+abort), where only advisor cards are
     *  kept (abort()'s #extractQueuedAdvisorCards preserves them as visible advice) and every other
     *  non-user steer (hidden goal/plan/budget, IRC/extension asides) is dropped, so abort()'s
     *  #drainStrandedQueuedMessages can't auto-resume the run the user just interrupted (the drain only
     *  fires while agent.hasQueuedMessages()). Plain Alt+Up dequeue preserves those non-user steers. */
    clearQueue(options?: {
        forInterrupt?: boolean;
    }): {
        steering: RestoredQueuedMessage[];
        followUp: RestoredQueuedMessage[];
    };
    /** Number of pending displayable messages (includes steering, follow-up, and next-turn messages).
     *  Reflects actual queued work (advisor cards included) — feeds hasPendingMessages()/RPC and the
     *  empty-submit abort gate. The user-restorable subset is surfaced by getQueuedMessages()/clearQueue(). */
    get queuedMessageCount(): number;
    getQueuedMessages(): {
        steering: readonly string[];
        followUp: readonly string[];
    };
    /**
     * Pop the last queued message (steering first, then follow-up).
     * Used by dequeue keybinding to restore messages to editor one at a time.
     * Steps over agent-authored queued messages (advisor cards, hidden/internal steers).
     */
    popLastQueuedMessage(): RestoredQueuedMessage | undefined;
    get skillsSettings(): SkillsSettings | undefined;
    /** Skills loaded by SDK (empty if --no-skills or skills: [] was passed) */
    get skills(): readonly Skill[];
    /** Skill loading warnings captured by SDK */
    get skillWarnings(): readonly SkillWarning[];
    getTodoPhases(): TodoPhase[];
    setTodoPhases(phases: TodoPhase[]): void;
    /** Currently-applied {@link TITLE_SYSTEM.md} override, or undefined when the
     *  bundled prompt is in effect. Consumed by {@link InteractiveMode} so the
     *  first-input title path and the replan refresh share one source. */
    get titleSystemPrompt(): string | undefined;
    /** Replace the title-generation system prompt override. Called by
     *  {@link InteractiveMode.refreshTitleSystemPrompt} after the session cwd
     *  changes (e.g. `/move` relocation) so the next replan refresh resolves
     *  against the destination project's override. */
    setTitleSystemPrompt(prompt: string | undefined): void;
    /**
     * Abort current operation and wait for agent to become idle.
     *
     * `reason` (e.g. `USER_INTERRUPT_LABEL`) rides the agent's `AbortController`
     * and surfaces verbatim on the aborted assistant message's `errorMessage`, so
     * the transcript can distinguish a deliberate user interrupt from an opaque
     * abort. Omit it for internal/lifecycle aborts.
     */
    abort(options?: {
        goalReason?: "interrupted" | "internal";
        reason?: string;
        /** Internal `/compact` startup keeps the manual-compaction marker alive while aborting the active turn. */
        preserveCompaction?: boolean;
    }): Promise<void>;
    /**
     * Start a new session, optionally with initial messages and parent tracking.
     * Clears all messages and starts a new session.
     * Listeners are preserved and will continue receiving events.
     * @param options - Optional initial messages and parent session path
     * @returns true if completed, false if cancelled by hook
     */
    newSession(options?: NewSessionOptions): Promise<boolean>;
    /**
     * Set a display name for the current session.
     */
    setSessionName(name: string, source?: "auto" | "user", trigger?: SessionNameTrigger): Promise<boolean>;
    /**
     * Fork the current session, creating a new session file with the exact same state.
     * Copies all entries and artifacts to the new session.
     * Unlike newSession(), this preserves all messages in the agent state.
     * @returns true if completed, false if cancelled by hook or not persisting
     */
    fork(): Promise<boolean>;
    /**
     * Set model directly.
     * Validates that a credential source is configured (synchronously, without
     * refreshing OAuth or running command-backed key programs). Active switches
     * always take effect; if the current transcript is too large for the target
     * model, the next prompt's compaction/error path owns that recovery instead
     * of leaving the session pinned to the old model.
     * @throws Error if no API key available for the model
     */
    setModel(model: Model, role?: string, options?: {
        selector?: string;
        thinkingLevel?: ThinkingLevel;
        persist?: boolean;
        currentContextTokens?: number;
    }): Promise<{
        switched: boolean;
    }>;
    /**
     * Set model temporarily (for this session only).
     * Validates that a credential source is configured (synchronously, without
     * refreshing OAuth or running command-backed key programs), saves to session
     * log but NOT to settings.
     * @throws Error if no API key available for the model
     */
    setModelTemporary(model: Model, thinkingLevel?: ConfiguredThinkingLevel, options?: {
        ephemeral?: boolean;
    }): Promise<void>;
    /**
     * Cycle to next/previous model.
     * Uses scoped models (from --models flag) if available, otherwise all available models.
     * @param direction - "forward" (default) or "backward"
     * @returns The new model info, or undefined if only one model available
     */
    cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
    /**
     * Resolve the configured role models in the given order plus the index of
     * the currently active one. Roles that have no configured model, or whose
     * configured model is not currently available, are skipped. The `default`
     * role falls back to the active model when no explicit assignment exists.
     *
     * Returns `undefined` only when there is no current model or no available
     * models at all; an empty `models` array is never returned (callers should
     * still guard on `models.length`).
     */
    getRoleModelCycle(roleOrder: readonly string[]): RoleModelCycle | undefined;
    /**
     * Apply a resolved role model as the active model without changing global
     * settings. Shared with role cycling and the plan-approval model slider.
     */
    applyRoleModel(entry: ResolvedRoleModel): Promise<void>;
    /**
     * Cycle through configured role models in a fixed order.
     * Skips missing roles and changes only the active session model.
     * @param roleOrder - Order of roles to cycle through (e.g., ["slow", "default", "smol"])
     * @param direction - "forward" (default) or "backward"
     */
    cycleRoleModels(roleOrder: readonly string[], direction?: "forward" | "backward"): Promise<RoleModelCycleResult | undefined>;
    /**
     * Get all available models with valid API keys, filtered by `enabledModels` when configured.
     * See {@link filterAvailableModelsByEnabledPatterns} for supported pattern forms and limitations.
     */
    getAvailableModels(): Model[];
    /**
     * Set the thinking level. `auto` enables per-turn classification; the selector
     * itself is never written to the session log, but resolved concrete levels are
     * persisted when real user turns are classified so resumed sessions keep the
     * last resolved effort instead of reverting to pending auto.
     */
    setThinkingLevel(level: ConfiguredThinkingLevel | undefined, persist?: boolean): void;
    /**
     * Cycle to next thinking level: off → auto → minimal..xhigh → off.
     * @returns New selector, or undefined if model doesn't support thinking
     */
    cycleThinkingLevel(): ConfiguredThinkingLevel | undefined;
    /**
     * True when the currently selected model's family is set to `priority` — the
     * `/fast` on/off state for the active model. Returns false when no model is
     * selected or the model exposes no service-tier family (e.g. Fireworks, which
     * has its own Providers › Fireworks Tier toggle).
     *
     * For "is priority actually applied to the next request?" use
     * {@link isFastModeActive} instead.
     */
    isFastModeEnabled(): boolean;
    /**
     * True when `priority` is actually realized on the wire for the currently
     * selected model (OpenAI/Google `service_tier`, direct Anthropic fast mode,
     * or Fireworks priority). Returns false for tiers the active model can't
     * realize and when no model is selected.
     */
    isFastModeActive(): boolean;
    /** Set one family's tier (or clear it with `undefined`); persists the change. */
    setServiceTierFamily(family: ServiceTierFamily, tier: ServiceTier | undefined): void;
    /**
     * `/fast on|off` targets the family of the currently selected model: it sets
     * (or clears) that family's `priority` tier. Returns `false` when the model
     * has no service-tier family, so callers can report that fast mode is
     * unavailable instead of claiming success.
     */
    setFastMode(enabled: boolean): boolean;
    toggleFastMode(): boolean;
    /**
     * Get available thinking levels for current model.
     */
    getAvailableThinkingLevels(): ReadonlyArray<Effort>;
    /**
     * Set steering mode.
     * Saves to settings.
     */
    setSteeringMode(mode: "all" | "one-at-a-time"): void;
    /**
     * Set follow-up mode.
     * Saves to settings.
     */
    setFollowUpMode(mode: "all" | "one-at-a-time"): void;
    /**
     * Set interrupt mode.
     * Saves to settings.
     */
    setInterruptMode(mode: "immediate" | "wait"): void;
    /**
     * Strip image content blocks from every message on the current branch and
     * persist the rewrite. Walks `SessionManager.getBranch()` in place — both
     * `SessionMessageEntry.message` and `CustomMessageEntry.content` arrays
     * are mutated, then `rewriteEntries` durably commits the new shape. The
     * agent's runtime view is rebuilt from the freshly-mutated entries so any
     * provider sessions caching message identity (Codex Responses) are torn
     * down to force a clean replay on the next turn.
     *
     * No-op when the branch carries no images; returns `{ removed: 0 }` and
     * skips the disk rewrite.
     */
    dropImages(): Promise<{
        removed: number;
    }>;
    /**
     * Surgically reduce context by dropping heavy content ("shake").
     *
     * - `images` delegates to {@link dropImages}.
     * - `elide` replaces whole tool-call results and large fenced/XML blocks
     *   with short placeholders that embed an `artifact://` recovery link.
     *
     * Mutates the branch in place, persists via `rewriteEntries`, replays the
     * rebuilt context through the agent, and tears down provider sessions that
     * cache message identity — same rewrite contract as {@link dropImages}.
     *
     * No-op (zero counts) when nothing is eligible.
     */
    shake(mode: ShakeMode, opts?: {
        config?: ShakeConfig;
        signal?: AbortSignal;
    }): Promise<ShakeResult>;
    /**
     * Manually compact the session context.
     * Aborts current agent operation first.
     * @param customInstructions Optional instructions for the compaction summary
     * @param options Optional callbacks for completion/error handling
     */
    compact(customInstructions?: string, options?: CompactOptions): Promise<CompactionResult>;
    /**
     * Cancel in-progress context maintenance (manual compaction, auto-compaction, or auto-handoff).
     */
    abortCompaction(): void;
    /** Trigger idle compaction through the auto-compaction flow (with UI events). */
    runIdleCompaction(): Promise<void>;
    /**
     * Cancel in-progress branch summarization.
     */
    abortBranchSummary(): void;
    /**
     * Cancel in-progress handoff generation.
     */
    abortHandoff(): void;
    /**
     * Check if handoff generation is in progress.
     */
    get isGeneratingHandoff(): boolean;
    /**
     * Generate a handoff document with a oneshot LLM call, then start a new session with it.
     *
     * @param customInstructions Optional focus for the handoff document
     * @param options Handoff execution options
     * @returns The handoff document text, or undefined if cancelled/failed
     */
    handoff(customInstructions?: string, options?: SessionHandoffOptions): Promise<HandoffResult | undefined>;
    /**
     * Toggle auto-compaction setting.
     */
    setAutoCompactionEnabled(enabled: boolean): void;
    /** Whether auto-compaction is enabled */
    get autoCompactionEnabled(): boolean;
    /**
     * Cancel in-progress retry.
     */
    abortRetry(): void;
    /** Whether auto-retry is currently in progress */
    get isRetrying(): boolean;
    /** Whether auto-retry is enabled */
    get autoRetryEnabled(): boolean;
    /**
     * Toggle auto-retry setting.
     */
    setAutoRetryEnabled(enabled: boolean): void;
    /**
     * Manually retry the last failed assistant turn.
     * Removes the error message from agent state and re-attempts with a fresh retry budget.
     * @returns true if retry was initiated, false if no failed turn to retry or agent is busy
     */
    retry(): Promise<boolean>;
    /**
     * Execute a bash command.
     * Adds result to agent context and session.
     * @param command The bash command to execute
     * @param onChunk Optional streaming callback for output
     * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)
     * @param options.useUserShell If true, allow caller to request configured user-shell routing
     */
    executeBash(command: string, onChunk?: (chunk: string) => void, options?: {
        excludeFromContext?: boolean;
        useUserShell?: boolean;
    }): Promise<BashResult>;
    /**
     * Record a bash execution result in session history.
     * Used by executeBash and by extensions that handle bash execution themselves.
     */
    recordBashResult(command: string, result: BashResult, options?: {
        excludeFromContext?: boolean;
    }): void;
    /**
     * Cancel running bash command.
     */
    abortBash(): void;
    /** Whether a bash command is currently running */
    get isBashRunning(): boolean;
    /** Whether there are pending bash messages waiting to be flushed */
    get hasPendingBashMessages(): boolean;
    /**
     * Execute Python code in the shared kernel.
     * Uses the same kernel session as eval's Python backend, allowing collaborative editing.
     * @param code The Python code to execute
     * @param onChunk Optional streaming callback for output
     * @param options.excludeFromContext If true, execution won't be sent to LLM ($$ prefix)
     */
    executePython(code: string, onChunk?: (chunk: string) => void, options?: {
        excludeFromContext?: boolean;
    }): Promise<PythonResult>;
    assertEvalExecutionAllowed(): void;
    /**
     * Track Python work started outside AgentSession.executePython so dispose can await and abort it too.
     */
    trackEvalExecution<T>(execution: Promise<T>, abortController: AbortController): Promise<T>;
    /**
     * Record a Python execution result in session history.
     */
    recordPythonResult(code: string, result: PythonResult, options?: {
        excludeFromContext?: boolean;
    }): void;
    /**
     * Cancel running Python execution.
     */
    abortEval(): void;
    /** Whether a Python execution is currently running */
    get isEvalRunning(): boolean;
    /** Whether there are pending Python messages waiting to be flushed */
    get hasPendingPythonMessages(): boolean;
    /**
     * Surfaces and consumes pending IRC incoming records before the next model
     * step can inject them automatically.
     *
     * Tool results already expose the formatted body to the model. Leaving the
     * same record in either pending IRC queue would deliver it a second time at
     * the next step boundary — including on `peek`, which is why inbox peeks
     * also drain here.
     */
    drainPendingIrcInboxMessages(agentId: string, opts?: {
        from?: string;
        limit?: number;
    }): IrcMessage[];
    /**
     * Deliver an IRC message into this session (recipient side; called by the
     * IrcBus). Emits the `irc_message` session event for UI cards and injects
     * the rendered message into the model's context as an `irc:incoming`
     * custom message:
     *
     * - mid-turn → queued on the aside channel and folded in at the next step
     *   boundary (non-interrupting, like async-result deliveries) → "injected";
     * - idle in plan mode → appended into context without waking an autonomous
     *   turn (convergence stays user-driven) → "injected";
     * - idle → starts a real turn with the message so the recipient wakes
     *   → "woken".
     *
     * Never blocks on the recipient's turn: the wake turn is fire-and-forget.
     *
     * When the sender expects a reply (`send await:true`) and this session
     * cannot produce a real reply turn in time — mid-turn with async execution
     * disabled (the next step boundary may be gated on the sender's own batch
     * finishing), or idle in plan mode (wake turns are suppressed) — an
     * ephemeral side-channel auto-reply is generated from the current context
     * (the old `respondAsBackground` path) and sent back over the bus on this
     * agent's behalf.
     */
    deliverIrcMessage(msg: IrcMessage, opts?: {
        expectsReply?: boolean;
    }): Promise<"injected" | "woken">;
    /**
     * Emit an IRC relay observation event on this session for UI rendering only.
     * Does not persist the record to history. Called by the IrcBus to surface
     * agent↔agent traffic on the main session.
     */
    emitIrcRelayObservation(record: CustomMessage): void;
    /**
     * Run a single ephemeral side-channel turn against this session's current
     * model + system prompt + history. The main turn's tool catalog is sent
     * to preserve the prompt cache, but the model is reminded not to call
     * tools and any tool calls are discarded. The side request
     * does not block on, or interfere with, any in-flight main turn. The
     * session's history and persisted state are NOT modified by this call.
     *
     * Used by `BtwController` (`/btw`) and `OmfgController` (`/omfg`) to share
     * the snapshot + stream pipeline. The snapshot includes any in-flight
     * streaming assistant text so the model sees the half-finished response
     * rather than missing context.
     */
    runEphemeralTurn(args: {
        promptText: string;
        onTextDelta?: (delta: string) => void;
        signal?: AbortSignal;
        dedupeReply?: boolean;
    }): Promise<{
        replyText: string;
        assistantMessage: AssistantMessage;
    }>;
    /**
     * Reload the current session from disk.
     *
     * Intended for extension commands and headless modes to re-read the current session
     * file and re-emit session_switch hooks.
     */
    reload(): Promise<void>;
    /**
     * Switch to a different session file.
     * Aborts current operation, loads messages, restores model/thinking.
     * Listeners are preserved and will continue receiving events.
     * @returns true if switch completed, false if cancelled by hook
     */
    switchSession(sessionPath: string): Promise<boolean>;
    /**
     * Create a branch from a specific entry.
     * Emits before_branch/branch session events to hooks.
     *
     * @param entryId ID of the entry to branch from
     * @returns Object with:
     *   - selectedText: The text of the selected user message (for editor pre-fill)
     *   - cancelled: True if a hook cancelled the branch
     */
    branch(entryId: string): Promise<{
        selectedText: string;
        cancelled: boolean;
    }>;
    branchFromBtw(question: string, assistantMessage: AssistantMessage): Promise<{
        cancelled: boolean;
        sessionFile: string | undefined;
    }>;
    /**
     * Navigate to a different node in the session tree.
     * Unlike branch() which creates a new session file, this stays in the same file.
     *
     * @param targetId The entry ID to navigate to
     * @param options.summarize Whether user wants to summarize abandoned branch
     * @param options.customInstructions Custom instructions for summarizer
     * @returns Result with editorText (if user message) and cancelled status
     */
    navigateTree(targetId: string, options?: {
        summarize?: boolean;
        customInstructions?: string;
    }): Promise<{
        editorText?: string;
        cancelled: boolean;
        aborted?: boolean;
        summaryEntry?: BranchSummaryEntry;
        /** Raw session context built during navigation — pass to renderInitialMessages to skip a second O(N) walk. */
        sessionContext?: SessionContext;
    }>;
    /**
     * Get all user messages from session for branch selector.
     */
    getUserMessagesForBranching(): Array<{
        entryId: string;
        text: string;
    }>;
    /**
     * Get session statistics.
     */
    getSessionStats(): SessionStats;
    /**
     * Get current context usage statistics.
     * Uses the last assistant message's usage data when available,
     * otherwise estimates tokens for all messages.
     */
    getContextBreakdown(options?: {
        contextWindow?: number;
        pendingMessages?: AgentMessage[];
    }): ContextUsageBreakdown | undefined;
    getContextUsage(options?: {
        contextWindow?: number;
    }): ContextUsage | undefined;
    /**
     * Monotonic counter that changes whenever the in-flight pending context
     * snapshot is set or cleared. Status-line context memoization keys on this so
     * a value computed mid-turn cannot persist after the turn ends/aborts.
     */
    get contextUsageRevision(): number;
    fetchUsageReports(signal?: AbortSignal): Promise<UsageReport[] | null>;
    /**
     * Redeem one saved Codex rate-limit reset for a specific account, injecting
     * the provider base URL like {@link AgentSession.fetchUsageReports}. Powers
     * the `/usage reset` command and auto-redeem. Never throws for business
     * outcomes — inspect the returned `code`.
     */
    redeemResetCredit(target: ResetCreditTarget, signal?: AbortSignal): Promise<ResetCreditRedeemOutcome>;
    /**
     * List saved Codex rate-limit resets per stored account, fetched live from
     * the dedicated credits endpoint (bypasses the usage cache). Powers the
     * `/usage reset` account selector.
     */
    listResetCredits(signal?: AbortSignal): Promise<ResetCreditAccountStatus[]>;
    /**
     * Export session to HTML.
     * @param outputPath Optional output path (defaults to session directory)
     * @returns Path to exported file
     */
    exportToHtml(outputPath?: string): Promise<string>;
    /**
     * Get text content of last assistant message.
     * Useful for /copy command.
     * @returns Text content, or undefined if no assistant message exists
     */
    getLastAssistantText(): string | undefined;
    hasCopyCandidateAssistantMessage(): boolean;
    /**
     * Get text content of the most recent visible handoff message.
     * Fresh handoff sessions store the handoff context as a custom message, not
     * an assistant message, so callers that copy the "last" message can use this
     * as a fallback before the new session has an assistant response.
     */
    getLastVisibleHandoffText(): string | undefined;
    /**
     * Format the entire session as plain text for clipboard export: system
     * prompt, model/thinking config, tool inventory, and the full transcript
     * rendered with markdown role headings (`## User`, `## Assistant`,
     * `### Tool Call`/`### Tool Result`).
     */
    formatSessionAsText(): string;
    /**
     * Dump the current session's LLM-facing request context as JSON to a
     * auto-named file in `os.tmpdir()`. This is the synchronous
     * `convertToLlm`-boundary snapshot — system prompt, tools (wire schemas),
     * thinking/service tier, and converted messages — with no network round-trip
     * and no arming flag, so advisor/side requests cannot intercept it.
     *
     * The file persists on disk and may contain the same raw context/secrets
     * as `/dump`; treat the path accordingly.
     *
     * @returns the written file path, or `undefined` when there are no messages.
     */
    dumpLlmRequestToTmpDir(): Promise<string | undefined>;
    /**
     * Enable or disable the advisor for this session. The setting is overridden for the session,
     * and the runtime is started or stopped to match.
     *
     * @returns true when the advisor is actively running after the call.
     */
    setAdvisorEnabled(enabled: boolean): boolean;
    /**
     * Toggle the advisor setting and start/stop the runtime accordingly.
     *
     * @returns true when the advisor is actively running after the call.
     */
    toggleAdvisorEnabled(): boolean;
    /**
     * Replace the live advisor roster from an edited `WATCHDOG.yml` (the `/advisor
     * configure` save path). Swaps the configs + shared baseline, then rebuilds the
     * runtimes in place so the change applies without a restart. When the advisor is
     * disabled the new configs are simply stored for the next enable.
     *
     * @returns the number of advisors active after the rebuild.
     */
    applyAdvisorConfigs(advisors: AdvisorConfig[], sharedInstructions: string | undefined): number;
    /**
     * Whether the advisor setting is enabled for this session.
     */
    isAdvisorEnabled(): boolean;
    /**
     * Whether a live advisor agent is attached to this session. True only when
     * `advisor.enabled` is set AND a model resolved for the `advisor` role AND
     * the advisor applies to this agent kind — i.e. the actual runtime exists,
     * not merely the setting. Drives the status-line badge and `/dump advisor`.
     */
    isAdvisorActive(): boolean;
    /**
     * The names of the tools available to advisors this session (the pool a
     * `/advisor configure` editor lists). The advisor is a full agent, so this is the
     * full built tool set; a tool whose optional factory returns null (e.g. lsp with
     * no servers) is absent.
     */
    getAdvisorAvailableToolNames(): string[];
    /**
     * The live advisor `Agent`, or `undefined` when no advisor runtime is
     * attached. Surfaced for diagnostics (`/dump advisor` already serializes
     * its transcript via {@link formatAdvisorHistoryAsText}) and so callers can
     * verify the advisor inherits the session's provider-shaping options
     * (`streamFn`, `promptCacheKey`, `providerSessionState`, ...).
     */
    getAdvisorAgent(): Agent | undefined;
    /**
     * Return structured advisor stats for the status command and TUI panel.
     */
    getAdvisorStats(): AdvisorStats;
    /**
     * Format a concise advisor status line for ACP/text output.
     */
    formatAdvisorStatus(): string;
    /**
     * Format the advisor agent's own transcript (its system prompt, config,
     * tools, and the markdown deltas it received plus its thinking/advise/read
     * calls) as plain text — the advisor-side equivalent of
     * {@link formatSessionAsText}. Returns null when no advisor is active.
     */
    formatAdvisorHistoryAsText(options?: {
        compact?: boolean;
    }): string | null;
    /**
     * Check if extensions have handlers for a specific event type.
     */
    hasExtensionHandlers(eventType: string): boolean;
    /**
     * Get the extension runner (for setting UI context and error handlers).
     */
    get extensionRunner(): ExtensionRunner | undefined;
}
