agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
96 lines • 5.08 kB
TypeScript
/**
* artifacts/capability — `ctx.artifacts`, the scope-bound door a tool gets.
*
* Shaped exactly like `ctx.credentials`:
* • ALWAYS present — with no store attached every method is a teaching
* refusal naming how to attach one, so a tool can never silently
* optional-chain past a store that is not there;
* • fail-closed — the refusal throws; `ctx.hasArtifacts` is the fact to
* branch on for an intentional degraded mode;
* • scope composed by the FRAMEWORK from the run's identity/session — the
* capability closes over it, so a tool cannot name, widen or replace the
* scope it resolves under. The five verbs here take no scope argument on
* purpose: that argument was already answered by whoever built the
* context.
*
* Every hop lands on the record AS IT HAPPENS: this module reports facts to a
* neutral sink (a plain callback), and the tool-dispatch layer adapts that
* sink onto the typed event channel. Neutral so this folder never imports the
* event system or the agent — core wires artifacts, never the reverse.
*/
import { type ArtifactListOptions, type ArtifactListResult, type ArtifactMeta, type ArtifactOrigin, type ArtifactRecord, type ArtifactRef, type ArtifactScope, type ArtifactStore, type PutArtifactInput, type SweptArtifact } from './types.js';
/** A tool's `put` — everything the caller owns EXCEPT `origin`, which the
* framework stamps from the run's own facts (never invented, never spoofed). */
export type ToolArtifactPutInput = Omit<PutArtifactInput, 'origin'>;
/**
* The capability on `ctx.artifacts` — the store's five verbs with the scope
* already answered.
*/
export interface ToolArtifacts {
/** Store a payload under this run's scope; returns the claim ticket. */
put(input: ToolArtifactPutInput): Promise<ArtifactMeta>;
/** The ticket without the payload. `null` for missing-or-expired. */
head(ref: ArtifactRef): Promise<ArtifactMeta | null>;
/** Ticket + payload. `null` for missing-or-expired; a digest mismatch throws. */
get(ref: ArtifactRef): Promise<ArtifactRecord | null>;
/** Remove one artifact this scope holds. */
delete(ref: ArtifactRef): Promise<void>;
/** Page through this scope's tickets, newest first. */
list(options?: ArtifactListOptions): Promise<ArtifactListResult>;
}
/** Which door a refusal happened at — the five verbs, plus `'dispatch'`:
* the framework's own resolution of a tool's declared `wants` (and the
* `present` tool's argument checks) BEFORE execute. Not a sixth store verb
* — a dispatch refusal is the tool-calls stage declining to run a tool
* whose declared data could not be delivered. */
export type ArtifactOp = 'put' | 'head' | 'get' | 'delete' | 'list' | 'dispatch';
/** Why a verb refused (or answered "no data" on the record). */
export type ArtifactRefusalReason = 'no-store' | 'missing-or-expired' | 'unknown-parent' | 'digest-mismatch' | 'invalid-input'
/** A `wants` ref resolved, but to the wrong kind — the wrong parcel for
* this ticket window (dispatch resolution only). */
| 'kind-mismatch';
/** One thing that happened at this door — meta only, never payloads. */
export type ArtifactEventFact = {
readonly type: 'minted';
readonly meta: ArtifactMeta;
} | {
readonly type: 'resolved';
readonly ref: ArtifactRef;
readonly via: 'head' | 'get';
readonly kind: string;
readonly bytes: number;
} | {
readonly type: 'expired';
readonly swept: SweptArtifact;
} | {
readonly type: 'refused';
readonly op: ArtifactOp;
readonly reason: ArtifactRefusalReason;
readonly ref?: ArtifactRef;
readonly detail?: string;
};
/** Where facts go. The dispatch layer adapts this onto the typed events. */
export type ArtifactEventSink = (fact: ArtifactEventFact) => void;
/** What `bindArtifacts` needs beyond the store and the scope. */
export interface BindArtifactsOptions {
/** Stamped onto every mint — the run's own facts, absent when unknown. A
* caller-supplied `origin` is dropped either way: with none here, a mint
* carries no origin at all. */
readonly origin?: ArtifactOrigin;
/** Fact sink. Absent = silent binding (raw store semantics, no record). */
readonly onEvent?: ArtifactEventSink;
}
/**
* Bind a store to one run's scope — the framework's move, made where the
* scope is known and a tool cannot reach.
*/
export declare function bindArtifacts(store: ArtifactStore, scope: ArtifactScope, options?: BindArtifactsOptions): ToolArtifacts;
/**
* The fail-closed capability used when NO store is attached. Every verb
* throws the same teaching refusal — loud, named, and on the record — so
* `ctx.artifacts` is never `undefined` and a missing store can never read as
* an empty one. Branch on `ctx.hasArtifacts` for an intentional no-store
* mode. (The `unconfiguredCredentialProvider` law, verb for verb.)
*/
export declare function unconfiguredArtifacts(onEvent?: ArtifactEventSink): ToolArtifacts;
//# sourceMappingURL=capability.d.ts.map