import type { Agent, AgentLoopFramework, AgentLoopFrameworkInput, AgentLoopPolicyOptions, AgentMessage, AgentState, AgentTool, ThinkingLevel } from "@catui/agent-core"; import { type RunTraceEventV1 } from "@catui/agent-core"; import type { DocumentContent, ImageContent, Model, TextContent } from "@catui/ai/types"; import type { Theme as ThemeContract } from "../theme-contract.js"; import type { BashResult } from "../platform/exec/bash-executor.js"; import { type CompactionResult } from "../session/compaction/index.js"; import { type ContextUsage, type ExtensionCommandContextActions, type ExtensionErrorListener, ExtensionRunner, type ExtensionUIContext, type InputSource, type ShutdownHandler, type ToolDefinition, type ToolInfo } from "../extensions-host/index.js"; import type { CustomMessage } from "../messages.js"; import type { ModelRegistry } from "../model-registry.js"; import { type PromptTemplate } from "../prompt/prompt-templates.js"; import type { ResourceLoader } from "../platform/config/resource-loader.js"; import { SessionManager, type BranchSummaryEntry, type SessionInfo, type SessionListProgress } from "../session/session-manager.js"; import type { SettingsManager } from "../platform/config/settings-manager.js"; import { AgentDirContext } from "../agent-dir/agent-dir-context.js"; import type { BashOperations } from "../tools/bash.js"; import { type ModelCycleResult } from "./model-controller.js"; import type { AgentSessionEventListener } from "./session-events.js"; import { type SessionSlashCommandDescriptor } from "./slash-command-catalog.js"; import { type CreateSessionFn } from "../sub-agent/index.js"; import { type SessionStats } from "./session-queries.js"; export type { SessionSlashCommandDescriptor } from "./slash-command-catalog.js"; export { CycleModelError } from "./model-controller.js"; export type { ModelCycleResult } from "./model-controller.js"; export { parseSkillBlock, pruneRecoverableErrorTail } from "./session-recovery.js"; export type { ParsedSkillBlock } from "./session-recovery.js"; export type { AgentSessionEvent, AgentSessionEventListener } from "./session-events.js"; export interface AgentSessionConfig { agent: Agent; sessionManager: SessionManager; settingsManager: SettingsManager; cwd: string; /** Global agent config directory for user-scoped resources. */ agentDir: string; /** Multi-agent context. */ agentCtx: AgentDirContext; /** Models to cycle through with Ctrl+P (from --models flag) */ scopedModels?: Array<{ model: Model; thinkingLevel: ThinkingLevel; }>; /** Resource loader for skills, prompts, themes, context files, system prompt */ resourceLoader: ResourceLoader; /** SDK custom tools registered outside extensions */ customTools?: ToolDefinition[]; /** Optional dynamic tool factory (e.g. MCP) refreshed on reload */ mcpToolsFactory?: () => Promise; /** Initial dynamic tools for first session build */ initialMcpTools?: ToolDefinition[]; /** @deprecated NanoSoul is suspended; ignored. */ soulManagerFactory?: () => Promise; /** Model registry for API key resolution and model discovery */ modelRegistry: ModelRegistry; /** @deprecated NanoSoul is suspended; ignored. */ soulManager?: any; /** Initial active built-in tool names. Default: [read, bash, edit, write] */ initialActiveToolNames?: string[]; /** Override base tools (useful for custom runtimes). */ baseToolsOverride?: Record; /** Mutable ref used by Agent to access the current ExtensionRunner */ extensionRunnerRef?: { current?: ExtensionRunner; }; /** External abort signal for stopping the session (e.g., from SubAgent runtime) */ signal?: AbortSignal; /** * Theme used to render custom extension tools when exporting a session to HTML. * Injected by the composition root (UI layer owns the theme); when omitted, HTML * export still works but skips custom-tool rendering. Keeps core/runtime from * importing the modes/ UI theme singleton (U2). */ theme?: ThemeContract; /** * Factory for creating child AgentSession instances (used by the Agent sub-agent tool). * Injected by the composition root (sdk.ts) to avoid a circular dependency * between agent-session.ts and sdk.ts. */ createSession?: CreateSessionFn; /** Debug event verbosity level. Default: "off" */ debugLevel?: "off" | "basic" | "verbose"; } export interface ExtensionBindings { uiContext?: ExtensionUIContext; commandContextActions?: ExtensionCommandContextActions; shutdownHandler?: ShutdownHandler; onError?: ExtensionErrorListener; } /** 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). Required if streaming. */ streamingBehavior?: "steer" | "followUp"; /** Source of input for extension input event handlers. Defaults to "interactive". */ source?: InputSource; } export type { SessionStats } from "./session-queries.js"; export type SlashCommandExecutor = (text: string) => Promise; /** Standard thinking levels */ /** * Base class holding AgentSession's session logic. The exported `AgentSession` * composes `SessionSettingsAccessors` for the settings getter/setter surface. */ declare class AgentSessionBase { readonly agent: Agent; readonly sessionManager: SessionManager; readonly settingsManager: SettingsManager; readonly agentCtx: AgentDirContext; private _scopedModels; private _unsubscribeAgent?; private _detachExternalAbort?; private readonly _listeners; private readonly _messageQueue; private _retryCoordinator; private _logger; private _bashRunner; private _extensionRunner; private _slashCommandExecutor; private readonly _eventHandler; private _resourceLoader; private _resourcesDiscovered; /** Injected theme for HTML-export custom-tool rendering (U2: no modes import). */ private _theme?; /** Debug event verbosity level. */ private _debugLevel; private _dbg; private _customTools; private _staticCustomTools; private _mcpToolsFactory?; private _baseToolRegistry; /** CC-style Agent tool — recreated on each _buildRuntime() */ private _agentTool?; /** Factory for creating child sessions (injected via config to avoid sdk.ts cycle) */ private _createSessionFactory?; /** Shared InProcessSubAgentBackend for session tracking (SendMessage routing, CC §XI) */ private _subAgentBackend?; private _cwd; private _extensionRunnerRef?; /** * Idempotency guard for the one-shot MCP capabilities hint CustomMessage. * Set after the first warmupMcpTools() persists the hint so a reload that * re-warms MCP doesn't append the hint a second time within the same * session. Reset on /clear and similar destructive operations; for * simplicity we leave it sticky here — appending the same hint twice is * harmless (it's just slightly more verbose context), but skipping is * tidier. */ private _mcpCapabilitiesInjected; private _initialActiveToolNames?; private _baseToolsOverride?; private _extensionUIContext?; private _extensionCommandContextActions?; private _extensionShutdownHandler?; private _extensionErrorListener?; private _extensionErrorUnsubscriber?; private _modelRegistry; private _agentDir; private _baseSystemPrompt; private readonly _modelController; private readonly _compactionController; private readonly _contextWindowController; private readonly _compactionCoordinator; private readonly _sessionTreeController; private readonly _lifecycleController; private readonly _toolOrchestrator; private readonly _toolRuntimeController; constructor(config: AgentSessionConfig); /** Model registry for API key resolution and model discovery */ get modelRegistry(): ModelRegistry; get cwd(): string; get agentDir(): string; /** * Return all currently available slash-like commands for the session. * Includes built-in commands, extension commands, prompt templates, and skills. */ getSlashCommands(): SessionSlashCommandDescriptor[]; /** * Try to execute an extension slash command directly. * Returns true when a matching extension command was found, even if it failed internally. */ tryExecuteExtensionCommand(text: string): Promise; executeSlashCommand(text: string): Promise; setSlashCommandExecutor(executor: SlashCommandExecutor | undefined): void; /** Emit an event to all listeners */ private _emit; private _emitDebug; private readonly _runTrace; private _handleAgentEvent; /** Extract text content from a message */ private _getUserMessageText; /** Find the last assistant message in agent state (including aborted ones) */ private _findLastAssistantMessage; /** * 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; /** * Temporarily disconnect from agent events. * User listeners are preserved and will receive events again after resubscribe(). * Used internally during operations that need to pause event processing. */ private _disconnectFromAgent; /** * Reconnect to agent events after _disconnectFromAgent(). * Preserves all existing listeners. */ private _reconnectToAgent; /** * Remove all listeners and disconnect from agent. * Call this when completely done with the session. */ dispose(): void; /** Full agent state */ get state(): AgentState; /** Current model (may be undefined if not yet selected) */ get model(): Model | undefined; /** Current thinking level */ get thinkingLevel(): ThinkingLevel; /** Current effective agent loop framework. */ get agentLoopFramework(): AgentLoopFramework; /** Whether agent is currently streaming a response */ get isStreaming(): boolean; /** Current effective system prompt (includes any per-turn extension modifications) */ get systemPrompt(): string; /** @deprecated NanoSoul is suspended; always undefined. */ get soulManager(): unknown | undefined; /** Latest completed semantic run trace, returned as an isolated snapshot. */ getLastRunTrace(): readonly RunTraceEventV1[] | undefined; /** Workspace-local JSONL path for the latest completed semantic run trace. */ getLastRunTracePath(): string | undefined; /** 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[]; /** * Get all configured tools with name, description, and parameter schema. */ getAllTools(): ToolInfo[]; /** * 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 on the next agent turn. */ setActiveToolsByName(toolNames: string[]): void; /** Whether auto-compaction is currently running */ get isCompacting(): boolean; /** All messages including custom types like BashExecutionMessage */ get messages(): AgentMessage[]; /** Current steering mode */ get steeringMode(): "all" | "one-at-a-time"; /** Current follow-up mode */ get followUpMode(): "all" | "one-at-a-time"; /** Current session file path, or undefined if sessions are disabled */ get sessionFile(): string | undefined; /** Current session ID */ get sessionId(): string; /** Current session display name, if set */ get sessionName(): string | undefined; /** Scoped models for cycling (from --models flag) */ get scopedModels(): ReadonlyArray<{ model: Model; thinkingLevel: ThinkingLevel; }>; /** Update scoped models for cycling */ setScopedModels(scopedModels: Array<{ model: Model; thinkingLevel: ThinkingLevel; }>): void; /** File-based prompt templates */ get promptTemplates(): ReadonlyArray; private _rebuildSystemPrompt; private _getActiveBaseToolNames; /** * Send a prompt to the agent. * - Handles extension commands (registered via api.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) */ prompt(text: string, options?: PromptOptions): Promise; /** * Try to execute an extension command. Returns true if command was found and executed. * * Delegates to ExtensionRunner.invokeCommand() so command dispatch, error * routing (emitError), and telemetry (ext_command_events) all happen in one * place rather than being scattered across modes. */ private _tryExecuteExtensionCommand; /** * Expand skill commands (/skill:name args) to their full content. * Returns the expanded text, or the original text if not a skill command or skill not found. * Emits errors via extension runner if file read fails. */ private _expandSkillCommand; /** * Queue a steering message to interrupt the agent mid-run. * Delivered after current tool execution, skips remaining tools. * Expands skill commands and prompt templates. Errors on extension commands. * @param images Optional image attachments to include with the message * @throws Error if text is an extension command */ steer(text: string, images?: ImageContent[]): Promise; /** * Queue a follow-up message to be processed after the agent finishes. * Delivered only when agent has no more tool calls or steering messages. * Expands skill commands and prompt templates. Errors on extension commands. * @param images Optional image attachments to include with the message * @throws Error if text is an extension command */ followUp(text: string, images?: ImageContent[]): Promise; /** * Internal: Queue a steering message (already expanded, no extension command check). */ private _queueSteer; /** * Internal: Queue a follow-up message (already expanded, no extension command check). */ private _queueFollowUp; /** * Throw an error if the text is an extension command. */ private _throwIfExtensionCommand; /** * Send a custom message to the session. Creates a CustomMessageEntry. * * Handles three cases: * - Streaming: queues message, processed when loop pulls from queue * - Not streaming + triggerTurn: appends to state/session, starts new turn * - Not streaming + no trigger: appends to state/session, no turn * * @param message Custom message with customType, content, display, details * @param options.triggerTurn If true and not streaming, triggers a new LLM turn * @param options.deliverAs Delivery mode: "steer", "followUp", or "nextTurn" */ sendCustomMessage(message: Pick, "customType" | "content" | "display" | "details">, options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn"; }): Promise; /** * Send a user message to the agent. Always triggers a turn. * When the agent is streaming, use deliverAs to specify how to queue the message. * * @param content User message content (string or content array) * @param options.deliverAs Delivery mode when streaming: "steer" or "followUp" */ sendUserMessage(content: string | (TextContent | ImageContent | DocumentContent)[], options?: { deliverAs?: "steer" | "followUp"; }): Promise; /** * Clear all queued messages and return them. * Useful for restoring to editor when user aborts. * @returns Object with steering and followUp arrays */ clearQueue(): { steering: string[]; followUp: string[]; }; /** Number of pending messages (includes both steering and follow-up) */ get pendingMessageCount(): number; /** Get pending steering messages (read-only) */ getSteeringMessages(): readonly string[]; /** Get pending follow-up messages (read-only) */ getFollowUpMessages(): readonly string[]; get resourceLoader(): ResourceLoader; /** * Abort current operation and wait for agent to become idle. */ abort(): Promise; /** * 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.parentSession - Optional parent session path for tracking * @param options.setup - Optional callback to initialize session (e.g., append messages) * @returns true if completed, false if cancelled by extension */ newSession(options?: { parentSession?: string; setup?: (sessionManager: SessionManager) => Promise; }): Promise; /** Set model directly. @throws if no API key available. */ setModel(model: Model): Promise; /** Cycle to next/previous model. @returns new model info, or undefined if only one. */ cycleModel(direction?: "forward" | "backward"): Promise; /** Set thinking level, clamped to model capabilities; persists on change. */ setThinkingLevel(level: ThinkingLevel): void; /** Set the session-level agent loop framework override. */ setAgentLoopFramework(framework: AgentLoopFrameworkInput | undefined): void; /** Update runtime loop policy options for subsequent turns. */ setLoopPolicy(options: Partial): void; /** Cycle to next thinking level. @returns new level, or undefined if unsupported. */ cycleThinkingLevel(): ThinkingLevel | undefined; /** Thinking levels available for the current model. */ getAvailableThinkingLevels(): ThinkingLevel[]; /** Whether the current model supports xhigh thinking level. */ supportsXhighThinking(): boolean; /** Whether the current model supports thinking/reasoning. */ supportsThinking(): boolean; /** * Set steering message mode. * Saves to settings. */ setSteeringMode(mode: "all" | "one-at-a-time"): void; /** * Set follow-up message mode. * Saves to settings. */ setFollowUpMode(mode: "all" | "one-at-a-time"): void; /** Current theme name. */ compact(customInstructions?: string): Promise; /** Request a handoff without interrupting the active tool batch. */ requestContextWindow(handoff: string): boolean; /** Commit a queued handoff before the next model request; preserve context on failure. */ prepareContextWindow(messages: AgentMessage[]): AgentMessage[]; /** * Cancel in-progress compaction (manual or auto). */ abortCompaction(): void; /** * Cancel in-progress branch summarization. */ abortBranchSummary(): void; /** * Toggle auto-compaction setting. */ setAutoCompactionEnabled(enabled: boolean): void; /** Whether auto-compaction is enabled */ get autoCompactionEnabled(): boolean; bindExtensions(bindings: ExtensionBindings): Promise; private extendResourcesFromExtensions; private _applyExtensionBindings; private _bindExtensionCore; private _buildRuntime; /** * Run the MCP tools factory and merge the result into `_customTools`. * Shared by reload() and warmupMcpTools(). Does NOT rebuild the runtime — * the caller decides when to call _buildRuntime() (reload batches it with the * resource refresh; warmup rebuilds on its own). * @returns number of MCP tools now loaded. */ private _refreshMcpTools; /** * Load MCP tools and merge them into the live runtime WITHOUT a full reload. * * Startup no longer blocks on MCP server spawn/handshake (which can take many * seconds — npx-based default servers measured ~20s). Interactive mode calls * this fire-and-forget once the UI is ready; one-shot modes (print/acp/rpc) * await it before their first turn (createAgentSession does this internally * unless `deferMcpInit` is set). No-op when MCP is disabled. * * Emits `sdk:mcp_ready` so the UI can surface a status line. */ warmupMcpTools(): Promise; reload(): Promise; /** Create the RetryCoordinator host adapter. */ private _createRetryHost; /** * Cancel in-progress retry. */ abortRetry(): void; /** * Wait for any in-progress retry to complete. * Returns immediately if no retry is in progress. */ private waitForRetry; /** Whether auto-retry is currently in progress */ get isRetrying(): boolean; /** * Toggle auto-retry setting. */ executeBash(command: string, onChunk?: (chunk: string) => void, options?: { excludeFromContext?: boolean; operations?: BashOperations; }): Promise; /** * 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; /** * 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 extension */ switchSession(sessionPath: string): Promise; /** * Set a display name for the current session. */ setSessionName(name: string): void; /** * Tag the current session with labels (e.g., "important", "bug-fix"). */ tagSession(tags: string[]): void; /** * Get the current session tags. */ getSessionTags(): string[]; /** * Create a fork from a specific entry. * Emits before_fork/fork session events to extensions. * * @param entryId ID of the entry to fork from * @returns Object with: * - selectedText: The text of the selected user message (for editor pre-fill) * - cancelled: True if an extension cancelled the fork */ fork(entryId: string): Promise<{ selectedText: string; cancelled: boolean; }>; /** * Navigate to a different node in the session tree. * Unlike fork() 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 * @param options.replaceInstructions If true, customInstructions replaces the default prompt * @param options.label Label to attach to the branch summary entry * @returns Result with editorText (if user message) and cancelled status */ navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string; }): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean; summaryEntry?: BranchSummaryEntry; }>; /** * Get all user messages from session for fork selector. */ getUserMessagesForForking(): Array<{ entryId: string; text: string; }>; /** * Get session statistics. */ getSessionStats(): SessionStats; getContextUsage(): ContextUsage | undefined; /** * Export session to HTML. * @param outputPath Optional output path (defaults to session directory) * @returns Path to exported file */ exportToHtml(outputPath?: string): Promise; /** * List sessions for the current project directory. * Returns session metadata sorted by last modified time (newest first). */ listSessions(onProgress?: SessionListProgress): Promise; /** * List all sessions across all project directories. * Returns session metadata sorted by last modified time (newest first). */ listAllSessions(onProgress?: SessionListProgress): Promise; /** Total cost in USD for this session. */ getTotalCost(): number; /** Total token usage breakdown for this session. */ getTotalTokens(): { input: number; output: number; cacheRead: number; cacheWrite: number; total: number; }; /** Tool call count for this session. */ getToolCallCount(): number; /** * Get text content of last assistant message. * Useful for /copy command. * @returns Text content, or undefined if no assistant message exists */ getLastAssistantText(): string | undefined; /** * 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; } declare const AgentSession_base: (abstract new (...args: any[]) => { getTheme(): string | undefined; setTheme(theme: string): void; getShowImages(): boolean; setShowImages(show: boolean): void; getShowTokenStats(): boolean; setShowTokenStats(enabled: boolean): void; getShowWorkingTrace(): boolean; setShowWorkingTrace(enabled: boolean): void; getShowMemoryTrace(): boolean; setShowMemoryTrace(enabled: boolean): void; getBuddyEnabled(): boolean; setBuddyEnabled(enabled: boolean): void; getBuddySpecies(): number; setBuddySpecies(species: number): void; getPresenceEnabled(): boolean; setPresenceEnabled(enabled: boolean): void; getShowHardwareCursor(): boolean; setShowHardwareCursor(enabled: boolean): void; getClearOnShrink(): boolean; setClearOnShrink(enabled: boolean): void; getQuietStartup(): boolean; setQuietStartup(quiet: boolean): void; getHideThinkingBlock(): boolean; setHideThinkingBlock(hide: boolean): void; getDoubleEscapeAction(): "fork" | "tree" | "none"; setDoubleEscapeAction(action: "fork" | "tree" | "none"): void; getEditorPaddingX(): number; setEditorPaddingX(padding: number): void; getAutocompleteMaxVisible(): number; setAutocompleteMaxVisible(maxVisible: number): void; getCodeBlockIndent(): string; getDefaultProvider(): string | undefined; setDefaultProvider(provider: string): void; getDefaultModel(): string | undefined; setDefaultModel(modelId: string): void; setDefaultModelAndProvider(provider: string, modelId: string): void; getThinkingBudgets(): import("../platform/config/settings-manager.js").ThinkingBudgetsSettings | undefined; getEnabledModels(): string[] | undefined; setEnabledModels(patterns: string[] | undefined): void; getImageAutoResize(): boolean; setImageAutoResize(enabled: boolean): void; getBlockImages(): boolean; setBlockImages(blocked: boolean): void; getShellPath(): string | undefined; setShellPath(path: string | undefined): void; getShellCommandPrefix(): string | undefined; setShellCommandPrefix(prefix: string | undefined): void; getAutoUpdate(): "always" | "prompt" | "never"; setAutoUpdate(mode: "always" | "prompt" | "never"): void; getEnableSkillCommands(): boolean; setEnableSkillCommands(enabled: boolean): void; get autoRetryEnabled(): boolean; setAutoRetryEnabled(enabled: boolean): void; settingsManager: SettingsManager; }) & typeof AgentSessionBase; /** * Public AgentSession: session lifecycle + settings accessor surface. * Settings getters/setters live in the SessionSettingsAccessors mixin; * session logic lives in AgentSessionBase. */ export declare class AgentSession extends AgentSession_base { }