UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

53 lines (52 loc) • 2.78 kB
/** * middleware/ledger — INTERNAL. Where a chain's decisions become record. * * Pattern: one writer for one committed key. * Role: core/ layer. `runChain` decides; this file is the only thing * that commits what it decided and emits the matching event, so * the three call sites cannot record the same fact three * different ways. * Emits: `agentfootprint.middleware.decision`, one per row. * * ## Why the original value is committed too * * A transform makes the trace and the wire disagree — the model saw one * string, the run remembers another. Committing the pair, with the * middleware's name and its `why` beside it, is what turns that * disagreement from a lie into a record. Every slice taken afterwards can * then find the moment the value changed and who changed it. * * The same reasoning covers the `'after-tool'` moment from the other side. A * result the model was not allowed to read still HAPPENED — the tool ran, the * side effect landed — so the refusal row carries the real result. A run that * dropped it would record an agent that called a tool and got nothing back, * which is not what occurred. * * This is not a redaction layer, and it is not trying to be one. For the * `'input'` phase the seed stage commits the transformed message, so the * model and the conversation never hold the original — but this row is NOT * its only copy: the run's input as passed (`run.entry` payload), a crash * checkpoint's `originalInput` and a paused * run's checkpoint (`RunnerPauseOutcome.checkpoint`, which `standingAgent` * stores for a paused session) keep it too — the full list, and what an app * must redact, is in `./README.md`, the one place it is maintained. An app * whose threat model says the original must not survive in the RECORD names * it in `Agent.create({ redact })`: `middlewareDecisions` (this state key — * every row's `before` / `after`; the `middleware.decision` events below carry * neither) is then the placeholder in every record. The checkpoints keep the real * values — a resume runs on them — so where the app stores a checkpoint, it * protects it. */ import type { MiddlewareDecision } from './types.js'; /** The scope surface this file needs. Structurally a `TypedScope<AgentState>`. */ export interface LedgerScope { middlewareDecisions?: readonly MiddlewareDecision[]; $emit(name: string, payload?: unknown): void; } /** * Append decision rows to the run's ledger and emit one event per row. * * No-op on an empty list, so an agent with no middleware never writes the * key at all and its commit log is the one it always had. */ export declare function recordDecisions(scope: LedgerScope, rows: readonly MiddlewareDecision[]): void;