Skip to content

feat(slack): per-agent bot routing seam — OSS half of ent#222 - #1758

Closed
dolho wants to merge 2 commits into
devfrom
feat/222-slack-per-agent-bot-routing-seam
Closed

feat(slack): per-agent bot routing seam — OSS half of ent#222#1758
dolho wants to merge 2 commits into
devfrom
feat/222-slack-per-agent-bot-routing-seam

Conversation

@dolho

@dolho dolho commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

The OSS companion to the enterprise per-agent Slack bots feature (trinity-enterprise#222, enterprise PR trinity-enterprise#232). Open-core split: OSS owns the seam, the private module owns the policy — same shape as the connector (#118), rooms (#1740), skill-runner (#1735).

What this adds

  • adapters/per_agent_bot.py — an inert-by-default hook registry (set_resolver / set_token_provider). With no resolver registered (OSS-only builds, or before the enterprise module loads) every path is a no-op and channel routing is byte-for-byte unchanged. Both hooks fail open — an error falls back to normal routing, so a bug here can never take Slack offline.
  • message_router._resolve_agent_and_token — consult the per-agent resolver first; a match wins over the channel binding and replies through the per-agent bot token. Falls through to channel routing when there's no binding / no token.
  • slack_adapter.parse_message — stamp the receiving bot identity (authorizations[0].user_id + api_app_id + team_id) into message.metadata, so the resolver can key on which bot got the event. Additive; inert for every non-Slack channel.

Tests

5 unit tests: inert-by-default, resolver reads recipient bot, non-Slack ignored, both hooks fail-open, token provider. Green.

Honest scope

  • The inert-by-default guarantee means this is safe to merge ahead of the enterprise side — it changes nothing until an enterprise resolver registers.
  • Needs live verification: the end-to-end path (a real Slack event routing to a per-agent bot) can't be proven by mocking — the authorizations recipient-bot extraction is the live point. Verified in isolation; end-to-end verification needs a live per-agent Slack app from the design partner.
  • Still to come in this OSS PR (or a follow-up): the enterprise-gated Vue channel panel (UI) to configure a per-agent bot. The backend seam is the keystone; the UI is additive.

Pairs with: trinity-enterprise#232 (module + transport manager), which registers the resolver + token provider into this seam.

🤖 Generated with Claude Code

dolho and others added 2 commits July 23, 2026 17:16
The edition-agnostic seam that lets the enterprise slack_per_agent_bots module
(trinity-enterprise#222) route an inbound Slack event to the agent whose
DEDICATED bot received it, and reply through that bot's own token — coexisting
with the workspace-level single bot.

- adapters/per_agent_bot.py — inert-by-default hook registry (set_resolver /
  set_token_provider). With no resolver registered (OSS-only, or before the
  enterprise module loads) every path is a no-op and channel routing is
  byte-for-byte unchanged. Both hooks FAIL OPEN — an error falls back to normal
  routing, so a bug can never take Slack offline.
- message_router._resolve_agent_and_token — consult the per-agent resolver
  first; a match wins over the channel binding and uses the per-agent bot token.
  Falls through to channel routing when no binding / no token.
- slack_adapter.parse_message — stamp the RECEIVING bot identity
  (authorizations[0].user_id + api_app_id + team_id) into message.metadata so
  the resolver can key on it. Additive; inert for every non-Slack channel.

Mirrors the connector seam (#118): OSS owns the seam, the enterprise module owns
the policy (it registers the resolver + token provider at startup).

5 unit tests: inert-by-default, resolver reads recipient bot, non-Slack ignored,
both hooks fail-open, token provider. The end-to-end path (real Slack event ->
per-agent routing) needs a live per-agent Slack app to verify — the recipient-bot
extraction from authorizations is the live-verification point.

Related to trinity-enterprise#222

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SlackAgentBotPanel: configure an agent's dedicated Slack bot from the Sharing tab,
mounted beside the existing workspace SlackChannelPanel.

- Entitlement-gated on `slack_per_agent_bots` via the enterprise store, so it is
  hidden entirely in OSS / unentitled builds — never a blank or broken section.
- Paste bot (xoxb-) + app-level (xapp-) tokens; on save the backend validates
  them against Slack auth.test and the panel then shows the RESOLVED identity
  (bot name, bot_user_id, team) rather than echoing anything secret. Tokens are
  write-only — a read never returns them.
- Enable/disable without re-entering credentials, replace tokens, and remove.
- Refusals surface the backend's NAMED code (wrong token type, bot already bound
  to another agent, Slack unreachable) instead of a generic failure, so the
  operator knows what to fix.

Verified the component compiles under Vite (HMR, no build errors); the flows
themselves still need a live per-agent Slack app to exercise end to end.

Related to trinity-enterprise#222
@dolho

dolho commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1763, which consolidates all three Slack partner follow-ups (ent#222 / ent#223 / ent#224) into a single OSS PR — they share a migration chain and ent#224 is gated by ent#223's consent flag, so reviewing them separately was artificial. Every commit from this branch is included there, plus the Alembic head fix. The enterprise half stays in trinity-enterprise#232 (repo boundary).

@dolho dolho closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant