/**
 * toolChoice/pick — ask the classifier which tool, and decide what is served.
 *
 * Pattern: a pure question builder (`toolChoiceQuestion`) + one async asker
 *          that never throws (`pickTools`) + one pure narrowing decision
 *          (`narrowServed`) — the `findings/judge.ts` split, with the
 *          serving half kept pure so every skip reason is testable without
 *          a run.
 * Role:    core/ layer. Reached ONLY from `slots/buildToolsSlot.ts ·
 *          composeStage` through `import()` under `.toolChoice()` — the
 *          optional-family law of docs-next's site budget (the
 *          `toolCalls.ts · judgeLanded` precedent): an agent that never
 *          asked never loads this module.
 *
 * WHAT IS ASKED. One `choice` question, id `TOOL_CHOICE_QUESTION`, whose
 * criteria are the OFFERED tools by name with their own descriptions —
 * declared before the call, the same law as declared tags and routes. The
 * state is the user's message and, when a skill is active, its id; nothing
 * the library wrote. The doors are not candidates: a door is served whatever
 * the classifier says, so asking about it would only dilute the distribution.
 *
 * WHAT IS DECIDED. `narrowServed` serves the top-N of the provider's ranking
 * plus the doors, in the merged wire's order, and the full wire — with the
 * reason on the row — when the classifier failed or scored fewer than N
 * names (`unavailable`), when fewer than N + 1 candidates were offered
 * (`too-few` — there is nothing to leave out), on the call right after a
 * miss (`after-miss` — the model just showed the ranking was wrong), and on
 * the wrap-up call (`wrap-up` — every tool comes off at assembly anyway).
 */
import type { Classifier, ClassifyRequest } from '../../../classify/types.js';
import { type NarrowSkipReason, type ToolChoiceEntry, type ToolChoiceScore } from './types.js';
/** One offered tool as the classifier reads it. */
export interface ToolCandidate {
    readonly name: string;
    readonly description: string;
}
/**
 * Build the classifier request for one call. Pure. `criteria` is `{ name:
 * description }` over the candidates in offered order; an empty description
 * is sent as the empty string, never invented.
 */
export declare function toolChoiceQuestion(userMessage: string, currentSkillId: string | undefined, candidates: readonly ToolCandidate[]): ClassifyRequest;
/** The classifier's answer for one call, before the serving decision — or the failure. */
export type Pick = {
    readonly ok: true;
    readonly classifier: {
        readonly name: string;
        readonly model: string;
    };
    readonly ranked: readonly ToolChoiceScore[];
    readonly chosen?: string;
    readonly confidence: number;
    readonly usage?: {
        readonly inputTokens: number;
        readonly outputTokens: number;
    };
    readonly latencyMs: number;
} | {
    readonly ok: false;
    readonly classifier: {
        readonly name: string;
    };
    readonly status?: number;
    readonly message: string;
    readonly latencyMs: number;
};
/**
 * Ask the classifier once. Never throws: a provider failure, a network
 * error, an abort, a reply with no `choice` answer under the question id —
 * all come back as `ok: false` with the latency spent, because the pick is
 * advisory and its absence must never cost the run its answer.
 */
export declare function pickTools(classifier: Classifier, input: {
    readonly userMessage: string;
    readonly currentSkillId?: string;
    readonly candidates: readonly ToolCandidate[];
}, signal?: AbortSignal): Promise<Pick>;
/** What `narrowServed` decides for one call. */
export interface Narrowing {
    /** The names to commit, in the merged wire's order. */
    readonly served: readonly string[];
    readonly narrowed: boolean;
    readonly narrowedSkipped?: NarrowSkipReason;
}
/**
 * Decide what is served for one call. Pure. `wire` is the merged list's
 * names in order; `doors` every name that is served whatever the ranking
 * says (`ALWAYS_SERVED_TOOLS` plus the app's own); `top` is N, or
 * `undefined` for an advisory-only agent (the full wire, `narrowed: false`,
 * no reason — nothing was skipped because nothing was asked).
 */
export declare function narrowServed(input: {
    readonly wire: readonly string[];
    readonly doors: ReadonlySet<string>;
    readonly pick: Pick;
    readonly top: number | undefined;
    /** This call's iteration — `after-miss` reads the outcome row at `iteration - 1`, never merely the last row on the record. */
    readonly iteration: number;
    /** The rows already on the record — the immediately-prior iteration's outcome is read for `after-miss`. */
    readonly prior: readonly ToolChoiceEntry[];
    readonly wrapUpAsked: boolean;
    /** The run's tools are off after the evidence revision (an enforcing answer validation). */
    readonly toolsOff?: boolean;
}): Narrowing;
/** Every name served whatever the ranking says: the framework's doors plus the app's own. */
export declare function alwaysServedNames(extra: readonly string[] | undefined): ReadonlySet<string>;
