Slack

The Omnigent Slack integration lets your team talk to agents directly from Slack. Mention the bot in a channel or DM it, and it starts an Omnigent session; replies stream into the thread live, and tool approvals show up as interactive Approve / Deny cards you can answer in place.

The core model is simple: one Slack thread ↔ one Omnigent session. Replying in a thread continues the same session; a new mention starts a new one.

The bot connects to a single Omnigent server chosen by the operator — users never enter a server URL. Each user still authenticates as their own Omnigent identity, so sessions run with per-user permissions and no shared credential ever passes through Slack.

What you can do

Setting it up (operator)

The bot is a Socket Mode Slack app — it opens an outbound WebSocket to Slack, so it needs no public HTTP endpoint and no request URLs.

1. Create the Slack app

Quick path — create the app from the manifest. At api.slack.com/apps → Create New App → From an app manifest, pick the workspace, and paste integrations/slack/deploy/slack-app-manifest.yaml from the repo. That configures Socket Mode, Interactivity, every scope and event the bot needs, and the /omnigent command in one step. For Databricks web-auth, uncomment users:read and users:read.email in the manifest before pasting it. A manifest cannot mint tokens, so finish with the two steps that a manifest can't do — generate a connections:write app-level token (Basic Information → App-Level Tokens) and Install to Workspace for the bot token — then skip to step 2 below only if you need to double-check the scopes.

Doing it by hand instead:

  1. Create a Slack app with Socket Mode and Interactivity enabled (Socket Mode also delivers the button/modal payloads — no request URL needed).
  2. Add the OAuth scopes and event subscriptions listed below.
  3. Add a slash command /omnigent (Features → Slash Commands). In Socket Mode the request URL is ignored, so any placeholder works.
  4. Install the app into your workspace.

2. Grant scopes

These scopes and events are mirrored in integrations/slack/deploy/slack-app-manifest.yaml — if you created the app from the manifest they're already set, and if you change one, change both together.

Bot token scopes (OAuth & Permissions → Bot Token Scopes) — all required:

ScopeWhy
app_mentions:readReceive @mentions — how the bot joins a channel thread.
chat:writePost, delete, and stream replies (including ephemeral nudges).
im:writeOpen a DM to send the setup button and logout confirmation.
im:historyRead DMs — DMs are a first-class entry point.
commandsRegister and receive /omnigent.
team:readRead the workspace name to label the login request.

Add two more bot scopes only for Databricks web-auth mode, where the bot signs the user's email into the enrollment link and matches it against the OAuth-authenticated identity; omit them in accounts / oidc mode:

ScopeWhy
users:readRead the user's profile to look up their email.
users:read.emailRead the user's email address for identity binding.

Channel history scopes — add only for the channel types where the bot will run: channels:history (public), groups:history (private), mpim:history (group DMs). If you only use DMs and channel @mentions, you can omit all three.

App-level token scope: connections:write (required for the Socket Mode connection).

Event subscriptions (Subscribe to bot events): app_mention, message.im, and message.channels / message.groups / message.mpim for the channel types you enabled above.

3. Install and configure the bot

The bot ships as the slack extra of Omnigent (the separate omnigent-slack package), installed in the same environment as the omni CLI so omni integration slack can find it:

uv tool install "omnigent[slack]"    # or, from a source checkout: uv sync --extra slack

Configuration comes from real environment variables — the bot does not read a .env file itself (matching omni server). Export the variables directly, or launch the bot under a tool that injects a .env (e.g. uv run --env-file .env omni integration slack). .env.example documents the full set to copy from:

VariableRequiredPurpose
OMNIGENT_SLACK_BOT_TOKENYesThe bot token (xoxb-…).
OMNIGENT_SLACK_APP_TOKENYesThe app-level token (xapp-…) for Socket Mode.
OMNIGENT_SERVER_URLYesThe one Omnigent server the bot talks to.
OMNIGENT_DEVICE_CLIENT_SECRETNoSet to the same value as the server's OMNIGENT_DEVICE_CLIENT_SECRET so only this bot can drive the device-grant login. Accounts mode only.
OMNIGENT_SLACK_TOKEN_ENCRYPTION_KEYNoA Fernet key that encrypts users' delegated tokens at rest. Without it, tokens live in memory only and users re-authenticate after a restart.
OMNIGENT_SLACK_DATABASE_PATHNoWhere the bot's SQLite store lives. Defaults to $OMNIGENT_DATA_DIR/omnigent_slack.sqlite3 (or ~/.omnigent/omnigent_slack.sqlite3).
LOG_LEVELNoSet to DEBUG when diagnosing why Slack events aren't producing replies.

Generate an encryption key with:

uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

If the Omnigent server runs as a Databricks App (header/proxy auth), the bot uses its own Databricks web-auth flow instead of the device/OIDC login — see Databricks Apps web-auth below for the extra OMNIGENT_SLACK_DATABRICKS_* variables it needs.

