/**
 * skillBrains — per-skill model switching (9.19.0). ONE owner of the brains
 * fold: the declared shapes (`ProviderChoice`, `EscalationPolicy`), the
 * build-time fold + check-up over BOTH declaration homes, and the runtime
 * `brainFor` consult callLLM makes.
 *
 * "THE CURSOR PICKS THE BRAIN." A skill graph already decides WHERE the run
 * is (the one cursor); this feature lets that same position decide WHO
 * answers — a cheap triage skill on a small model, the refund skill on the
 * strong one — with zero new stages: `callLLM` consults `brainFor(cursor,
 * escalated)` once at the top and resolves field by field down the stated
 * precedence chain:
 *
 *   **escalation > per-skill brain > `.configure()` `resolvedModel` >
 *   build-time default** — the more specific context wins (evidence > node
 *   > run > build). Field-by-field at the call site: `provider =
 *   brain?.provider ?? deps.provider`; `model = brain?.model ??
 *   (runConfigured ? scope.resolvedModel : undefined) ?? deps.model`;
 *   `cacheStrategy = brain?.cacheStrategy ?? deps.cacheStrategy`. A brain
 *   naming only a MODEL inherits the agent's provider; a brain naming only a
 *   foreign PROVIDER may not inherit the agent's model id (that id belongs
 *   to another vendor's namespace and would fail mid-turn, on exactly the
 *   iteration the cursor enters the skill) — refused at build instead.
 *
 * TWO declaration homes, one meaning: `defineSkill({ provider, model })`
 * keeps the choice beside the skill it serves; `SkillGraphOptions.providers`
 * keeps a fleet's choices in one place at the mount. The same id declared in
 * both with different choices is refused naming both homes — configuration
 * that disagrees with itself must not pick a silent winner.
 *
 * ESCALATE-ON-EVIDENCE. `escalation: { provider, model?, afterRefusals: N }`
 * — when the gate refuses N routing picks in ONE turn (`skill.rejected` —
 * reachability, posture, OR a self-call: all three refusal arms count, and
 * they are real recorded refusals, never vibes), the rest
 * of the turn runs on the escalation brain, `skill.escalated` goes on the
 * record once, and the next turn's seed de-escalates. The loop the model is
 * flubbing gets the bigger brain until the turn resolves.
 *
 * Vendor-neutral by construction: a brain is an `LLMProvider` OBJECT (any
 * port implementation), never a vendor name this library resolves.
 */
import type { LLMProvider } from '../../provider-contract/types.js';
import type { CacheStrategy } from '../../cache/types.js';
import type { Injection } from '../../injection-engine/types.js';
/** One brain: a provider port, optionally pinned to a model. `model` absent
 *  → resolved down the precedence chain (legal only while the provider is
 *  the agent's own — see the module header for why a foreign provider must
 *  name its model). */
export interface ProviderChoice {
    readonly provider: LLMProvider;
    readonly model?: string;
}
/** Escalate-on-evidence policy (see the module header). */
export interface EscalationPolicy extends ProviderChoice {
    /** Gate refusals (`skill.rejected`) in ONE turn that flip the rest of the
     *  turn onto this brain. Integer ≥ 1.
     *
     *  ALL THREE refusal arms count — an unreachable pick, a pick a `strictness`
     *  posture declined, and a SELF-CALL (`read_skill` naming the cursor's own
     *  skill). The self-call arm composes a notice rather than a refusal, and it
     *  still counts here on purpose: what this budget measures is a model asking
     *  the graph where it stands instead of working, which is the same stuck loop
     *  whichever arm answered it. */
    readonly afterRefusals: number;
}
/** A brain as the fold stores it: `defineSkill({ model })` alone is legal
 *  (the agent's own provider, another model), so `provider` is optional
 *  HERE while the declared `ProviderChoice` requires it. */
export interface SkillBrainDecl {
    readonly provider?: LLMProvider;
    readonly model?: string;
}
/** The two per-skill declaration homes, folded — plus the out-of-band
 *  policies. Produced by {@link foldSkillBrains} at `Agent.build()`. */
