/**
 * Extension system types.
 *
 * Extensions are TypeScript modules that can:
 * - Subscribe to agent lifecycle events
 * - Register LLM-callable tools
 * - Register commands
 * - Interact with the user via basic UI primitives (select, confirm, input, notify)
 */
import type { TSchema } from '@sinclair/typebox';
import type { AssistantMessage, AssistantMessageEvent, ImageContent, Message, Model, TextContent, ToolResultMessage } from '../../ai/types.js';
import type { AgentModelRegistry } from '../../contracts/agent-model.js';
import type { RuntimeExtensionRuntime } from '../../contracts/extension-runtime.js';
import type { AgentMessage, AgentState, AgentToolResult, AgentToolUpdateCallback, RuntimePermissionPolicy, ToolDefinition, ToolExecutionContext } from '../../core/core-types.js';
import type { CompactionPreparation, CompactionResult } from '../../core/session/compaction/index.js';
import type { CustomMessage } from '../messages.js';
import type { ShortcutLlmProxyClient } from '../providers/shortcut-llm-proxy-client.js';
import type { BashOperations, BashResult } from '../tools/bash-types.js';
export type { AgentToolResult, AgentToolUpdateCallback, ToolDefinition };
/**
 * Context passed to post-turn handlers after each turn_end.
 * Extends ExtensionContext with turn-specific data.
 */
export interface PostTurnContext extends ExtensionContext {
    /** Index of the turn that just completed (0-based). */
    turnIndex: number;
    /** The final assistant message from this turn. */
    message: AgentMessage;
    /** Tool results produced during this turn. */
    toolResults: ToolResultMessage[];
}
/**
 * A registered post-turn handler that runs after each turn_end.
 *
 * Handlers execute between turns with cancellation on user input,
 * priority ordering, and error isolation. The runtime currently runs
 * handlers sequentially.
 */
export interface PostTurnHandler {
    /** Unique name for logging/debugging. */
    name: string;
    /** Execution priority — lower runs first. 0-99 reserved for infrastructure. */
    priority: number;
    /** Compatibility field. The current runtime ignores this and runs handlers sequentially. */
    parallel: boolean;
    /** Fast synchronous check — return false to skip this turn. */
    shouldRun(ctx: PostTurnContext): boolean;
    /** Async work. Receives an AbortSignal that is aborted before the next handler starts once user input is pending. */
    execute(ctx: PostTurnContext, signal: AbortSignal): Promise<void>;
}
/**
 * Context passed to stop hooks when the agent loop completes (agent_end).
 */
export interface StopHookContext {
    /** Full conversation history from the agent_end event. */
    messages: AgentMessage[];
    /** The last assistant message, if any. */
    lastAssistantMessage: AssistantMessage | undefined;
    /** Current turn index. */
    turnIndex: number;
    /** AgentController state (model, tools, messages, isStreaming, etc.) */
    state: AgentState;
}
/**
 * Result returned by a stop hook.
 */
export interface StopHookResult {
    action: 'continue' | 'pass';
    /** If 'continue': inject these as follow-up user messages. */
    followUpMessages?: string[];
    /** If 'continue': blocking errors formatted distinctly as follow-ups. */
    blockingErrors?: string[];
}
/**
 * A stop hook runs after the agent loop completes (on agent_end),
 * before retry/compaction. Can inject follow-up messages to restart
 * the agent loop.
 *
 * Registered via ExtensionAPI.registerStopHook().
 */
export interface StopHook {
    /** Unique name for logging/debugging. */
    name: string;
    /** Execution priority — lower runs first. */
    priority: number;
    /**
     * Called after each agent turn completes (on agent_end).
     * Return 'continue' with follow-up messages to restart the agent,
     * or 'pass' to let the next hook run.
     */
    onTurnEnd(ctx: StopHookContext): Promise<StopHookResult>;
}
/** Options for extension UI dialogs. */
export interface ExtensionUIDialogOptions {
    /** AbortSignal to programmatically dismiss the dialog. */
    signal?: AbortSignal;
    /** Timeout in milliseconds. Dialog auto-dismisses with live countdown display. */
    timeout?: number;
}
/**
 * UI context for extensions to request interactive UI.
 * Keep this intentionally small and host-agnostic.
 */
