agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
178 lines • 9.47 kB
TypeScript
/**
* read — the ONE recognizer the tool-dispatch loop calls.
*
* Pattern: one reader, every dispatch door (the `applyResultCeiling`
* precedent). The batch loop and the credential/resume execute
* boundary both call this at the moment a handler's return lands, so
* a resumed call declares its coverage exactly as an inline one; the
* raise site (`../../core/agent/stages/toolCalls.ts` · `declareRaisedAbsence`)
* calls it on the `absence` a `requestInput` declared (9.114.0).
* Role: core/ layer, pure. Recognition and normalization only — the caller
* owns the events, the scope write and the delivered status.
* Emits: N/A.
*
* Zero-cost when unused: two `typeof` checks and a key lookup for every
* result that is neither shape, and `undefined` back. Nothing is emitted,
* nothing is written, and the value the model reads is the value the tool
* returned — byte for byte.
*/
import type { ToolResultStatus } from '../toolOutcome.js';
import { type DeclaredProvenance } from '../described.js';
import { type DeclaredPeriod } from './period.js';
import type { ComposedCoverage } from './composedCoverage.js';
import type { Coverage, InProgressItem, TryInsteadTool } from './types.js';
/**
* What the LIBRARY composed for a call of a tool that declares its `search`
* (`search/compose.ts`) — the fields a {@link ComposedCoverage} row adds. The
* typed items ride `CoverageFacts.coverage.checked`.
*/
export interface ComposedFacts {
/** `'local'` for a first-party tool; `'remote'` over MCP or from a ToolProvider. */
readonly origin: ComposedCoverage['composed'];
readonly resultId: string;
readonly handle: string;
readonly complete: boolean;
readonly gaps: ComposedCoverage['gaps'];
}
/** One coverage statement found in a result, before the caller stamps it with
* the call it came from. */
export interface CoverageFacts {
readonly kind: 'absence' | 'ledger';
readonly coverage: Coverage;
/** Present for `'absence'` — what the search was for. */
readonly lookedFor?: string;
/**
* Present for `'absence'` when it declared a sentence (9.113.0) — trimmed,
* as `absent()` mints it, and never parsed for a tool name. Not a piece of
* coverage, so it sits beside `coverage` rather than inside it.
*/
readonly tryInstead?: string;
/** Present for `'absence'` when it declared a typed tool (9.113.0) — a copy. */
readonly tryInsteadTool?: TryInsteadTool;
/**
* Present for `'absence'` when it declared where it looked and when that
* source was measured (honesty step 7b) — the record's camelCase form, read
* by the ONE provenance rule a described result is held to; a copy.
*/
readonly provenance?: DeclaredProvenance;
/**
* The period the declaration's read covered (honesty step 7b) — the record's
* camelCase form, read by the ONE period rule (`period.ts`); a copy. Absent
* when none was declared, and when the envelope declared one the rule set
* refuses — read, never repaired: it is left off the record (dev-warned) and
* the model still reads what the tool wrote.
*/
readonly period?: DeclaredPeriod;
/**
* Present for a `coverage()` ledger that declared a well-formed, non-empty
* `in_progress` — what its read found still running, its outcome not known
* yet (`inProgress.ts`). A malformed list is left off the record (dev-warned)
* and the model still reads what the tool wrote.
*/
readonly inProgress?: readonly InProgressItem[];
/**
* Present only when the library composed this statement from the tool's
* `search` — never read off an envelope: a value cannot forge it.
*/
readonly composed?: ComposedFacts;
}
/** What one recognized result declares. `undefined` from
* {@link readCoverageResult} means "neither shape": untouched path. */
export interface CoverageReading {
/**
* The status the framework DELIVERS for this call. `'absent'` when an
* absence is in play — never `'failure'`, and that is the point: a status
* of `'failure'` would route an honest empty answer down the same edge as
* a broken collector, which is the exact confusion the primitive removes.
* Undefined for a bare ledger — a ledger says nothing about the outcome,
* only about its boundary.
*/
readonly status?: ToolResultStatus;
/** In declaration order: the outer ledger first, then the absence it
* wraps. Usually one entry; two only when an author bounded an absence. */
readonly declared: readonly CoverageFacts[];
}
/**
* The value the MODEL is served for one finalized tool result: a recognized
* envelope with `short` and `kind` removed from every coverage item. They are
* RECORD-ONLY: the events, the tracked `coverageDeclared` rows and the answer
* account carry them; the model's request never does, so a tool that
* declares them costs no request byte.
*
* And one thing ADDED, derived from what the tool declared (honesty step 7b,
* bench round 1): a declared `period` whose store did not hold all of the
* time the read asked about is served with its verdict word and that word's
* one note clause (see `withPeriodServed`). A `covered` period, a malformed
* one and a result with none gain nothing.
*
* The shapes, and nesting: a bare absence; a ledger — its own lists AND
* whatever it bounds, read by this same function (so `coverage(absent(…))`,
* `coverage(coverage(…))` and a semantic envelope under a ledger are all
* served stripped; the depth is whatever the author built); and a semantic
* envelope's `coverage`.
*
* NOT stripped, because none is a recognized envelope OBJECT: a result
* returned as JSON TEXT (an `mcpClient` result in its default text mode is
* one), an `af_absent` whose `checked` is empty or missing (the recognizer
* refuses it, so it is ordinary data — served and recorded as written), and
* an absence buried inside a tool's own domain object or array. A key whose
* value is `null` counts as omitted (a JSON producer's `None`), so it is
* served as written and stamps nothing.
*
* Zero-cost when unused: the SAME reference back for every value that is not
* a recognized envelope, and for every envelope whose items declare neither
* key — so a caller that stamps `tool_end.modelResult` by reference
* inequality stamps nothing new. A structural copy only when a key was
* present.
*
* Asked at the ENTRY of `../../core/agent/stages/toolCalls.ts` · `afterMoment`, which every
* dispatch path (the batch loop and the four resume doors) calls for a result
* that ran — so every after-tool link is handed the served value, and none
* can re-serve the fields — and by the result ceiling and the column judge at
* both execute boundaries, which measure what the model will read.
*/
export declare function servedToModel(value: unknown): unknown;
/**
* True when `modelResult` is exactly what {@link servedToModel} produced from
* `result` — the two channels carry ONE value and differ only by the
* record-only fields. `../../core/agent/stages/toolCalls.ts` · `placeResults` then gives
* both channels the ticket, as it does when they are one reference, so a
* declaring tool never keeps its whole payload on `tool_end.result`.
*/
export declare function strippedOnly(result: unknown, modelResult: unknown): boolean;
/**
* Pure: the served form of `value`, and nothing remembered (the answer account
* reads through this; the dispatch door asks {@link servedToModel}, which also
* records the pair for `strippedOnly`).
*
* `seen` guards a ledger that bounds ITSELF (`v.result = v`, directly or
* further down): a holder met twice is returned as found, so a pathological
* cycle is served as written instead of overflowing the stack.
*/
export declare function strip(value: unknown, seen?: WeakSet<object>): unknown;
/**
* The inverse of the serve, for a reader of what the model was served: every
* declaring object's verdict word and period clause removed (the note swap is
* kept — it is the absence's own static note), the rest as found. The same
* reference back when nothing was served. Asked by the evidence projection
* (`evidence.ts` · `absenceEvidenceProjection`): words the library derived
* never ground an answer.
*
* It walks the shapes the serve writes — an object with a `period`, and a
* ledger's `af_coverage` and `result`, to any depth — and nothing else.
*/
export declare function withoutServedPeriod(value: unknown, seen?: WeakSet<object>): unknown;
/**
* Read one finalized tool result for coverage declarations — and, since
* honesty step 7b, for the period each declaration's read covered and an
* absence's source and time. `toolName` names the tool in the one dev
* warning a malformed period or provenance gets (it is left off the record,
* never repaired); the dispatch door passes it, a post-hoc reader need not.
*
* The two shapes compose: `coverage(absent({…}), {…})` is a search that found
* nothing AND a boundary around the search, so both are declared and the
* delivered status is still `'absent'` — the ledger bounds the answer, it
* does not change what the answer was.
*/
export declare function readCoverageResult(value: unknown, toolName?: string): CoverageReading | undefined;
//# sourceMappingURL=read.d.ts.map