agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
113 lines • 6.32 kB
TypeScript
/**
* artifacts/wants — ref ARGUMENTS at dispatch (Phase 2, Leg 1).
*
* A tool declares `wants: { dataset: 'dataset/rows' }` and the FRAMEWORK
* resolves the ref before `execute`: the model speaks the ~26-char ticket as
* the argument, dispatch redeems it under the run's own scope and kind-checks
* the meta, and the handler receives the RESOLVED DATA (plus the meta on
* `ctx.wanted`). A stale, unknown, or wrong-kind ref NEVER reaches the tool —
* the model reads a teaching refusal that lists the live refs of the wanted
* kind in scope (the innerRunRecords law: correct by naming what CAN resolve).
*
* The declare-and-push precedent, verb for verb: `needs` resolves a credential
* before execute and injects `ctx.credential`; `wants` resolves data before
* execute and injects it into the args. Both fail closed; neither lets the
* model fabricate what the framework could not deliver.
*
* This module is the PURE half — validation at `defineTool` time and
* resolution over the five-verb port. It never imports `core/`: the
* tool-dispatch layer composes the scope, adapts the outcome onto the typed
* `agentfootprint.artifacts.*` events (`op: 'dispatch'`), and refuses the
* call. Core wires artifacts; never the reverse.
*/
import type { ArtifactMeta, ArtifactScope, ArtifactStore } from './types.js';
import type { ArtifactRefusalReason } from './capability.js';
/**
* The declaration on a tool: argument name → the artifact `kind` that
* argument must resolve to (consumer vocabulary, exact-match — no wildcards,
* no hierarchy; `'dataset/rows'` is one kind, not a family).
*/
export type ToolWants = Readonly<Record<string, string>>;
/**
* Refuse a `wants` declaration this library cannot honor, at definition time
* — naming the tool and the fix, never at the first dispatch of the first
* run. Checks, per declared argument:
* • the kind is a non-empty string (a blank kind can never match a mint);
* • when the tool's `inputSchema` declares `properties`, the argument
* exists there (a wants-arg the model is never offered can never be
* filled);
* • when that property declares a `type`, it is `'string'` — the model
* passes the REF, never the bytes, so any other type is a schema that
* asks the model to inline what this feature exists to keep out.
*/
export declare function assertToolWants(toolName: string, wants: ToolWants | undefined, inputSchema: Readonly<Record<string, unknown>> | undefined): void;
/** One dispatch-time refusal — the fact the tool-calls stage puts on the
* record (`artifacts.refused`, `op: 'dispatch'`) beside the teaching
* sentence the model reads. */
export interface WantsRefusal {
readonly argName: string;
/** The kind the declaration wanted. */
readonly kind: string;
readonly reason: Extract<ArtifactRefusalReason, 'invalid-input' | 'missing-or-expired' | 'kind-mismatch' | 'digest-mismatch'>;
/** The ref the model spoke, when it spoke one. */
readonly ref?: string;
/** The teaching sentence for THIS argument (the combined refusal the model
* reads joins every argument's sentence). */
readonly detail: string;
}
/** What resolving a tool's `wants` produced. Exactly one arm. */
export type WantsResolution = {
readonly ok: true;
/** The call args with every resolved argument REPLACED by its data. */
readonly args: Readonly<Record<string, unknown>>;
/** The claim tickets behind each resolved argument — `ctx.wanted`. */
readonly wanted: Readonly<Record<string, ArtifactMeta>>;
/** The refs that resolved, in declaration order — for the record. */
readonly resolved: ReadonlyArray<{
readonly argName: string;
readonly meta: ArtifactMeta;
}>;
} | {
readonly ok: false;
readonly refusals: readonly WantsRefusal[];
/** The one teaching refusal the model reads — every failed argument's
* sentence, joined. */
readonly refusal: string;
};
/**
* Resolve one tool's declared `wants` against the run's scope — the whole
* Leg-1 law in one place, shared by every dispatch door.
*
* An ABSENT declared argument is judged by the tool's own schema (9.38.0):
* `required` there means the tool cannot do its job without the data, so
* dispatch refuses BY NAME rather than executing a handler that believes its
* payload was resolved; anything else is optional, and the model choosing not
* to pass one is legitimate (`ctx.wanted` simply has no entry for it — absent
* and empty are different facts). Pass the tool's `inputSchema` to have that
* judged; omit it and every declared argument is treated as optional, which is
* what a door that genuinely has no schema can honestly say.
*
* Per declared argument that is PRESENT in the call args:
* • a non-string value is refused (`invalid-input`) — the argument is
* spoken as the ref string, never the bytes;
* • a ref that does not resolve in scope is refused
* (`missing-or-expired`), listing the live refs of the wanted kind;
* • a ref whose meta.kind differs from the declaration is refused
* (`kind-mismatch`), naming both kinds and listing what would fit;
* • a stored payload failing its digest re-check is refused
* (`digest-mismatch`) — corrupt bytes never reach the tool as if whole.
*
* ALL declared args are judged before answering, so one refusal teaches the
* whole correction rather than one argument per retry loop.
*/
export declare function resolveToolWants(store: ArtifactStore, scope: ArtifactScope, toolName: string, wants: ToolWants, args: Readonly<Record<string, unknown>>, inputSchema?: Readonly<Record<string, unknown>>): Promise<WantsResolution>;
/**
* The teaching refusal for a `wants`-declaring tool dispatched where NO
* artifact store is attached — fail-closed, naming the config fix, because
* running the tool with the raw ref string where it expects resolved data
* would be accepted-and-silently-wrong. (Agent dispatch refuses this at
* BUILD for statically registered tools; this sentence serves the doors
* that only meet the tool at dispatch — provider-served tools, `mcpServe`.)
*/
export declare function wantsNeedsStoreRefusal(toolName: string, wants: ToolWants): string;
//# sourceMappingURL=wants.d.ts.map