Skip to content

Slack adapter

Socket Mode events, thread routing, Block Kit, and response lifecycle for the Slack adapter.

FilePurpose
src/adapters/slack/bot.tsSlack bot core: Socket Mode events, slash commands, Block Kit actions, file download, message sending.
src/adapters/slack/blocks.tsMarkdown → native Slack blocks, and <@userName><@U…> mention resolution.
src/adapters/slack/context.tsCreates the Slack ConversationResponder; handles reply modes, working state, and long messages.
src/adapters/slack/session.tsSlack channel/thread session key rules.
src/adapters/slack/response-lifecycle.tsSlack response lifecycle and streaming updates.
src/adapters/slack/tool-pack.tsThe Slack tool pack injected into the runtime.
src/adapters/slack/tools/*Slack-specific tools such as attachment and Block Kit support.

The Slack adapter mainly handles:

  • app_mention
  • message
  • slash commands: /pi-login, /pi-session, /pi-model, /pi-sandbox, /pi-new, /pi-admin, /pi-extensions, /pi-auto-reply — registration and routing both derive from src/commands/manifest.ts, though the Slack App manifest itself still has to be updated by hand
  • Block Kit actions
  • assistant thread / status APIs

DMs trigger mikan directly. Shared-channel messages require a mention, interaction, or matching auto-reply policy. An ordinary unmentioned human reply in a channel thread is logged but does not trigger a run; thread session isolation does not bypass the trigger policy.

Slack has explicit channel and thread models, so session keys are separated accordingly:

Slack scenariosessionKey
Channel top-level messagechannelId
Thread replychannelId:threadTs
Event anchor runchannelId:anchorTs

This lets channel conversations and thread conversations keep separate session contexts.

The agent writes standard Markdown/GFM (the platform-neutral response source). The adapter renders it through Slack’s native markdown block, so Slack itself translates the Markdown — bold, italic, strikethrough, inline and fenced code, links, nested lists, and blockquotes — into rich text. Legacy Slack-style <url|label> links are converted to [label](url) before rendering. Markdown pipe tables render as native Slack table blocks.

The Slack adapter also supports:

  • top-level or thread reply mode
  • working / assistant status
  • Slack’s native streaming API (chat.startStream / appendStream / stopStream) as well as edit-based progress updates
  • Block Kit rendering for headings, paragraphs, lists, code fences, and tables
  • file uploads

Block Kit output follows Slack limits: prose is split into markdown blocks of at most 12,000 characters at paragraph boundaries, table cells are truncated around 2,000 characters, and a message is capped at 50 blocks. Use file output for very large structured results.

The response source is platform-neutral, so the model writes <@userName> using the names from the prompt’s Users table. The adapter converts those to Slack’s native <@U…> form on every outgoing path — new messages, edits, and stream deltas alike — because Slack only links and notifies on the raw user id. Lookup covers userName and displayName case-insensitively, a display name never shadows someone else’s userName, already-native ids pass through, and an unknown name is left verbatim rather than guessed at. A mention split across two stream deltas stays unresolved in that delta and is resolved by the final canonical render.

Slack file attachments are downloaded into the conversation office’s attachments/ directory as <timestamp>_<sanitized-name>, then passed to the runtime as shared mikan attachment metadata with an office-relative path. This is the same shared helper every adapter uses; the Slack adapter contributes only the download call.

stop / /stop first stops the current thread session. If used at top level in a channel, the adapter uses the current running sessions to decide whether it can safely stop the matching session.