Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Discord adapter. Requires a Discord bot token.
| `allow_all_users` | bool \| omit | auto-detect | `true` = any user; `false` = only `allowed_users`. Omitted = inferred from list. |
| `allowed_users` | string[] | `[]` | User IDs to allow. Only checked when `allow_all_users` resolves to false. |
| `allow_bot_messages` | string | `"off"` | `"off"` — ignore all bot messages. `"mentions"` — only process bot messages that @mention this bot. `"all"` — process all bot messages (capped by `max_bot_turns`). |
| `trusted_bot_ids` | string[] | `[]` | When non-empty, only these bot IDs pass the bot gate. Empty = any bot (mode permitting). Ignored when `allow_bot_messages = "off"`. |
| `trusted_bot_ids` | string[] | `[]` | When non-empty, only these bot IDs pass the bot gate. Empty = any bot (mode permitting). **Admission override:** a trusted bot that @mentions this bot bypasses `allow_bot_messages` mode entirely (treated as human @mention, can pull bot into threads). |
| `allow_user_messages` | string | `"involved"` | `"involved"` — reply in threads bot has participated in without @mention; channel messages require @mention; DMs always process. `"mentions"` — always require @mention. `"multibot-mentions"` — like `"involved"`, but require @mention once another bot has posted in the thread. |
| `allow_dm` | bool | `false` | `true` = respond to Discord DMs; `false` = ignore DMs. `allowed_users` still applies in DMs. Each DM user consumes one session slot. |
| `max_bot_turns` | u32 | `100` | Max consecutive bot turns per thread before throttling (soft limit). Human message resets the counter. A compiled-in hard cap of 1000 consecutive bot messages is always enforced. |
Expand Down
16 changes: 11 additions & 5 deletions docs/discord.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ trusted_bot_ids = ["123456789012345678"] # only this bot's messages pass throug

Empty (default) = any bot can pass through (subject to the mode check).

**Admission override:** A trusted bot that explicitly @mentions this bot bypasses the `allow_bot_messages` mode entirely — the mention is treated the same as a human @mention. This allows trusted bots to pull this bot into threads even when `allow_bot_messages = "off"`. Messages from trusted bots *without* @mention still follow normal gating.

### `allowed_role_ids`

Role IDs that trigger the bot, same as a direct @mention. This enables users to invoke multiple bots simultaneously with a single role mention (e.g. `@AllBots review this`).
Expand Down Expand Up @@ -282,19 +284,23 @@ In a multi-bot setup, every bot enforces an **involvement gate** before processi

**Rule:** A bot must be **involved** (thread owner or has previously replied) before it will process any message in that thread.

**Key constraint:** Only a human @mention can pull a bot into a thread for the first time. A bot @mentioning another bot that is not yet involved will be **silently dropped**.
**Key constraint:** Only a human @mention — or a @mention from a bot in `trusted_bot_ids` — can pull a bot into a thread for the first time. A @mention from an untrusted bot will be **silently dropped**.

```
Bot A's thread (Bot B not yet involved):
Bot A's thread (Bot B not yet involved, Bot A NOT in Bot B's trusted_bot_ids):

Bot A: "@Bot_B please review this" → ❌ dropped (Bot B not involved)
Bot A: "@Bot_B please review this" → ❌ dropped (Bot B not involved, Bot A untrusted)
Human: "@Bot_B please review this" → ✅ Bot B replies, now involved
Bot A: "@Bot_B any updates?" → ✅ processed (Bot B is involved)

Bot A's thread (Bot B not yet involved, Bot A IS in Bot B's trusted_bot_ids):

Bot A: "@Bot_B please review this" → ✅ treated as human @mention, Bot B becomes involved
```

**Why:** This prevents bots from pulling other bots into arbitrary threads without human consent, protects session pool resources, and eliminates cross-thread chain reactions.
**Why:** This prevents untrusted bots from pulling other bots into arbitrary threads without human consent, protects session pool resources, and eliminates cross-thread chain reactions. Trusted bots are explicitly authorized by the admin.

**Workaround:** Pre-involve all needed bots at thread creation by @mentioning them (or using a shared role via `allowed_role_ids`).
**Workaround (without trusted_bot_ids):** Pre-involve all needed bots at thread creation by @mentioning them (or using a shared role via `allowed_role_ids`).