export interface ExtensionUIContext {
    /** Show a selector and return the user's choice. */
    select(title: string, options: string[], opts?: ExtensionUIDialogOptions): Promise<string | undefined>;
    /** Show a confirmation dialog. */
    confirm(title: string, message: string, opts?: ExtensionUIDialogOptions): Promise<boolean>;
    /** Show a text input dialog. */
    input(title: string, placeholder?: string, opts?: ExtensionUIDialogOptions): Promise<string | undefined>;
    /** Show a notification to the user. */
    notify(message: string, type?: 'info' | 'warning' | 'error'): void;
}
export interface ContextUsage {
    /** Estimated context tokens, or null if unknown (e.g. right after compaction, before next LLM response). */
    tokens: number | null;
    contextWindow: number;
    /** Context usage as percentage of context window, or null if tokens is unknown. */
    percent: number | null;
}
export interface CompactOptions {
    customInstructions?: string;
    onComplete?: (result: CompactionResult) => void;
    onError?: (error: Error) => void;
}
export interface ExtensionSessionContextValue {
    messages: AgentMessage[];
    thinkingLevel: string;
    model: {
        provider: string;
        modelId: string;
    } | null;
}
export interface ExtensionCompactionEntry {
    summary: string;
    firstKeptEntryId: string;
    tokensBefore: number;
    details?: unknown;
    fromHook?: boolean;
}
export interface ExtensionSessionView {
    getCwd(): string;
    getSessionDir(): string;
    getSessionId(): string;
    getSessionFile(): string | undefined;
    getLeafId(): string | null;
    getLeafEntry(): unknown;
    getEntry(id: string): unknown;
    getLabel(entryId: string): string | undefined;
    getBranch(): unknown[];
    getHeader(): unknown;
    getEntries(): unknown[];
    getTree(): unknown[];
    getSessionName(): string | undefined;
}
export interface ExtensionSessionSetupStore extends ExtensionSessionView {
    appendMessage(message: Message | CustomMessage, durationMs?: number): string;
    appendCustomEntry<T = unknown>(customType: string, data?: T): string;
    appendCustomMessageEntry<T = unknown>(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details?: T): string;
    appendThinkingLevelChange(thinkingLevel: string): string;
    appendModelChange(provider: string, modelId: string): string;
    appendSessionInfo(name?: string): string;
    buildSessionContext(): ExtensionSessionContextValue;
}
/**
 * Context passed to extension event handlers.
 */
export interface ExtensionContext extends ToolExecutionContext {
    /** UI methods for user interaction */
    ui: ExtensionUIContext;
    /** Whether UI is available (false in print/RPC mode) */
    hasUI: boolean;
    /** Current working directory */
    cwd: string;
    /** Session manager (read-only) */
    sessionManager: ExtensionSessionView;
    /** Model registry for API key resolution */
    modelRegistry: AgentModelRegistry;
    /** Shortcut LLM proxy client for direct helper model calls */
    shortcutLlmProxy: ShortcutLlmProxyClient;
    /** Current model (may be undefined) */
    model: Model<any> | undefined;
    /** Whether the agent is idle (not streaming) */
    isIdle(): boolean;
    /** Abort the current agent operation */
    abort(): void;
    /** Whether there are queued messages waiting */
    hasPendingMessages(): boolean;
    /** Gracefully shutdown pi and exit. Available in all contexts. */
    shutdown(): void;
    /** Get current context usage for the active model. */
    getContextUsage(): ContextUsage | undefined;
    /** Trigger compaction without awaiting completion. */
    compact(options?: CompactOptions): void;
    /** Get the current effective system prompt. */
    getSystemPrompt(): string;
    /** Deduct child sub-agent usage from parent budget tracking. */
    deductChildUsage(usage: {
        input: number;
        output: number;
        turns: number;
    }): void;
    /** Get the current conversation messages (including custom types). */
    getMessages(): AgentMessage[];
    /** Get the current tool definitions. */
    getTools(): readonly ToolDefinition[];
}
/**
 * Extended context for command handlers.
 * Includes session control methods only safe in user-initiated commands.
 */
