Slack agents
How an agent behaves in Slack — when it answers, who it thinks is talking, how it handles bursts of messages, and how you stop it.
Connect a Slack workspace to an agent and Keiki runs it in every channel and DM the app is invited to. This page is the behaviour contract: what the agent answers, what it stays quiet about, what it knows about the person talking, and how you stop it. None of it needs code, and all of it is the same whether the agent's tools come from the SDK, a capability, a custom tool, or an MCP server.
When the agent answers
Slack sends the app every event in a channel it belongs to. Keiki classifies each one before the agent ever sees it:
| Event | What happens |
|---|---|
| A direct message to the app | Always answered. |
| A message that mentions the app | Answered; the agent joins that thread. |
| A reply in a thread the agent is already in | Delivered to the agent, which decides whether to speak (below). |
| An edit that adds a mention the agent had not seen | Treated as a new mention. |
| A file shared without a mention, right after the agent asked for one | Delivered into the thread where it asked. |
| Anything else — channel chatter, joins, reactions | Ignored. It never reaches the model. |
Channel membership is the read gate: the agent only sees channels the app was invited to. Authors from other workspaces in a Slack Connect channel are treated like everyone else, so a partner or a customer in a shared channel can talk to the agent. Restricting who may act through the agent is a separate, explicit policy — see Identity and permissions — and a person the policy turns down is told so in the thread, never silently ignored.
Staying quiet
In a thread the agent has joined, people keep talking to each other. The agent is told, on every message, whether it was mentioned, whether someone else was, and whether it is a DM, and its instructions say when to answer:
- Addressed to it (mention, DM, a question that follows up on its last answer): answer.
- Two people talking to each other, or someone tagging a colleague: say nothing.
- Unsure: stay quiet. A missed message costs a re-ask; an interruption costs trust.
An empty answer delivers nothing — no "…" message, no reaction — so a quiet turn is invisible. The turn is still recorded in the trace, marked silent, so you can see what the agent chose not to say.
Who is talking
Each message reaches the agent with a structured speaker: the Slack display name, the user id, and — when identity is configured — the person's role and permissions in your system. The agent addresses people by name, knows when the speaker changes mid thread, and can tell that the person approving an action is not the one who asked for it.
Nothing about a speaker is guessed. Without an identity configuration the agent knows the name and Slack id and is told that it does not know who this is beyond that.
Several messages at once
People type in bursts. Keiki's steering gathers messages that land close together into one turn, and decides what to do with a message that arrives while a turn is already running:
midTurnStrategy | A message lands mid-turn |
|---|---|
queue (default) | It waits, and starts the next turn as soon as this one ends — the agent sees everything that was said meanwhile. |
interrupt | The running turn is asked to stop, and the queue drains into one fresh turn with all of it. |
The queue is per thread (per DM for direct messages), so two threads never block each other, and an operator's follow-up never runs in parallel with the turn it is correcting. Steering is on by default for Slack; tune it in the agent's config:
steering: { debounceWindow: '2 seconds', midTurnStrategy: 'queue', maxBatch: 5 }Stopping the agent
Any message to the agent that is only an instruction to stop — stop, @agent cancel that,
please kill the run — ends what the thread is doing: the running turn is aborted, queued
messages are discarded, and the agent reacts to your message to confirm. A message that
merely starts with "stop" (stop suggesting the annual plan and…) is a normal message.
While a turn runs, the agent's status message in the thread carries a Stop button that does the same thing, for people who would rather click. Stopping never approves or declines a pending action; those stay where they are until someone decides.
Date and time
Every turn starts with the current UTC time, injected by the platform, so "next Tuesday" and "end of the month" resolve correctly and the agent never has to ask. The model is told the user cannot see this line, so it does not read it back.
Approvals in Slack
When a tool needs a human's decision, the agent posts a card in the thread with what it is
about to do and Approve / Decline buttons. Who may press them, what the card shows,
and what happens after are covered in Governed actions. The same decision
can be made by replying in words (yes, go ahead), or from the dashboard when the Slack
buttons have expired.
Handoff threads
When an agent escalates a conversation to Slack, or your backend
hands it to a person, Keiki opens a thread in the
agent's Slack destination with Reply to visitor, Hand back to agent and
Close thread. Operators can also work the conversation by posting in that thread. Operators
are the people on the install's Slack operator list, plus your organization's owners and admins
when Slack shares their email with the app (the users:read.email scope). Without that scope,
only the listed operators count.
| You post in the thread | What happens |
|---|---|
| A plain message | It goes to the customer on their channel, and the conversation is taken over in the same step. The agent stops any turn in progress and stays quiet while you hold the conversation. ✅ means it was delivered. ⚠️ comes with the reason it wasn't. |
A message that starts with // | It stays in Slack. Nothing is sent, nothing is taken over, and the agent carries on. |
| A message that mentions a teammate | Same as //: it stays internal. |
@<app> <note> | A note to the agent, not to the customer. If you hold the conversation, it is handed back first. The agent then answers the customer in its own words, and the thread quotes what it sent. If the agent sends nothing, or its answer isn't delivered, the thread says so. If a turn is already running, you get ⏳ instead: the agent reads the note when that turn ends, and the thread doesn't report what happens after that, so check the conversation. The dashboard transcript shows the note as "Teammate note from Slack: …". |
You can't send the customer a reply and leave the agent running, because sending a reply is what takes the conversation over. While you hold the conversation, the customer's new messages are mirrored into the thread and recorded in the transcript. When the conversation is handed back, the agent reads them. The hold lapses 30 minutes after you take over or last reply. Hand back to agent, or a note, returns the conversation sooner. Replying — in the thread, or with the Reply to visitor form — is how you take the conversation over; there is no separate take-over step.
- Someone who isn't an operator gets a private message saying nothing was sent.
- Attachments are not forwarded to the customer, and a message with only an attachment doesn't take over the conversation. Editing a reply after it was sent doesn't send it again.
- If another operator, or a takeover from the dashboard, already holds the conversation, your reply or note is refused, and you're told who has it.
- A conversation that reached the agent over Slack, Teams, email or the API can't be answered from the thread. The thread tells you where to reply instead.
Setting up Slack
Connect Slack from the agent's Channels page, or from your backend with a
partner deployment (type: "slack" with the bot token
and signing secret). The app manifest Keiki generates asks for every scope the behaviours
above need; a workspace that installed before a scope was added keeps working, and the
Channels page names what is missing. Add users:read.email yourself if your
identity webhook wants the speaker's email.