agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
106 lines (105 loc) • 5.5 kB
TypeScript
/**
* Commentary templates — bundled English prose for narrating an
* agentfootprint run, plus the small engine that picks the right
* template per event and substitutes payload values.
*
* Audience split (load-bearing):
* • COMMENTARY — pure prose for the bottom panel of any viewer
* (Lens, CLI tail, log file). NO technical numbers,
* NO field dumps, NO library terms.
* • DETAILS — token counts, durations, args, IDs. The right
* panel / DevTools / structured-log territory.
*
* Architecture (3 pieces):
* 1. `defaultCommentaryTemplates` — flat `key → string` map.
* i18n-ready: ship a Spanish/Japanese/etc. version with the same
* keys, pass via `commentaryTemplates` on the renderer.
* 2. `selectCommentaryKey(event)` — per-event-type routing fn.
* Returns `string` (render this key), `null` (skip — too
* low-signal for prose), or `undefined` (fall through to a
* caller-supplied default humanizer).
* 3. `extractCommentaryVars(event, ctx)` — builds the
* `{ appName, userPrompt, toolName, descClause, ... }` bag the
* template will be rendered with.
*
* Plus a tiny non-recursive `renderCommentary(template, vars)`.
*
* Why this lives in agentfootprint (not Lens):
* The keys ARE agentfootprint event types. The prose teaches
* agentfootprint concepts (slot composition, ReAct, tool-calling).
* Consumers building agentfootprint Agents ship their voice / locale
* alongside their system prompt and tool registry. Lens (or any
* other viewer) is just a renderer that consumes this surface.
*
* Verb discipline (encoded in the prose):
* • `{{appName}}` (active actor) — called, dispatched, returned,
* decided, read, built
* • LLM (passive actor) — suggested, responded, produced,
* asked for, gave
* The split reflects the architectural truth: LLMs don't act, the
* orchestrating system does.
*/
import type { AgentfootprintEvent } from '../../../events/registry.js';
/** Flat map of template keys to template strings. Keys use a dotted
* hierarchy mirroring event types + payload branches
* (`'stream.llm_start.iter1'`, `'context.injected.rag'`). Values may
* contain `{{name}}` placeholders that `renderCommentary` substitutes. */
export type CommentaryTemplates = Readonly<Record<string, string>>;
/**
* The bundled English templates. Override per-key via the renderer's
* `templates` option — partial overrides are spread on top of these
* defaults so consumers only ship what they want to change.
*/
export declare const defaultCommentaryTemplates: CommentaryTemplates;
/** Context the var-extractor reads from. Anything that's NOT in the
* event payload (consumer-supplied appName, tool registry lookup) goes
* here. Pure data — no closures, no I/O. */
export interface CommentaryContext {
/** The system that orchestrates the LLM. Substituted as the active
* actor in every line ("Acme called the LLM"). Default: `'Chatbot'`. */
readonly appName: string;
/** Resolves a tool name to its registered description ("Get current
* weather for a city"). Used to compose the optional `descClause`
* for `stream.tool_start`. Sync — Lens-style consumers precompute
* the lookup map from `context.injected source='registry'` events. */
readonly getToolDescription?: (toolName: string) => string | undefined;
}
/**
* Pick the template key for an event. Branches encoded in the key
* suffix (no conditional logic in the templates themselves).
*
* `null` → explicit skip (baseline injections, low-signal events)
* `undefined` → fall through to caller's default humanizer
* `string` → render `templates[key]` with `extractCommentaryVars`
*/
export declare function selectCommentaryKey(event: AgentfootprintEvent): string | null | undefined;
/**
* Build the variable bag for a given event. Flat `name → string` map;
* `renderCommentary` substitutes by name. Templates use whatever names
* this function produces.
*
* Two-step composition for `stream.tool_start`: the optional
* `descClause` is a rendered sub-template. We pre-render it here so
* the outer template stays a single non-recursive substitution pass.
*/
export declare function extractCommentaryVars(event: AgentfootprintEvent, ctx: CommentaryContext, templates?: CommentaryTemplates): Record<string, string>;
/**
* Resolve the agent name from an event's `meta.subflowPath`.
*
* Walks the path right-to-left, skipping library-internal segments
* (slot subflows, agent-routing subflows, thinking handlers), and
* returns the FIRST meaningful segment with the optional `step-`
* Sequence prefix stripped. For events with no meaningful path
* (single-Agent runners, top-level events), falls back to `appName`.
*/
export declare function extractAgentName(event: AgentfootprintEvent, ctx: CommentaryContext): string;
/**
* Render a template by substituting `{{name}}` placeholders from the
* vars bag. Missing keys render as empty string — keeps prose
* forgiving when an optional field isn't present.
*
* Non-recursive: a substituted value is NOT itself processed for
* placeholders. Compose sub-templates upstream (see
* `extractCommentaryVars`).
*/
export declare function renderCommentary(template: string, vars: Record<string, string>): string;