export interface ExtensionCommandContext extends ExtensionContext {
    /** Wait for the agent to finish streaming */
    waitForIdle(): Promise<void>;
    /** Start a new session, optionally with initialization. */
    newSession(options?: {
        parentSession?: string;
        setup?: (sessionManager: ExtensionSessionSetupStore) => Promise<void>;
    }): Promise<{
        cancelled: boolean;
    }>;
    /** Fork from a specific entry, creating a new session file. */
    fork(entryId: string): Promise<{
        cancelled: boolean;
    }>;
    /** Navigate to a different point in the session tree. */
    navigateTree(targetId: string, options?: {
        label?: string;
    }): Promise<{
        cancelled: boolean;
    }>;
    /** Switch to a different session file. */
    switchSession(sessionPath: string): Promise<{
        cancelled: boolean;
    }>;
    /** Reload extensions, skills, prompts, and themes. */
    reload(): Promise<void>;
}
/** Fired after session_start to allow extensions to provide additional resource paths. */
export interface ResourcesDiscoverEvent {
    type: 'resources_discover';
    cwd: string;
    reason: 'startup' | 'reload';
}
/** Result from resources_discover event handler */
export interface ResourcesDiscoverResult {
    skillPaths?: string[];
    promptPaths?: string[];
    themePaths?: string[];
    agentPaths?: string[];
}
/** Fired on initial session load */
export interface SessionStartEvent {
    type: 'session_start';
}
/** Fired before switching to another session (can be cancelled) */
export interface SessionBeforeSwitchEvent {
    type: 'session_before_switch';
    reason: 'new' | 'resume';
    targetSessionFile?: string;
}
/** Fired after switching to another session */
export interface SessionSwitchEvent {
    type: 'session_switch';
    reason: 'new' | 'resume';
    previousSessionFile: string | undefined;
}
/** Fired before context compaction (can be cancelled or customized) */
export interface SessionBeforeCompactEvent {
    type: 'session_before_compact';
    preparation: CompactionPreparation;
    branchEntries: unknown[];
    customInstructions?: string;
    signal: AbortSignal;
}
/** Fired after context compaction */
export interface SessionCompactEvent {
    type: 'session_compact';
    compactionEntry: ExtensionCompactionEntry;
    fromExtension: boolean;
}
/** Fired on process exit */
export interface SessionShutdownEvent {
    type: 'session_shutdown';
}
export type SessionEvent = SessionStartEvent | SessionBeforeSwitchEvent | SessionSwitchEvent | SessionBeforeCompactEvent | SessionCompactEvent | SessionShutdownEvent;
/** Fired before each LLM call. Can modify messages. */
export interface ContextEvent {
    type: 'context';
    messages: AgentMessage[];
}
/** Fired after user submits prompt but before agent loop. */
export interface BeforeAgentStartEvent {
    type: 'before_agent_start';
    prompt: string;
    images?: ImageContent[];
    systemPrompt: string;
}
/** Fired when an agent loop starts */
export interface AgentStartEvent {
    type: 'agent_start';
}
/** Fired when an agent loop ends */
export interface AgentEndEvent {
    type: 'agent_end';
    messages: AgentMessage[];
}
/** Fired at the start of each turn */
export interface TurnStartEvent {
    type: 'turn_start';
    turnIndex: number;
    timestamp: number;
}
/** Fired at the end of each turn */
export interface TurnEndEvent {
    type: 'turn_end';
    turnIndex: number;
    message: AgentMessage;
    toolResults: ToolResultMessage[];
}
/** Fired when a message starts (user, assistant, or toolResult) */
export interface MessageStartEvent {
    type: 'message_start';
    message: AgentMessage;
}
/** Fired during assistant message streaming with token-by-token updates */
export interface MessageUpdateEvent {
    type: 'message_update';
    message: AgentMessage;
    assistantMessageEvent: AssistantMessageEvent;
}
/** Fired when a message ends */
export interface MessageEndEvent {
    type: 'message_end';
    message: AgentMessage;
}
/** Fired when a tool starts executing */
export interface ToolExecutionStartEvent {
    type: 'tool_execution_start';
    toolCallId: string;
    toolName: string;
    args: any;
}
/** Fired during tool execution with partial/streaming output */
export interface ToolExecutionUpdateEvent {
    type: 'tool_execution_update';
    toolCallId: string;
    toolName: string;
    args: any;
    partialResult: any;
}
/** Fired when a tool finishes executing */
export interface ToolExecutionEndEvent {
    type: 'tool_execution_end';
    toolCallId: string;
    toolName: string;
    result: any;
    isError: boolean;
}
/** Fired when user executes a bash command via ! prefix */
export interface UserBashEvent {
    type: 'user_bash';
    command: string;
    cwd: string;
}
/** Source of user input */
export type InputSource = 'interactive' | 'rpc' | 'extension';
/** Fired when user input is received, before agent processing */
export interface InputEvent {
    type: 'input';
    text: string;
    images?: ImageContent[];
    source: InputSource;
}
/** Result from input event handler */
export type InputEventResult = {
    action: 'continue';
} | {
    action: 'transform';
    text: string;
    images?: ImageContent[];
} | {
    action: 'handled';
};
/** Fired before a tool executes. Can block. */
export interface ToolCallEvent {
    type: 'tool_call';
    toolName: string;
    toolCallId: string;
    input: Record<string, unknown>;
}
/** Fired after a tool executes. Can modify result. */
export interface ToolResultEvent {
    type: 'tool_result';
    toolName: string;
    toolCallId: string;
    input: Record<string, unknown>;
    content: (TextContent | ImageContent)[];
    details: unknown;
    isError: boolean;
}
/** Union of all event types */
export type ExtensionEvent = ResourcesDiscoverEvent | SessionEvent | ContextEvent | BeforeAgentStartEvent | AgentStartEvent | AgentEndEvent | TurnStartEvent | TurnEndEvent | MessageStartEvent | MessageUpdateEvent | MessageEndEvent | ToolExecutionStartEvent | ToolExecutionUpdateEvent | ToolExecutionEndEvent | UserBashEvent | InputEvent | ToolCallEvent | ToolResultEvent;
export interface ToolCallEventResult {
    block?: boolean;
    reason?: string;
}
export interface ContextEventResult {
    messages?: AgentMessage[];
}
export interface BeforeAgentStartEventResult {
    message?: Pick<CustomMessage, 'customType' | 'content' | 'display' | 'details'>;
    systemPrompt?: string;
}
/** Result from user_bash event handler */
export interface UserBashEventResult {
    operations?: BashOperations;
    result?: BashResult;
}
export interface ToolResultEventResult {
    content?: (TextContent | ImageContent)[];
    details?: unknown;
    isError?: boolean;
}
export interface SessionBeforeSwitchResult {
    cancel?: boolean;
}
export interface SessionBeforeCompactResult {
    cancel?: boolean;
    compaction?: CompactionResult;
}
export interface CommandCompletion {
    value: string;
    label: string;
    description?: string;
}
export interface RegisteredCommand {
    name: string;
    description?: string;
    getArgumentCompletions?: (argumentPrefix: string) => CommandCompletion[] | null;
    handler: (args: string, ctx: ExtensionCommandContext) => Promise<void>;
}
/** Handler function type for events */
export type ExtensionHandler<E, R = undefined> = (event: E, ctx: ExtensionContext) => Promise<R | void> | R | void;
/**
 * ExtensionAPI passed to extension factory functions.
 */
