import type { Prompter } from "./prompter.js";
/** One choice in a select question. */
export interface SelectOption<T> {
    /** Stable handle so external answers (flags, JSON) can address rich values. */
    id: string;
    label: string;
    value: T;
    hint?: string;
    /**
     * A leading run of featured options forms a searchable picker's default
     * viewport: with no filter typed, only they are in view, and scrolling or
     * filtering reaches the rest of the list. Featured options must be sorted
     * to the front. Meaningless without `search`.
     */
    featured?: boolean;
}
/**
 * One choice in a multi-select question. Row availability is part of the
 * question, not the renderer: an interactive picker renders the reason next to
 * the row, and {@link withAnswers} enforces the same semantics on external
 * answers (a disabled id is refused with its reason, a locked value is part of
 * every answer whether or not it was named).
 */
export interface MultiSelectOption<T> extends SelectOption<T> {
    /** Visible and explained but not pickable. Mutually exclusive with `locked`. */
    disabled?: boolean;
    /** Why the row is disabled; shown by pickers and quoted by answer refusals. */
    disabledReason?: string;
    /** Always selected and cannot be toggled off. */
    locked?: boolean;
    /** Why the row is locked; shown by pickers (e.g. "always available"). */
    lockedReason?: string;
}
/**
 * A keyed question a box sends through the channel. The key is the question's
 * stable identity: decorators match pre-supplied answers and requiredness
 * opt-ins against it, and resolutions are announced under it.
 */
export type Question<T> = {
    key: string;
    message: string;
    /** Found in the world (e.g. an on-disk link). Feeds the confirm/assume rungs. */
    detected?: T;
    /** A safe assumption (e.g. the most popular model). Used by "assume". */
    recommended?: T;
    /**
     * Questions are skippable by default; required is the opt-in. Headless and
     * assume runs auto-skip non-required questions, and only a required question
     * may refuse a headless run ({@link InteractionRequired}). Set by the box
     * when intrinsic, or by the flow via {@link withRequired}.
     */
    required?: boolean;
    /**
     * Synthesized by a decorator (e.g. the confirm-detected rung): interaction
     * mechanics, not a box value, so bases render it but do not announce it.
     */
    internal?: boolean;
} & ({
    kind: "select";
    options: readonly SelectOption<T>[];
    /** Offer the type-ahead filter line in interactive renderers. */
    search?: boolean;
    /** Placeholder for the filter line while it is empty. */
    placeholder?: string;
} | {
    kind: "confirm";
} | {
    kind: "text";
    validate?: (raw: string) => string | null;
    sensitive?: boolean;
    /** Presentation hint: ghost text shown while the input is empty. */
    placeholder?: string;
});
/**
 * A keyed multi-select question. Its answer is a set (`T[]`) while its options
 * carry single values (`T`), so it travels through the paired
 * {@link Asker.askMany} instead of being a {@link Question} kind: forcing it
 * through `ask<T>` would make the channel lie about the answer type.
 */
