UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

272 lines (271 loc) • 13.6 kB
/** * arguments/rows — the inputs layer's verdicts, as rows on the one ledger. * * Pattern: Map. One row type, its builders and its well-formedness check — * the one owner of the `argument` row kind. `findings/types.ts` · * `FindingsRow` imports the type; this folder never imports * `findings/` (the one-way law). * Role: core/ layer leaf of the inputs layer (honesty layer 2). * Emits: N/A (the Record stage emits one `agentfootprint.findings.argument` * per row through the ledger's emit half). * * ## What a row may hold * * Names, enums and counts — and, in three fields only (`value`, `proposed`, * `quote`), a value AS THE TOOL'S OWN ARGUMENT VIEW SHOWS IT * (`core/toolShownArgs.ts` · `shownArgsOf`): a key the view hides reads * `'REDACTED'`. The layer's checks run on the raw value in memory; the raw * value is never stored on a row. * * ## One row per ruled argument per call, stamped with the turn * * `turn` is `AgentState.turnNumber` when the row was filed — the conversation * turn, not the window position — because the ledger crosses turns on a * continued conversation and `iteration` restarts at 1 every run. The * standing fold reads this turn's rows only. */ /** Where a ruled argument's value came from, as the library's checks placed it. */ export type ArgumentSource = 'said' | 'answered' | 'result' | 'app' | 'default' | 'model'; /** * Why the library asked: the call left the value out (`missing`); the value * was present and its declared source did not trace it (`unverified` — under * declared sources, on an `ask` rule); the person's answer did not fit the * property's own schema and the field was asked again (`invalid-answer`). */ export type ArgumentAsked = 'missing' | 'unverified' | 'invalid-answer'; /** What the model claimed about a value's source (`_findings.from`; `'none'`: it declared nothing). */ export type ArgumentClaim = 'user' | 'result' | 'turn' | 'app' | 'assumed' | 'none'; /** Which declared-source check failed — the claim is filed as written, never repaired. */ export type ArgumentCheckFailed = 'quote-not-found' | 'composed-message' | 'unknown-result' | 'placed-result' | 'not-in-result' | 'no-earlier-turn' | 'not-in-earlier-turns' | 'only-in-model-answer' | 'not-in-app-text' | 'uncheckable'; /** * The inputs layer's verdict on ONE ruled argument of ONE tool call — a row on * `AgentState.findingsLedger` (`kind: 'argument'`). * * Filed: `source: 'default'` (the library filled the declared default, or the * model sent that same value itself — `proposed` says which), `source: * 'model'` (the model sent another value, and the record does not trace it), * `asked` with no source (the call left an `ask` argument out and the person * was asked — `missing` — or asked again after an answer that did not fit — * `invalid-answer`), and `source: 'answered'` (the person's answer filled the * value; `free` when it came through a free-text field). * * Under declared sources (`.inputsLayer({ argumentSources: true })` or * `.findings({ argumentSources: true })`) a present * value's row also carries the model's claim and the library's check of it * (`sourcedRowOf`): `claimed` (`'none'` when it declared nothing), `source` * `said` / `result` / `app` / `answered` when the check traced it (`matched`, * `reading`, `earlier`, `result`, `setAside`, `argumentsFrom`, `appSource`), * `failed` when it did not, `coincides` for the library's own lookup (a hint, * never a source), and `asked: 'unverified'` with the model's value as * `proposed` when an `ask` rule asks about it. A FREE argument a `from` entry * named is filed with no `rule`. A reader skips a member it does not know. */ export interface ArgumentRow { readonly kind: 'argument'; /** `AgentState.turnNumber` when the row was filed — the conversation turn. */ readonly turn: number; readonly toolCallId: string; readonly toolName: string; readonly iteration: number; /** The top-level argument name — schema vocabulary, like `toolName`. */ readonly argument: string; /** The rule on the argument; absent on a free argument a `from` entry named. */ readonly rule?: 'ask' | 'assume'; /** Set on the argument `Tool.period` names. */ readonly period?: true; /** Where the value came from; absent only on an asked row. */ readonly source?: ArgumentSource; readonly asked?: ArgumentAsked; /** * The value the call runs with, in the tool's own argument view, clipped * (`integrity/argumentLeaves.ts` · `clipValue`); `'REDACTED'` when the view * hides the argument. */ readonly value?: string; /** * The MODEL's own value, same view: on a `default` row, the default the model * sent itself (absent = the library filled it); on an `answered` row, the * value the person's answer replaced. */ readonly proposed?: string; readonly claimed?: ArgumentClaim; /** * How the value was traced to its source: the quote holds it, a declared * phrase names it, an earlier answer in another spelling — or, under * `.time()`, it IS the window of a mention the reader recorded (`mention`: * the library filled it from that window, or the model's quote named that * mention and its value equals the window). */ readonly matched?: ArgumentMatched; /** * The model's `quote`, clipped (`QUOTE_CHARS`) — `'REDACTED'` on an agent * where ANY tool in reach can hide arguments (it registers a tool that * carries an argument view, or wires a ToolProvider — whatever it lists): a * quote is free text, and may hold any value a tool hides, in any spelling. */ readonly quote?: string; readonly reading?: true; readonly earlier?: true; readonly result?: string; readonly setAside?: 'open' | 'noise' | 'ruled-out'; readonly argumentsFrom?: 'listed' | 'unlisted'; readonly appSource?: string; readonly free?: true; readonly coincides?: 'person' | 'result' | 'app'; readonly malformed?: number; readonly failed?: ArgumentCheckFailed; } /** How a traced value was matched — see {@link ArgumentRow.matched}. */ export type ArgumentMatched = 'quote' | 'phrase' | 'spelling' | 'mention'; /** The closed vocabularies, for the checkpoint door and the event. */ export declare const ARGUMENT_MATCHED: readonly ArgumentMatched[]; export declare const ARGUMENT_SOURCES: readonly ArgumentSource[]; export declare const ARGUMENT_ASKED: readonly ArgumentAsked[]; export declare const ARGUMENT_CLAIMS: readonly ArgumentClaim[]; export declare const ARGUMENT_CHECKS_FAILED: readonly ArgumentCheckFailed[]; /** The placeholder a hidden argument reads — the redacted mirror's own. */ export declare const HIDDEN_VALUE = "REDACTED"; /** * One value as a row may hold it: the shown view's value, printed as a string * (`String()` for a number or a boolean, JSON for anything else) and clipped. * A value the view replaced with the placeholder stays the placeholder. */ export declare function shownValue(value: unknown): string; /** What the Record stage knows about one ruled argument of one call. */ export interface ArgumentVerdict { readonly toolCallId: string; readonly toolName: string; readonly argument: string; readonly rule: 'ask' | 'assume'; readonly period?: true; readonly source: 'default' | 'model'; /** The value the call runs with, in the shown view (the fill, or the model's). */ readonly shownValue: unknown; /** The model's own value in the shown view — set only when the model sent one. */ readonly shownProposed?: unknown; } /** The identity of one ruled argument of one call — what every row builder is handed. */ export interface ArgumentIdentity { readonly toolCallId: string; readonly toolName: string; readonly argument: string; readonly rule: 'ask' | 'assume'; readonly period?: true; } /** * The row for an argument the person is ASKED for: no `source` (nobody has * given the value yet) and no `value` (there is none). `invalid-answer` when * an answer did not fit the property's own schema and the field is asked * again — the answer itself is not kept. */ export declare function askedRowOf(who: ArgumentIdentity, stamp: { readonly turn: number; readonly iteration: number; }, asked?: ArgumentAsked): ArgumentRow; /** * The row for a value the person's ANSWER filled (`source: 'answered'`): the * value the call runs with, in the tool's own argument view; `proposed` only * when the call had carried a value the answer replaced (absent = the call * left it out); `free` when the answer came through a free-text field — a * name the person typed, which is provenance and never support. */ export declare function answeredRowOf(who: ArgumentIdentity & { readonly shownValue: unknown; readonly shownProposed?: unknown; readonly free?: true; }, stamp: { readonly turn: number; readonly iteration: number; }): ArgumentRow; /** * The row for one verdict. `proposed` is written only on a `default` row the * model sent itself — on a `model` row the value IS the proposal, and a second * copy would say nothing. */ export declare function argumentRowOf(verdict: ArgumentVerdict, stamp: { readonly turn: number; readonly iteration: number; }): ArgumentRow; /** * The row for a value the library filled from the turn's ONE window of the * person's (the time layer, `time/bind.ts`): from the window the person * settled in the time ask (`answered`, `matched: 'mention'`), from a `model` * reader's unconfirmed reading of their words (`said`, `matched: 'mention'` * and `reading` — it can support "not sure", never "known"), or from a window * set in a UI (`app`, `appSource: 'time.window'`). A `said` window with no * `reading` is read back only from a record an earlier version filed. The * call's `call-window` row names the window and the form. */ export declare function windowRowOf(who: ArgumentIdentity & { readonly shownValue: unknown; readonly window: 'said' | 'derived-from-reading' | 'answered' | 'control'; }, stamp: { readonly turn: number; readonly iteration: number; }): ArgumentRow; /** The `appSource` of a value filled from the run's `time.window` (a UI control). */ export declare const TIME_WINDOW_SOURCE = "time.window"; /** * A quote's bound on a row — the bound a basis row's one-line texts carry * (`findings/types.ts` · `PROPOSITION_CHARS`, which this leaf cannot import), * the cut STATED in the text (`…[clipped N chars]`). */ export declare const QUOTE_CHARS = 240; /** A quote as a row holds it: at most `QUOTE_CHARS`, the cut stated. */ export declare function shownQuote(quote: string): string; /** * The declared-sources check's verdict on one argument of one call, as the * row builder is handed it — enums, flags, ids and labels. The value, the * model's proposal and the quote arrive ALREADY in the tool's own argument * view (`shownValue`, `shownProposed`: `'REDACTED'` where the view hides the * argument; `shownQuoteText`: `'REDACTED'` while any tool in reach can hide * arguments — a quote is free text and may hold any hidden value), so a raw * value never reaches a row. */ export interface SourcedVerdict { readonly toolCallId: string; readonly toolName: string; readonly argument: string; /** Absent on a free argument a `from` entry named. */ readonly rule?: 'ask' | 'assume'; readonly period?: true; /** Absent exactly when the person is asked (`asked: 'unverified'`). */ readonly source?: ArgumentSource; readonly asked?: 'unverified'; /** The value the call runs with, in the shown view — absent on an asked row. */ readonly shownValue?: unknown; /** The model's own value, in the shown view — on a `default` row (V1) and an asked row. */ readonly shownProposed?: unknown; readonly claimed: ArgumentClaim; readonly matched?: ArgumentMatched; /** The model's quote — `'REDACTED'` while any tool in reach can hide arguments (`resolve.ts` · `quotesMayShow`). */ readonly shownQuoteText?: string; readonly reading?: true; readonly earlier?: true; readonly result?: string; readonly setAside?: 'open' | 'noise' | 'ruled-out'; readonly argumentsFrom?: 'listed' | 'unlisted'; readonly appSource?: string; readonly coincides?: 'person' | 'result' | 'app'; readonly malformed?: number; readonly failed?: ArgumentCheckFailed; } /** * The row for one argument the declared-sources check judged (honesty layer * 2, declared sources): the identity, the verdict's * source (or `asked: 'unverified'` — the person is asked, and nothing runs on * the model's value), the value in the tool's own view, and every field of the * claim and the check as the verdict carries it. Written in the one field order * `ArgumentRow` declares. */ export declare function sourcedRowOf(verdict: SourcedVerdict, stamp: { readonly turn: number; readonly iteration: number; }): ArgumentRow; /** * Whether a value read from a stored checkpoint is an `argument` row this * library could have written: the identity strings, a numeric `iteration` and * `turn`, a `source` or an `asked` in its vocabulary, and every enum field in * its own. The checkpoint door's `argument` arm (`core/runCheckpoint.ts`). */ export declare function argumentRowIsWellFormed(r: Readonly<Record<string, unknown>>): boolean;