UNPKG

agentfootprint

Version:

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

120 lines (119 loc) • 6.39 kB
/** * What every row reader shares: the view, the declarations, the per-sentence * catch, and the small pointer / var builders. * * `say` is THE way a reader writes a line. It fills the template and, when the * fill throws (a programming error — a var the reader forgot), writes * `unreadable.line@1` instead and counts it in `unread`. One bad line never * crashes the account. */ import { parseMaybeJson, readEmptiness, type EmptinessReading } from '../../core/agent/emptiness.js'; import { type SentenceSpec } from '../render.js'; import type { TemplateId } from '../templates.js'; import type { AccountSource, AnswerAccountDeclarations, RecordPointer, Sentence, SentenceVar } from '../types.js'; import { type RecordingView, type ViewEvent } from '../view.js'; export interface ReadContext { readonly view: RecordingView; readonly declarations: AnswerAccountDeclarations; /** A resumed leg: a `pause.resume`, or a `turn_end` with no `turn_start`. */ readonly resumedLeg: boolean; /** The iteration of the last `stream.llm_start` before `turn_end` (the answering iteration). */ readonly answeringIteration?: number; /** Write one line through the per-sentence catch. */ say(id: TemplateId, spec?: SentenceSpec): Sentence; /** Count one row the account passed over because its shape did not fit. */ noteUnread(): void; /** Items (and their characters) already printed — the readability budget. */ readonly budget: { itemChars: number; items: number; }; } /** * The bulleted items the account prints IN TOTAL — declared coverage items and * flagged values. Each item rides a sentence (its text three times: `text`, * `parts`, `vars`, plus pointers, about 1 KB of structure), so both the count * and the characters are bounded; past either, items fold into `items.more@1` * for readability. `size.ts` separately enforces the serialized-byte bound. */ export declare const ITEM_BUDGET: { readonly items: 12; readonly chars: 3000; }; /** Take one item of `chars` characters from the budget; `false` = fold it. */ export declare function takeItem(ctx: ReadContext, chars: number): boolean; /** @internal — a test hook: force the fill of one template id to throw. */ export interface AccountInternals { readonly failTemplate?: string; } /** * Long text the account prints in TOTAL, past the first `SHORT_TEXT_CHARS` of * each value: once spent, every later value is cut at `SHORT_TEXT_CHARS` (marked * `clipped`, followed by `part.clipped@1`). This keeps ordinary accounts * readable; `size.ts` separately enforces the serialized-byte bound. */ export declare const LONG_TEXT_BUDGET = 3000; export declare const SHORT_TEXT_CHARS = 300; /** * A text value a call's FACT carries is cut here (the sentence that prints it * carries up to 2,000). A cut that keeps an ordinary account readable; the * account's size bound itself is measured in bytes (`../size.ts`). */ export declare const FACT_TEXT_CHARS = 200; export declare function makeSay(internals: AccountInternals | undefined, onUnread: () => void, budget?: { longChars: number; }): (id: TemplateId, spec?: SentenceSpec) => Sentence; export declare const jsonPointer: (...keys: readonly (string | number)[]) => string; /** * A pointer the account did not build itself — the standing fold's witnesses * (`core/agent/assessment/assess.ts`) — with its identity cut as the * builders below cut theirs. */ export declare function boundedPointer(p: RecordPointer): RecordPointer; export declare function at(event: ViewEvent, ...keys: readonly (string | number)[]): RecordPointer; /** A derived leaf on an event (`#emptiness`, `#meta/runId`). */ export declare function derived(event: ViewEvent, path: string): RecordPointer; export declare const stateAt: (key: string, ...keys: readonly (string | number)[]) => RecordPointer; export declare const historyAt: (index: number, path: string, toolCallId?: string) => RecordPointer; export declare function declarationAt(declarations: AnswerAccountDeclarations, field: string): RecordPointer; /** A tool name the LIBRARY recorded (its claims are vouched by the template). */ export declare const toolVar: (name: string, from?: RecordPointer) => SentenceVar; /** * The `{{name:skill}}` pair: the id (library) and, when there is one, its plain * label — the recorded `title` (library) or the app's declared label (app). */ export declare function skillVars(ctx: ReadContext, name: string, id: string, from?: RecordPointer): Record<string, SentenceVar>; export { parseMaybeJson, readEmptiness, type EmptinessReading }; /** The app's declared rows key for one tool, when it declared one. */ export declare const rowsAtOf: (declarations: AnswerAccountDeclarations, toolName: string) => string | undefined; /** The source a sentence's emptiness claim rests on. */ export declare const emptinessSource: (reading: { readonly source?: 'library' | 'app'; }) => AccountSource; /** * The count a fact carries from one reading — ONE owner for the three fact * kinds (this run's calls, the calls before a pause, the earlier answers in * view): how many rows, who counted them, and where in the value read the * count was taken (a JSON pointer; "show me" points there — `shown.ts`). */ export declare const countedFields: (reading: EmptinessReading) => { readonly rows?: number; readonly emptinessSource?: 'library' | 'app'; readonly countedAt?: string; }; /** * `sharedState.history`, when it is an array — and EMPTY when the record keeps * it out (a redaction policy's placeholder). It asks the view first, so the * view's guard counts the kept-out history as handled; a reader that would * otherwise say something about what the history lacks asks * `view.isStateKeptOut('history')` itself (`inView.ts`, `checked.ts`). */ export declare function historyOf(view: RecordingView): readonly unknown[]; /** * True when this recorded history entry is a message a PERSON wrote — the * one rule (`lib/saidByPerson.ts` · `isSaidByPerson`), read off a record. A * `role: 'user'` entry is not enough: the library writes frames in that role * (an evidence correction, a budget wrap-up, a drop notice), and one that * lands after a turn's results would move "the current request" past them. */ export declare function isPersonEntry(entry: unknown): boolean;