UNPKG

agentfootprint

Version:

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

79 lines • 4.5 kB
/** * requestEvidence — the door a tool call's REQUEST attempts enter its run by. * * Pattern: a per-dispatch port (the `LLMCallHooks` precedent) + one allow-list. * Role: The seam between a retry series that runs BELOW a tool — inside * `mcpClient`'s transport, built before any run existed — and the run * that dispatched the call. The dispatcher builds the port for each * call (`requestEvidenceFor`) and puts it on the call's context under * an internal symbol; the MCP tool hands it down to the series that * sends its request (`mcp/requestLedger.ts`); the series files ONE * record when the request ends. Nothing here retries anything. * Emits: agentfootprint.tools.request_attempts * * Why a port and not a scope: a decorator constructed by the consumer before * any run exists can never reach a scope (`resilienceHooks` says the same of * the provider decorators). The call is the one thing both sides share, so the * port rides the call — and the identity on the event (`toolCallId`, * `toolName`, `iteration`, the run's ids in the meta) is stamped HERE, from the * dispatch, never by whatever files: a filing cannot claim another call. * * Why an internal symbol and not a public `ctx` field: this is the evidence * path for the library's own retry series, not a retry API. A public member * would invite every tool to file attempts the library cannot vouch for. The * symbol is enumerable, so a wrapper that copies the context (`{ ...ctx }`) * keeps it; a filing from anyone else still passes the allow-list below. * * The port only FILES. What cancels a call is the call's own context signal * (`ctx.signal`), read by the MCP tool whether or not a port rides along — a * cancellation must never depend on evidence being attributable. * * THE ALLOW-LIST is the whole record: numbers, the library's own words, the * endpoint label the app declared or else the endpoint's ORIGIN. A filing with * anything else in it — a field nobody listed, a word outside its set, a URL * with a path, an array that is not a plain one — is refused whole, never * trimmed into something it did not say. Values are read as DATA, through own * property descriptors, so no getter, `toJSON` or array subclass a filing * carries ever runs or reaches the record. */ import type { ToolsRequestAttemptsPayload } from '../events/payloads.js'; import { type EmitableScope } from '../recorders/core/typedEmit.js'; /** At most this many attempts are listed per request; the rest are counted (`omitted`). */ export declare const MAX_REQUEST_ATTEMPT_ROWS = 100; /** What a request's series files: the event's payload, less the identity the dispatcher stamps. */ export type RequestEvidence = Omit<ToolsRequestAttemptsPayload, 'toolCallId' | 'toolName' | 'iteration'>; /** Where one dispatched call's request evidence goes. */ export interface RequestEvidenceSink { /** File the request's record. Never throws; a filing outside the allow-list is refused whole. */ file(evidence: RequestEvidence): void; } /** @internal The context key the dispatcher puts a call's sink under. */ export declare const REQUEST_EVIDENCE: unique symbol; /** What the dispatcher spreads into a call's context. */ export interface RequestEvidencePort { readonly [REQUEST_EVIDENCE]: RequestEvidenceSink; } /** The call the sink files for — the dispatch's own identity. */ export interface RequestEvidenceCall { readonly toolName: string; readonly toolCallId: string; readonly iteration: number; } /** * The port for one dispatch: files `agentfootprint.tools.request_attempts` * mid-stage through `typedEmit` (so the run's redaction rule serves it, and the * meta carries the real `runtimeStageId`), stamped with `call`. */ export declare function requestEvidenceFor(scope: EmitableScope, call: RequestEvidenceCall): RequestEvidencePort; /** * The sink a call's context carries, or `undefined` — a context from a door with * no run (`mcpServe`, a direct `execute`), or one a wrapper rebuilt without it. * Never throws, whatever `ctx` is. */ export declare function requestEvidenceOf(ctx: unknown): RequestEvidenceSink | undefined; /** * `raw` rebuilt from allow-listed fields — a fresh plain object — or * `undefined` when anything in it is outside the list. */ export declare function servedRequestEvidence(raw: unknown): RequestEvidence | undefined; //# sourceMappingURL=requestEvidence.d.ts.map