export interface MultiSelectQuestion<T> {
    key: string;
    message: string;
    options: readonly MultiSelectOption<T>[];
    /** Found in the world. Feeds the confirm/assume rungs and pre-marks pickers. */
    detected?: readonly T[];
    /** A safe assumption. Used by "assume"; pre-marks pickers without a detection. */
    recommended?: readonly T[];
    /** Same opt-in as {@link Question.required}: only it may refuse a headless run. */
    required?: boolean;
    /** Same as {@link Question.internal}: rendered but never announced. */
    internal?: boolean;
    /**
     * Block an empty submission in interactive pickers. Distinct from `required`,
     * which is the headless-refusal opt-in: a question can refuse to be skipped
     * headlessly while still accepting an empty interactive selection.
     */
    requireSelection?: boolean;
    /** Offer the type-ahead filter line in interactive renderers. */
    search?: boolean;
    /** Placeholder for the filter line while it is empty. */
    placeholder?: string;
}
/** Builds a select question without spelling the discriminant at call sites. */
export declare const select: <T>(q: Omit<Extract<Question<T>, {
    kind: "select";
}>, "kind">) => Question<T>;
/** Builds a confirm question without spelling the discriminant at call sites. */
export declare const confirm: (q: {
    key: string;
    message: string;
    detected?: boolean;
    recommended?: boolean;
    required?: boolean;
    internal?: boolean;
}) => Question<boolean>;
/** Builds a text question without spelling the discriminant at call sites. */
export declare const text: (q: {
    key: string;
    message: string;
    detected?: string;
    recommended?: string;
    required?: boolean;
    validate?: (raw: string) => string | null;
    sensitive?: boolean;
    placeholder?: string;
}) => Question<string>;
/** The capability boxes see. Pure and flow-agnostic: Prompter's successor. */
export interface Asker {
    ask<T>(question: Question<T>): Promise<T>;
    /**
     * The multi-select channel. Paired with {@link ask} instead of being a
     * question kind because the answer type (`T[]`) differs from the option type
     * (`T`); every rung treats it with the same ladder semantics as `ask`.
     */
    askMany<T>(question: MultiSelectQuestion<T>): Promise<T[]>;
}
/** A ladder rung: wraps an asker and resolves (or rewrites) some questions. */
export type AskerDecorator = (inner: Asker) => Asker;
/** Thrown when a skippable question is skipped, so the box can branch on it. */
export declare class SkippedSignal extends Error {
    readonly key: string;
    constructor(key: string);
}
/** Any question the channel can carry, for signals that quote one. */
export type AnyQuestion = Question<unknown> | MultiSelectQuestion<unknown>;
/**
 * Headless refusal that keeps the whole question: an agent driver can relay
 * exactly what is missing (key, message, options) instead of a bare string.
 */
export declare class InteractionRequired extends Error {
    readonly question: AnyQuestion;
    constructor(question: AnyQuestion);
}
/** Thrown when a pre-supplied answer fails the question's own validation. */
export declare class InvalidAnswerError extends Error {
    readonly key: string;
    constructor(key: string, message: string);
}
/** How a question got its value, so nothing is silently assumed. */
export type ResolutionSource = "answer" | "detected" | "assumed" | "asked" | "skipped";
/** One announced question outcome. */
export interface Resolution {
    key: string;
    value: unknown;
    source: ResolutionSource;
}
/** Every resolution is announced with its source: nothing is silently assumed. */
export interface AskerEvents {
    onResolved?: (resolution: Resolution) => void;
}
/**
 * The interactive base: renders every question through the existing
 * {@link Prompter}, pre-filled with detected/recommended. A user cancel
 * surfaces as the prompter's own WizardCancelledError and propagates to the
 * runner unchanged. There is no skip gesture on non-required questions yet:
 * the current prompter has none, so it arrives with the prompter rework.
 */
export declare function interactiveAsker(prompter: Prompter, events?: AskerEvents): Asker;
/**
 * The base for flag/agent stacks: auto-skips non-required questions (announced
 * as "skipped"), refuses required ones structurally with
 * {@link InteractionRequired}.
 */
export declare function headlessAsker(events?: AskerEvents): Asker;
/**
 * Pre-answers by question key (flags, config, agent tool args), validated
 * against the question they answer. Unmatched questions fall through.
 */
export declare function withAnswers(answers: Record<string, unknown>, events?: AskerEvents): AskerDecorator;
/**
 * Flow-level opt-in to requiredness by key: the flow declares which questions
 * it cannot proceed without, boxes stay oblivious. Composes with box-intrinsic
 * `required` flags (whichever marks it, it is required).
 */
export declare function withRequired(keys: readonly string[]): AskerDecorator;
/** The two detected/recommended rungs of the ladder. */
export type AnswerPolicy = "confirm-detected" | "assume";
/**
 * The detected/recommended rungs. "confirm-detected" turns a detected value
 * into a one-keystroke confirm (synthesized as an internal question through
 * the same channel); "assume" takes detected or recommended silently but
 * announced, skips the non-required unknowable, and escalates only required
 * unknowables to the inner asker.
 */
export declare function withPolicy(policy: AnswerPolicy, events?: AskerEvents): AskerDecorator;
