agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
91 lines (90 loc) • 4.46 kB
TypeScript
/**
* search/compose — ONE call's coverage, composed by the library from the
* tool's `search` declaration and the arguments the call ran with.
*
* Pattern: linear composition of small pure rules —
* door (what came back) → inEffect (each argument) → facets
* (`checked`) → gaps → complete → the reading the funnel files.
* Role: semantics/ layer, pure. Called by the dispatch funnel
* (`../../../core/agent/stages/searchCoverage.ts` · `buildSearchCoverage`) at
* every execute boundary, before `declareCoverage`.
* Emits: N/A (the funnel emits, from the reading).
*
* ## Composition can only under-claim — relative to a TRUE declaration
*
* A declaration that misdescribes the query (an undeclared qualifier, a wrong
* `complete`) is the trusted base, said out loud: the law holds relative to
* it, never to the backend.
*
* A facet enters `checked` from exactly four places — a value the call sent, a
* declared default composed as if sent, a declared fixed qualifier, the
* declared (or sent) source — plus a host-filled identity filter (which only
* narrows) and a result-declared period (which only narrows). Every
* classified argument none of them covers is a bearing gap; a cap in effect,
* a declared server cap, a page, a `partial` outcome is a completeness gap
* unless the result says otherwise. So the composed scope is never wider than
* what the declaration and the arguments say. A wrong declaration is the
* trusted base, said out loud.
*
* ## Local and remote
*
* A `search` that arrived over MCP or from a `ToolProvider` composes the same
* shape, marked `'remote'`, and is never `complete`: it establishes nothing.
*/
import type { PeriodShapes } from '../../../time/periodForm.js';
import type { ToolResultStatus } from '../../toolOutcome.js';
import { type CoverageReading } from '../read.js';
import { type HostFilledArguments } from './inEffect.js';
import type { ToolSearch } from './types.js';
/** One call of a search tool, as the funnel knows it. */
export interface SearchCall {
readonly toolName: string;
readonly toolCallId: string;
readonly iteration: number;
/** The row's address — `claim-result/<namespace>/<runId>/<ordinal>`, minted by the funnel. */
readonly resultId: string;
/** The read's short handle (`r3`), minted with the identity. */
readonly handle: string;
readonly search: ToolSearch;
/** `'remote'` for a declaration that arrived over MCP or from a `ToolProvider`. */
readonly origin: 'local' | 'remote';
/** The arguments the tool ran with — after the inputs layer's fill and any before-tool rewrite. */
readonly args: Readonly<Record<string, unknown>>;
/** Arguments `hostFilled` fills inside `execute`, from its own declaration. */
readonly hostFilled?: HostFilledArguments;
/** The tool's `period`, for resolving a relative window. */
readonly period?: PeriodShapes;
/**
* When the call was DISPATCHED (an ISO instant) — the clock the time layer
* hands the tool, or the dispatch moment. A relative window resolves here,
* so a call resumed after a pause composes the span it actually ran with.
*/
readonly dispatchedAt?: string;
/** The outcome the tool declared on its effects envelope, if any. */
readonly status?: ToolResultStatus;
}
/** What composition made of one result. */
export type SearchComposition = {
readonly outcome: 'composed';
/** What the model is served: the composed absence, or the value as returned. */
readonly value: unknown;
readonly reading: CoverageReading;
}
/** The result hand-wrote `checked` beside the declaration: filed as written, composed never. */
| {
readonly outcome: 'declined';
readonly code: 'checked-beside-declared';
};
/**
* Compose one call's coverage from its search, or `undefined` when the value
* came through no door (plain data: nothing is composed, nothing changes).
* THROWS a refusal (`refused: …`) for a `coverage()` wrapped around a
* declared absence — at the execute boundary, that is the call's error.
*
* @example
* ```ts
* const out = composeSearchCoverage(call, absent({ what: 'x', checked: 'declared' }));
* out?.outcome === 'composed' && out.reading.declared[0]?.composed?.complete;
* ```
*/
export declare function composeSearchCoverage(call: SearchCall, result: unknown): SearchComposition | undefined;