/** * 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>; /** * 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> | 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; /** 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>; /** The claim tickets behind each resolved argument — `ctx.wanted`. */ readonly wanted: Readonly>; /** 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>, inputSchema?: Readonly>): Promise; /** * 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;