/**
 * PeerStreamProxy — phase 1 of Pool Dashboard Streaming
 * (POOL-DASHBOARD-STREAM-SPEC §2.2, the scalability core).
 *
 * ONE multiplexed upstream connection per PEER machine, shared by every local
 * dashboard client and every remote session on that peer. The local
 * WebSocketManager (phase 2) owns local clients and the browser WS; this class
 * owns ONLY the single upstream link and the bookkeeping that makes it correct:
 *
 *  - reference-counted subscriptions (per (session) → set of local clientIds);
 *  - a 60s idle grace after the last unsubscribe, cancelled if a subscribe
 *    arrives during it (no thrash opening/closing on a flapping client);
 *  - ONE bounded reconnect on an upstream drop, resubscribing every current
 *    subscription; a reconnect that does not open within reconnectTimeoutMs, or
 *    a second drop, surfaces `machine-unreachable` (P19 — never a reconnect
 *    storm);
 *  - subscriptions that arrive mid-(re)connect are QUEUED and merged into the
 *    resubscribe batch the moment the link opens — never lost;
 *  - the peer URL is re-resolved on every subscribe; a changed URL tears the
 *    old link down and opens a new one (no stale-URL duplicate links).
 *
 * Everything external is injected (transport, timers, clock) so the whole state
 * machine is deterministically unit-testable without real sockets or wall time.
 * This module performs NO tmux capture and NO local fan-out polling — capture
 * happens only on the owning machine; fan-out to local clients is the injected
 * `onFrameToClients`. (TAP POINT, ops#1: remote subs never enter the local
 * polling loop.)
 */
export type ProxyState = 'idle' | 'connecting' | 'active' | 'idle-scheduled' | 'closing' | 'closed';
/** A live upstream connection handle (one peer's /ws/pool-stream). */
export interface UpstreamTransport {
    /** Send a frame to the peer (subscribe/unsubscribe/input/key/history). */
    send(frame: Record<string, unknown>): void;
    /** Tear the connection down. Idempotent. */
    close(): void;
}
/** Handlers the proxy gives the transport at connect time. */
export interface UpstreamHandlers {
    /** The link opened and is ready to carry frames. */
    onOpen(): void;
    /** A frame arrived from the peer (output/history/session_ended/error/…). */
    onFrame(frame: Record<string, unknown>): void;
    /** The link closed (clean or error). */
    onClose(): void;
}
export type TimerHandle = {
    __brand: 'PeerStreamProxyTimer';
};
export interface PeerStreamProxyDeps {
    peerMachineId: string;
    /** Resolve the peer's CURRENT url (re-read each subscribe for URL-change detection). */
    resolveUrl: () => string | null;
    /** Open an upstream link to `url`; the returned transport drives `handlers`. */
    connect: (url: string, handlers: UpstreamHandlers) => UpstreamTransport;
    /** Fan a peer frame out to every LOCAL client subscribed to (session). */
    onFrameToClients: (session: string, frame: Record<string, unknown>) => void;
    /** Surface an error to every local client subscribed to (session). */
    onError: (session: string, code: 'peer-stream-lost' | 'machine-unreachable', detail?: string) => void;
    now: () => number;
    setTimer: (ms: number, fn: () => void) => TimerHandle;
    clearTimer: (t: TimerHandle) => void;
    /** Idle grace before closing an unsubscribed link (default 60_000). */
    idleGraceMs?: number;
    /** Reconnect open deadline (default 10_000). */
    reconnectTimeoutMs?: number;
    logger?: (line: string) => void;
}
export declare class PeerStreamProxy {
    private readonly d;
    private state;
    private transport;
    private connectedUrl;
    /** session → set of local clientIds subscribed to it. THE source of truth:
     *  on every (re)open we resubscribe from this map, so a subscribe that
     *  arrives mid-connect is never lost and a mid-connect unsubscribe is never
     *  replayed. */
    private readonly subs;
    private idleTimer;
    private reconnectTimer;
    private reconnectUsed;
    constructor(d: PeerStreamProxyDeps);
    get currentState(): ProxyState;
    /** Total live subscriptions across all sessions (refcount). */
    get refCount(): number;
    /** Snapshot of session → clientIds (tests / introspection). */
    sessionsView(): Record<string, string[]>;
    private log;
    /** A local client subscribes to a remote session on this peer. */
    subscribe(session: string, clientId: string): void;
    /** A local client unsubscribes (or disconnects). */
    unsubscribe(session: string, clientId: string): void;
    /** Forward an input/key frame for a session (caller already gated allowRemoteInput). */
    relayInput(frame: Record<string, unknown>): void;
    /** Forward a read-only request frame (e.g. a `history` scrollback fetch) to
     *  the owning machine. Spec §2.2: capture happens ONLY on the owning machine
     *  — history for a remote session must travel upstream, never hit local
     *  tmux. Dropped unless the link is active (a request racing a reconnect is
     *  simply re-issued by the user's next scroll; stale queued requests are
     *  worse than none). */
    relayFrame(frame: Record<string, unknown>): void;
    private openFresh;
    private onUpstreamOpen;
    private onUpstreamFrame;
    private onUpstreamClose;
    private scheduleIdleClose;
    private failAll;
    private teardownTransport;
    /** Force-close (peer removed, server shutdown). Idempotent. */
    close(): void;
}
//# sourceMappingURL=PeerStreamProxy.d.ts.map