Problem
Slack transport currently only starts listeners for app_mention and DM message events in src/operator_ai/transport/slack.py, so there is no way to trigger an agent from a reacji on an existing message or thread.
That blocks a useful Slack-native workflow: react to a message in #triage / #dev / a thread and let an agent pick it up in-place, without mentioning the bot or rewriting the request.
Proposed approach
Add an opt-in reaction trigger config for Slack transports, scoped per agent.
Example config shape for v1:
agents:
reviewer:
transport:
type: slack
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
reaction_triggers:
- emoji: robot_face
channels: ["#triage", "#dev"]
- emoji: eyes
roles: ["admin"]
If reaction_triggers is absent, ignore reaction events entirely.
Implementation sketch
- Extend
TransportConfig to accept a Slack reaction trigger list.
- In
SlackTransport.start(), add a reaction_added listener.
- Ignore non-message items (
file, file_comment) and any reactions that do not match configured rules.
- Fetch the reacted-to message by channel + ts so we can recover the actual message text, author, attachments, and
thread_ts.
- Synthesize an
IncomingMessage and hand it to the existing dispatcher so auth, memory, thread context, and reply posting all work unchanged.
- Use a synthetic message/event ID for dedupe, e.g.
reaction:<event_ts>, while setting root_message_id to the target thread root (thread_ts or ts).
Recommended v1 filters
emoji (required)
channels allowlist by name or ID
users and/or roles allowlist so reaction triggers still respect Operators auth model
- Ignore bot-authored reaction events
Notes
- Slacks
reaction_added event requires the reactions:read scope.
- The event payload only contains a lightweight item reference for messages, so an extra Web API read is required to recover the full message/thread context.
- This adds at least one extra Slack read per trigger, so we should keep the rule matching cheap and avoid unnecessary follow-up calls.
- Docs/setup should mention the added Slack scope and reinstall requirement for existing apps.
Acceptance criteria
- A Slack agent can be triggered by a configured emoji reaction without an
@mention.
- Reactions are ignored unless they match configured rules.
- The agent replies in the reacted messages thread, not an unrelated/new conversation.
- The reacting user is treated as the requesting user for auth and memory scoping.
- Non-message reaction items are ignored cleanly.
- Tests cover rule matching, synthetic message construction, and dedupe/thread behavior.
Problem
Slack transport currently only starts listeners for
app_mentionand DMmessageevents insrc/operator_ai/transport/slack.py, so there is no way to trigger an agent from a reacji on an existing message or thread.That blocks a useful Slack-native workflow: react to a message in
#triage/#dev/ a thread and let an agent pick it up in-place, without mentioning the bot or rewriting the request.Proposed approach
Add an opt-in reaction trigger config for Slack transports, scoped per agent.
Example config shape for v1:
If
reaction_triggersis absent, ignore reaction events entirely.Implementation sketch
TransportConfigto accept a Slack reaction trigger list.SlackTransport.start(), add areaction_addedlistener.file,file_comment) and any reactions that do not match configured rules.thread_ts.IncomingMessageand hand it to the existing dispatcher so auth, memory, thread context, and reply posting all work unchanged.reaction:<event_ts>, while settingroot_message_idto the target thread root (thread_tsorts).Recommended v1 filters
emoji(required)channelsallowlist by name or IDusersand/orrolesallowlist so reaction triggers still respect Operators auth modelNotes
reaction_addedevent requires thereactions:readscope.Acceptance criteria
@mention.