Back to Copilotkit

slack

showcase/shell-docs/src/content/reference/bot/slack/index.mdx

1.60.16.0 KB
Original Source

Overview

slack(opts) returns a SlackAdapter — the Slack implementation of the engine's PlatformAdapter boundary. It handles ingress via Bolt (Socket Mode by default), renders the JSX vocabulary to Block Kit within Slack's per-element budgets, streams agent replies via throttled chat.update, and decodes interactions to opaque action ids. @copilotkit/bot-slack is the only package in the stack that talks to Slack.

For app creation, tokens, and first run, see the Slack quickstart.

Signature

ts
import { slack } from "@copilotkit/bot-slack";

function slack(opts: SlackAdapterOptions): SlackAdapter;

Parameters

<PropertyReference name="opts" type="SlackAdapterOptions" required> Adapter configuration. <PropertyReference name="botToken" type="string" required> Slack bot token (`xoxb-…`), used for the Web API (posting, updating, history). </PropertyReference> <PropertyReference name="appToken" type="string" required> Slack app-level token (`xapp-…`) with `connections:write`, used for Socket Mode. </PropertyReference> <PropertyReference name="socketMode" type="boolean" default="true"> Run over Socket Mode — an outbound WebSocket, no public URL needed. Set `false` for HTTP mode, which requires `signingSecret`. </PropertyReference> <PropertyReference name="signingSecret" type="string"> Request signing secret; required when `socketMode` is `false`. </PropertyReference> <PropertyReference name="port" type="number" default="0"> HTTP port for non-socket mode; `0` lets the OS assign one. Ignored under Socket Mode. </PropertyReference> <PropertyReference name="logLevel" type="LogLevel" default="LogLevel.INFO"> Bolt log level. </PropertyReference> <PropertyReference name="interruptEventNames" type="ReadonlySet<string>"> Custom-event names the run renderer treats as agent interrupts. </PropertyReference> <PropertyReference name="showToolStatus" type="boolean" default="true"> Surface `:wrench:` / `:white_check_mark:` tool-status rows in the streamed reply while the agent calls tools. </PropertyReference> </PropertyReference>

Return Value

A SlackAdapter to pass into createBot({ adapters }). It advertises platform: "slack", ackDeadlineMs: 3000, and:

ts
capabilities: {
  supportsStreaming: true,
  supportsModals: false,
  supportsTyping: false,
  supportsReactions: false,
  maxBlocksPerMessage: 50,
}

The capability-gated Thread methods are all backed: getMessages() via conversations.replies, lookupUser(query) via directory search, and postFile(...) via files.uploadV2. Inbound file uploads are downloaded and delivered to the agent as multimodal content parts.

Usage

ts
import { createBot } from "@copilotkit/bot";
import { slack, defaultSlackTools, defaultSlackContext } from "@copilotkit/bot-slack";

const bot = createBot({
  adapters: [
    slack({
      botToken: process.env.SLACK_BOT_TOKEN!, // xoxb-…
      appToken: process.env.SLACK_APP_TOKEN!, // xapp-… (Socket Mode)
    }),
  ],
  agent: (threadId) => makeAgent(threadId),
  tools: [...defaultSlackTools, ...appTools],
  context: [...defaultSlackContext, ...appContext],
});

Behavior

Ingress

The Slack listener pre-filters events to the turns the bot should answer — @-mentions, replies in threads it owns, and DMs — so a single onMention handler usually covers everything. Conversation history is rebuilt from Slack (conversations.replies / conversations.history) on every turn: Slack is the source of truth, so bot restarts don't lose conversations.

Streaming

thread.runAgent() and thread.stream(...) post a placeholder and edit it in place as text arrives: chat.update calls are queued per message with a minimum gap between flushes (default 800ms); long replies roll over into follow-up messages at a soft 3500-character limit (under Slack's ~4000), breaking at the last newline or space and keeping fenced code blocks whole; dangling markdown (an unclosed fence or bold span) is auto-closed on each flush so the in-flight message always renders. Text is translated per chunk by markdownToMrkdwn.

Interactions (ack-first)

Every block_actions click is acked within Slack's 3-second deadline (ackDeadlineMs: 3000), then handled asynchronously. decodeInteraction extracts the opaque minted id (ck:…), the control's value, and the message ref — those are the only things that ride in the Slack payload; handler code, other props, and bind() args stay server-side. Unrelated clicks decode to events the bot harmlessly ignores; clicks on actions lost to a restart (with the default in-memory ActionStore) are acked but ignored.

Initialization

The underlying Bolt App is constructed with deferInitialization — construction is side-effect-free, and bot.start() owns initialization (Bolt init(), then an awaited auth.test to resolve the bot's own user id), so auth and config errors surface to the caller instead of firing in the background.

What's NOT in v1

  • Modals / true batched form submit
  • OAuth / multi-workspace install (single bot token only)
  • Durable (Redis/DB) ActionStore — in-memory only; actions expire on restart
  • Proactive posting (the bot replies only to turns it's part of)
  • Reactions