Problem
Slack channel behavior has three related issues (parallel to #349 for Telegram):
1. User Identity Bug
Agent cannot see who sent a message in channels. The adapter passes sender_id (Slack user ID like U123ABC) but not the user's display name or real name. The agent sees:
What do you think about this approach?
Instead of:
[Channel: #engineering]
[From: John Smith (@johndoe)]
What do you think about this approach?
Root cause: slack_adapter._parse_mention() and _parse_thread_reply() don't fetch user info or include channel name in the context.
2. No Observation Mode
Slack adapter only handles:
app_mention — @mentions in channels
message.im — DMs
- Thread replies where bot already responded
There's no way for an agent to observe channel conversations without being explicitly @mentioned. This limits agents that should monitor discussions and contribute when relevant.
Slack API constraint: Observing all channel messages requires the channels:history and/or groups:history OAuth scopes, plus subscribing to message events (not just app_mention).
3. No Proactive Channel Messaging
Agent can wake up on schedule but cannot send messages to Slack channels. Issue #321 covers proactive DMs to users; this is about channel broadcasts.
Current state:
- Channel bindings are stored in
slack_channel_agents
- Agent can send responses within the request/response flow
- No MCP tool for outbound channel messages outside that flow
Proposed Solution
Phase 1: Fix User Identity
Fetch user info via Slack API and include in context:
async def _enrich_user_context(self, event: dict, team_id: str) -> dict:
"""Fetch user display name and channel name for context."""
bot_token = db.get_slack_workspace_bot_token(team_id)
user_id = event.get("user")
channel_id = event.get("channel")
context = {}
if bot_token and user_id:
user_info = await slack_service.get_user_info(bot_token, user_id)
if user_info:
context["display_name"] = user_info.get("real_name") or user_info.get("name")
context["username"] = user_info.get("name")
if bot_token and channel_id:
channel_info = await slack_service.get_channel_info(bot_token, channel_id)
if channel_info:
context["channel_name"] = channel_info.get("name")
return context
Update message_router.py to format this context (shared with Telegram, see #349).
Phase 2: Observation Mode
Add ability for agents to observe channel conversations:
Option A: Event subscription approach
- Add
channels:history / groups:history scopes to OAuth
- Subscribe to
message events for bound channels
- New
observation_mode flag on slack_channel_agents
- When enabled, all messages (not just @mentions) route to agent
- Agent can respond with
[NO_REPLY] to stay silent
Option B: Periodic polling (simpler)
- Scheduled job fetches recent messages from bound channels
- Agent receives batch context periodically
- Can choose to respond or not
Recommendation: Option A aligns with Telegram #349 and provides real-time awareness.
Database changes:
- Add
observation_mode boolean to slack_channel_agents
- Add
channel_messages table for history persistence (or reuse session pattern)
Phase 3: Proactive Channel Messaging
Share MCP tools with Telegram (#349):
// Discover channels agent is bound to
list_channel_groups(channel_type?: "telegram" | "slack")
→ Array<{channel_type, channel_id, channel_name, ...}>
// Send to a channel
send_group_message(
channel_type: "telegram" | "slack",
channel_id: string,
message: string,
thread_ts?: string // Slack: optionally post in existing thread
) → {sent: boolean, message_id?: string}
Slack-specific considerations:
- Use
chat.postMessage with the workspace's bot token
- Respect channel-level rate limits
- Consider
unfurl_links: false for cleaner automated posts
Files to Modify
| File |
Changes |
src/backend/adapters/slack_adapter.py |
Add _enrich_user_context(), include in parsed messages |
src/backend/adapters/message_router.py |
Shared group context formatting (with #349) |
src/backend/services/slack_service.py |
Add get_user_info(), get_channel_info() if missing |
src/backend/db/slack_channels.py |
Add observation_mode column, message history |
src/backend/adapters/transports/slack_socket.py |
Subscribe to message events when observation enabled |
src/mcp-server/src/tools/channels.ts |
Shared tools (with #349) |
OAuth Scope Changes
Current scopes (likely):
chat:write, chat:write.customize
users:read, users:read.email
app_mentions:read
Additional scopes for observation mode:
channels:history — read public channel messages
groups:history — read private channel messages
channels:read — get channel info
Acceptance Criteria
Related
Problem
Slack channel behavior has three related issues (parallel to #349 for Telegram):
1. User Identity Bug
Agent cannot see who sent a message in channels. The adapter passes
sender_id(Slack user ID likeU123ABC) but not the user's display name or real name. The agent sees:Instead of:
Root cause:
slack_adapter._parse_mention()and_parse_thread_reply()don't fetch user info or include channel name in the context.2. No Observation Mode
Slack adapter only handles:
app_mention— @mentions in channelsmessage.im— DMsThere's no way for an agent to observe channel conversations without being explicitly @mentioned. This limits agents that should monitor discussions and contribute when relevant.
Slack API constraint: Observing all channel messages requires the
channels:historyand/orgroups:historyOAuth scopes, plus subscribing tomessageevents (not justapp_mention).3. No Proactive Channel Messaging
Agent can wake up on schedule but cannot send messages to Slack channels. Issue #321 covers proactive DMs to users; this is about channel broadcasts.
Current state:
slack_channel_agentsProposed Solution
Phase 1: Fix User Identity
Fetch user info via Slack API and include in context:
Update
message_router.pyto format this context (shared with Telegram, see #349).Phase 2: Observation Mode
Add ability for agents to observe channel conversations:
Option A: Event subscription approach
channels:history/groups:historyscopes to OAuthmessageevents for bound channelsobservation_modeflag onslack_channel_agents[NO_REPLY]to stay silentOption B: Periodic polling (simpler)
Recommendation: Option A aligns with Telegram #349 and provides real-time awareness.
Database changes:
observation_modeboolean toslack_channel_agentschannel_messagestable for history persistence (or reuse session pattern)Phase 3: Proactive Channel Messaging
Share MCP tools with Telegram (#349):
Slack-specific considerations:
chat.postMessagewith the workspace's bot tokenunfurl_links: falsefor cleaner automated postsFiles to Modify
src/backend/adapters/slack_adapter.py_enrich_user_context(), include in parsed messagessrc/backend/adapters/message_router.pysrc/backend/services/slack_service.pyget_user_info(),get_channel_info()if missingsrc/backend/db/slack_channels.pyobservation_modecolumn, message historysrc/backend/adapters/transports/slack_socket.pymessageevents when observation enabledsrc/mcp-server/src/tools/channels.tsOAuth Scope Changes
Current scopes (likely):
chat:write,chat:write.customizeusers:read,users:read.emailapp_mentions:readAdditional scopes for observation mode:
channels:history— read public channel messagesgroups:history— read private channel messageschannels:read— get channel infoAcceptance Criteria
[Channel: #name] [From: Display Name (@username)]prefixobservation_modeflag enables seeing all channel messages[NO_REPLY]to skip respondinglist_channel_groupsreturns agent's Slack channelssend_group_messagedelivers to Slack channelsRelated