agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
104 lines • 5.16 kB
TypeScript
/**
* definitions — the tool lists a pool serves, and when each stops being served.
*
* Pattern: one list type, two owners — a client incarnation, or a shared entry.
* Role: § 6 of the design. A definition is what one `tools/list` entry says:
* name, description, input schema, and the declarations its `_meta`
* carries. The executable half (the connection) is never shared.
*
* - PER CLIENT (the default): a conversation's list is its own client's, and
* it belongs to that client INCARNATION alone — a replacement client, even
* one opened from the same plan, is a new session whose list may differ,
* so it lists again.
* - SHARED, only under the app's declared contract: one entry per context
* (`plan.ts`), listed on the client of whichever conversation needs it,
* bounded at `MAX_DEFINITION_ENTRIES` entries; a list longer than
* `MAX_SHARED_TOOLS` is used by its own client and not shared.
*
* A list stops being served in four ways, plus a time limit: the configuration
* (a new context is a new list), the server's tools-changed notice, the app's
* `permissionsChanged`, and `toolsChanged`. Invalidation moves the list's
* GENERATION at once — every tool bound to an older one is refused — and marks
* it to be listed again. A lapsed time limit lists again at the next list, and
* moves the generation only if the new list differs. A generation counts within
* one list: a replacement client and a new entry start counts of their own, and
* a binding names the list it came from, so no number of an old list can match
* a new one. A listing still in flight when its list's generation moved is
* discarded, never written.
*
* Every list is a frozen plain-data copy (`mcp/plainData.ts` · `copyJsonObject`),
* written once per generation, by one listing at a time.
*/
import type { Tool } from '../../tools/tools.js';
import type { McpListedTool } from '../types.js';
/** One definition, frozen, and its canonical JSON — what "the same definition" compares. */
export interface Definition {
readonly listed: McpListedTool;
readonly canonical: string;
}
/** A frozen copy of `listed`, or `undefined` when it is not plain JSON data. */
export declare function definitionOf(listed: McpListedTool): Definition | undefined;
/**
* One list: an incarnation's own or a shared entry. Its `id` is stable for its
* life; its `generation` moves on invalidation, and on a time-limit relisting
* that changed it.
*/
export declare class DefinitionList {
readonly id: string;
private current;
private tools;
/** Has any listing been written? */
private written;
/** 0 until the first listing is written. */
generation: number;
/** Invalidated: listed again before it is served. */
stale: boolean;
/** When it was last written, on the pool's clock. */
listedAt: number;
/** The listing in flight, joined by anyone who needs the same list. */
listing: Promise<void> | undefined;
/** Can it be served at `now`, as it stands? */
servable(now: number, ttlMs: number): boolean;
/**
* Invalidate: every older binding is refused, the next list lists again, and
* a listing in flight is discarded — even a first one, before anything was
* written. Generations only ever increase.
*/
invalidate(): void;
/** The definition named `name`, at the current generation. */
definition(name: string): Definition | undefined;
/** The client's own wrapped tool named `name` (per-client lists only). */
tool(name: string): Tool | undefined;
/** Every definition, in listing order. */
all(): readonly Definition[];
/**
* Write a listing that began at generation `began`. Refused — `false`, nothing
* written — when the generation moved while it ran. A first write moves the
* generation to a number no binding has seen; a write after invalidation keeps
* the moved one; a time-limit rewrite moves it only when the list differs.
*/
write(began: number, definitions: readonly Definition[], tools: readonly Tool[] | undefined, now: number): boolean;
}
/** A shared entry: a list and the tenant its context carries. */
export interface SharedEntry {
readonly list: DefinitionList;
readonly tenant: string;
}
/**
* The shared definition entries, by context — bounded at
* `MAX_DEFINITION_ENTRIES`, the least recently used dropped first. A dropped
* entry's bindings are refused as stale: the entry they name is gone.
*/
export declare class SharedDefinitions {
private readonly maxEntries;
private readonly entries;
constructor(maxEntries?: number);
get size(): number;
/** The entry for `context`, created empty when there is none; most recently used. */
entryFor(context: string, tenant: string): SharedEntry;
/** The entry for `context` if it still exists — never created. */
peek(context: string): SharedEntry | undefined;
/** Invalidate every entry `matches` selects. */
invalidate(matches: (entry: SharedEntry) => boolean): void;
}
//# sourceMappingURL=definitions.d.ts.map