/** * debug-capture.ts — Local offline capture sink for evals and debugging. * * Design contract: * - Off by default; activated only via the `debugCapture` setting (project * or personal). When off: every public function is a no-op, no folders * are created, no files are written. * - Local-only by construction. The module writes only to directories the * user/extension explicitly bound to via `enable({ projectPath, personalPath })`. * Paths are validated: must be absolute, contain no `..` traversal * segments or NUL bytes, and fit within a 4 KiB budget. * - Best-effort: every filesystem operation is wrapped in try/catch and * swallows errors via `logger.debug`. A capture failure must never break * the agent runtime, dashboard, or scheduler. * - Append-only JSONL for events/audit/errors (single-write atomic appends); * atomic JSON upsert for `manifest.json`, `metrics.json`, `index.json` * (write-temp-then-rename so partial writes never produce invalid JSON). * - Rotation: per-file 25 MiB ceiling. When exceeded, the tail half is kept * so the most recent events survive older truncation — bounded storage * with deterministic freshness. * * The module is a pure sink: it does NOT register hook or telemetry * handlers. Wiring lives in `src/index.ts` so the public DSL stays * dependency-free (testable in isolation without a HookRegistry/HookRuntime). */ export interface DebugCapturePaths { /** Project-local capture root. Absolute path. Optional. */ projectPath?: string; /** Personal capture root. Absolute path. Optional. */ personalPath?: string; } export interface DebugCaptureManifest { /** ISO-8601 timestamp the capture started. */ enabledAt: string; /** Stable identifier for this capture session. Used to correlate captures * across the project and personal roots. */ sessionUuid: string; /** Paths actually accepted by `enable()` (after validation + mkdirSync). */ paths: { project: string | null; personal: string | null; }; options: { maxBytesPerFile: number; }; } /** True if capture is active. Handlers and direct callers should short- * circuit when this is false to keep the no-op path zero-cost. */ export declare function isDebugCaptureEnabled(): boolean; /** Snapshot of the manifest written at enable time. Null while disabled. */ export declare function getDebugCaptureManifest(): DebugCaptureManifest | null; /** * Activate capture for the given paths. Idempotent: calling twice without * disable() in between is a no-op that returns the existing manifest. * Returns null when no path is writable — capture stays disabled in that * case so we never silently drop data on the floor. */ export declare function enable(paths: DebugCapturePaths, sessionUuidHint?: string): DebugCaptureManifest | null; /** * Deactivate capture. Optionally writes a closing `index.json` next to the * manifest (default: yes) so the two roots can be cross-checked offline. */ export declare function disable(writeFinalIndex?: boolean): void; /** * Reset internal state without touching the filesystem. Used by tests * between cases — production callers don't need this because `enable()` * and `disable()` are already idempotent + safe to repeat. */ export declare function resetDebugCapture(): void; /** * Append one JSONL line per active root to * `/agents//events.jsonl`. No-op when capture is disabled. */ export declare function appendAgentEvent(agentId: string, event: string, data?: unknown): void; /** * Append a structured error record (with stack trace for Error instances) to * `/agents//errors.log`. No-op when capture is disabled. */ export declare function appendError(agentId: string, err: unknown, context?: Record): void; /** * Atomic upsert of `/agents//metrics.json`. Replaces the * previous metrics atomically (write temp → rename) so concurrent reads * never see a half-written JSON object. No-op when capture is disabled. */ export declare function upsertAgentMetrics(agentId: string, metrics: Record): void; /** * Append one execution record to `/schedules//executions.jsonl`. * The jobId is included for correlation across renames; the jobName doubles * as the friendly filesystem key. No-op when capture is disabled. */ export declare function appendScheduleEvent(jobId: string, jobName: string, event: string, data?: unknown): void; /** * Append one RPC audit record to `/rpc/audit.jsonl`. No-op when * capture is disabled. */ export declare function appendRpcAudit(payload: Record): void; /** Test-only entry point: trigger the same rotation logic as the * per-append call site, but with a caller-supplied byte threshold. * Lets tests exercise the rotation codepath with a few KiB of seed * data instead of writing 25+ MiB on disk. Production code never * calls this — keep the surface narrow (one arg, no callbacks). */ export declare function __test_rotateIfNeeded(path: string, maxBytes: number): void;