/**
 * Multi-strategy error recovery system.
 *
 * Replaces the single-strategy SessionRetry with per-category error
 * classification and distinct recovery strategies. Different errors
 * get fundamentally different treatment:
 *
 * - rate_limit: long exponential backoff (5s base)
 * - overloaded: shorter backoff with jitter, optional model fallback
 * - server_error: exponential backoff with jitter (1s base, 5 attempts, ~24s total)
 * - context_overflow: escalating compaction (standard → aggressive → surface)
 * - auth_error: no retry, surface immediately
 * - tool_error: not retried at session level (model handles via error tool_results)
 * - unknown: conservative single retry
 *
 * The sync/async split from SessionRetry is preserved:
 * classifyError + promise creation happens synchronously in _handleAgentLoopEvent,
 * strategy dispatch happens asynchronously in the queued _processAgentLoopEvent.
 */
import type { AssistantMessage } from '../../ai/types.js';
import type { RuntimeErrorRecoverySettings } from '../../contracts/agent-settings.js';
import type { ShortcutXLModelStreamErrorDetails } from '../../contracts/model-stream.js';
/**
 * Error categories for multi-strategy recovery.
 *
 * `max_output_tokens` and `tool_error` are included for documentation
 * completeness but are handled elsewhere:
 * - max_output_tokens: stopReason='length', handled by plan 06
 * - tool_error: surfaced to model as error tool_results in agent-loop
 */
export type ErrorCategory = 'rate_limit' | 'overloaded' | 'server_error' | 'context_overflow' | 'max_output_tokens' | 'auth_error' | 'tool_error' | 'unknown';
export type ErrorRecoveryEvent = {
    type: 'recovery_start';
    category: ErrorCategory;
    attempt: number;
    maxAttempts: number;
    delayMs: number;
    errorMessage: string;
} | {
    type: 'recovery_end';
    category: ErrorCategory;
    success: boolean;
    attempt: number;
    finalError?: string;
} | {
    type: 'recovery_fallback';
    category: ErrorCategory;
    fallbackModel: string;
};
export type ErrorRecoveryEventListener = (event: ErrorRecoveryEvent) => void;
export type ErrorRecoverySleep = (ms: number, signal?: AbortSignal) => Promise<void>;
/**
 * Callback interface for recovery actions that need to touch agent state.
 * Keeps the recovery module decoupled from AgentController/Session internals.
 */
export interface ErrorRecoveryActions {
    removeLastAssistantMessage(): void;
    continue(): Promise<void> | void;
    /**
     * Run compaction with overridden keepRecentTokens.
     * Returns true if compaction succeeded.
     * Used by the context_overflow strategy for escalating compaction.
     */
    runCompaction(keepRecentTokens: number): Promise<boolean>;
    /**
     * Switch to a fallback model and optionally strip thinking blocks.
     * Returns true if the switch succeeded.
     * Used by the overloaded strategy for model fallback.
     */
    switchToFallbackModel(fallbackModel: string, stripThinking: boolean): Promise<boolean>;
}
/**
 * Function to check if an assistant message represents a context overflow error.
 * Injected to avoid a direct dependency on owned AI's isContextOverflow.
 */
export type ContextOverflowChecker = (message: AssistantMessage) => boolean;
/**
 * owned AI's `AssistantMessage` widened with the runtime-specific `errorDetails`
 * field carried on structured stream errors. Used everywhere runtime code
 * needs to read or write this field.
 */
export type RuntimeAssistantMessage = AssistantMessage & {
    errorDetails?: ShortcutXLModelStreamErrorDetails;
};
/** Accessor for the runtime-only `errorDetails` field on a owned AI assistant message. */
export declare function getErrorDetails(message: AssistantMessage): ShortcutXLModelStreamErrorDetails | undefined;
/**
 * Classify an error into a recovery category.
 *
 * MUST remain synchronous — called from the sync _handleAgentLoopEvent handler
 * before async queue processing, to ensure the recovery promise exists
 * before waitForRetry() is called.
 *
 * @returns ErrorCategory if error is classifiable, null if not an error
 */
