import type { ClientSession as Session } from '@moxxy/sdk'; import type { Channel, ChannelHandle, ChannelStartOptsBase, PermissionResolver, TunnelProviderDef } from '@moxxy/sdk'; import type { VaultStore } from '@moxxy/plugin-vault'; import { SlackClient } from './channel/slack-client.js'; export interface SlackChannelLogger { debug?(msg: string, meta?: Record): void; info?(msg: string, meta?: Record): void; warn?(msg: string, meta?: Record): void; error?(msg: string, meta?: Record): void; } export interface SlackChannelOptions { readonly vault: VaultStore; /** * Tunnel provider used to expose the local ingest server publicly. Defaults to * the self-hosted `proxyTunnel` (imported directly, like the webhooks tunnel); * tests pass a fake provider so they never hit the network. */ readonly tunnelProvider?: TunnelProviderDef; /** Tools the model may call autonomously. `['*']` allows every registered tool. */ readonly allowedTools?: ReadonlyArray; /** Debounce window for streaming `chat.update` edits (ms). Default 1000. */ readonly editFrameMs?: number; /** Bind host for the local ingest server. Default 127.0.0.1. */ readonly host?: string; readonly logger?: SlackChannelLogger; /** Injectable Slack client factory (tests). */ readonly makeClient?: (token: string) => SlackClient; } export interface SlackStartOpts extends ChannelStartOptsBase { readonly session: Session; /** * If true, arm a TOFU pairing window: the first verified inbound event from a * team/channel is captured and (via `onPairConfirm`) persisted to the vault. */ readonly pair?: boolean; /** Override allowedTools at start (the CLI forwards channel config / flags). */ readonly allowedTools?: ReadonlyArray; } /** Fires when a verified event lands during a pairing window. */ export interface PairCandidate { readonly teamId: string; readonly channelId: string; } export declare class SlackChannel implements Channel { readonly name = "slack"; /** * Installed on the session by the CLI dispatcher. Replaced in `start()` once * we can expand a `['*']` allow-list against the live tool registry; until * then it denies everything (safe default before start). */ permissionResolver: PermissionResolver; private readonly opts; private session; private client; private ingest; private tunnel; private handle; private resolveRunning; private botUserId; private authorization; private model; private readonly turns; private currentChannelId; private currentThreadTs; private lastChannelId; private lastThreadTs; private logUnsub; private readonly tofu; private publicUrl; constructor(opts: SlackChannelOptions); /** The public Request URL to paste into Slack's Event Subscriptions. */ get requestUrl(): string | null; /** Subscribe to pairing candidates (the setup/pair flows use this). */ onPairCandidate(listener: (c: PairCandidate) => void): () => void; /** Persist a team/channel as authorized (called by the pair flow on confirm). */ confirmPairing(candidate: PairCandidate): Promise; start(startOpts: SlackStartOpts): Promise; private openTunnel; /** * TOFU pairing hook: while a pairing window is armed, the first verified event * captures the team/channel and notifies listeners. It's "consumed" (no turn) * so the very first message just establishes trust. Returns true when consumed. */ private handlePairingCandidate; /** * Fire-and-forget a turn for an authorized event. Single-flight (v1): if a * turn is already running we drop the new event with a thread reply rather * than corrupting the per-turn state. The ingest handler has already ACKed. */ private dispatchTurn; private runTurn; /** * Post the assistant's prose for a turn this channel did not initiate. Gated by * `!busy` (our own turns stream via the frame pump) and by having served a * thread at least once. Skipped for our own turnIds (robust to async ordering / * replay, invariant #8). */ private mirrorForeignTurn; } //# sourceMappingURL=channel.d.ts.map