/**
 * agent_bridge — append-only JSONL persistence for warm-pool session
 * history (WAB.3, 0.5.95).
 *
 * Authoritative spec: `docs/tasks/T-warm-agent-chat-bridge.md` WAB.3.
 * Architecture: `docs/tasks/WAB.1-architecture.md` decision (c) + Section 2
 * module layout (≤200 LOC budget for this file).
 *
 * On-disk contract:
 *   - One JSONL file per session at
 *     `~/.opensquid/agent-bridge/sessions/<slug>.jsonl`.
 *   - Each row is exactly one serialized `ChatHistoryEntry`.
 *   - Writes are APPEND-ONLY (`fs.appendFile`); files are never truncated
 *     or rewritten. Restart-resume reads the full file front-to-back into
 *     memory at session creation, so history order = byte order.
 *
 * Why JSONL (not SQLite):
 *   - Append-only matches the access pattern (1 turn = N entries appended;
 *     never a partial update).
 *   - `O_APPEND` writes ≤ `PIPE_BUF` (4KB on macOS / 4KB on Linux) are
 *     atomic per POSIX. Most history entries (text content) are well under
 *     this. Long entries split JSONL `\n` cleanly so the reader's
 *     line-by-line tolerance handles interleaved partial writes too.
 *   - Same shape as the chat-daemon inbox and the drift catalog
 *     (`src/runtime/drift_catalog.ts`) — consistent file format makes ops
 *     + grepability easier.
 *
 * Slug encoding (filesystem safety):
 *   - SessionKey slug form is `<platform>:<chatId>[:<threadId>]` — colons
 *     are not safe on Windows + interact poorly with shell tooling.
 *   - We replace `:` with `__` (rare in identifiers so we can recover the
 *     original by inverse on debug), then replace any character that is
 *     NOT alphanumeric / `-` / `_` with `_`. This avoids path-traversal
 *     vectors (no `/`, no `..`, no nulls) while keeping the filename
 *     readable for the common case (Telegram + Discord ids are
 *     alphanumeric with optional `-` prefix for TG supergroups).
 *
 * Tolerance:
 *   - Malformed JSON line on load: skipped + reported via `onWarn`; valid
 *     rows still load. Never throws on a corrupt line.
 *   - Schema-mismatched line on load: same — warn + skip.
 *   - Missing file on load: returns `[]` (fresh session). The directory
 *     is created lazily on first `appendEntries`, so reads never need
 *     to mkdir.
 *
 * Imports from: node:fs/promises, node:os, node:path, ./types.js.
 * Imported by: session_manager.ts.
 */
import { type ChatHistoryEntry } from './types.js';
/**
 * Filesystem-safe encoding of a session slug. Pure function (no I/O) so it
 * can be reused by the session manager + tests for deterministic asserts.
 */
export declare function encodeSessionSlug(slug: string): string;
export interface SessionPersistenceOptions {
    /** Override the sessions root (default: `~/.opensquid/agent-bridge/sessions`). */
    root?: string;
    /** Structured warn sink for malformed rows. */
    onWarn?: (message: string) => void;
}
export declare class SessionPersistence {
    private readonly root;
    private readonly warn;
    /** Tracks which session dirs we have already ensured exist (avoids
     *  redundant mkdir syscalls on the hot append path). */
    private readonly dirsEnsured;
    constructor(opts?: SessionPersistenceOptions);
    /**
     * Resolve the absolute file path for a session. Exposed for tests +
     * the session manager's eviction-flush callback diagnostics.
     */
    pathFor(slug: string): string;
    /**
     * Read all history entries for a session, in append order. Missing file
     * → empty array (fresh session). Malformed / schema-mismatched rows are
     * skipped + warned; valid rows are still returned. Non-streaming: history
     * is bounded by LRU cap × N turns and the agent loop needs the full array.
     */
    loadHistory(slug: string): Promise<ChatHistoryEntry[]>;
    /**
     * Append history entries to the session's JSONL. Uses `fs.appendFile`
     * (which opens with `O_APPEND`) so concurrent writers serialize at the
     * kernel level for sub-PIPE_BUF rows. Per-line `\n` framing tolerates
     * the (extremely rare) case of an interleaved >4KB row.
     *
     * Atomic-or-throw contract: either the whole batch of entries is
     * appended, or this method throws and the caller's state should not
     * advance. We build one string + one syscall to keep the atomicity
     * boundary at "all or nothing" for the typical small batch.
     */
    appendEntries(slug: string, entries: ChatHistoryEntry[]): Promise<void>;
    /** Lazy mkdir guarded by an in-memory set — keeps repeat appends cheap. */
    private ensureDir;
}
//# sourceMappingURL=session_persistence.d.ts.map