4. Run it

The bot runs as a background daemon managed by the omni CLI:

omni integration slack               # run in the foreground (Ctrl-C to stop)
omni integration slack --background  # run in the background (detached)
omni integration slack status        # is the background bot running?
omni integration slack stop          # stop the background bot
omni integration slack logs          # print the log path
omni integration slack logs -f       # follow the log

Running --background while the bot is already up is a no-op that reports the existing process. A foreground run refuses to start if a background daemon already holds the socket (stop it first).

Per-user setup

The first time someone interacts with the bot without having configured it, the bot DMs them a Set up Omnigent button. The button opens a modal that:

  1. Validates connectivity to the operator's server, and — if the server has authentication enabled — walks the user through login (see below).
  2. Lets them pick an agent and host (both required, populated from the server) and a workspace path — the absolute directory on the host where each session's runner starts. It defaults to the host's home directory.

The choice is saved per (Slack workspace, user). Run /omnigent any time to change agent, host, or workspace, and /omnigent logout to revoke your token and clear all saved settings.

Each new session launches a fresh runner on the chosen host, rooted at the configured workspace.

Authentication

For servers with authentication enabled, each Slack user logs in with their own Omnigent identity inside the /omnigent modal. The bot auto-detects the server's auth mode and picks the matching flow:

Tokens are stored encrypted at rest when OMNIGENT_SLACK_TOKEN_ENCRYPTION_KEY is set; otherwise they're kept in memory only and lost on restart (users simply re-authenticate).

Databricks Apps web-auth

When the Omnigent server is a Databricks App, its proxy authenticates every request and injects the user's identity — a Socket-Mode event carries no such proxy-authenticated request, so the device/OIDC flows can't be driven. Instead the bot runs a custom U2M OAuth app (authorization code + PKCE, offline_access) through an enrollment page it serves as its own Databricks App:

  1. On /omnigent, the bot looks up the user's email, generates a PKCE verifier and single-use nonce, and posts a Sign in with Databricks link to the workspace /oidc/v1/authorize.
  2. The user signs in; Databricks redirects back to the bot's /auth/callback with a single-use, PKCE-bound code, which the bot exchanges for an access + refresh token pair.
  3. Identity binding — the callback requires the OAuth-authenticated email to equal the Slack email signed into the link, so a link bound to one user can't store another's token. A mismatch is refused.
  4. Confirm before storing — the callback shows a consent page naming the exact identities being linked; the token is persisted only when the user submits the confirming button, then the setup modal advances automatically.

The bot presents the access token to the server; the proxy validates it and injects the real identity header, so no server-side change is needed. It refreshes silently, so the user signs in once, not hourly.

Enable it with OMNIGENT_SLACK_SERVER_AUTH=databricks plus these variables:

VariableRequiredPurpose
OMNIGENT_SLACK_DATABRICKS_CLIENT_IDYesThe custom U2M OAuth app's client id (public).
OMNIGENT_SLACK_DATABRICKS_CLIENT_SECRETYesThe custom U2M OAuth app's client secret.
OMNIGENT_SLACK_DATABRICKS_STATE_SECRETYesHMAC key signing the enrollment state (≥32 chars of entropy, e.g. openssl rand -hex 32). Its own secret, separate from the OAuth client secret.
OMNIGENT_SLACK_DATABRICKS_APP_URLYes¹Public base URL of the bot's own Databricks App — the enrollment link base and OAuth redirect base. Not platform-injected, and the URL only exists after the first deploy, so it's wired in on a second deploy pass.
OMNIGENT_SLACK_DATABRICKS_SCOPESNoRequested scopes (space-separated). Defaults to all-apis; must be a superset of the server app's scopes. openid + offline_access are always added.

¹ Not enforced at startup — the bot boots without it, but no enrollment link is issued until it's set. The enrollment web server always binds DATABRICKS_APP_PORT (8000 by convention, platform-injected); there's no override knob.

Register the custom OAuth app's redirect URI as <app-url>/auth/callback. To deploy the bot as its own Databricks App, see integrations/slack/deploy/databricks/README.md in the repo.

Approvals and questions

When the agent needs you — a tool-call approval or a multiple-choice question — it appears in the thread:

Cards are visible to the whole channel, but only the thread owner can act on them. Answering from the web UI works too: the card in Slack finalizes automatically once the request is resolved anywhere.

Sharing a channel with your team

Slack channels are multi-user, so the bot enforces a per-thread owner model: a thread belongs to whoever started it. If someone else replies in your thread, they get a private note pointing them to start their own thread — they are never added to your session.

If you message a thread while the bot is still replying, it privately asks you to wait (or continue in the web UI). A message to an idle thread just continues the conversation.

Supported and not supported

Supported

Not supported

Troubleshooting