/**
 * fileObservability — the typed event stream, one JSON line per event, in a
 * local file.
 *
 * The sink for the shop that has no collector. Every other adapter in this
 * folder ships somewhere: CloudWatch, X-Ray, an OTLP endpoint. A great many
 * on-premises deployments have none of those — they have a directory, a log
 * shipper (Filebeat, Fluent Bit, Vector, `promtail`, `journald`, or a person
 * with `grep`), and a rule that nothing leaves the network. NDJSON on disk is
 * the format all of those already read, so the sink is the file.
 *
 * Zero dependencies: `node:fs`, lazily required at construction so merely
 * importing `agentfootprint/observe` stays browser-safe (this factory is
 * Node-only; calling it in a browser throws by name).
 *
 * ## The line
 *
 * One `JSON.stringify(event)` per line, newline-terminated, appended in
 * dispatch order — the SAME envelope `cloudwatchObservability` puts in a log
 * event, so a query written against one reads the other:
 *
 * ```jsonl
 * {"type":"agentfootprint.agent.turn_start","payload":{…},"meta":{"runId":"…","sessionId":"…"}}
 * {"type":"agentfootprint.stream.tool_end","payload":{…},"meta":{…}}
 * ```
 *
 * Nothing is summarized, bounded or redacted on the way out. **A payload that
 * must not be on that disk must not reach this strategy** — narrow it with
 * `eventTypes` / `tier` / `sampleRate`, or apply a footprintjs
 * `RedactionPolicy` upstream, exactly as with every other sink. (For a bounded
 * record by construction, `auditExport({ payloadMode: 'bounded' })` is the
 * adapter that does that job.)
 *
 * ## Buffered, not synchronous
 *
 * `exportEvent` is sync and never touches the disk: it serializes, buffers, and
 * returns. Batches are appended asynchronously on a size trigger
 * (`maxBufferEvents` / `maxBufferBytes`), on a timer (`flushIntervalMs`), and on
 * `flush()` — all through ONE queue (`outbox.ts`), so one append is in flight
 * at a time: this sink's appends never interleave, and lines land in dispatch
 * order (#223). That is not atomicity — a disk that fails partway through an
 * append (ENOSPC) can still leave part of a line behind, and that batch is
 * reported dropped. A hard kill loses the buffer and can interrupt the append
 * in flight — the price of not making telemetry a term in agent-loop latency.
 * Call `flush()` (or `agent.shutdown()`, which does) at process end; see the
 * 8.12.0 lifecycle laws on {@link BaseStrategy.flush}.
 *
 * ## Rotation is ONE generation, and that is deliberate
 *
 * With `maxBytes` set, a batch that would push the file past the ceiling first
 * renames it to `<path>.1` — **replacing any previous `.1`** — and starts a
 * fresh file. That is the whole policy. There is no `.2`, no compression, no
 * time-based schedule, no cross-process coordination (two processes writing one
 * file each keep their own byte count and will both rotate it). It exists so an
 * unattended agent cannot fill a disk, and for nothing else: **retention is a
 * log-management daemon's job**, and `logrotate` with `copytruncate`, Fluent Bit,
 * or a systemd timer will do it properly. Omit `maxBytes` — the default — and
 * this adapter never renames anything, which is the right choice when a real
 * rotator already owns the file.
 *
 * @example
 * ```ts
 * import { fileObservability } from 'agentfootprint/observe';
 *
 * const telemetry = agent.enable.observability({
 *   strategy: fileObservability({
 *     path: '/var/log/agentfootprint/events.ndjson',
 *     maxBytes: 64 * 1024 * 1024,   // safety ceiling; logrotate owns retention
 *   }),
 * });
 *
 * // … run …
 * await agent.shutdown();           // flushes + stops everything enabled
 * ```
 */
import type { AgentfootprintEvent, AgentfootprintEventType } from '../../events/registry.js';
import type { ObservabilityStrategy } from '../../strategies/types.js';
export interface FileObservabilityOptions {
    /** Absolute or relative path to the NDJSON file. **Required.** Its parent
     *  directories are created (`recursive`) and the file itself is claimed at
     *  construction, so an unwritable path fails where you wrote it rather than
     *  at the first event — the only moment a caller is still watching. */
    readonly path: string;
    /** Rotation ceiling in bytes. Omitted → **never rotates** (the right choice
     *  when `logrotate` or a shipper already owns the file). Set → a batch that
     *  would cross the ceiling first renames the file to `<path>.1`, replacing
     *  any previous `.1`, and starts fresh. ONE generation, no compression, no
     *  cross-process coordination — see the note in this module's docstring. */
    readonly maxBytes?: number;
    /** Max events buffered before a forced append. Default 100. */
    readonly maxBufferEvents?: number;
    /** Max buffered payload bytes (UTF-8) before a forced append. Default 65536
     *  (64 KB) — a local write is cheap, so this is far larger than the network
     *  adapters' 10 KB. */
    readonly maxBufferBytes?: number;
    /** Forced-append interval when traffic is sparse, in ms. Default 1000.
     *  `0` disables the timer — only size triggers and `flush()` write. */
    readonly flushIntervalMs?: number;
    /** Narrow what lands on the disk. Becomes the strategy's
     *  {@link ObservabilityStrategy.relevantEventTypes}, so the dispatcher does
     *  not even forward the rest — the filter costs nothing at the hot path.
     *  Omitted → every event the `tier` lets through is written. */
    readonly eventTypes?: readonly AgentfootprintEventType[];
    /**
     * Where delivery failures go — a full disk, a revoked permission, a path
     * whose directory was removed under a long-running process.
     *
     * Same law as the network adapters (8.11.0): **telemetry that fails
     * invisibly is indistinguishable from telemetry that works.** Unhandled,
     * failures reach a rate-limited `console.error`. The batch that failed is
     * dropped, never requeued, so a disk that has been full for an hour cannot
     * grow the buffer without bound.
     */
    readonly onError?: (error: Error, event?: AgentfootprintEvent) => void;
    /** Test seam — inject a filesystem. Bypasses `node:fs` entirely, which is
     *  also what lets the rotation policy be asserted without a real disk. */
    readonly _fs?: FileSinkFs;
}
/**
 * The slice of the filesystem this adapter touches — five calls, named by what
 * the adapter uses them FOR rather than by their `node:fs` signatures.
 *
 * Sync members run once, at construction; the append path is async so the agent
 * loop never waits on a disk.
 */
export interface FileSinkFs {
    /** Create the log file's parent directory chain. */
    mkdirSync(dir: string, options: {
        readonly recursive: boolean;
    }): void;
    /** Claim the file at construction. This is the writability refusal: an
     *  unwritable path throws HERE, with the caller still on the stack. */
    appendFileSync(file: string, data: string): void;
    /** Current size, so the rotation counter starts from what is already there
     *  rather than from zero on every restart. */
    statSync(file: string): {
        readonly size: number;
    };
    /** Append one batch of NDJSON lines. */
    appendFile(file: string, data: string): Promise<void>;
    /** Rotate: `<path>` → `<path>.1`, replacing any previous `.1`. */
    rename(from: string, to: string): Promise<void>;
}
/**
 * NDJSON-to-a-local-file observability strategy. See
 * {@link FileObservabilityOptions} for the per-option contract, and this
 * module's docstring for the rotation policy and what is NOT bounded.
 */
export declare function fileObservability(opts: FileObservabilityOptions): ObservabilityStrategy;
//# sourceMappingURL=file.d.ts.map