/** * CheckInRecorder — the audit record for evidence-carrying human consent. * * Two exports, both for `agentfootprint.checkin.*`: * * • `checkInEventsBridge(...)` — an {@link EmitBridge} factory (same shape as * `costRecorder` / `permissionRecorder`). Forwards `checkin.request` / * `checkin.decision` `$emit`s to the EventDispatcher so * `agent.on('agentfootprint.checkin.*')` fires. Attached ALWAYS by the * Agent (zero-cost until a `checkIn`-declaring tool trips) — this is pure * wiring, not a store. * * • `CheckInRecorder` — a compose-a-STORE recorder (no base class). Attach it * with `agent.attach(new CheckInRecorder())` to CAPTURE every ask + decision * as a queryable record — the "the decision is a record" pillar. It reads * the same `$emit` stream (footprintjs calls `onEmit` for every emit), so it * works whether or not any `agent.on(...)` listener is attached. * * Pattern: Factory over EmitBridge (wiring) + compose-a-store CombinedRecorder * (capture). Telemetry rides the EMIT channel — never CommitBundle. */ import type { CombinedRecorder, EmitEvent } from 'footprintjs'; import { EmitBridge, type EmitBridgeOptions } from './EmitBridge.js'; import type { CheckInRequest } from '../../core/checkin.js'; export type CheckInRecorderBridgeOptions = Omit & { readonly id?: string; }; /** * The always-on dispatcher bridge for `agentfootprint.checkin.*`. Wiring only: * forwards the emits so typed `agent.on(...)` listeners fire. */ export declare function checkInEventsBridge(options: CheckInRecorderBridgeOptions): EmitBridge; /** One captured check-in ask. */ export interface CheckInRequestRecord { readonly toolName: string; readonly toolCallId: string; readonly iteration: number; /** The typed ask + evidence pack. */ readonly request: CheckInRequest; } /** One captured human decision. */ export interface CheckInDecisionRecord { readonly toolName: string; readonly toolCallId: string; readonly iteration: number; readonly approved: boolean; readonly by: string; readonly note?: string; } /** Roll-up counts across a run (or the recorder's lifetime). */ export interface CheckInStats { readonly requested: number; readonly approved: number; readonly declined: number; /** Asks with no decision yet (paused, awaiting a human). */ readonly pending: number; } /** * A queryable store of every check-in ask + decision. Attach once; read after * the run (or between runs — it accumulates across `agent.run()` calls until * you detach or make a fresh one). * * @example * const checkins = new CheckInRecorder(); * agent.attach(checkins); * await agent.run({ message }); * // ...human approves... * await agent.resume(outcome.checkpoint, checkInApproved({ by: 'alice' })); * checkins.getStats(); // { requested: 1, approved: 1, declined: 0, pending: 0 } * checkins.getDecisions(); // [{ toolName, approved: true, by: 'alice', ... }] */ export declare class CheckInRecorder implements CombinedRecorder { readonly id: string; private readonly requests; private readonly decisions; /** * @param id Stable recorder id for idempotent attach/detach. Give distinct * ids if you attach more than one to the same agent. */ constructor(id?: string); onEmit(event: EmitEvent): void; /** Every check-in ask captured, oldest first. */ getRequests(): readonly CheckInRequestRecord[]; /** Every human decision captured, oldest first. */ getDecisions(): readonly CheckInDecisionRecord[]; /** Roll-up counts. `pending` = asks still awaiting a decision. */ getStats(): CheckInStats; /** Drop all captured records (e.g. between runs when reusing the recorder). */ reset(): void; }