UNPKG

agentfootprint

Version:

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

180 lines (179 loc) • 9.08 kB
/** * scripts/attachment — `.scriptTravelsWithTheAnswer()`: the recorded program * itself, under the answer, labelled from the execution record. * * Pattern: composed by the FRAMEWORK, never requested from the model (the * `.limitsTravelWithTheAnswer()` precedent, `semantics/coverage/ * answer.ts`). Pure: rows in, data and text out. * Role: core/ layer. Read by the final branch (`stages/prepareFinal.ts`): * the data rides `turn_end.scripts` on every answer under the arm; * the text is appended to a PROSE answer through the one composer * (`composeAnswerWithCoverage`), after the standing line and the * limits block. A typed answer gets the data only — its bytes are * never edited. * Emits: N/A. * * ## Why the library attaches it * * An app's rule "always show the script" is a rule the model can break, and * the cheapest revision that passes a names-and-numbers check is to delete * the script. The attachment needs no cooperation from the model: the program * comes from the record (the row's `text` — what was SUBMITTED to the tool), * so there is nothing to retype and nothing to check. When the model also * showed a matched copy, the attachment is still appended: the model's copy * never decides whether the authoritative one is delivered. * * ## What is attached (design § 4.1, § 4.4) * * - `which: 'last'` (default): the last call of the turn that REACHED the tool * (its label is not `submitted`); `'all'`: every such call, at most * {@link MAX_ATTACHED_SCRIPTS}, the rest counted in `omitted`. * - `maxBytes` (default 4,000 UTF-8 bytes) bounds each program shown, cut at a * code-point boundary; a cut says so — `[incomplete: showing N of M bytes — * not the whole program]` and `truncated.shownBytes` — and is never * advertised as a complete, reproducible program. * - A program over `MAX_SCRIPT_BYTES` has no text on its row: it is counted in * `omitted` and the text says "one script of N bytes is over the bound and * is not shown". Calls past `MAX_SCRIPT_ROWS` have no row: counted too. * * Every line is library-authored and past tense, names the CALL (never a * destination), and uses the label's words (`label.ts` · `labelWords`). * * @example * ```ts * const data = scriptsOfAnswer(scriptRowsOfTurn(ledger, turn).current, { which: 'last', maxBytes: 4000 }); * scriptsBlock(data); * // Script submitted to run_code on call c1 — completed, exit 0. Attached by the library from the run record: * // * // ```python * // print(41 + 1) * // ``` * ``` */ import type { ResolvedScriptTravel, ScriptLabel, ScriptRow, ScriptTravelOptions, ShownScript, TurnEndScripts } from './types.js'; /** Scripts attached under `which: 'all'`; the rest are counted (`omitted`). Design § 4.1. */ export declare const MAX_ATTACHED_SCRIPTS = 8; /** Default UTF-8 bytes of each program shown. Design § 4.1. */ export declare const DEFAULT_ATTACHED_BYTES = 4000; /** * Check `.scriptTravelsWithTheAnswer()`'s options once, at the call site, into * the arm the chart carries. Refusals name the option and the fix. */ export declare function resolveScriptTravel(opts?: ScriptTravelOptions): ResolvedScriptTravel; /** * What travels with this turn's answer — the data form (`turn_end.scripts`). * `rows` is this turn's reading (`rows.ts` · `scriptRowsOfTurn`); fresh plain * objects, never a reference into the ledger. */ export declare function scriptsOfAnswer(rows: readonly ScriptRow[], travel: ResolvedScriptTravel, scriptsDropped?: number): TurnEndScripts & { readonly overSize: readonly ScriptRow[]; }; /** The label line of one attached script — library words, past tense, the call named. */ export declare function scriptLabelLine(script: ShownScript): string; /** The line that stands for a script whose program is kept out of the text (`withheldFromText`). */ export declare function scriptWithheldLine(script: ShownScript): string; /** One attached script as text: its label line, then the program in the library's fence. */ export declare function shownScriptBlock(script: ShownScript): string; /** * What the text BEFORE the attachment leaves open, as a CommonMark renderer * reads it — the rendering boundary (finding: a model answer that ends inside * an unfinished fence would take the library's programs in, and a fence line * inside a program would then close it, leaving the rest as prose). * * - `clean`: nothing the attachment could fall into. * - `closed`: a fence is still open; the attachment begins by closing it * (`fence`, the same character and length). * - `unsettled`: the reader stopped (an indented fence, raw HTML, display * math — `../evidence/codeRegions.ts`), so nothing says what a program would * render inside. The programs stay out of the text; the data keeps them. * * `before` is exactly the text the attachment follows — the composer's * (`semantics/coverage/answer.ts` · `composeAnswerWithCoverage`). Read with a * line ending after it, as it is composed: an opening fence on its last line * opens there. */ export type AttachmentBoundary = { readonly kind: 'clean'; } | { readonly kind: 'closed'; readonly fence: string; } | { readonly kind: 'unsettled'; }; /** The boundary after `before` (see {@link AttachmentBoundary}). */ export declare function attachmentBoundary(before: string): AttachmentBoundary; export type RecordedBoundary = { readonly kind: 'clean'; } | { readonly kind: 'closed'; readonly char: 'backtick' | 'tilde'; readonly length: number; } | { readonly kind: 'unsettled'; }; /** * The boundary to rebuild a recorded text behind — `undefined` when its fence * is longer than `within`, the text it would have to be found in: the one * repetition a reader makes is bounded by what it reads. */ export declare function rebuildBoundary(recorded: RecordedBoundary, within: number): AttachmentBoundary | undefined; /** The label an attached script can carry: a call that never reached the tool is never attached (`reached`). */ export type AttachedLabel = Exclude<ScriptLabel, 'submitted'>; /** An attached script as a reader gets it back: its label, by type, one an attached script can carry. */ export type RecordedScript = ShownScript & { readonly label: AttachedLabel; }; /** One attached script read back, at its position in the record's `shown`. */ export interface RecordedShown { readonly index: number; readonly script: RecordedScript; } /** A saved `turn_end.scripts`, read back. */ export interface RecordedScripts { /** The entries a writer of this library files, at their record position. */ readonly shown: readonly RecordedShown[]; /** * What no writer files, counted once each: an entry that does not read * back, every entry past `MAX_ATTACHED_SCRIPTS` (the writer attaches no * more), an `omitted` that is not a whole number, a boundary * ({@link RecordedBoundary}) that is not one a writer states, or a * `scripts` value that is not `{ shown: [...] }`. Zero means every field * was read back — only then can the attached text be rebuilt. */ readonly unread: number; /** `omitted`, when the record's is a whole number. */ readonly omitted?: number; /** The rendering boundary, when the record's is one a writer states. */ readonly boundary?: RecordedBoundary; } /** * A saved `turn_end.scripts` — THE reader of the shape for a reader of a * record. `undefined` when the event carries no scripts arm. `within` is the * length of the answer the block was appended to (a fence longer than it is * refused). Bounded by the writer's cap: no reader iterates more entries * than a writer attaches. */ export declare function recordedScripts(value: unknown, within?: number): RecordedScripts | undefined; /** * The text form: one block per attached script, then one line per omission — * `''` when nothing travels (the composer then appends nothing). Every cut and * every omission is stated. Behind a `closed` boundary the block opens with * the closing fence; behind an `unsettled` one each script is its label line * alone (`scriptWithheldLine`) — the programs ride the data only. */ export declare function scriptsBlock(scripts: TurnEndScripts & { readonly overSize?: readonly ScriptRow[]; }, boundary?: AttachmentBoundary): string; /** * The text form behind the boundary the text before it leaves — `''` when * nothing travels (no boundary is then taken). The second value is the * boundary the text was written behind: what `turn_end.scripts` records. */ export declare function scriptsBlockAfter(scripts: TurnEndScripts & { readonly overSize?: readonly ScriptRow[]; }, before: string): { readonly text: string; readonly boundary: AttachmentBoundary; }; /** The data form as detached plain data — what `turn_end.scripts` carries. */ export declare function turnEndScriptsOf(scripts: TurnEndScripts, boundary?: AttachmentBoundary): TurnEndScripts;