/** * unsupported-argument — the model ACTED on a value nothing served, at the * CHOICE seam. * * Pattern: pure function over (the calls, the frame they were chosen from); * the domain check. * Role: the decidable fragment of "did the model bind this reference to * something the run actually served?". The measured failure: on the * second turn of a triage conversation the window had dropped the * user message carrying the true entity id and kept the assistant's * own rendered answer. Asked for the status of "that machine", the * model resolved the reference out of its OWN prior prose, took a * truncated job-name fragment for a machine name, called the lookup * tool with it, got an honest "nothing found", and told the person * their protected machine had no backup record. Every shipped rail * passed honestly — the coverage envelope, the absence envelope and * the evidence gate all held, because every value in the answer * really WAS grounded. The defect was the REFERENT, bound wrong at * the argument, and the argument was the one seam with no check. * * DECLARED, NEVER INFERRED — the `argumentsFrom` precedent, second use. A * tool is this check's subject only because its author said its arguments * come from another tool's results; a tool that declares nothing is never * examined, which is the whole zero-delta story. One declaration now arms two * seams: `dangling-reference` asks whether the ground is still in reach when * the tool is OFFERED, this asks whether the value the model chose came from * that ground when the tool is CALLED. * * ASSISTANT TEXT IS NOT GROUND, and that is the entire idea. The system * prompt, the user's messages and every tool result are things the RUN put in * front of the model. Its own earlier turns are things it wrote. A value whose * only source is the model's own rendered prose has been re-derived from a * rendering rather than read from evidence, and re-deriving is exactly how a * truncated job name becomes a machine name. * * THE FENCES, stated because they are the check's honesty: * - NON-STRINGS ARE NEVER CHECKED. Numbers, booleans and null are not * identifier-shaped, and substring-grounding them would accuse every * literal the model legitimately computed. * - VALUES UNDER FOUR CHARACTERS ARE NEVER CHECKED. Below that, substring * matching is noise in both directions — 'up' and 'a1' land inside * unrelated words in any corpus. * - SERVED ANYWHERE PASSES, case-insensitively. The value need only appear * inside SOME served string; see the README's stated leniency. * - DECLARED VOCABULARY PASSES. A value the tool's own `inputSchema` lists in * an `enum` was served BY THE SCHEMA — the model read it off the tool * definition, not out of its own prose. * - EXTERNAL GROUNDS PASS, and file the source that excused them (9.72.0). * An app may hand the corpus values the RUN never served but the APP * verified itself — the driving case: a person clicked a row in a data * panel, the app checked the clicked cells against the artifact the panel * renders, and an identifier the model takes from that verified selection * is not fabricated. HONESTY NOTE, because this is an assertion door: the * library records what the app asserts — verifying the assertion (against * the artifact, the click, whatever the app's ground truth is) is the * app's duty, and the `source` label travels with every excusal so a * reader can audit the chain instead of trusting it. * Two states remain, and they file DIFFERENT messages because they call for * different fixes: grounded only in the model's own prose (re-fetch the real * ground), and grounded nowhere in the window at all (nothing served it). * * Detection only. Nothing here blocks, rewrites or delays a call. */ import type { ContextError } from '../finding/types.js'; /** One tool call the model chose to make, with the arming that makes it checkable. */ export interface ArgumentChoice { readonly toolName: string; /** The provider's id for this call — what the witness points a reader at. */ readonly toolCallId: string; readonly args: Readonly>; /** `Tool.argumentsFrom` — the tools whose results were meant to ground these arguments. */ readonly argumentsFrom: readonly string[]; /** * Values the tool's own `inputSchema` declares in an `enum`. Best-effort and * FLAT (see {@link declaredEnumValuesOf}): a value declared for one field * excuses that value at any field of the same tool. Deliberately lenient on * an accusation boundary. */ readonly declaredEnums?: ReadonlySet; } /** * One value the APP verified against ground the run itself never observed * (9.72.0). `value` is the verified text; `source` is the app's short label * for where it came from (e.g. `'viewer-selection'`) — the audit handle that * travels onto the record whenever this entry excuses an argument. */ export interface ExternalGround { readonly value: string; readonly source: string; } /** * One argument value an external ground excused — the audit trail of an app * assertion. Filed alongside the findings so the record can say WHICH source * grounded a value, not merely that no finding was raised. */ export interface ExternalGrounding { readonly toolName: string; readonly toolCallId: string; /** Dot-path of the argument leaf the ground excused. */ readonly path: string; readonly value: string; /** The app's label from the {@link ExternalGround} entry that matched. */ readonly source: string; } /** The frame the model chose from, split by whether it can GROUND anything. */ export interface ChoiceCorpus { /** System prompt, user messages, tool results — everything the RUN served. */ readonly grounded: readonly string[]; /** The model's own earlier turns. Never ground: this is what the defect is made of. */ readonly assistant: readonly string[]; /** * App-asserted grounds the run did not serve (9.72.0). DECLARED, never * ambient — the caller composes this from a provider the app registered; * absent or empty, the check is byte-identical to what it always was. The * library records what the app asserts here; verifying the assertion is * the app's duty, and each entry's `source` label is kept on the excusal * record so the chain stays auditable. */ readonly external?: readonly ExternalGround[]; } /** What the choice-seam check found — the findings AND the excusals. */ export interface UnsupportedArguments { readonly findings: readonly ContextError[]; /** Values an app-asserted external ground excused, with their sources. */ readonly externalGroundings: readonly ExternalGrounding[]; } /** * Best-effort: every string an `enum` array declares anywhere in a tool's * `inputSchema`. Flat by design — a per-path map would be more precise, and on * an accusation boundary the lenient direction is the safe one. An absent or * non-object schema yields an empty set, which simply means no enum fence. */ export declare function declaredEnumValuesOf(schema: unknown): ReadonlySet; /** * Check every armed call's identifier-like string arguments against the frame * the model chose from. * * @param calls the ARMED tool calls of one response — a caller filters by * `argumentsFrom` before calling; nothing here re-decides who is a subject. * @param corpus the exact request this response was assembled from, split into * what the run served and what the model itself wrote. * @param epoch the run iteration, stamped on every witness. */ export declare function unsupportedArgumentsOf(calls: readonly ArgumentChoice[], corpus: ChoiceCorpus, epoch: number): UnsupportedArguments; //# sourceMappingURL=check.d.ts.map