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
TypeScript
/**
* 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;