/**
 * Reconnecting WebSocket primitive shared by the SDK's WS-based realtime
 * helpers (text-stream, session-connect, observer).
 *
 * The platform exposes three workspace-scoped WebSocket surfaces with similar
 * lifecycle requirements:
 *
 *   * Resume on transient drops with exponential backoff + full jitter
 *   * Treat permanent-rejection close codes (1008 / 3003 / 4001 / 4003 /
 *     4004 / 4100 / 4403) as terminal — fail fast, do not loop
 *   * Treat 4029 / 1013 (rate limit / cap) as reconnectable on a slow floor,
 *     surfacing a ``rate_limited`` reconnect reason
 *   * Watchdog the connection: if no message arrives within an idle window,
 *     the upstream is dead even if the TCP socket has not closed; force a
 *     reconnect (with a NON-terminal close code so the loop actually rebuilds)
 *   * Surface every typed message through ``onMessage`` and every state
 *     transition through ``onStateChange``
 *
 * Before this primitive existed, every consumer (developer-console
 * useStreamSession, useCallObserver, agent-engineering scripts) re-implemented
 * the loop with subtle bugs (different idle thresholds, missing retry budget,
 * no terminal-code handling). This module is the single canonical
 * implementation; resource helpers compose it with their own message parsers
 * and protocol details.
 *
 * @see ReconnectingWebSocket
 * @see EventsResource for the analogous SSE-based helper
 */
/** Lifecycle states reported via {@link ReconnectingWebSocketOptions.onStateChange}. */
export type ReconnectingWebSocketState = 'connecting' | 'open' | 'closing' | 'closed' | 'reconnecting' | 'terminal';
/** Reasons surfaced to {@link ReconnectingWebSocketOptions.onError} on a terminal close. */
export type ReconnectingWebSocketErrorReason = 'auth' | 'rate_limited' | 'client_error' | 'server_error' | 'reconnect_budget_exhausted' | 'aborted' | 'idle_watchdog' | 'open_failed' | 'unknown';
/**
 * Coarse cause surfaced to {@link ReconnectingWebSocketOptions.onReconnect}
 * describing WHY the loop is reconnecting. Lets consumers vary the banner
 * copy ("connection went quiet" vs. "too many connections — retrying").
 *
 *   * ``idle_watchdog`` — no frame arrived within ``idleTimeoutMs``; the
 *     watchdog force-closed the (apparently dead) socket.
 *   * ``rate_limited``  — the server closed with a rate-limit code (4029 /
 *     1013); the backoff floor is raised before retrying.
 *   * ``transient``     — an ordinary network drop / unclassified close code.
 */
export type ReconnectingWebSocketReconnectReason = 'idle_watchdog' | 'rate_limited' | 'transient';
/** Structured terminal error surfaced to consumers. */
export declare class ReconnectingWebSocketError extends Error {
    readonly reason: ReconnectingWebSocketErrorReason;
    readonly closeCode: number | undefined;
    readonly closeReason: string | undefined;
    readonly attempts: number;
    constructor(message: string, reason: ReconnectingWebSocketErrorReason, closeCode: number | undefined, closeReason: string | undefined, attempts: number);
}
/**
 * Constructor for the underlying WebSocket. Defaults to the global
 * ``WebSocket`` (browsers, Bun, Deno, Node 22+ via global) if available.
 *
 * Pass an explicit factory in environments without a global (older Node) or
 * for tests that swap in a mock.
 */
