UNPKG

agentfootprint

Version:

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

92 lines (91 loc) • 4.92 kB
/** * assessment/sources — what each reason rests on * (`AnswerAssessment.reasons[].source`): specific calls, the answer itself, or * the run as a whole. * * Pattern: a projection inside the one Fold (`assess.ts` · `assessAnswer`). * The KIND is declared per reason (`reasons.ts` · `REASONS`); for a * `calls` reason this module reads the calls off the very rows the * reason's witnesses point at — a history result carries its call id, * a committed row names its call — and the call's tool name off the * same row, or off the run's `history` for a row that names only an id. * It reads the committed record only, and adds nothing to it. * Role: core/ layer, pure. Asked once per fired reason by the fold; every * reader takes the source from the fold — the answer layer's line * (`compose.ts` · `standingLineOf`) and the answer account's "How * sure" counts (`answer-account/facts/howSure.ts`) — and never * re-derives it. * Emits: N/A. * * ## Names and ids only * * A source holds call ids and registered tool names, never a value, an * argument or a result: the rows it reads hold more, and it copies two strings * out of each. * * ## Which calls a row names * * - a tool result in `history` — its own call; * - a coverage, argument, period or middleware row — its own call (the row's * `toolCallId`, with the row's `toolName`); * - a conflict row — the calls of the readings that disagree (`witnesses`); * - a contingent row — the results the value came from (`carriers`), and an * argument row behind `value-contingent` — the result it names (`result`): * the reason is that a value came from results the model itself set aside; * - an answer check that names its tool and not its call * (`declaration-overclaimed`, the report row's `tools` — kept by the one * projection onto the report, `negativeClaims/delivery.ts` · * `reportWithNegatives`) — every call of that tool this turn: it may * over-report, it never hides (the fold's rule for "rests on"). * * A row that names no call, a call whose tool's name the record does not * hold, and every entry of a list (a conflict's readings, a contingent value's * carriers, an answer check's tools) that names no call — beside the entries * that do — is COUNTED in `unnamed`, never dropped and never guessed. Only a * known call id is said once. * * ## A list is read by index, never through its own code * * Every list on a row — a conflict's readings, a contingent value's carriers, * an answer check's tools, a request's `toolCalls` — is read through ONE * reader, `rowList` (`lib/boundedJson.ts` · `readOwnList`, the bounded-copy * convention as a reader): a plain array only, its own `length`, the own data * property at each index, at most `MAX_ROW_LIST_ENTRIES`. A record handed to * `assessAnswer` may be host-built, so a list can be sparse, carry its own * iterator or put a getter at an index: the iterator and the getter are never * called. A hole or a getter's entry names no call; the entries past the bound * are counted, never read; a value that is not a plain array is not trusted as * a list — one call the record cannot name. Each is counted in `unnamed`. */ import { type OwnList } from '../../../lib/boundedJson.js'; import type { AssessmentPointer, AssessmentReason, AssessmentReasonSource } from './types.js'; /** The most entries the fold reads off one list on a row; the rest are counted, never read. */ export declare const MAX_ROW_LIST_ENTRIES = 1000; /** * A list on a row, as the fold reads it — THE one reader, shared by the * projection here and the fold's placement (`assess.ts` · `conflictOfThisTurn`, * `callsNamedBy`): `readOwnList` at `MAX_ROW_LIST_ENTRIES`. `undefined` when * the value is not a plain array. */ export declare function rowList(value: unknown): OwnList | undefined; /** What the projection reads: the committed state, and this turn's results (for a tool-name join). */ export interface SourceRead { readonly state: Readonly<Record<string, unknown>>; readonly turnResults: readonly { readonly toolCallId?: string; readonly toolName?: string; }[]; } /** * What one fired reason rests on — its declared `kind`, and for `calls` the * calls its witnesses' rows name, each once, in witness order. * * @example * ```ts * reasonSourceOf('declared-absent', 'calls', [historyPointer], read); * // { kind: 'calls', calls: [{ toolCallId: 'c1', toolName: 'load_attachment' }] } * reasonSourceOf('stopped-early', 'run', [statePointer('stoppedEarly')], read); * // { kind: 'run' } * ``` */ export declare function reasonSourceOf(reason: AssessmentReason, kind: AssessmentReasonSource['kind'], witness: readonly AssessmentPointer[], read: SourceRead): AssessmentReasonSource;