/**
 * extract — which tokens in an answer are DATA, and therefore have to be
 * grounded in a tool result.
 *
 * Pattern: a pure classifier over normalized tokens and the small shared
 *          range/precision leaves, so it can be unit-tested on strings with no agent, no
 *          chart and no model.
 * Role:    core/ layer. The hard half of `namesAndNumbersFromEvidence`.
 * Emits:   N/A.
 *
 * ## The rule, and why every part of it is there
 *
 * A naive "flag every number" extractor makes the feature worse than useless.
 * It flags `24 hours`, `3 issues` and `first`, and under `guard` it then spends
 * a real turn asking the model to justify the word "three" — which is the
 * retry loop this library exists to remove. So the default is CONSERVATIVE: it
 * would rather miss a fabricated value than accuse a correct answer.
 *
 * A token is a candidate only if:
 *
 *   1. **It contains a digit.** English prose is alphabetic. Requiring a digit
 *      is the single cheapest separator between "data someone read off a
 *      screen" and "a word". It is also the rule's biggest blind spot, stated
 *      plainly: a fabricated all-letters name (`esxi-host-alpha`) is invisible
 *      to the default and needs a declared shape.
 *   2. **It is distinctive.** Either
 *        • an IDENTIFIER — digits mixed with letters or with structural
 *          punctuation (`:` `_` `-` `/` `.`), at least 4 characters:
 *          `0xef0101`, `fc1/3`, `21:00:00:24:ff:4a:12:03`, `UCSB-B200-M5`; or
 *        • a NUMBER with at least `minDigits` (default 4) digits: `41,200`,
 *          `18450`, `786432`.
 *   3. **It is not prose wearing a number.** Three exclusions, each from a
 *      real sentence in the material:
 *        • a number with a short unit or word glued to it — `32G`, `100MB`,
 *          `47th`, `2h`, `48-port`, `$20/month` — is judged on its NUMBER
 *          alone, so `48-port switch` never trips while `41200iops` still
 *          does;
 *        • a bare `N/M` of one or two digits each — `24/7`, `1/2` — is a ratio
 *          in prose. A two-number port id is spelled the same way, so the
 *          conservative reading wins and a domain that needs it declares a
 *          shape;
 *        • anything the caller declared exempt.
 *
 * Declared shapes are tested FIRST and win: an app that says "this is what my
 * identifiers look like" has better information than these heuristics.
 */
import type { ResolvedEvidenceGate, UnsupportedValue } from './types.js';
/**
 * A value the answer asserts, and the rule that made it one. `token` is the
 * glued-unit spelling the value was read off (`1007us` for the value
 * `1007`), present only when there was one — the lookup meets the result
 * under that spelling too (`candidateForms`), and the index is never
 * widened to do it (9.110.0).
 */
export type Candidate = UnsupportedValue & {
    readonly token?: string;
    /** The precision the answer actually wrote, only under the decimal dial. */
    readonly decimalSpelling?: string;
    /** An intact range can ground or exempt both of the endpoints it spelled. */
    readonly rangeToken?: string;
};
/** True when the caller declared this token exempt. */
export declare function isDeclaredExempt(token: string, gate: ResolvedEvidenceGate): boolean;
/**
 * Classify ONE normalized token. Returns the candidate it produces, or
 * `undefined` when the token is prose.
 *
 * Exported for the unit tests, which is the whole reason the classifier is a
 * function over a string rather than a loop body.
 */
export declare function classifyToken(token: string, gate: ResolvedEvidenceGate, 
/** Under the figures dial: the numbers the answer writes wearing a unit
 *  (`figures.ts` · `figureNumbersOf`). A number in it is a candidate
 *  whatever its digit count — `53.2%` and `6.3 TB` are readings. */
figures?: ReadonlySet<string>): Candidate | undefined;
/**
 * The spellings a candidate is looked up under: its value's forms
 * (`lookupForms` — the `0x` rule), plus the glued-unit token's when the
 * value was read off one. The LOOKUP side widens, the index never does: a
 * result carrying `1007us` grounds an answer's `1007us`, while a result
 * carrying `latency 2024ms` does not ground an answer's prose year `2024`
 * — that candidate came from a bare token and asks for `2024` alone. The
 * one place both the gate (`gate.ts · checkAnswer`) and the contingent
 * check (`findings/contingent.ts`) take a candidate's forms from.
 */
export declare function candidateForms(candidate: Candidate): readonly string[];
/** How one answer is read. */
export interface AnswerReadOptions {
    /** Under the figures dial: the numbers the answer writes wearing a unit
     *  (`figures.ts` · `figureNumbersOf`, computed by the caller that loaded
     *  it — the module stays off a plain agent's graph). */
    readonly figures?: ReadonlySet<string>;
    /** A read-only exact-token view supplied by the caller's corpus. Whole
     *  carried values win before a numeric range is interpreted as endpoints. */
    readonly exactTokens?: {
        readonly has: (value: string) => boolean;
    };
    /**
     * The answer is TYPED (`.outputSchema()`): one JSON object or array is read
     * by its VALUES only. Its object keys are the schema's field names
     * (`p95LatencyMs`, `sha256`, `ipv4`), not values the answer asserts — read
     * as values, a digit-bearing name was an identifier no tool result
     * carried. The price, stated: an answer keyed BY data (an id as a key) is
     * not judged on its keys. Absent, keys are read too.
     */
    readonly typed?: boolean;
}
/** One reading of an answer: the values it asserts, and how much of it was never read. */
export interface AnswerReading {
    /** Every distinct value the answer asserts, in first-appearance order. */
    readonly candidates: readonly Candidate[];
    /**
     * Words and numbers past `MAX_ANSWER_TOKENS` that were never classified or
     * looked up — 0 when the whole answer was read. A value among them could be
     * invented and would go unfound, so a reading with any is not a clean one.
     */
    readonly unread: number;
}
/**
 * Every distinct value the answer asserts, in first-appearance order.
 *
 * De-duplicated by value: a port named six times is one claim to ground, and
 * a correction that lists it six times reads like noise. A typed answer is
 * read value by value (`answerTexts`).
 */
export declare function extractCandidates(answer: string, gate: ResolvedEvidenceGate, 
/** Under the figures dial: the numbers the answer writes wearing a unit
 *  (`figures.ts` · `figureNumbersOf`, computed by the caller that loaded
 *  it — the module stays off a plain agent's graph). */
figures?: ReadonlySet<string>, 
/** A read-only exact-token view supplied by the caller's corpus. Whole
 *  carried values win before a numeric range is interpreted as endpoints. */
exactTokens?: {
    readonly has: (value: string) => boolean;
}): readonly Candidate[];
/**
 * Read one answer: the values it asserts (`extractCandidates`), and how many
 * of its words and numbers lay past the ceiling, never read. The reading
 * stops CLASSIFYING at `MAX_ANSWER_TOKENS`; it never stops counting.
 */
export declare function readAnswer(answer: string, gate: ResolvedEvidenceGate, options?: AnswerReadOptions): AnswerReading;
