/**
 * Wire types for the Slack agent endpoints in custom-integrations.
 *
 * GET responses use camelCase keys; POST responses currently return
 * `author` as a raw snake_case dict (known backend inconsistency —
 * see plan-slack-m1-ui Backend status, 2026-06-09). The client
 * normalises in api/index.ts so the UI layer only sees one shape.
 */
export interface SlackMessageAuthor {
    externalUserId: string;
    /** Backend leaves this null when the message author hasn't been
     *  enriched via users.info (e.g. fresh chat.postMessage responses).
     *  UI falls back to externalUserId for display. */
    name: string | null;
    avatarUrl?: string | null;
    isSelf: boolean;
}
export interface SlackMessageAuthorRaw {
    external_user_id: string;
    name: string | null;
    avatar_url?: string | null;
    is_self: boolean;
}
export interface SlackMessage {
    externalMessageId: string;
    externalThreadTs?: string;
    channelId: string;
    author: SlackMessageAuthor;
    text: string;
    postedAt: string;
    editedAt?: string | null;
    deletedAt?: string | null;
    clientMsgId?: string;
    /** Raw Slack message subtype (e.g. 'channel_join', 'channel_leave');
     *  null/undefined for ordinary user messages. The UI derives
     *  replyability (and any future type-specific rendering) from this —
     *  see isReplyable(). */
    subtype?: string | null;
    /** UI-only flag for an in-flight outbound send. Never on the wire —
     *  the optimistic row gets dropped when refetchQueries lands the
     *  server's canonical state. */
    isPending?: boolean;
}
/** Whether a link points at a single Slack thread (UI-5) or a whole
 *  channel (B4). Channel mode renders every thread in the channel. */
export type SlackTargetKind = 'thread' | 'channel';
/** Channel-mode grouping (B4): one Slack thread — a root message plus
 *  its replies (oldest-first). Sourced from the backend's `threads[]`. */
export interface SlackThreadGroup {
    rootTs: string;
    root: SlackMessage;
    replies: SlackMessage[];
    replyCount: number;
    /** Older-reply pagination cursor contract (see SlackOlderPage).
     *  isTrimmed: older replies exist beyond this page. nextCursor: oldest
     *  reply ts in this page (pass as the next before_ts), null when
     *  exhausted. pageSize: window size used. */
    isTrimmed?: boolean;
    nextCursor?: string | null;
    pageSize?: number;
}
/** Why the current viewer can't post into the linked channel. Backend
 *  computes this per-request against the viewer's OWN Slack token:
 *   - 'not_in_channel'      → connected but not a member (or has left)
 *   - 'slack_not_connected' → no active Slack auth for this viewer
 *  Null/undefined when the viewer can post. */
export type SlackPostBlockReason = 'not_in_channel' | 'slack_not_connected';
export interface ExternalConversation {
    hiverConvId: string;
    channelId: string;
    channelName: string;
    externalThreadTs?: string;
    /** 'channel' → render `threads` grouped; 'thread' (default) → render
     *  the flat `messages` list with a pinned parent. */
    targetKind?: SlackTargetKind;
    messages: SlackMessage[];
    /** Present in channel mode: one entry per Slack thread in the linked
     *  channel. Undefined in thread mode. */
    threads?: SlackThreadGroup[];
    isTrimmed?: boolean;
    /** Oldest message ts in this page — pass back as before_ts to fetch the
     *  preceding page. Null/undefined when older history is exhausted. */
    nextCursor?: string | null;
    /** Window size the server used for this page (default 25, max 50). */
    pageSize?: number;
    /** Per-viewer composer hint (computed against the viewer's own Slack
     *  token). Defaults to true when the server omits it. */
    viewerCanPost?: boolean;
    /** Reason the composer is blocked when viewerCanPost is false. */
    viewerPostBlockReason?: SlackPostBlockReason | null;
}
/** Result of an imperative older-page fetch (NOT polled — fetched on
 *  demand when the agent scrolls to the top). `messages` carries the
 *  older thread replies (thread mode); `threads` carries older thread
 *  roots (channel mode). The render layer merges these with the live
 *  polled page-0 window, deduping by externalMessageId. */
export interface SlackOlderPage {
    messages: SlackMessage[];
    threads?: SlackThreadGroup[];
    /** Oldest ts in this page; pass as the next before_ts. Null when the
     *  server reports no older history remains. */
    nextCursor?: string | null;
    /** Whether older history still exists beyond this page. */
    isTrimmed: boolean;
    /** Window size used by the server for this page. */
    pageSize: number;
}
export interface MemberResolutionReport {
    added: Array<{
        email: string;
        externalUserId: string;
    }>;
    notOnSlack: string[];
}
export interface StartExistingChannelRequest {
    hiverConvId: string;
    channelId: string;
    messageText: string;
}
export interface StartNewChannelRequest {
    hiverConvId: string;
    channelName: string;
    /** Backend wants a bool; UI state holds a UX-friendly enum and
     *  transforms at the API boundary (see StartModal). */
    isPrivate: boolean;
    memberEmails: string[];
    messageText: string;
}
export interface StartResponse {
    externalConversationId: string;
    channelId: string;
    externalThreadTs?: string;
    postedMessage: SlackMessage;
    memberResolutionReport?: MemberResolutionReport;
}
export interface PostMessageRequest {
    hiverConvId: string;
    text: string;
    /** Channel mode (B4): root ts of the thread to reply into. Omit to
     *  post a new top-level message (a new thread) to the channel. */
    externalThreadTs?: string;
}
export interface ChannelMember {
    externalUserId: string;
    name: string;
    avatarUrl?: string;
}
export interface SlackChannel {
    externalChannelId: string;
    name: string;
    isPrivate: boolean;
    isMember: boolean;
}
//# sourceMappingURL=wire.d.ts.map