export declare function classifyError(message: AssistantMessage, isContextOverflow: ContextOverflowChecker): ErrorCategory | null;
interface StrategyConfig {
    /** Maximum retry attempts. 0 = no retry. */
    maxAttempts: number;
    /** Base delay in ms for exponential backoff. */
    baseDelayMs: number;
    /** Maximum delay cap in ms. */
    maxDelayMs: number;
    /** Whether to add jitter to delay. */
    jitter: boolean;
    /** Whether this category supports model fallback after exhaustion. */
    supportsFallback: boolean;
    /** Actionable message for the user when recovery is exhausted. */
    userMessage?: string;
}
/**
 * Server-error retry budget. 5 attempts × exponential backoff (1s, 2s, 4s,
 * 8s, 10s capped) with ±25% jitter rides out the 5–30s WKWebView fetch flakes
 * ("Load failed", connection resets) that were killing real Excel runs on
 * staging with the previous 2-attempt strategy. Exported so tests can assert
 * against the same source of truth.
 */
export declare const SERVER_ERROR_MAX_ATTEMPTS = 5;
/** Compute delay for a given attempt using the strategy config. */
export declare function computeDelay(config: StrategyConfig, attempt: number): number;
/** Exported for tests */
export declare function getCompactionKeepRecentTokens(attempt: number): number;
/** Get the strategy config for a category. Exported for tests. */
export declare function getStrategyConfig(category: ErrorCategory): StrategyConfig;
export declare class SessionErrorRecovery {
    private _abortController;
    private _attempt;
    private _category;
    private _promise;
    private _resolve;
    private _listener;
    private readonly _sleep;
    constructor(options?: {
        sleep?: ErrorRecoverySleep;
    });
    get attempt(): number;
    get category(): ErrorCategory | null;
    get isRetrying(): boolean;
    onEvent(listener: ErrorRecoveryEventListener): void;
    private _emit;
    private _continueRecovery;
    /** Reset all state to idle. */
    private _reset;
    /**
     * Create the recovery promise synchronously when an agent_end event arrives.
     *
     * This MUST be called synchronously from the agent event handler, before
     * any async processing. The reason: agent.prompt() resolves as soon as
     * agent_end fires, and the caller immediately calls wait().
     * If the promise isn't created yet, wait() returns immediately
     * and the caller proceeds before the recovery completes.
     *
     * @returns the classified ErrorCategory if a recovery promise was created, null otherwise
     */
    prepareForAgentEnd(lastAssistant: AssistantMessage | undefined, isContextOverflow: ContextOverflowChecker, settings: RuntimeErrorRecoverySettings): ErrorCategory | null;
    /**
     * Handle a classified error. Called from the async agent_end processing.
     * Dispatches to the appropriate per-category strategy.
     *
     * @returns true if recovery was initiated (retry or fallback), false if exhausted
     */
    handleError(message: AssistantMessage, category: ErrorCategory, settings: RuntimeErrorRecoverySettings, actions: ErrorRecoveryActions): Promise<boolean>;
    /**
     * Handle context overflow with escalating compaction.
     * Attempt 1: standard compaction (keep 20k tokens)
     * Attempt 2: aggressive compaction (keep 10k tokens)
     * Attempt 3+: surface error to user
     */
    private _handleContextOverflow;
    /**
     * Attempt model fallback after retries are exhausted.
     */
    private _attemptModelFallback;
    /** Handle abort during sleep. */
    private _handleAbort;
    /**
     * Called when a successful assistant response arrives mid-recovery.
     * Resets state and resolves the promise.
     */
    onSuccess(): void;
    /** Cancel in-progress recovery. */
    abort(): void;
    /** Wait for any in-progress recovery to complete. */
    wait(): Promise<void>;
}
export {};
//# sourceMappingURL=session-error-recovery.d.ts.map