> 📖 Full design details: [docs/messaging.md — Involvement Gate](messaging.md#involvement-gate)

Expand Down
30 changes: 26 additions & 4 deletions docs/messaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ BotA in thread: here's my analysis
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `allow_bot_messages` | string | `"off"` | `"off"` — ignore bot messages. `"mentions"` — only process bot messages that @mention this bot. `"all"` — process all bot messages (capped by `max_bot_turns`). |
| `trusted_bot_ids` | string[] | `[]` | Whitelist of bot IDs. For Slack, entries may be Bot User IDs (`U...`) or Bot IDs (`B...`); `U...` matching requires `users:read` so OpenAB can call `bots.info`. Empty = any bot (mode permitting). Ignored when `allow_bot_messages = "off"`. |
| `trusted_bot_ids` | string[] | `[]` | Whitelist of bot IDs. For Slack, entries may be Bot User IDs (`U...`) or Bot IDs (`B...`); `U...` matching requires `users:read` so OpenAB can call `bots.info`. Empty = any bot (mode permitting). **Admission override:** a trusted bot that @mentions this bot bypasses `allow_bot_messages` mode entirely (treated as human @mention). |
| `max_bot_turns` | u32 | `20` | Max consecutive bot turns per thread before throttling. A human message resets the counter. |

> **Safety:** When `allow_bot_messages = "all"`, a separate hardcoded cap of 10 consecutive bot turns applies regardless of `max_bot_turns`.
Expand Down Expand Up @@ -267,7 +267,7 @@ All message routing in OpenAB is guarded by the **involvement gate** — a pre-d

### Design principle

**Humans are the gatekeepers.** A bot cannot participate in a thread until a human explicitly pulls it in via @mention. Bots cannot pull other bots into threads — only humans can.
**Humans are the gatekeepers.** A bot cannot participate in a thread until a human explicitly pulls it in via @mention. Bots cannot pull other bots into threads — only humans can, **unless** the sending bot is in the target bot's `trusted_bot_ids` (see [Trusted bot admission override](#trusted-bot-admission-override) below).

### How a bot becomes involved

Expand Down Expand Up @@ -297,7 +297,11 @@ Inbound message in thread
│ │
│ ├─ From a human → pass (bot will reply and become involved)
│ │
│ └─ From another bot → ❌ DROP (bot-to-bot cannot break the gate)
│ └─ From another bot:
│ │
│ ├─ Sender in trusted_bot_ids → pass (same as human @mention)
│ │
│ └─ Otherwise → ❌ DROP (bot-to-bot cannot break the gate)
└─ Message dropped — never reaches Dispatcher or SessionPool
```
Expand All @@ -318,8 +322,26 @@ This is an intentional safety constraint:
| Bot A @mentions Bot B (Bot B not yet involved) | ❌ Silently dropped |
| Bot A @mentions Bot B (Bot B already involved) | ✅ Processed per `allow_bot_messages` mode |
| Human @mentions Bot B, then Bot A @mentions Bot B | ✅ Works — Bot B is already involved |
| **Trusted** Bot A @mentions Bot B (Bot A in Bot B's `trusted_bot_ids`) | ✅ Treated as human @mention — Bot B becomes involved |

### Workaround for bot-to-bot handoff
### Trusted bot admission override

When a bot is listed in another bot's `trusted_bot_ids` and explicitly @mentions that bot, the mention is treated identically to a human @mention:

- The target bot becomes **involved** in the thread
- The `allow_bot_messages` mode check is **bypassed** entirely
- The message is dispatched to the session

This enables bot-to-bot coordination (e.g. a coordinator bot pulling reviewer bots into threads) without requiring human intervention for every thread.

**Requirements:**
- The sending bot must be in the target bot's `trusted_bot_ids` config
- The sending bot must explicitly @mention the target bot
- Messages from trusted bots **without** @mention still follow normal `allow_bot_messages` gating

**Safety:** `trusted_bot_ids` defaults to empty — this feature is entirely opt-in. The `max_bot_turns` cap still applies after involvement to prevent runaway loops.

### Workaround for bot-to-bot handoff (without trusted_bot_ids)

If you need Bot A to hand off to Bot B in a thread where Bot B is not yet involved:

Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ pub struct DiscordConfig {
/// the allowlist filters further. Empty = allow any bot (mode permitting).
/// Only relevant when `allow_bot_messages` is `"mentions"` or `"all"`;
/// ignored when `"off"` since all bot messages are rejected before this check.
///
/// **Admission override**: a trusted bot that explicitly @mentions this bot
/// bypasses the `allow_bot_messages` mode entirely (treated as human @mention).
/// This allows trusted bots to pull this bot into threads regardless of mode.
#[serde(default)]
pub trusted_bot_ids: Vec<String>,
#[serde(default)]
Expand Down
Loading
Loading