/**
 * Media router — one LLM call that picks which image/video model fits
 * the user's request, with alternatives and cost estimates, so the agent
 * can show a clean AskUser proposal before spending.
 *
 * Principle (matches turn-analyzer): harness orchestrates, free model
 * decides. No keyword-to-model mapping in TypeScript. Classifier reads
 * the prompt + the current gateway catalog (pulled dynamically), picks
 * one recommended model plus a cheaper + a premium alternative, and
 * explains the choice in one sentence.
 *
 * Cost estimates come from `gateway-models.ts` — always dynamic,
 * margin-adjusted.
 */
import type { ModelClient } from './llm.js';
export type MediaKind = 'image' | 'video';
export type MediaStyle = 'photoreal' | 'illustration' | 'anime' | 'logo' | 'concept' | 'other';
export type MediaPriority = 'cost' | 'quality' | 'balanced';
export interface MediaChoice {
    model: string;
    estimatedCostUsd: number;
    rationale: string;
}
export interface MediaProposal {
    kind: MediaKind;
    quantity: number;
    durationSeconds?: number;
    maxDurationSeconds?: number;
    recommended: MediaChoice;
    cheaper?: MediaChoice;
    premium?: MediaChoice;
    intent: {
        style: MediaStyle;
        priority: MediaPriority;
    };
    /**
     * A fuller rewrite of the user's prompt, following the 5-slot template
     * (scene/subject/details/use-case/constraints). Null when the classifier
     * judged the input already well-specified, or when the env opt-out is set,
     * or when the rewrite was identical to the raw input. When non-null, the
     * AskUser layout surfaces it as "Refined:" with a "Use ORIGINAL" option.
     */
    refinedPrompt: string | null;
    refinementSummary: string;
    totalCostUsd: number;
}
export declare function clearMediaRouterCache(): void;
/**
 * Normalize a refined prompt: trim, cap length, reject obvious junk.
 * Returns null when the value should be treated as absent (missing,
 * non-string, empty after trim).
 *
 * Exported for testability — invariants matter more here than elsewhere
 * because the output is user-visible and paid for.
 */
export declare function validateRefined(raw: unknown, maxChars: number): string | null;
/**
 * Whitespace-insensitive, case-insensitive identity check — if the
 * classifier's "refinement" is just the input with different spacing,
 * don't bother the user with a "Refined:" block.
 */
export declare function isEffectivelyIdentical(a: string, b: string): boolean;
export declare const REFINED_PROMPT_MAX_CHARS = 500;
export declare const REFINEMENT_SUMMARY_LIMIT = 80;
export interface AnalyzeMediaOpts {
    kind: MediaKind;
    prompt: string;
    client: ModelClient;
    quantity?: number;
    durationSeconds?: number;
    signal?: AbortSignal;
    /**
     * One-shot opt-out — caller stripped a `///` prefix from the user's input
     * and wants the proposal rendered without a Refined block or Use-original
     * option. The classifier still runs (for model selection), but the
     * refinement is discarded at parse time.
     */
    skipRefine?: boolean;
}
/**
 * Pick the best model + alternatives for this media request. Returns null
 * on any failure path (classifier timeout, parse error, empty catalog) so
 * the caller can fall back to its old hardcoded default rather than
 * blocking the user.
 */
export declare function analyzeMediaRequest(opts: AnalyzeMediaOpts): Promise<MediaProposal | null>;
/**
 * Render a proposal as the user-facing AskUser question. Layout matches
 * the spec from v3.8.31 planning: recommended first with • bullet,
 * alternatives below with ○ bullets, prices include the 5% margin note.
 */
export declare function renderProposalForAskUser(p: MediaProposal, userPrompt: string): {
    question: string;
    options: Array<{
        id: string;
        label: string;
    }>;
};
