import type { HttpClient } from "./http-client.js";
import type { MessageRecord } from "../types.js";
/**
 * WebSocket connection state machine states.
 */
export type WsState = "disconnected" | "connecting" | "connected" | "polling" | "reconnecting";
/**
 * Configuration for CloudWsClient.
 */
export interface CloudWsClientOptions {
    /** Server base URL (http:// or https://). */
    endpoint: string;
    /** Cloud session ID. */
    sessionId: string;
    /** Peer ID. Can be empty string initially (deferred connection). */
    peerId: string;
    /** Bearer auth token. */
    token: string;
    /** HttpClient instance for poll fallback. */
    http: HttpClient;
    /** Callback for a single message from a "message" frame. */
    onMessage: (msg: MessageRecord) => void;
    /** Callback for a batch of messages from "queued_messages" frame. */
    onMessages: (msgs: MessageRecord[]) => void;
    /** Callback when a peer connects. */
    onPeerConnected: (peer: {
        peerId: string;
        name: string;
        project: string;
    }) => void;
    /** Callback when a peer disconnects. */
    onPeerDisconnected: (peerId: string) => void;
    /** Callback for peers snapshot frame. */
    onPeersSnapshot: (peers: unknown[]) => void;
    /** Callback for state changes. */
    onStateChange: (state: WsState) => void;
    /** Optional callback for error frames from the server. */
    onError?: (frame: {
        code: string;
        message: string;
    }) => void;
    /** Optional callback for delivery acknowledgment frames from the server. */
    onDeliveryAck?: (payload: {
        messageId: string;
        peerId: string;
    }) => void;
    /** Optional callback when peer has been evicted by server (stale cleanup). */
    onPeerEvicted?: () => void;
    /** Poll interval in milliseconds (default: 5000). */
    pollIntervalMs?: number;
}
/**
 * WebSocket client with reconnection logic and poll fallback.
 *
 * Manages the full connection lifecycle:
 * - Connects to server WS endpoint with token + peerId auth
 * - Dispatches all 6 WsServerFrame types to callbacks
 * - Falls back to HTTP polling on disconnect
 * - Reconnects with exponential backoff (1s-30s with 10% jitter)
 * - Supports deferred peerId via setPeerId() for lazy connection
 * - Supports runtime credential updates via updateToken/updateSessionId
 */
