UNPKG

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