/** * 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 (normalize.ts is the only * import), 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. */ export type Candidate = UnsupportedValue; /** 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): Candidate | undefined; /** * 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. */ export declare function extractCandidates(answer: string, gate: ResolvedEvidenceGate): readonly Candidate[];