export declare class CloudWsClient {
    private ws;
    private state;
    private readonly backoff;
    private reconnectTimer;
    private pollTimer;
    /**
     * DELIVERY-PATH EVIDENCE. Before 3.23.9 a poll failure was caught and only logger.warn'd, so the
     * bridge had no idea whether its FALLBACK delivery path worked — and `healthy` could not be
     * computed honestly because the data did not exist. Captured evidence that nothing reads is the
     * defect shape this codebase keeps hitting.
     */
    private lastPollOkAt;
    private lastPollError;
    private lastWsErrorSticky;
    private pingTimer;
    private lastMessageId;
    /**
     * Advance the poll cursor AND persist it.
     *
     * Every assignment must go through here. `lastMessageId` was in-memory only, so a restart sent
     * no cursor and the relay replayed the whole channel ("If lastMessageId is not provided (first
     * poll), returns all messages") — re-injecting days-old broadcasts on every restart.
     */
    private advanceCursor;
    /**
     * Seed the cursor from disk when this process has not consumed anything yet.
     *
     * Only ever fills a NULL cursor: a live in-memory position is always newer than the stored one,
     * so it must never be overwritten by a restore.
     */
    private restoreCursor;
    private lastWsError;
    private connectionGeneration;
    private opts;
    /**
     * Wall-clock timestamp (ms since epoch) of the last frame received on this
     * WebSocket. Survives reconnects and disconnects -- intentionally NOT reset
     * on close, so an operator can see how stale the transport is.
     * Surfaced to cogent_health_check as transport.wsLastFrameAt (ISO string).
     */
    private lastFrameAt;
    constructor(options: CloudWsClientOptions);
    /**
     * Initiate WebSocket connection to the server.
     * Guards against empty peerId -- call setPeerId() first.
     */
    connect(): void;
    /**
     * Disconnect from the WebSocket and stop all timers.
     */
    disconnect(): void;
    /**
     * Set or update the peerId.
     * This is the trigger mechanism for deferred WS connection.
     * Plan 05 creates CloudWsClient at startup with empty peerId (peerId is
     * only known after register-peer succeeds). After registration, it calls
     * this method to initiate the WS connection.
     */
    setPeerId(peerId: string): void;
    /**
     * Get the current connection state.
     */
    getState(): WsState;
    /**
     * True iff the WebSocket is in the "connected" state.
     * Surfaced to cogent_health_check as transport.wsConnected.
     */
    isConnected(): boolean;
    /**
     * ISO 8601 string of the last frame arrival time, or null if no frame has
     * been received on this client instance yet. Survives reconnects --
     * deliberately NOT reset on close so an operator can see staleness across
     * reconnect cycles.
     * Surfaced to cogent_health_check as transport.wsLastFrameAt.
     */
    getLastFrameAt(): string | null;
    /**
     * Raw wall-clock timestamp (ms since epoch) of the last frame, or null if
     * no frame has arrived. Sibling of `getLastFrameAt()` -- same source field,
     * but returns the underlying number so the consumer can compute deltas
     * (e.g. transport.wsAgeMs in cogent_health_check) without re-parsing the
     * ISO string. Survives reconnects, same as `getLastFrameAt()`.
     * Audit L2.
     */
    /**
     * What this bridge's DELIVERY situation actually is — the input `healthy` needs in order to
     * describe THIS BRIDGE rather than the relay's uptime.
     */
    getDeliveryDiagnostics(): {
        pollActive: boolean;
        lastPollOkAt: number | null;
        lastPollError: string | null;
        lastWsError: string | null;
    };
    getLastFrameAtMs(): number | null;
    /**
     * Update the last seen message ID (e.g., from HttpBackend operations).
     */
    updateLastMessageId(id: string): void;
    /**
     * Update the auth token for reconnection.
     * If currently connected, disconnects and reconnects with the new token.
     */
    updateToken(newToken: string): void;
    /**
     * Point the socket at a DIFFERENT relay host.
     *
     * 🔴 WHY THIS EXISTS. The two relays are different HOSTS (cogent.tools vs app.cogent.tools), not
     * just different credentials. `updateToken`/`updateSessionId` existed and `opts.endpoint` did
     * not — so an agent that joined a FREE channel and then a TEAM channel in the SAME PROCESS kept
     * dialling the OLD host while presenting the NEW token and session id. The upgrade 401s, the
     * backoff retries it every 1-4s forever, and the inbound queue never drains — while HTTP looks
     * perfectly healthy, because reinitCloudBackend builds a fresh HttpClient with the right base.
     * Reported live by peer marvin-mobile on 2026-09-05 (queueLength stuck at 410).
     *
     * No-ops when the host is unchanged: re-joining another channel on the SAME relay is the common
     * case, and tearing down a healthy socket for it would be a self-inflicted reconnect storm.
     */
    updateEndpoint(newEndpoint: string): void;
    /**
     * Update the session ID.
     * If currently connected, disconnects and reconnects.
     */
    updateSessionId(newSessionId: string): void;
    /**
     * Replace the HttpClient used for polling.
     * The next poll cycle will use the new client automatically.
     */
    updateHttp(newHttp: HttpClient): void;
    /**
     * Send a delivery acknowledgment frame to the server, which routes it
     * to the original sender so they know the message was received.
     */
    sendDeliveryAck(messageId: string, fromPeerId: string, toPeerId: string): void;
    private setState;
    private dispatchFrame;
    private scheduleReconnect;
    private startPolling;
    private stopPolling;
    private clearTimers;
    private clearReconnectTimer;
    private clearPingTimer;
}
//# sourceMappingURL=ws-client.d.ts.map