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
TypeScript
/**
* 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;