export interface ExtensionAPI {
    on(event: 'session_start', handler: ExtensionHandler<SessionStartEvent>): void;
    on(event: 'session_before_switch', handler: ExtensionHandler<SessionBeforeSwitchEvent, SessionBeforeSwitchResult>): void;
    on(event: 'session_switch', handler: ExtensionHandler<SessionSwitchEvent>): void;
    on(event: 'session_before_compact', handler: ExtensionHandler<SessionBeforeCompactEvent, SessionBeforeCompactResult>): void;
    on(event: 'session_compact', handler: ExtensionHandler<SessionCompactEvent>): void;
    on(event: 'session_shutdown', handler: ExtensionHandler<SessionShutdownEvent>): void;
    on(event: 'context', handler: ExtensionHandler<ContextEvent, ContextEventResult>): void;
    on(event: 'turn_start', handler: ExtensionHandler<TurnStartEvent>): void;
    on(event: 'turn_end', handler: ExtensionHandler<TurnEndEvent>): void;
    on(event: 'before_agent_start', handler: ExtensionHandler<BeforeAgentStartEvent, BeforeAgentStartEventResult>): void;
    on(event: 'input', handler: ExtensionHandler<InputEvent, InputEventResult>): void;
    on(event: 'tool_call', handler: ExtensionHandler<ToolCallEvent, ToolCallEventResult>): void;
    on(event: 'tool_result', handler: ExtensionHandler<ToolResultEvent, ToolResultEventResult>): void;
    on(event: 'before_agent_start', handler: ExtensionHandler<BeforeAgentStartEvent, BeforeAgentStartEventResult>): void;
    /** Register a tool that the LLM can call. */
    registerTool<TParams extends TSchema = TSchema, TDetails = unknown>(tool: ToolDefinition<TParams, TDetails>): void;
    /** Register a custom command. */
    registerCommand(name: string, options: Omit<RegisteredCommand, 'name'>): void;
    /** Append a custom entry to the session for state persistence (not sent to LLM). */
    appendEntry<T = unknown>(customType: string, data?: T): void;
    /** Register a handler that runs after each turn_end with priority ordering and cancellation. */
    registerPostTurnHandler(handler: PostTurnHandler): void;
    /** Register a stop hook that runs after the agent loop completes (agent_end). */
    registerStopHook(hook: StopHook): void;
}
/** Extension factory function type. Supports both sync and async initialization. */
export type ExtensionFactory = (shortcut: ExtensionAPI) => void | Promise<void>;
export type ExtensionVisibility = 'visible' | 'internal';
export interface RegisteredTool {
    definition: ToolDefinition;
    extensionPath: string;
}
/** Tool info with name, description, and parameter schema */
export type ToolInfo = Pick<ToolDefinition, 'name' | 'description' | 'parameters'>;
type HandlerFn = (...args: unknown[]) => Promise<unknown>;
/**
 * Action implementations bound by runner.bindCore().
 * Only includes methods still exposed on the public ExtensionAPI.
 */
