Slack surface, phase 1, is merged: mention an agent, it replies in the thread
Release notes 2026-09-15 — the Slack chat surface (vocion-core#251) is on main and deployed: app_mention and DM in, agent reply out, channels bound to agents, no Approve/Reject buttons in Slack yet. Updates our earlier post, which described it as in review.
Shipped: vocion-core#251, merged to main; deployed to the Metacto instance on 2026-09-15. Part of the 2026-09-15 release notes. This supersedes the status line in A chat surface is a trust boundary, which described the same slice as a draft PR.
The outcome
A team that already works in Slack should be able to ask an agent a question where the work is, and get the answer in the thread, without the agent gaining any new power to act. Phase 1 is exactly that and nothing more: the app manifest in the guide requests message and mention scopes only, no interactive components.
What it is
packages/core/src/libs/surfaces/— aChatSurfaceAdaptercontract and a registry, mirroring how connectors are registered. Slack is the only implementation; a second surface is a second file.- The Slack adapter — v0 HMAC signature verification with a five-minute replay window and a constant-time compare;
url_verificationhandled; bot messages and edits ignored; replies posted withchat.postMessageover plainfetch. chat_channel_binding(migration0082) — a Slack event names its workspace (team_id) and channel (event.channel) but knows nothing of Vocion's orgs or agents, so the binding — channel (or"*"for DMs) plus team → org + agent — is how the server finds both (packages/core/src/libs/surfaces/slack.ts,parseInbound).channelId: "*"with ateamIdis the DM catch-all. CRUD at/api/v1/chat-bindings.ChatSurfaceService.handleInbound— bind → budget preflight → one conversation per thread per sender →runAgentDeepwith history → reply. The conversation is the sameconversationrow the dashboard uses.POST /api/webhooks/slack— verify before parse; unverified is a 401; acknowledge fast, run the turn asynchronously; Slack retries are dropped so one mention is one turn.
Ships dark: SLACK_SIGNING_SECRET, SLACK_BOT_TOKEN, VOCION_SLACK_EVENTS=1. Twelve tests. Setup guide: docs/guides/slack.md.
What phase 1 deliberately does not do
No Approve or Reject in Slack. An agent's proposals still go to the review queue and the Needs-you inbox; Slack is a place to ask, not a place to authorise. That was decision 025's Option 1, and the reasoning in the earlier post still holds: a chat surface is a trust boundary, and the authorisation model for a button in someone else's client is a separate decision.
Also merged today
vocion-core#253 (merged as c46580ee) adds the bot introducing itself on member_joined_channel and personas that belong to agents (an agent_persona table, migration 0090). It needs a Slack manifest change (subscribe member_joined_channel, add channels:read/groups:read) and an app reinstall to do anything live.
For operators
Register a Slack app with the manifest in the guide, set the three variables, bind a channel to an agent, mention the agent. If the signature does not verify you get a 401 and nothing runs.
Where this lives in the repo
- Adapter and registry:
libs/surfaces/(slack.ts,registry.ts, tests) - Binding table:
packages/core/migrations/0082_chat_channel_binding.sqlandapp/api/v1/chat-bindings/route.ts - Inbound handling:
services/ChatSurfaceService.ts(+ test) - Webhook:
app/api/webhooks/slack/route.ts - Guide:
docs/guides/slack.md - PR: #251; #253 (merged
c46580ee)