/**
 * ChatV2RelayAdapter — bridge between the local chat-v2 service and the
 * Cloud relay queue.
 *
 * Two responsibilities:
 *
 * 1. **Inbound RPC** — when a `chat_request` message lands on the local
 *    inbound queue, dispatch the named method against ChatV2Service (with
 *    the supplied `directory` / `presence` providers for the OSS-only
 *    methods), then return a `chat_response` to the originating device.
 *
 * 2. **Outbound events** — register an `onBroadcast` listener on
 *    {@link ChatV2Gateway} so every local broadcast (user message + agent
 *    reply) is also forwarded as a `chat_event` to all *known Portal
 *    devices* (devices that have issued a `chat_request` to this OSS
 *    recently). That keeps Portal UI in lockstep with the local one
 *    without any additional polling on Portal beyond its existing
 *    `/queue/poll` loop.
 *
 * Design notes:
 *   - Closed-set method dispatch via the {@link CHAT_RPC_METHODS} table;
 *     unknown methods produce `unsupported_method` errors so Portal sees
 *     a structured response rather than a hang.
 *   - Portal-device tracking is in-memory with an idle TTL (10 min).
 *     A Portal that sits silent for >TTL stops receiving chat_events;
 *     its next chat_request re-registers it. Survives across local OSS
 *     restarts via re-registration on the first chat_request, no
 *     persistence required.
 *   - Errors from ChatV2Service (ChatError) propagate verbatim via the
 *     response envelope so Portal can render the same error UX as the
 *     local OSS UI does on the REST surface.
 *
 * @module services/chat-v2/chat-v2.relay-adapter.service
 */
import type { EventEmitter } from 'events';
import { type ComponentLogger } from '../core/logger.service.js';
import { type ChatChannelDTO, type ChatMessageDTO } from './types.js';
import type { ChatV2Service } from './chat-v2.service.js';
import type { IAgentDirectoryProvider, IAgentPresenceProvider } from '../../controllers/chat-v2/chat-v2.controller.js';
/**
 * Wire frame produced by {@link ChatV2Gateway.broadcast} — duplicated here
 * as `unknown`-typed in the relay payload so this module doesn't have to
 * pull in the WebSocket layer's types. We only forward; we do not inspect.
 */
type GatewayWireEvent = unknown;
/**
 * Minimal CloudSyncService surface the adapter depends on. Declared
 * narrowly so tests can stub a fake sync without spinning up the real
 * polling loop / fetch lifecycle.
 */
export interface ICloudSyncLike extends EventEmitter {
    sendMessage(toDeviceId: string, type: 'chat_response' | 'chat_event', payload: unknown): Promise<void>;
    /**
     * Optional queueId-direct send. When present, the adapter prefers it
     * for chat_response routing — Portal's queueId may not yet be in the
     * 30s device-poll cache, and the production relay routes by queueId
     * anyway. Methods without this fall back to sendMessage (stub-friendly).
     */
    sendToPeerQueueId?(peerQueueId: string, type: 'chat_response' | 'chat_event', payload: unknown): Promise<void>;
}
/**
 * Minimal ChatV2Gateway surface the adapter depends on — only the
 * `onBroadcast` listener registration.
 */
export interface IChatV2GatewayLike {
    onBroadcast(listener: (channelId: string, event: GatewayWireEvent) => void): () => void;
}
/**
 * Minimal ChatV2DispatcherService surface. Required for the
 * `sendMessage` RPC method — when Portal sends a user message via the
 * relay, the adapter persists it (via ChatV2Service) AND fires the
 * dispatcher so the bound agent's PTY actually receives a
 * `[CHAT:<id>]` prompt. The HTTP controller does the same thing for
 * local /agents traffic; this brings the relay path to parity.
 */
