/**
 * The deferral gate's POLICY SEAT as vocabulary: the three states a deployment may declare
 * (`RunnerDeps.toolSearch`), the word a disclosure reports, and the ONE reader that lowers the seat to the
 * single number the gate compares against. Pure — no harness, no roster, no I/O. `runner/tool-defer-gate.ts`
 * owns the gate that consumes the number.
 */
/** The default threshold percent: with no seat, the candidate set defers when its inlined size reaches this
 *  percent of the model's context window. `DEFER_THRESHOLD_FRACTION` is derived from it. */
export declare const TOOL_SEARCH_DEFAULT_THRESHOLD_PERCENT = 10;
/** The typed refusal a `toolSearch` seat outside its domain raises: a bad value never selects an arm
 *  silently and never folds back to the default gate. */
export declare const TOOL_SEARCH_INVALID_CODE = "config.tool_search_invalid";
/**
 * The deployment's deferral-gate SETTING: `true` = the candidate set defers on every request, `false` = it never defers (and with no caller list no
 * ToolSearch mounts), `{mode:"auto", thresholdPercent}` = the threshold gate at that percent of the context window (omitted ⇒
 * {@link TOOL_SEARCH_DEFAULT_THRESHOLD_PERCENT}). Absent ⇒ that default — the pre-seat behavior.
 *
 * @contract defer_gate.one_threshold — the three states are THREE VALUES OF ONE NUMBER, never three code paths: {@link resolveToolSearch} lowers the seat to
 * one threshold fraction (`true` ⇒ `0`, so every candidate byte is over budget; `false` ⇒ `Infinity`, so no candidate set reaches it; `auto:N` ⇒ `N/100`) and
 * `deferralGate` keeps its ONE comparison `gatedChars ≥ thresholdChars`; a second arm for a new state breaks this contract rather than extending it. It
 * governs rule ② ONLY — `TaskSpec.deferTools` still defers under every state, `alwaysLoadTools` still pins first, and `RunnerDeps.deferMode` remains the
 * orthogonal knob that WIDENS rule ②'s candidate set.
 */
export type ToolSearchSetting = boolean | {
    mode: "auto";
    thresholdPercent?: number;
};
/** The three arms of {@link ToolSearchSetting} as one word — the disclosure face's vocabulary. */
export declare const TOOL_SEARCH_POLICIES: readonly ["always", "never", "auto"];
export type ToolSearchPolicy = (typeof TOOL_SEARCH_POLICIES)[number];
/** The lowered seat: the word a disclosure reports, and the ONE number the gate compares against. */
export interface ResolvedToolSearch {
    policy: ToolSearchPolicy;
    /** Present iff `policy === "auto"` — the percent the deployment asked for (or the default). */
    thresholdPercent?: number;
    /** The fraction of the context window the candidate set must reach: `0` (always) / `Infinity` (never) /
     *  `thresholdPercent / 100`. This is the gate's only input from the seat. */
    fraction: number;
}
/**
 * The ONE reader of {@link ToolSearchSetting} (`@contract defer_gate.one_threshold`). A value outside the domain refuses loudly with
 * {@link TOOL_SEARCH_INVALID_CODE} — an unparseable knob must not fall back to the default gate while the deployment believes its declaration took effect.
 * The object form's MEMBER SET is part of that domain (`mode` + `thresholdPercent`, nothing else): an unknown key is the same silent fold one level down,
 * since a misspelled percent lowers to the default while the declaration looks like it took effect.
 * `thresholdPercent` must be a finite number in `[0, 100]`, and the two ends are NOT symmetric: `auto:0` is
 * an exact alias for `true` (threshold 0), while `auto:100` is the FULL-WINDOW threshold, not `false`. Only
 * `false` never defers.
 */
export declare function resolveToolSearch(setting: ToolSearchSetting | undefined): ResolvedToolSearch;
/**
 * Describe a REJECTED value for a refusal message — the ONE describer the config seats share — without ever
 * throwing, and without letting the rejected value NARRATE ITSELF.
 *
 * Two failures, one root. `JSON.stringify` is not total (a BigInt and a circular object make it throw, and a
 * formatter that throws inside a refusal replaces the coded refusal with an uncoded `TypeError`) — and where
 * it does not throw it calls the VALUE'S OWN `toJSON`, i.e. it asks the thing being rejected to say what it
 * is. Measured: `{ toJSON: () => "open" }` described itself as `"open"`, so a refusal of an illegal object
 * read as a refusal of a legal word; `{ toJSON: () => undefined }` fell through to `String(value)` and read
 * `[object Object]`; a function printed its whole source (unbounded, and equally forgeable through
 * `toString`); and `NaN` / `Infinity` — the two numbers these seats reject most — both came out as `null`,
 * the spelling of a value the caller never wrote.
 *
 * So: PRIMITIVES are spelled exactly (a number through `String`, which is total and keeps `NaN`, `Infinity`
 * and the `-0` a caller can only have written deliberately); anything else is DESCRIBED structurally — its
 * shape, its constructor's name and its own top-level keys, bounded. Reading those may still hit a proxy's
 * traps (every read is guarded), but NOTHING a read hands back is turned into text unless it is already a
 * primitive of the expected domain — a formatting hook of the value is never what writes this sentence. A description is worth less to a reader than the value would be; a WRONG value is worth less than
 * nothing, because it sends them to fix something they did not write.
 */
export declare function describeRejectedValue(value: unknown): string;
/**
 * The INERT spelling of a lowered policy — the exact seat value that lowers back to it, with no accessor,
 * proxy or prototype left on the path. A nested accessor (`get thresholdPercent()`) answers every later read
 * differently, so a phase that runs several passes over one cached object still measures several different
 * numbers. Round-trip: `resolveToolSearch(toolSearchSettingOf(r))` deep-equals `r` for every `r` this module
 * mints.
 */
export declare function toolSearchSettingOf(resolved: ResolvedToolSearch): ToolSearchSetting;
