/** * Brainstorm Audit Layer (Phase 5) * * Captures every dispatch / inline-read / skip decision the parent LLM * makes during a brainstorm. The audit log is the single source of truth * for what happened — used by: * - The user reviewing the conversation afterward * - /senai-doctor surfacing "you skipped 2 dispatches the registry * would have suggested" warnings (Phase 6) * - Future analytics on brainstorm quality * * Design: * - Pure functions: createAuditSession, appendDecision, renderAuditLog. * - I/O lives in writeAuditLog + readAuditLog, both atomic. * - The session lives in memory; written to disk at finalizeAuditSession. * This means a crash mid-discussion loses in-flight decisions but never * corrupts the on-disk log. */ import type { MissionType } from "../core/state.js"; /** Marker written at the top of brainstorm-dispatch.md so doctor + tests * recognize the file even when contents are minimal. */ export declare const AUDIT_LOG_MARKER = ""; /** Decisions a parent LLM can make on each turn. */ export type DecisionKind = "inline" | "dispatched" | "skipped" | "side-channel"; /** One recorded decision — what the parent did on a given turn. */ export interface DispatchDecision { /** 1-based turn number within the brainstorm. */ turn: number; /** ISO timestamp the decision was logged. */ timestamp: string; /** Snippet of the user's input that triggered this decision. */ userInput: string; /** What the parent chose to do. */ decision: DecisionKind; /** Agent name when decision === "dispatched" or "side-channel". */ agent?: string; /** Human-readable reason for the decision. */ reason: string; /** Approximate tokens used by this turn (optional). */ tokens?: number; /** Wall-clock time spent on the turn (optional). */ elapsedMs?: number; /** Task description (for dispatched/side-channel only). */ task?: string; /** Expected output hint (for dispatched only). */ expectedOutput?: string; } /** In-memory audit session — accumulates decisions before finalize. */ export interface AuditSession { brainstormRunId: string; seed: string; startedAt: string; /** Mutable array of decisions in turn order. */ decisions: DispatchDecision[]; } /** Summary stats computed at finalizeAuditSession. */ export interface AuditSummary { totalDispatches: number; dispatchesByAgent: Record; inlineReads: number; skipped: number; sideChannel: number; wallClockMs: number; briefSectionsFilled: number; briefSectionsTotal: number; } /** Create a new audit session. Pure — no file I/O. */ export declare function createAuditSession(brainstormRunId: string, seed: string): AuditSession; /** Append a decision to the session. Mutates the session in place. * Returns the session for fluent chaining. */ export declare function appendDecision(session: AuditSession, decision: DispatchDecision): AuditSession; /** Count decisions by kind. Pure. */ export declare function summarizeDecisions(decisions: readonly DispatchDecision[]): { totalDispatches: number; dispatchesByAgent: Record; inlineReads: number; skipped: number; sideChannel: number; }; /** Build the full audit summary for a session. Combines decision counts * with caller-supplied wall-clock + brief-coverage numbers. */ export declare function buildSummary(session: AuditSession, extras: { wallClockMs: number; briefSectionsFilled: number; briefSectionsTotal: number; }): AuditSummary; /** Render a single decision as a markdown block. Pure. */ export declare function renderDecision(d: DispatchDecision): string; /** Render the full audit log as a markdown string. Pure. */ export declare function renderAuditLog(session: AuditSession, summary: AuditSummary): string; /** Write the audit log atomically to the brainstorm folder. * Returns the absolute path of the file written. */ export declare function writeAuditLog(cwd: string, session: AuditSession, summary: AuditSummary): string; /** Read an existing audit log from disk. Returns null if the file does * not exist. Parses the marker line + body into an AuditSession-like * shape (does not reconstruct decisions — just metadata). */ export declare function readAuditLog(cwd: string, brainstormRunId: string): { brainstormRunId: string; startedAt: string; seed: string; content: string; } | null; /** Compute brief coverage (filled vs total sections) by scanning a brief. * When a missionType is given, the section list comes from * requiredBriefSectionsFor — a bugfix brief counts 10 sections, not 6. */ export declare function countBriefSections(brief: string, missionType?: MissionType): { filled: number; total: number; };