/**
 * WebSocket Manager — real-time terminal streaming for the dashboard.
 *
 * Handles client subscriptions to tmux sessions, streams terminal output
 * via diff-based updates, and forwards input to sessions.
 *
 * Protocol (JSON messages):
 *
 * Client → Server:
 *   { type: 'subscribe', session: 'session-name' }
 *   { type: 'unsubscribe', session: 'session-name' }
 *   { type: 'history', session: 'session-name', lines: 5000 }
 *   { type: 'input', session: 'session-name', text: 'some input' }
 *   { type: 'key', session: 'session-name', key: 'C-c' }
 *   { type: 'ping' }
 *
 * Server → Client:
 *   { type: 'output', session: 'session-name', data: '...terminal output...' }
 *   { type: 'history', session: 'session-name', data: '...', lines: N }
 *   { type: 'sessions', sessions: [...] }
 *   { type: 'session_ended', session: 'session-name' }
 *   { type: 'subscribed', session: 'session-name' }
 *   { type: 'unsubscribed', session: 'session-name' }
 *   { type: 'input_ack', session: 'session-name', success: true }
 *   { type: 'pong' }
 *   { type: 'error', message: '...' }
 */
import type { Server as HttpServer } from 'node:http';
import type { SessionManager } from '../core/SessionManager.js';
import type { StateManager } from '../core/StateManager.js';
import type { HookEventReceiver } from '../monitoring/HookEventReceiver.js';
import type { StreamTicketStore } from './StreamTicketStore.js';
import { type UpstreamTransport, type UpstreamHandlers } from './PeerStreamProxy.js';
/** Requesting side (§2.2): opens an upstream /pool-stream to a peer machine. The
 *  connector owns mint-ticket + ws-connect (it has the mesh client + peer URLs). */
export interface PoolStreamConnector {
    /** Open an upstream link to `machineId`'s /pool-stream (mint a ticket, connect).
     *  Returns null if the peer is unreachable / has no URL. */
    connect: (machineId: string, handlers: UpstreamHandlers) => UpstreamTransport | null;
}
export declare class WebSocketManager {
    private wss;
    private clients;
    private sessionOutputCache;
    private streamInterval;
    private heartbeatInterval;
    private sessionBroadcastInterval;
    private sessionManager;
    private state;
    private authToken?;
    private registryPath?;
    private hookEventReceiver?;
    /** Pool Dashboard Streaming (serving side): consumes one-time tickets on the
     *  /pool-stream upgrade. Absent → /pool-stream is refused (feature dark). */
    private streamTicketStore?;
    /** Serving-side gate: may a PEER machine send input/key to a local session?
     *  Default false (security: keystroke forwarding is a lateral-movement vector). */
    private poolStreamAllowRemoteInput;
    /** Requesting side (§2.2): opens upstream /pool-stream links to peers. */
    private poolStreamConnector?;
    /** This machine's id — a subscribe whose machineId === this is served locally. */
    private selfMachineId?;
    /** One multiplexed upstream proxy per peer machine (requesting side). */
    private peerProxies;
    constructor(options: {
        server: HttpServer;
        sessionManager: SessionManager;
        state: StateManager;
        authToken?: string;
        instarDir?: string;
        hookEventReceiver?: HookEventReceiver;
        streamTicketStore?: StreamTicketStore;
        poolStreamAllowRemoteInput?: boolean;
        poolStreamConnector?: PoolStreamConnector;
        selfMachineId?: string;
    });
    private authenticate;
    private verifyToken;
    /** §2.1: a session name must be tmux-safe before it ever reaches send-keys. */
    private isValidSessionName;
    /**
     * Gate a write (input/key) before it reaches tmux. Returns true if allowed.
     * On rejection it sends the honest error frame and returns false.
     *  - invalid name  → invalid-session (§2.1 injection guard);
     *  - PEER client + remote input disabled → input-not-allowed (§2.3 default-off);
     *  - session not running locally → session-not-found (never relay to tmux blind).
     */
    private gateWrite;
    /** True when the subscribe targets a session on ANOTHER machine. */
    private isRemoteTarget;
    /** Get-or-create the one multiplexed upstream proxy for a peer machine.
     *
     *  A cached proxy that reached `closed` (idle-grace close after the last
     *  viewer left, or machine-unreachable after the bounded reconnect failed)
     *  is EVICTED and replaced: a closed PeerStreamProxy ignores every further
     *  subscribe by design, so returning it made the peer permanently
     *  unstreamable until a server restart — the 2026-06-08 live bug ("connects
     *  but the terminal stays blank, and never recovers after a hiccup"). A
     *  fresh user-initiated subscribe is a fresh episode with its own bounded
     *  reconnect budget, so P19 (no reconnect storms) is preserved. */
    private peerProxyFor;
    /** Fan a peer frame out to every LOCAL client subscribed to (machineId, session). */
    private fanRemoteFrame;
    /** Surface an honest error to every local client subscribed to (machineId, session). */
    private sendRemoteError;
    /** Tear down a client's remote subscriptions (on unsubscribe-all / close). */
    private dropRemoteSubsForClient;
    private handleMessage;
    /**
     * Stream terminal output to subscribed clients.
     * Uses diff-based approach: only sends new content since last capture.
     */
    private startStreaming;
    /**
     * Resolve display names by cross-referencing the topic-session registry.
     * Maps tmux session names to their Telegram topic names.
     */
    private getTopicDisplayNames;
    private buildSessionList;
    private sendSessionList;
    /**
     * Broadcast a custom event to all connected dashboard clients.
     * Used by PasteManager for paste_delivered / paste_acknowledged events.
     */
    broadcastEvent(event: Record<string, unknown>): void;
    /**
     * Register an in-process subscriber for events broadcast through this
     * manager. Returns an unsubscribe handle.
     *
     * Used by the Layer 3 DeliveryFailureSentinel to receive
     * `delivery_failed` events emitted by the script-side detector
     * (Layer 2c). The sentinel reacts in <1s rather than waiting for
     * its 5-minute watchdog tick.
     */
    private eventSubscribers;
    subscribeEvents(fn: (event: Record<string, unknown>) => void): () => void;
    private broadcastSessionList;
    private clientId;
    private send;
    /**
     * Graceful shutdown — close all connections and stop intervals.
     */
    shutdown(): void;
}
//# sourceMappingURL=WebSocketManager.d.ts.map