/**
 * Chat gateway — unified surface for bot-style chat connections (v0.7).
 *
 * Three concrete adapters plug into this interface: Telegram, Discord,
 * Slack. Each is independently activatable via a token in
 * `~/.opensquid/config.json` `chat_connections.<platform>.bot_token`.
 * Adapters that don't have a token configured are skipped at startup —
 * opensquid works fine with zero, one, or all three chat platforms wired.
 *
 * Scope decisions (per [[find-simple-solutions]]):
 * - Single bot owner per platform (no multi-tenancy, no OAuth flows).
 *   The user runs their OWN bot with their OWN token.
 * - Bot owner is the only authorized chat participant by default.
 *   Allowlist of additional chat/channel/user ids in config.
 * - Outbound-only connection mechanism (long-poll, WebSocket, Socket
 *   Mode) — no inbound HTTP server, no webhook URL, no public
 *   ingress required. Works behind any NAT.
 * - Text-only messages in v0.7. Images / files / inline buttons
 *   deferred until concrete user-need surfaces.
 *
 * Why a gateway abstraction at all (vs per-platform code paths):
 * the opensquid-side MCP tool surface (`chat.send`, `chat.list_channels`,
 * the inbound-message → MCP-context bridge) should NOT care which
 * platform a message came from. The gateway normalizes incoming events
 * to a single `ChatMessage` shape and routes outgoing sends by
 * channel id.
 *
 * What is NOT in this file:
 * - Token storage / config loading (lives in `chat/config.ts`).
 * - The actual SDK calls (live in `chat/adapters/<platform>.ts`).
 * - The MCP tool wiring (lives in `chat/mcp-tools.ts`).
 *
 * What IS in this file: types + the `ChatGateway` orchestrator that
 * holds N adapters and presents a single send/subscribe API.
 */
/** Stable platform identifier. */
export type ChatPlatform = "telegram" | "discord" | "slack";
/**
 * A logical destination for outbound messages. The shape varies by
 * platform but always normalizes to a single string id from opensquid's
 * point of view. Adapters parse the id back to platform-native form
 * (Telegram chat_id, Discord channel snowflake, Slack channel id).
 *
 * Format: `<platform>:<native_id>`, e.g. `telegram:8075471258`.
 */
export type ChannelId = string;
/** Normalized inbound message (platform-agnostic shape). */
export interface ChatMessage {
    /** Stable id from the platform (snowflake / message_id / ts). */
    id: string;
    /**
     * Optional thread / topic id for platforms that support sub-threads
     * within a single channel (Telegram forum topics, Discord threads).
     * Preserved on inbound so the daemon can route to per-topic projects;
     * preserved on outbound so replies thread correctly. v0.7.2.
     */
    threadId?: string;
    /** Which platform delivered this. */
    platform: ChatPlatform;
    /** Channel the message was posted in (DM, group, server channel). */
    channel: ChannelId;
    /** Display name of the sender (best-effort; falls back to user id). */
    sender: string;
    /** Native sender id (preserved for allowlist checks + replies). */
    senderId: string;
    /** Message body, text only in v0.7. */
    text: string;
    /** Wall-clock the platform stamped the message with. */
    receivedAt: Date;
    /** True when the message contains @-mention or trigger-word for the bot. */
    mentionsBot: boolean;
}
/**
 * Outbound message payload — text-only in v0.7. `replyTo` populates
 * platform-specific threading metadata where it's free (Telegram
 * `reply_to_message_id`, Slack `thread_ts`, Discord `message_reference`).
 */
export interface OutboundMessage {
    channel: ChannelId;
    text: string;
    /** Source message id to reply-thread under (best-effort per platform). */
    replyTo?: string;
    /**
     * Optional thread / topic id for platforms that support sub-threads
     * within a single channel:
     *
     * - **Telegram**: `message_thread_id` for forum topics (a supergroup
     *   with Topics enabled has multiple topic threads, each one a
     *   separate logical channel within the same chat_id). v0.7.2.
     * - **Discord**: thread id for thread messages.
     * - **Slack**: not used (Slack threading is via `replyTo` only).
     *
     * Adapters that don't support thread_id ignore it.
     */
    threadId?: string;
}
/** Result of a single send. Adapter-defined fields preserved opaquely. */
export interface SendResult {
    platform: ChatPlatform;
    /** Native id of the delivered message — for future edit / delete / react. */
    messageId: string;
    /** Wall-clock the platform stamped the delivered message with. */
    deliveredAt: Date;
}
/** Inbound message handler. Async return for await-based pipelines. */
export type MessageHandler = (msg: ChatMessage) => Promise<void> | void;
/**
 * What every chat adapter must implement. Adapter authors hold the
 * platform-specific SDK / WebSocket / long-poll loop; this interface
 * is the only surface opensquid touches.
 *
 * Lifecycle:
 *   1. `start()` — open the connection (login, attach handlers).
 *      Resolves when ready to receive + send. Throws on auth failure.
 *   2. `onMessage(handler)` — subscribe to inbound events. Multiple
 *      subscriptions stack; all fire per message in registration order.
 *   3. `send(message)` — push outbound. Throws on permanent failures
 *      (channel doesn't exist, bot kicked); transient errors are
 *      retried internally per the adapter's policy.
 *   4. `shutdown()` — close the connection cleanly. Idempotent.
 *
 * `identity()` returns the bot's own user info (username + native id) —
 * used by the gateway to recognize self-authored messages and to
 * resolve @-mention tokens to bot identity.
 */
export interface ChatAdapter {
    /** Platform identifier this adapter handles. */
    readonly platform: ChatPlatform;
    start(): Promise<void>;
    shutdown(): Promise<void>;
    onMessage(handler: MessageHandler): void;
    send(message: OutboundMessage): Promise<SendResult>;
    identity(): Promise<{
        username: string;
        nativeId: string;
    }>;
}
/**
 * Holds N adapters and presents the unified API. Construct with the
 * adapter array you want active for this opensquid process. Pre-wired
 * tokens live in config; the factory at `chat/factory.ts` builds the
 * adapter array based on which tokens are present.
 */
export declare class ChatGateway {
    private readonly adapters;
    private readonly handlers;
    private started;
    constructor(adapters: ChatAdapter[]);
    /** Open all adapter connections in parallel. First failure surfaces. */
    start(): Promise<void>;
    /** Close all adapter connections. Errors are logged and swallowed
     * so shutdown is always best-effort. */
    shutdown(): Promise<void>;
    /** Subscribe to inbound messages from any platform. */
    onMessage(handler: MessageHandler): void;
    /** Route an outbound message to the right adapter by `channel` prefix. */
    send(message: OutboundMessage): Promise<SendResult>;
    /** List the platforms currently wired up. Used by `chat.list_channels`. */
    activePlatforms(): ChatPlatform[];
    /**
     * v0.7.2 — get the active adapter for a platform so daemon-internal
     * code paths (e.g. `chat-daemon-worker` RPC dispatch) can reach
     * platform-specific methods like Telegram's `createTopic`. Returns
     * undefined when the platform isn't activated.
     */
    getAdapter(platform: ChatPlatform): ChatAdapter | undefined;
    private dispatch;
}
export declare class ChatGatewayError extends Error {
    readonly hint?: string | undefined;
    constructor(message: string, hint?: string | undefined);
}
export declare function formatChannelId(platform: ChatPlatform, nativeId: string): ChannelId;
export declare function platformFromChannel(channel: ChannelId): ChatPlatform;
export declare function nativeIdFromChannel(channel: ChannelId): string;
//# sourceMappingURL=gateway.d.ts.map