/**
 * skillIntent — the intent side of the turn-start routing cascade (SG-C):
 * candidate projection, duplicate-example normalization, the graph's
 * `TurnRoutingPlan` (what the agent's RouteTurn stage consumes), and the
 * leave-one-out `checkupIntents` audit.
 *
 * One module owns how a declared `{ intent, examples }` becomes a scoreable
 * {@link IntentCandidate}, so the router, the menu, the audit and the record
 * can never project the same declaration differently. Pure over the entry
 * declarations `skillGraph.ts` hands it; imports no engine machinery beyond
 * the injection-context type.
 */
import { type InjectionContext } from './types.js';
import type { RouteWitness, SkillMatchData } from './skillMatch.js';
import type { IntentCandidate, IntentScorer } from './intentScorer.js';
import { type RoutingPolicy } from './routingPolicy.js';
import type { GraphCheckup, GraphProblem } from './skillGraphCheckup.js';
/** The slice of an entry declaration this module reads — structural, so
 *  `skillGraph.ts`'s module-private `EntryDecl` satisfies it unchanged. */
export interface IntentEntryDecl {
    readonly id: string;
    readonly when?: (ctx: InjectionContext) => boolean;
    readonly match?: SkillMatchData;
    /** The data matcher's EVIDENCE extractor (9.28.0), compiled beside `when` —
     *  called only on the rule that wins the turn. Absent for a `when` rule. */
    readonly witness?: (ctx: InjectionContext) => RouteWitness | undefined;
}
/**
 * The graph's turn-routing surface — everything the agent's RouteTurn stage
 * needs from the graph, projected ONCE at build. Present on every flat graph
 * (a decision `tree()` routes by predicate and has no turn start to route);
 * `scorer` is present only when `.classify()` / `start.classify` configured.
 */
export interface TurnRoutingPlan {
    /** The configured classifier — tier 2 over declared intents. */
    readonly scorer?: IntentScorer;
    /** The resolved tie policy the verdicts are judged under (recorded). */
    readonly policy: RoutingPolicy;
    /** Every entry id, declaration order — the `unmatched` menu. */
    readonly entryIds: readonly string[];
    /** Tier 1: the first NON-intent conditional entry whose predicate passes,
     *  declaration order — its id, plus the `witness` evidence when the rule was
     *  DATA and it yielded quotable text (9.28.0; a `when` rule has none).
     *  Unconditional entries are defaults, not rules — they never beat an
     *  inherited cursor. A throwing predicate is a no-match (dev-warned), the
     *  same law the cursor resolver applies. A rule for a skill the caller's role
     *  may not read (`ctx.hiddenSkillIds`) is never consulted: tier 1 obeys the
     *  same role filter as tiers 2 and 3. */
    firstRuleMatch(ctx: InjectionContext): {
        readonly id: string;
        readonly witness?: RouteWitness;
    } | undefined;
    /** Tier-2 candidates: the declared intent entries, minus `exclude`. */
    intentCandidates(exclude?: ReadonlySet<string>): readonly IntentCandidate[];
    /** The incumbent as a candidate: its declared intent when it is an intent
     *  entry, else its description (never fabricated — `id` is a graph node). */
    incumbentCandidate(id: string): IntentCandidate;
}
/** Build the plan. `describeFor` resolves a skill id to its description (the
 *  incumbent's fallback intent text). */
export declare function buildTurnRoutingPlan(input: {
    readonly entries: readonly IntentEntryDecl[];
    readonly describeFor: (id: string) => string | undefined;
    readonly policy: RoutingPolicy;
    readonly scorer?: IntentScorer;
}): TurnRoutingPlan;
/** Case/whitespace-normalize an example for the duplicate check — the SAME
 *  string under two intents is scorer luck, whatever its casing. */
export declare function normalizeExample(example: string): string;
/**
 * The one PROVABLE intent-pair fact, decidable from the data alone: the same
 * (normalized) example declared under two intents. Whichever wins is scorer
 * luck, so it is named — both ids and the string. Everything else about two
 * example sets needs the configured scorer ({@link runCheckupIntents}).
 */
export declare function findDuplicateIntentExamples(entries: readonly IntentEntryDecl[]): readonly GraphProblem[];
/**
 * The leave-one-out intent audit behind `graph.checkupIntents()` — run each
 * declared example through the CONFIGURED scorer (the router that will
 * actually run; auditing with a different scorer would prove nothing about
 * production) against all intent candidates, with the example's own intent
 * represented by its REMAINING examples. An example whose top-1 is a
 * different intent, or whose top-vs-own pairwise margin is inside
 * `nearTieMargin`, is an `overlapping-intents` warning naming the example,
 * both intents, both numbers, and the fix.
 *
 * Honesty boundaries, stated in the messages: only the configured scorer's
 * view is checked; `when` predicates are opaque and never claimed checked;
 * tier-1 regex/keyword rules that fire BEFORE tier 2 are named as unaudited
 * shadowers when present. On an `llmClassifier` graph this costs one model
 * call per example — the caller's docstring says so.
 */
export declare function runCheckupIntents(input: {
    readonly entries: readonly IntentEntryDecl[];
    readonly scorer: IntentScorer;
    readonly policy: RoutingPolicy;
    readonly signal?: AbortSignal;
}): Promise<GraphCheckup>;
