import type { EventBus } from "./event-bus.js"; import type { SlashCommand } from "./slash-commands.js"; /** Internal mode gate. Env wins; otherwise `~/.gg/internal.json` must set * `diagnostics: true`. Result is cached for the process lifetime. */ export declare function isInternalDiagnosticsEnabled(): boolean; /** Test seam: reset the cached flag after mutating env/file state. */ export declare function resetInternalDiagnosticsCacheForTests(): void; export declare function diagnosticsSessionsDir(): string; export interface TurnDiagnostics { turn: number; stopReason: string; inputTokens: number; outputTokens: number; cacheRead?: number; cacheWrite?: number; ttftMs?: number; providerDurationMs: number; } export interface ToolDiagnostics { calls: number; errors: number; /** Non-zero exits, empty greps, tool-rejection strings — failures the tools * report as ordinary result text (isError stays false). */ softErrors: number; totalMs: number; maxMs: number; invalidArgAttempts: number; truncatedResults: number; } export interface ErrorCluster { digest: string; count: number; sample: string; } export interface RepeatEntry { tool: string; argsDigest: string; count: number; } export interface SessionDiagnosticsRecord { version: 1; sessionId: string; cwd: string; provider: string; model: string; startedAt: number; endedAt?: number; modelSwitches: { at: number; provider: string; model: string; }[]; turns: TurnDiagnostics[]; toolStats: Record; errorClusters: ErrorCluster[]; repeats: RepeatEntry[]; truncations: { reason: string; continued: boolean; }[]; compactions: { originalCount: number; newCount: number; }[]; totals: { turns: number; toolCalls: number; toolErrors: number; inputTokens: number; outputTokens: number; cacheRead: number; cacheWrite: number; }; } /** The subset of TurnMetricPayload the recorder consumes — kept structural so * agent-session can pass its full payload without an import cycle. */ export interface TurnMetricLike { turn: number; stopReason: string; usage: { inputTokens: number; outputTokens: number; cacheRead?: number; cacheWrite?: number; }; timing: { providerDurationMs: number; ttftMs?: number; }; } export declare class SessionDiagnosticsRecorder { private record; private pendingCalls; private repeatCounts; private clusterCounts; private unsubscribers; private fallbackSessionId?; private getSessionId; constructor(opts: { /** Lazy on purpose: hosts assign the session id at first prompt, AFTER * initialize() creates this recorder. A string snapshot would name the * record file `.json` (empty id). */ sessionId: string | (() => string); cwd: string; provider: string; model: string; dir?: string; }); private dir?; /** Subscribe to the session bus. Safe to call once per session. */ attach(bus: EventBus): void; /** Called from AgentSession.persistTurnMetric — the authoritative per-turn * usage/timing source. Also the incremental flush point. */ recordTurnMetric(metric: TurnMetricLike): void; private materializeDerived; private filePath; /** Best-effort incremental write — a diagnostics write failure must never * affect the session. Synchronous ON PURPOSE: the record is a few KB written * once per turn (negligible next to the network call it follows), and a sync * write cannot be lost when a headless host exits immediately after the * final turn_end — an async fire-and-forget flush racing process exit was * silently dropping the whole session's record. */ flush(): Promise; finalize(): Promise; getRecord(): Readonly; snapshotForTests(): SessionDiagnosticsRecord; repeatsForTests(): RepeatEntry[]; errorClustersForTests(): ErrorCluster[]; /** Compact agent-readable summary of THIS session (for session_stats). */ summary(): string; } export declare function summarizeRecord(r: SessionDiagnosticsRecord): string; export interface DiagnosticsAggregate { sessionCount: number; report: string; } /** Read the newest N session records and produce a ranked, agent-readable * findings report. Findings are ordered by cost × frequency, most urgent * first; "what works" is included so regressions are visible. */ export declare function aggregateRecentDiagnostics(limit?: number, dir?: string): DiagnosticsAggregate; /** Internal-only command; registered exclusively when the internal flag is on. */ export declare function createDiagnoseCommand(): SlashCommand;