export type WebSocketFactory = (url: string, protocols?: string | string[]) => WebSocket;
/** Options for {@link ReconnectingWebSocket}. */
export interface ReconnectingWebSocketOptions {
    /** Target ws:// or wss:// URL. */
    url: string;
    /**
     * Optional WebSocket subprotocols. The platform's auth scheme passes the
     * bearer token here (``['auth', token]``) so it never appears in the URL.
     *
     * Frozen at subscribe time and reused verbatim on every (re)connect. For a
     * long-lived stream whose bearer token can expire between reconnects, pass
     * {@link getProtocols} instead so each (re)connect picks up a fresh token.
     */
    protocols?: string | string[];
    /**
     * Optional provider for the WebSocket subprotocols, invoked on EACH
     * (re)connect to obtain fresh subprotocols (typically a refreshed bearer
     * token in the ``['auth', token]`` pair). Use this for long-lived streams
     * where the token can expire while the loop is reconnecting — the static
     * {@link protocols} freeze the value at subscribe time and would replay a
     * stale token on every retry, looping forever against a 4403.
     *
     * When provided, this takes precedence over {@link protocols}. May return
     * synchronously or as a promise. If it throws / rejects, the connection
     * attempt fails as ``open_failed`` and the reconnect loop retries (so a
     * transient token-refresh hiccup is survivable). Falls back to
     * {@link protocols} when absent — fully backward compatible.
     */
    getProtocols?: () => string | string[] | Promise<string | string[]>;
    /**
     * Initial backoff delay (ms). Doubles with full jitter on each successive
     * failure up to ``maxDelayMs``. Default ``1_000``.
     */
    initialDelayMs?: number;
    /** Cap on the reconnect backoff delay (ms). Default ``30_000``. */
    maxDelayMs?: number;
    /**
     * Maximum number of reconnect attempts before giving up. Default ``10``.
     * Set to ``Infinity`` to retry forever (rely on AbortSignal for shutdown).
     */
    maxReconnects?: number;
    /**
     * Idle watchdog (ms). If no message arrives within this window the
     * connection is considered dead and is force-closed; the reconnect loop
     * then handles the rebuild. Default ``45_000`` (matches the longest
     * platform endpoint heartbeat). Set to ``0`` to disable.
     */
    idleTimeoutMs?: number;
    /**
     * Cancellation signal. Aborting the signal closes the underlying socket
     * and stops the reconnect loop. The ``done`` promise resolves once the
     * teardown completes.
     */
    signal?: AbortSignal;
    /**
     * Optional WebSocket factory. Defaults to ``globalThis.WebSocket`` if
     * available; throws at first connect attempt otherwise.
     */
    webSocketFactory?: WebSocketFactory;
    /**
     * Invoked when the underlying socket transitions states. Mostly useful
     * for surfacing reconnects to the UI (e.g., "Reconnecting…" banner).
     */
    onStateChange?: (state: ReconnectingWebSocketState) => void;
    /**
     * Invoked once per inbound frame. ``MessageEvent.data`` is delivered raw
     * — consumers parse JSON / binary themselves.
     */
    onMessage: (event: MessageEvent) => void;
    /**
     * Invoked just before each reconnect attempt with the 1-based attempt
     * number, the planned delay (ms), the close code that triggered the
     * reconnect, and a coarse ``reason`` describing WHY we are reconnecting.
     *
     * ``reason`` lets consumers tailor the banner copy: ``idle_watchdog`` →
     * "connection went quiet, reconnecting…"; ``rate_limited`` → "too many
     * connections — retrying…"; ``transient`` → generic "reconnecting…". The
     * ``reason`` field is additive — existing consumers that ignore it keep
     * working unchanged.
     */
    onReconnect?: (info: {
        attempt: number;
        delayMs: number;
        closeCode: number | undefined;
        reason: ReconnectingWebSocketReconnectReason;
    }) => void;
    /**
     * Invoked exactly once per ``ReconnectingWebSocket`` instance on a
     * terminal failure (consumer-aborted, reconnect budget exhausted, or a
     * close code in the terminal set: 1008 / 3003 / 4001 / 4003 / 4004 /
     * 4100 / 4403). The {@link ReconnectingWebSocketError.closeCode} carries
     * the originating wire close code (when one exists) so consumers can
     * branch on it.
     */
    onError?: (error: ReconnectingWebSocketError) => void;
}
/** Handle returned by {@link createReconnectingWebSocket}. */
export interface ReconnectingWebSocketHandle {
    /**
     * Resolves when the loop has fully stopped (post-abort cleanup complete).
     * Never rejects; consume errors via ``onError``.
     */
    done: Promise<void>;
    /**
     * Send a frame on the currently open socket. Throws if no socket is
     * currently open. Use ``onStateChange`` to gate sends, or buffer in
     * caller code.
     */
    send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;
    /**
     * Close the connection and stop reconnecting. Equivalent to aborting the
     * caller-supplied signal. Idempotent.
     */
    close(code?: number, reason?: string): void;
    /** Latest known lifecycle state. */
    readonly state: ReconnectingWebSocketState;
}
/**
 * Build a managed reconnecting WebSocket.
 *
 * The returned handle is the only public surface; the underlying
 * ``WebSocket`` is held privately so consumers cannot bypass the lifecycle
 * machinery (which would defeat the watchdog and reconnect loop).
 *
 * @example
 * ```ts
 * const handle = createReconnectingWebSocket({
 *   url: client.conversations.sessionConnectUrl({ serviceId, entityId }),
 *   // Re-mint the auth subprotocols on EACH (re)connect so a token that
 *   // expires mid-stream is refreshed before the retry — falls back to the
 *   // static `protocols` option when omitted.
 *   getProtocols: async () => sessionConnectAuthProtocols(await freshToken()),
 *   onMessage: (e) => console.log('frame', e.data),
 *   onStateChange: (s) => console.log('state', s),
 *   onReconnect: ({ reason, delayMs }) => {
 *     if (reason === 'rate_limited') showBanner('Too many connections — retrying…');
 *     else if (reason === 'idle_watchdog') showBanner('Connection went quiet — reconnecting…');
 *   },
 *   onError: (err) => console.error('terminal:', err.reason, err.closeCode),
 * });
 *
 * handle.send(JSON.stringify({ type: 'user_text', text: 'hi' }));
 * await handle.done;  // resolves after handle.close() or terminal error
 * ```
 */
export declare function createReconnectingWebSocket(options: ReconnectingWebSocketOptions): ReconnectingWebSocketHandle;
//# sourceMappingURL=reconnecting-websocket.d.ts.map