/**
 * agent_bridge — chokidar-watched inbox JSONL → typed event emission
 * (WAB.2, 0.5.94).
 *
 * Authoritative source: `docs/tasks/WAB.1-architecture.md` decisions (a) +
 * (b); `docs/tasks/T-warm-agent-chat-bridge.md` WAB.2 Part B spec.
 *
 * Reads the CANONICAL per-project inbox path:
 *   `~/.opensquid/projects/<projectUuid>/inbox/<platform>.jsonl`
 *
 * NO env-override hack. The WAB.1 revision #1 lifted the per-project
 * routing fix into WAB.2 Part A precisely so this bridge could read the
 * correct path day one. If the bridge ever ends up watching the orphan
 * inbox, that's a routing bug — fix it in Part A, not here.
 *
 * Boundary discipline:
 *   1. Inbox JSONL rows are written by the chat daemon (`src/channels`)
 *      in the on-disk row shape (snake_case). The bridge adapts that shape
 *      to the modern `InboundChatEvent` (camelCase, zod-validated).
 *   2. Validation happens at the boundary (every parsed line goes
 *      through `inboundChatEventSchema.parse` — malformed rows surface
 *      as a structured warn-and-skip, not a crash).
 *   3. Byte-offset cursor per file — append-only is the file contract
 *      (`O_APPEND` writes ≤ PIPE_BUF are atomic per POSIX); the bridge
 *      reads only the unread tail, never re-emits already-consumed rows.
 *   4. Truncation safety — if file size < cursor, the bridge resets the
 *      cursor to 0 and re-reads from the start (matches the legacy
 *      log-rotation pattern; chokidar fires `unlink` + `add` on rotate,
 *      and the cursor reset on `add` handles in-place truncate too).
 *
 * chokidar config (per spec):
 *   - `awaitWriteFinish: {stabilityThreshold: 50, pollInterval: 25}`
 *     suppresses partial-write events while keeping latency low
 *     (50ms ≪ typical Telegram inter-message gap; the legacy daemon
 *     batches inbox appends, so 50ms is enough headroom).
 *   - `ignoreInitial: false` — on bridge startup, we want to consume
 *     the existing tail (cursor starts at 0 → reads the whole file
 *     once → cursor advances → idle until next change). This is the
 *     correct restart-resume semantic for a long-running daemon.
 *
 * Imports from: chokidar, node:fs/promises, node:path, node:os, zod,
 *   ./event_bus.js, ./types.js.
 * Imported by: (future) dispatcher.ts, daemon.ts.
 */
import type { AgentEventBus } from './event_bus.js';
export interface TransportBridgeOptions {
    bus: AgentEventBus;
    projectUuid: string;
    /**
     * Owning umbrella id (T-CHAT-AS-TERMINAL CAT.5). When set, the bridge
     * watches the UMBRELLA inbox (`~/.opensquid/umbrellas/<id>/inbox/`) instead
     * of the per-project inbox, and stamps `umbrellaId` onto every emitted
     * event so the dispatcher's T-DEL arbitration can read the umbrella lease.
     * Omitted ⇒ legacy per-project inbox + lease (the general/project path).
     * `inboxRoot` (tests) still wins over both when provided.
     */
    umbrellaId?: string;
    /**
     * Override the inbox root (default: `~/.opensquid/projects/<uuid>/inbox/`,
     * or `~/.opensquid/umbrellas/<id>/inbox/` when `umbrellaId` is set).
     * Provided ONLY for tests — production callers must not pass this.
     * Distinct from "env-override" because there is no env var that flips
     * this; the caller wires it explicitly in test setUp.
     */
    inboxRoot?: string;
    /**
     * Glob restricting which files in the inbox dir we watch. Defaults to
     * `*.jsonl` so future platforms (discord.jsonl, slack.jsonl) auto-pick
     * up. Tests may narrow to `telegram.jsonl` for determinism.
     */
    fileGlob?: string;
    /** Test seam — usePolling forces chokidar's polling backend. */
    usePolling?: boolean;
    /** Structured warn sink for malformed rows + watcher errors. */
    onWarn?: (message: string) => void;
    /**
     * Test/observability seam (T-FLAKE-TRANSPORT-BRIDGE) — fires on watcher
     * events + consume entry. No-op when unset; sibling of `onWarn` (same trust
     * model). Observability ONLY — putting logic here is misuse.
     */
    onEvent?: (kind: 'add' | 'change' | 'unlink' | 'consume', path: string) => void;
}
export declare class InboxTransportBridge {
    private readonly opts;
    private watcher;
    /** TBW.1: resolves an in-flight start() ready-wait when shutdown() runs —
     *  code-owned close semantics (chokidar's close()-mid-init emissions are
     *  undocumented; we never depend on them). */
    private closeRequested;
    /** Per-file byte-offset cursor. Key = absolute file path. */
    private readonly cursors;
    /** Per-file in-flight read serializer — chokidar may fire `change` twice
     *  for one write burst; we want exactly one read at a time per file. */
    private readonly readChains;
    private readonly warn;
    private readonly inboxDir;
    private readonly fileGlob;
    private stopped;
    constructor(opts: TransportBridgeOptions);
    /** Open the chokidar watcher. Re-call after `shutdown()` throws. */
    start(): Promise<void>;
    /** The bridge's fileGlob is a basename pattern ('*.jsonl' or a literal like
     *  'telegram.jsonl'); `*` is the one supported wildcard. */
    private matchesFileGlob;
    /** Close the watcher; drain in-flight reads. Idempotent. */
    shutdown(): Promise<void>;
    /**
     * Test-only hook — synchronously reports the current cursor for a file.
     * Production callers do not depend on this; exported for invariant
     * assertions in the audit harness.
     */
    cursorFor(path: string): number | undefined;
    /** Serialize reads per-file to prevent overlapping consumers. */
    private scheduleConsume;
    /**
     * Read from the cursor to end-of-file; parse each newline-delimited row;
     * validate via zod; emit `inbound`. Cursor advances by the byte length
     * of every line we consumed (including the trailing `\n`), so a
     * partial trailing line is held for the next `change` event.
     */
    private consumeTail;
    /** Parse one JSONL line, adapt to InboundChatEvent, emit. */
    private emitRow;
    /**
     * Map legacy snake_case `InboxMessage` shape → modern camelCase
     * `InboundChatEvent`. Returns null + warns when the row doesn't look
     * like a legacy InboxMessage at all (forward-compat: future writers
     * with a `kind: 'inbound_message'` field that already matches the
     * modern schema would skip this adapter; we let zod take the shot).
     */
    private adaptLegacyRow;
}
//# sourceMappingURL=transport_bridge.d.ts.map