export interface ExtensionActions {
    appendEntry: <T = unknown>(customType: string, data?: T) => void;
    refreshTools: () => void;
}
/**
 * Actions for ExtensionContext (ctx.* in event handlers).
 * Required by all modes.
 */
export interface ExtensionContextActions {
    getModel: () => Model<any> | undefined;
    isIdle: () => boolean;
    abort: () => void;
    hasPendingMessages: () => boolean;
    shutdown: () => void;
    getContextUsage: () => ContextUsage | undefined;
    compact: (options?: CompactOptions) => void;
    getSystemPrompt: () => string;
    deductChildUsage: (usage: {
        input: number;
        output: number;
        turns: number;
    }) => void;
    getMessages: () => AgentMessage[];
    getTools: () => readonly ToolDefinition[];
    getPermissionPolicy: () => RuntimePermissionPolicy;
}
/**
 * Actions for ExtensionCommandContext (ctx.* in command handlers).
 * Only needed for interactive mode where extension commands are invokable.
 */
export interface ExtensionCommandContextActions {
    waitForIdle: () => Promise<void>;
    newSession: (options?: {
        parentSession?: string;
        setup?: (sessionManager: ExtensionSessionSetupStore) => Promise<void>;
    }) => Promise<{
        cancelled: boolean;
    }>;
    fork: (entryId: string) => Promise<{
        cancelled: boolean;
    }>;
    navigateTree: (targetId: string, options?: {
        label?: string;
    }) => Promise<{
        cancelled: boolean;
    }>;
    switchSession: (sessionPath: string) => Promise<{
        cancelled: boolean;
    }>;
    reload: () => Promise<void>;
}
/**
 * Shared runtime created by loader, bound by runner.
 * Contains action stubs that are replaced with real implementations.
 */
export interface ExtensionRuntime extends RuntimeExtensionRuntime, ExtensionActions {
}
/** Loaded extension with all registered items. */
export interface Extension {
    path: string;
    resolvedPath: string;
    visibility: ExtensionVisibility;
    handlers: Map<string, HandlerFn[]>;
    tools: Map<string, RegisteredTool>;
    commands: Map<string, RegisteredCommand>;
    postTurnHandlers: PostTurnHandler[];
    stopHooks: StopHook[];
}
/** Result of loading extensions. */
export interface LoadExtensionsResult {
    extensions: Extension[];
    errors: Array<{
        path?: string;
        error: string;
    }>;
    /** Shared runtime - actions are throwing stubs until runner.bindCore() */
    runtime: ExtensionRuntime;
}
export interface ExtensionError {
    extensionPath: string;
    event: string;
    error: string;
    stack?: string;
}
//# sourceMappingURL=types.d.ts.map