agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
76 lines (75 loc) • 3.97 kB
TypeScript
/**
* causalEvidenceRecorder — the evidence bridge (backlog Phase-1 #5).
*
* Harvests, DURING the run, everything a causal snapshot needs beyond
* (query, finalContent) — from events the engine already fires:
*
* stream.tool_start/tool_end → ToolCallRecord (name, args, resultPreview, errored)
* — none for a bracket carrying `notDispatched`
* (9.113.0): that call never ran
* stream.llm_end → tokenUsage accumulation + iteration high-water
* agent.turn_start/turn_end → durationMs (+ authoritative totals when seen)
* FlowRecorder.onDecision → DecisionRecord with footprintjs decide()/select()
* operator-level evidence (rule, conditions, chosen)
* context.evaluated routing → DecisionRecord per skill the graph routed to
*
* Pattern: CombinedRecorder (Convention 1 — single purpose: evidence
* accumulation); per-turn reset anchored on `agent.turn_start`
* (Convention 4 — executor `clear()` resets between runs; same-
* executor pause/resume PRESERVES pre-pause evidence by design).
* PII note: tool args/results and decide() evidence persist into snapshots.
* A causal memory is working state — a later run replays it to the
* model — so the Agent feeds this recorder's tool calls from its
* REAL-value path: under an agent's `redact` the snapshot still keeps
* the real arguments and results, as it keeps the real question and
* answer (`writeSnapshot` reads those from the conversation the run
* computed on). The one part it cannot read real is the DECISIONS:
* footprintjs serves its flow channel (`onDecision` / `onSelected`)
* under the run's policy, so a selected value inside decide()
* evidence is the placeholder there (a named limit,
* `src/redaction/README.md`). Values are bounded (`maxPreviewChars`
* for results, `maxFieldChars` for args/evidence); treat the snapshot
* store as PII-bearing and protect it accordingly.
*
* The Agent attaches this automatically when a CAUSAL memory is mounted and
* threads `collect` into the memory write mount (`evidenceSource`) — so
* `writeSnapshot` persists real evidence instead of zeros.
*/
import type { FlowDecisionEvent, FlowSelectedEvent } from 'footprintjs';
import type { DecisionRecord, ToolCallRecord } from './types.js';
/** What the bridge delivers to `writeSnapshot` for one run. */
export interface RunEvidence {
readonly iterations: number;
readonly decisions: ReadonlyArray<DecisionRecord>;
readonly toolCalls: ReadonlyArray<ToolCallRecord>;
readonly durationMs: number;
readonly tokenUsage: {
readonly input: number;
readonly output: number;
};
}
export interface CausalEvidenceRecorderOptions {
/** Recorder id (default 'causal-evidence'). */
readonly id?: string;
/** Max chars kept of each tool result preview. Default 200. */
readonly maxPreviewChars?: number;
/** Max serialized chars kept of tool ARGS and decision EVIDENCE (the
* PII-dense fields). Oversized values are replaced by a truncated
* preview marker. Default 2000. */
readonly maxFieldChars?: number;
}
export interface CausalEvidenceRecorderHandle {
readonly id: string;
/** Snapshot the evidence accumulated for the CURRENT run. */
collect(): RunEvidence;
clear(): void;
onEmit(event: {
name: string;
payload: unknown;
}): void;
onDecision(event: FlowDecisionEvent): void;
onSelected(event: FlowSelectedEvent): void;
}
/** Build the evidence-harvesting recorder. Attach via `.watch(rec)` (the
* Agent does this automatically for CAUSAL memories). */
export declare function causalEvidenceRecorder(options?: CausalEvidenceRecorderOptions): CausalEvidenceRecorderHandle;