agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
86 lines (85 loc) • 4.21 kB
TypeScript
/**
* tools/script — a tool's declaration that it RUNS a program, and the one rule
* that judges the declaration.
*
* Pattern: Map. `Tool.script` is plain data on the tool, judged by ONE assert
* at the three doors a declaration crosses (the `askOrAssume`
* precedent, `tools/declare.ts`): definition (`tools.ts` ·
* `defineTool`), dispatch (`scriptOf`, for a Tool built by hand or
* served by a ToolProvider, which never passes `defineTool`) and MCP
* ingest (`mcp/toolExtras.ts` · `readToolExtras`).
* Role: tools/ layer leaf (L7). Imports nothing from the agent.
* Emits: N/A.
*
* ## Declared, never inferred
*
* A tool is code-running because it SAYS so. The library never reads it off a
* tool's name (`run_code`), a property named `code` or the description. A tool
* that runs code and does not declare it has no submissions on the record, so
* a script the model shows for it is prose, as it always was. `codeRunnerTool`
* declares `{ argument: 'code', language }` on the tool it builds.
*
* What the declaration arms (design: docs/design/2026-10-script-artifact.md):
* the dispatch loop files one `script` row per call of the tool while
* `.namesAndNumbersFromEvidence({ code: 'match' })` or
* `.scriptTravelsWithTheAnswer()` is on — the program SUBMITTED to the tool,
* and what the runner reported. Never evidence for a value.
*
* @example
* ```ts
* const runSql = defineTool({
* name: 'run_sql',
* description: 'Run a read-only SQL query and print the rows.',
* inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] },
* script: { argument: 'query', language: 'sql' },
* execute: async ({ query }) => db.read(query),
* });
* ```
*/
/** Where a code-running tool's program travels, and its language. */
export interface ToolScript {
/** The ONE string argument of the input schema that carries the program. */
readonly argument: string;
/**
* The program's language — the info string of the fence the library renders
* the program in (`python`, `sql`). One plain token: no line break, no
* backtick (a backtick fence's info string may not hold one), at most 64
* characters. Absent → a fence with no info string.
*/
readonly language?: string;
}
/**
* The one judge of a `script` declaration: refuses, naming the tool, the field
* and the fix — a declaration the dispatch loop could not honour, never one it
* would quietly repair.
*
* - a plain object holding only `argument` and `language`;
* - `argument` names a property of the input schema whose `type` is `'string'`
* (a program is text), and not a `wants` argument (that one carries an
* artifact ref, then its data — never the program);
* - `language`, when present, is one plain token the fence can carry.
*/
export declare function assertToolScript(toolName: string, script: unknown, inputSchema: Readonly<Record<string, unknown>> | undefined, wants?: Readonly<Record<string, unknown>>): void;
/** A frozen copy of a well-formed declaration: the two fields, nothing else. */
export declare function copyToolScript(script: ToolScript): ToolScript;
/** The tool shape the dispatch door reads — structurally any `Tool`. */
export interface ScriptToolLike {
readonly schema: {
readonly name: string;
readonly inputSchema?: Readonly<Record<string, unknown>>;
};
readonly script?: unknown;
readonly wants?: Readonly<Record<string, unknown>>;
}
/**
* THE DISPATCH DOOR — the tool's `script` declaration, judged once per tool
* object, or `undefined` when it declares none.
*
* A tool built by hand or served by a ToolProvider never passed `defineTool`,
* so its declaration is judged here by the same assert. One that fails it is
* NOT a code-running tool for the record (no row is filed, a block the model
* shows for it is prose) — never half-honoured — and dev mode names the rule
* once, the MCP ingest law (`mcp/toolExtras.ts`): the rail stays unarmed and
* the developer is told which and why.
*/
export declare function scriptOf(tool: ScriptToolLike | undefined): ToolScript | undefined;