export interface IChatV2DispatcherLike {
    dispatchMessage(channel: ChatChannelDTO, message: ChatMessageDTO): Promise<unknown>;
}
/** Constructor options for {@link ChatV2RelayAdapter}. */
export interface ChatV2RelayAdapterOptions {
    /** The chat service to dispatch RPC calls against. */
    service: ChatV2Service;
    /** The gateway whose broadcasts we mirror to the relay. */
    gateway: IChatV2GatewayLike;
    /** Cloud sync — used both to listen for chat_request and to send replies. */
    cloudSync: ICloudSyncLike;
    /**
     * Optional ChatV2 dispatcher — fired after a Portal-sourced user
     * `sendMessage` RPC so the bound agent's PTY receives the
     * `[CHAT:<id>]` prompt (mirrors the HTTP controller's post-ack path).
     * When omitted (REST-only tests), Portal messages persist but agents
     * don't react — verify carefully before relying on this fallback.
     */
    dispatcher?: IChatV2DispatcherLike;
    /** Directory provider — backs the `listAgents` RPC method. */
    directory?: IAgentDirectoryProvider;
    /** Presence provider — backs the `getAgentPresence` RPC method. */
    presence?: IAgentPresenceProvider;
    /**
     * Idle TTL for Portal device registration (defaults to 10 min). A
     * Portal device that sits silent longer than this stops receiving
     * forwarded chat_events; the next chat_request re-registers it.
     * Override only in tests.
     */
    portalIdleTtlMs?: number;
    /** Optional logger override (defaults to a fresh component logger). */
    logger?: ComponentLogger;
    /** Optional clock for tests. */
    now?: () => number;
}
/**
 * Wraps a ChatV2Service so it can be driven over the Cloud relay queue
 * from Cloud Portal. Single instance per running OSS process; instantiated
 * by `index.ts` once both the gateway and the cloud sync are ready.
 */
export declare class ChatV2RelayAdapter {
    private readonly service;
    private readonly gateway;
    private readonly cloudSync;
    private readonly dispatcher?;
    private readonly directory?;
    private readonly presence?;
    private readonly portalIdleTtlMs;
    private readonly logger;
    private readonly now;
    /** deviceId → last-seen ms timestamp; used for the portal-idle-TTL filter. */
    private readonly knownPortals;
    /** Cached unsubscribe hooks so {@link stop} can detach cleanly. */
    private unsubscribeMessage;
    private unsubscribeBroadcast;
    constructor(options: ChatV2RelayAdapterOptions);
    /**
     * Wire the adapter to the cloud-sync inbound stream and the gateway's
     * broadcast hook. Idempotent — calling twice is a logged no-op so the
     * server bootstrap can be safe under partial restart sequences.
     */
    start(): void;
    /** Detach all listeners. Safe to call during shutdown. */
    stop(): void;
    /**
     * Number of currently-tracked Portal devices. Exposed for tests and
     * future observability dashboards (e.g. `/api/cloud/status`).
     */
    knownPortalCount(): number;
    /**
     * Wrap {@link dispatchRpc} with the request-envelope unwrap and reply
     * round-trip. Always resolves; any error is mapped to a structured
     * `chat_response` so Portal never hangs on a missing reply.
     *
     * @param msg - Raw IncomingMessage from CloudSyncService
     */
    private handleRequest;
    /**
     * Dispatch one of the closed-set RPC methods. Each branch maps to the
     * equivalent ChatV2Service / directory / presence call.
     *
     * Auth model: the originating Portal device shares an account with this
     * OSS (the relay's `userId` check guarantees it), so we build a
     * `ChatPrincipal` from the OSS's local owner id. Portal cannot
     * impersonate other users — that's enforced at the relay layer.
     *
     * @param method - One of {@link CHAT_RPC_METHODS}
     * @param params - Method-specific args
     * @param fromDevice - The originating Portal device id (for audit)
     * @returns The method-specific result (any JSON-serializable value)
     */
    private dispatchRpc;
    /**
     * Fan a gateway broadcast out to every currently-known Portal device.
     * Drops expired (idle > TTL) entries inline so the set self-cleans.
     *
     * @param channelId - The channel whose timeline changed
     * @param event - The gateway wire event (typed `unknown` here so this
     *   module doesn't import the WS layer's types)
     */
    private handleBroadcast;
    /** Evict Portal device entries whose last-seen is older than the TTL. */
    private pruneExpiredPortals;
    private buildPrincipal;
    private requireString;
    private optionalString;
    private optionalNumber;
    private serializeChannel;
    private serializeMessage;
    private errorEnvelope;
}
export {};
//# sourceMappingURL=chat-v2.relay-adapter.service.d.ts.map