export interface ToolCallEvent { tool: string; durationMs: number; outcome: "ok" | "error" | "timeout"; errorCode?: string; } export interface ToolStat { calls: number; success: number; errors: number; errorCodes: Record; lastCalledAt: number; lastSuccessAt: number | null; lastErrorAt: number | null; totalDurationMs: number; } export interface ToolStatsDoc { version: 1; startedAt: number; updatedAt: number; totalCalls: number; tools: Record; } /** * Per-process writer. One instance per project path. Loads existing * stats from disk on construction; subsequent record() calls update * the in-memory doc and schedule a debounced atomic flush. dispose() * is the synchronous escape hatch — call from process shutdown. */ export declare class ToolStatsWriter { private filePath; private doc; private dirty; private flushTimer; private flushDelayMs; constructor(projectPath: string, opts?: { flushDelayMs?: number; }); private loadOrInit; /** * Record a single tool-call event. Updates the in-memory doc and * schedules a debounced flush. Never throws. */ record(event: ToolCallEvent): void; private scheduleFlush; /** * Flush in-memory doc to disk atomically. Used by both the debounced * timer and dispose(). Atomicity via tmp+rename so concurrent readers * (sverklo profile suggest) never see a half-written file. */ flushSync(): void; /** * Synchronous flush + cancel pending timer. Call from process shutdown * paths so the last in-memory updates land on disk before exit. */ dispose(): void; /** Path the writer flushes to. Useful for tests + the read API below. */ getFilePath(): string; } /** * Read the structured tool-stats doc for a project. Returns null if no * stats have been recorded yet — callers fall back to scanning * activity.jsonl in that case. */ export declare function readToolStats(projectPath: string): ToolStatsDoc | null;