export interface FoldedSkillBrains {
    /** Per-skill brains, keyed by skill id (both homes merged, conflicts
     *  refused). Empty map = no per-skill brain anywhere. */
    readonly bySkill: ReadonlyMap<string, SkillBrainDecl>;
    readonly escalation?: EscalationPolicy;
    /** Tier-3 decider — consumed by the RouteTurn stage, never by callLLM. */
    readonly decider?: ProviderChoice;
}
/** What one `brainFor` consult resolves to — the winning rung, with the
 *  cache strategy that provider needs (cache markers are provider-aware).
 *  `provider`/`model` absent = that field falls to the next rung of the
 *  precedence chain at the call site. */
export interface ResolvedBrain {
    readonly provider?: LLMProvider;
    readonly model?: string;
    readonly cacheStrategy: CacheStrategy;
    /** WHY this brain won — stamped on `llm_start.brain` so the record says
     *  which rung answered, not just which model. */
    readonly via: 'skill' | 'escalation';
    /** The tenure that picked it (via `'skill'` only). */
    readonly skillId?: string;
}
/** The consult callLLM makes once at the top of the stage, with the
 *  ADVANCED cursor and the run's escalation flag. Undefined result = no
 *  brain rung won; the agent's own configuration resolves as always. */
export type BrainFor = (activeCursor: string | undefined, escalated: boolean) => ResolvedBrain | undefined;
/** The per-skill brain declared on `defineSkill({ provider, model })`,
 *  read off the metadata bag (the engine-ignores-unknown precedent —
 *  `projectActiveInjection` never projects it, so the live provider object
 *  stays on the closure-held list and never crosses a scope boundary). */
export declare function skillBrainOf(injection: Injection): SkillBrainDecl | undefined;
/** Inputs to the fold — everything the check-up judges against. */
export interface FoldSkillBrainsArgs {
    /** The FINAL injection list (every declaration home has landed). */
    readonly injections: readonly Injection[];
    /** The mount options' three fields, verbatim. */
    readonly providers?: Readonly<Record<string, ProviderChoice>>;
    readonly escalation?: EscalationPolicy;
    readonly decider?: ProviderChoice;
    /** Whether a skill graph is mounted at all (`.skillGraph()` ran). */
    readonly graphMounted: boolean;
    /** The mounted graph's node ids — undefined when the graph object carries
     *  no `nodes` (a structurally-typed graph built before they existed);
     *  brains cannot be validated against such a graph and are refused. */
    readonly nodeIds?: ReadonlySet<string>;
    /** The agent's own provider name — the foreign-provider check's anchor. */
    readonly agentProviderName: string;
}
/**
 * Fold both declaration homes into one frozen map, running every build-time
 * refusal (the check-up). Returns `undefined` when nothing was declared —
 * the zero-cost gate every caller branches on.
 */
export declare function foldSkillBrains(args: FoldSkillBrainsArgs): FoldedSkillBrains | undefined;
/** Per-brain cache strategies, resolved ONCE at chart build (the brain set
 *  is static; cache markers are provider-aware). A brain on the agent's own
 *  provider — the same provider OBJECT — keeps the agent's strategy,
 *  including an explicit override, rather than re-resolving behind the
 *  caller's back; any other provider gets the strategy its own declared
 *  `promptCaching` selects (`cacheStrategyFor`), never one looked up by name. */
export interface BuildBrainForArgs {
    readonly brains: FoldedSkillBrains;
    readonly agentProvider: LLMProvider;
    readonly agentCacheStrategy: CacheStrategy;
    readonly agentModel?: string;
    /** A host-supplied strategy remains authoritative for this provider,
     * including model-only brains. Never inspect its unused declaration. */
    readonly agentCacheStrategyIsOverride?: true;
}
/** Build the runtime consult. See {@link BrainFor}. */
export declare function buildBrainFor(args: BuildBrainForArgs): BrainFor;
/** Describe the brain that WOULD serve a cursor pre-escalation — the
 *  `skill.escalated` event's honest `from` field, resolved by the same
 *  precedence chain callLLM applies (skill brain > configured > default). */
export declare function describeServingBrain(args: {
    readonly brains: FoldedSkillBrains;
    readonly cursor: string | undefined;
    readonly agentProviderName: string;
    readonly defaultModel: string;
    readonly resolvedModel?: string;
}): {
    readonly provider: string;
    readonly model: string;
};
