/** * Pure, testable core of the Slack → Copilot bridge. NO Slack/Bolt import here * so the decision logic can be unit-tested with injected fakes. * * Mirrors a lean subset of Hermes' Slack behavioral contract: * - DMs always respond; channels respond only on @mention (unless requireMention=false). * - Unauthorized users (not in the allowlist) are silently ignored. * - The bot's own `<@BOT>` mention is stripped from the text before forwarding. * - Resolution / Copilot errors come back as friendly Slack replies (never silent failure * of a request we accepted). */ import type { ResolveSessionResult } from "../comms/resolve-session.js"; import type { AskResult } from "../comms/index.js"; export interface SlackMessageInput { text: string; userId?: string; channelType: "im" | "channel"; /** true when the bot was @mentioned (channel messages) */ isMention: boolean; /** thread to reply in (event.thread_ts ?? event.ts) */ threadTs?: string; /** bot's own user id, for stripping the mention token */ botUserId?: string; } export interface SlackHandlerDeps { resolve: (opts: { flag?: string; env?: string; }) => ResolveSessionResult; ask: (session: string, text: string) => Promise; /** ["*"] = allow all; empty/unset denies all. */ allowedUsers?: string[] | null; /** require @mention in channels (default true). */ requireMention?: boolean; /** COPILOT_TMUX_SESSION passthrough for resolution. */ sessionEnv?: string; } export interface SlackReply { /** text to post back, or null to stay silent. */ reply: string | null; /** thread to reply in. */ threadTs?: string; } /** Allowlist check: only an explicit "*" allows everyone. */ export declare function isUserAllowed(userId: string | undefined, allowed?: string[] | null): boolean; /** Strip a `<@BOTID>` mention (and tidy whitespace) from message text. */ export declare function stripMention(text: string, botUserId?: string): string; export declare function handleSlackMessage(input: SlackMessageInput, deps: SlackHandlerDeps): Promise;