UNPKG

agentfootprint

Version:

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

59 lines (58 loc) • 3.25 kB
/** * boundTool — a tool bound to its conversation, the list it came from and that * list's generation, re-checked after every wait and at the send gate. * * Pattern: the shape every MCP tool takes (`mcpClient.ts` · `mcpToolShape`) + * one `execute` that runs the five steps of § 6.3. * Role: A bound tool carries its BINDING: the key it was listed for; the * context it was listed in (per client, the configuration; shared, the * entry's context); the list it came from (per client, the client * incarnation; shared, the entry); and that list's generation. Its * `execute`: * * 1. checks that the calling run's caller identity encodes to its key — * otherwise `identity-mismatch`, nothing sent; * 2. checks its binding is current — otherwise `stale-tools`, nothing sent; * 3. is admitted against `maxOperations` and takes a lease on its key's * client, opening one from a plan resolved now when the key has none. A * client whose plan's context differs from the binding's refuses with * `binding-changed` (the client stays for the next list). A client that is * not the incarnation the tool was listed from is checked against its OWN * fresh list — the call goes on only if that list holds the same * definition, compared as canonical JSON; otherwise `stale-tools`. Under * the app's sharing contract, a replacement whose context matches uses the * shared entry as it stands; * 4. after every wait, checks step 2 again — and asks the app's `share` * again, so a changed authorization name refuses it (`binding-changed`); * 5. sends through the send gate (`mcp/requestGuard.ts`), asked at every * boundary the request crosses — before every attempt, after a credential * arrives, after signing — classifies the outcome (a server's 404 for the * session is `SessionExpiredError`; a handle this process lost is * `LocalSessionHandleLostError`; neither is repeated), files its use, and * releases its lease once the request's own work has settled. * * Never another conversation's connection: the binding is to the KEY, and a * client of any other key never serves it. */ import type { Tool } from '../../tools/tools.js'; import type { Definition } from './definitions.js'; import { type PoolKey } from './identityKey.js'; import type { Pool } from './pool.js'; import { UseFacts } from './record.js'; /** What a bound tool was listed under. */ export interface Binding { readonly key: PoolKey; /** The definitions context: `pc:<configHash>` per client, `sh:<context>` shared. */ readonly context: string; readonly shared: boolean; /** The list it came from: an incarnation's own, or a shared entry's. */ readonly listId: string; readonly generation: number; /** Per client: the incarnation it was listed from. */ readonly clientId?: string; readonly definition: Definition; } /** A callable tool for `binding`, shaped as every MCP tool is. */ export declare function boundTool(pool: Pool, binding: Binding): Tool; /** A refusal's word on the record. */ export declare function noteRefusal(facts: UseFacts, error: unknown): void;