refactor!: rename chat providers to channels

This commit is contained in:
Peter Steinberger
2026-01-13 06:16:43 +00:00
parent 0cd632ba84
commit 90342a4f3a
393 changed files with 8004 additions and 6737 deletions

View File

@@ -8,8 +8,8 @@ read_when:
Clawdbot reads an optional **JSON5** config from `~/.clawdbot/clawdbot.json` (comments + trailing commas allowed).
If the file is missing, Clawdbot uses safe-ish defaults (embedded Pi agent + per-sender sessions + workspace `~/clawd`). You usually only need a config to:
- restrict who can trigger the bot (`whatsapp.allowFrom`, `telegram.allowFrom`, etc.)
- control group allowlists + mention behavior (`whatsapp.groups`, `telegram.groups`, `discord.guilds`, `agents.list[].groupChat`)
- restrict who can trigger the bot (`channels.whatsapp.allowFrom`, `channels.telegram.allowFrom`, etc.)
- control group allowlists + mention behavior (`channels.whatsapp.groups`, `channels.telegram.groups`, `channels.discord.guilds`, `agents.list[].groupChat`)
- customize message prefixes (`messages`)
- set the agent's workspace (`agents.defaults.workspace` or `agents.list[].workspace`)
- tune the embedded agent defaults (`agents.defaults`) and session behavior (`session`)
@@ -50,7 +50,7 @@ clawdbot gateway call config.apply --params '{
```json5
{
agents: { defaults: { workspace: "~/clawd" } },
whatsapp: { allowFrom: ["+15555550123"] }
channels: { whatsapp: { allowFrom: ["+15555550123"] } }
}
```
@@ -74,10 +74,12 @@ To prevent the bot from responding to WhatsApp @-mentions in groups (only respon
}
]
},
whatsapp: {
// Allowlist is DMs only; including your own number enables self-chat mode.
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } }
channels: {
whatsapp: {
// Allowlist is DMs only; including your own number enables self-chat mode.
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } }
}
}
}
```
@@ -189,7 +191,7 @@ Included files can themselves contain `$include` directives (up to 10 levels dee
"./clients/schmidt/broadcast.json5"
]},
whatsapp: { groupPolicy: "allowlist" }
channels: { whatsapp: { groupPolicy: "allowlist" } }
}
```
@@ -366,12 +368,12 @@ Metadata written by CLI wizards (`onboard`, `configure`, `doctor`).
}
```
### `whatsapp.dmPolicy`
### `channels.whatsapp.dmPolicy`
Controls how WhatsApp direct chats (DMs) are handled:
- `"pairing"` (default): unknown senders get a pairing code; owner must approve
- `"allowlist"`: only allow senders in `whatsapp.allowFrom` (or paired allow store)
- `"open"`: allow all inbound DMs (**requires** `whatsapp.allowFrom` to include `"*"`)
- `"allowlist"`: only allow senders in `channels.whatsapp.allowFrom` (or paired allow store)
- `"open"`: allow all inbound DMs (**requires** `channels.whatsapp.allowFrom` to include `"*"`)
- `"disabled"`: ignore all inbound DMs
Pairing codes expire after 1 hour; the bot only sends a pairing code when a new request is created. Pending DM pairing requests are capped at **3 per provider** by default.
@@ -380,36 +382,40 @@ Pairing approvals:
- `clawdbot pairing list whatsapp`
- `clawdbot pairing approve whatsapp <code>`
### `whatsapp.allowFrom`
### `channels.whatsapp.allowFrom`
Allowlist of E.164 phone numbers that may trigger WhatsApp auto-replies (**DMs only**).
If empty and `whatsapp.dmPolicy="pairing"`, unknown senders will receive a pairing code.
For groups, use `whatsapp.groupPolicy` + `whatsapp.groupAllowFrom`.
If empty and `channels.whatsapp.dmPolicy="pairing"`, unknown senders will receive a pairing code.
For groups, use `channels.whatsapp.groupPolicy` + `channels.whatsapp.groupAllowFrom`.
```json5
{
whatsapp: {
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "+447700900123"],
textChunkLimit: 4000, // optional outbound chunk size (chars)
mediaMaxMb: 50 // optional inbound media cap (MB)
channels: {
whatsapp: {
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "+447700900123"],
textChunkLimit: 4000, // optional outbound chunk size (chars)
mediaMaxMb: 50 // optional inbound media cap (MB)
}
}
}
```
### `whatsapp.accounts` (multi-account)
### `channels.whatsapp.accounts` (multi-account)
Run multiple WhatsApp accounts in one gateway:
```json5
{
whatsapp: {
accounts: {
default: {}, // optional; keeps the default id stable
personal: {},
biz: {
// Optional override. Default: ~/.clawdbot/credentials/whatsapp/biz
// authDir: "~/.clawdbot/credentials/whatsapp/biz",
channels: {
whatsapp: {
accounts: {
default: {}, // optional; keeps the default id stable
personal: {},
biz: {
// Optional override. Default: ~/.clawdbot/credentials/whatsapp/biz
// authDir: "~/.clawdbot/credentials/whatsapp/biz",
}
}
}
}
@@ -420,21 +426,23 @@ Notes:
- Outbound commands default to account `default` if present; otherwise the first configured account id (sorted).
- The legacy single-account Baileys auth dir is migrated by `clawdbot doctor` into `whatsapp/default`.
### `telegram.accounts` / `discord.accounts` / `slack.accounts` / `signal.accounts` / `imessage.accounts`
### `channels.telegram.accounts` / `channels.discord.accounts` / `channels.slack.accounts` / `channels.signal.accounts` / `channels.imessage.accounts`
Run multiple accounts per provider (each account has its own `accountId` and optional `name`):
```json5
{
telegram: {
accounts: {
default: {
name: "Primary bot",
botToken: "123456:ABC..."
},
alerts: {
name: "Alerts bot",
botToken: "987654:XYZ..."
channels: {
telegram: {
accounts: {
default: {
name: "Primary bot",
botToken: "123456:ABC..."
},
alerts: {
name: "Alerts bot",
botToken: "987654:XYZ..."
}
}
}
}
@@ -452,7 +460,7 @@ Notes:
Group messages default to **require mention** (either metadata mention or regex patterns). Applies to WhatsApp, Telegram, Discord, and iMessage group chats.
**Mention types:**
- **Metadata mentions**: Native platform @-mentions (e.g., WhatsApp tap-to-mention). Ignored in WhatsApp self-chat mode (see `whatsapp.allowFrom`).
- **Metadata mentions**: Native platform @-mentions (e.g., WhatsApp tap-to-mention). Ignored in WhatsApp self-chat mode (see `channels.whatsapp.allowFrom`).
- **Text patterns**: Regex patterns defined in `agents.list[].groupChat.mentionPatterns`. Always checked regardless of self-chat mode.
- Mention gating is enforced only when mention detection is possible (native mentions or at least one `mentionPattern`).
@@ -469,7 +477,7 @@ Group messages default to **require mention** (either metadata mention or regex
}
```
`messages.groupChat.historyLimit` sets the global default for group history context. Providers can override with `<provider>.historyLimit` (or `<provider>.accounts.*.historyLimit` for multi-account). Set `0` to disable history wrapping.
`messages.groupChat.historyLimit` sets the global default for group history context. Providers can override with `channels.<provider>.historyLimit` (or `channels.<provider>.accounts.*.historyLimit` for multi-account). Set `0` to disable history wrapping.
Per-agent override (takes precedence when set, even `[]`):
```json5
@@ -483,15 +491,17 @@ Per-agent override (takes precedence when set, even `[]`):
}
```
Mention gating defaults live per provider (`whatsapp.groups`, `telegram.groups`, `imessage.groups`, `discord.guilds`). When `*.groups` is set, it also acts as a group allowlist; include `"*"` to allow all groups.
Mention gating defaults live per provider (`channels.whatsapp.groups`, `channels.telegram.groups`, `channels.imessage.groups`, `channels.discord.guilds`). When `*.groups` is set, it also acts as a group allowlist; include `"*"` to allow all groups.
To respond **only** to specific text triggers (ignoring native @-mentions):
```json5
{
whatsapp: {
// Include your own number to enable self-chat mode (ignore native @-mentions).
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } }
channels: {
whatsapp: {
// Include your own number to enable self-chat mode (ignore native @-mentions).
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } }
}
},
agents: {
list: [
@@ -509,41 +519,43 @@ To respond **only** to specific text triggers (ignoring native @-mentions):
### Group policy (per provider)
Use `*.groupPolicy` to control whether group/room messages are accepted at all:
Use `channels.*.groupPolicy` to control whether group/room messages are accepted at all:
```json5
{
whatsapp: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"]
},
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:123456789", "@alice"]
},
signal: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"]
},
imessage: {
groupPolicy: "allowlist",
groupAllowFrom: ["chat_id:123"]
},
msteams: {
groupPolicy: "allowlist",
groupAllowFrom: ["user@org.com"]
},
discord: {
groupPolicy: "allowlist",
guilds: {
"GUILD_ID": {
channels: { help: { allow: true } }
channels: {
whatsapp: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"]
},
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:123456789", "@alice"]
},
signal: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"]
},
imessage: {
groupPolicy: "allowlist",
groupAllowFrom: ["chat_id:123"]
},
msteams: {
groupPolicy: "allowlist",
groupAllowFrom: ["user@org.com"]
},
discord: {
groupPolicy: "allowlist",
guilds: {
"GUILD_ID": {
channels: { help: { allow: true } }
}
}
},
slack: {
groupPolicy: "allowlist",
channels: { "#general": { allow: true } }
}
},
slack: {
groupPolicy: "allowlist",
channels: { "#general": { allow: true } }
}
}
```
@@ -553,7 +565,7 @@ Notes:
- `"disabled"`: block all group/room messages.
- `"allowlist"`: only allow groups/rooms that match the configured allowlist.
- WhatsApp/Telegram/Signal/iMessage/Microsoft Teams use `groupAllowFrom` (fallback: explicit `allowFrom`).
- Discord/Slack use channel allowlists (`discord.guilds.*.channels`, `slack.channels`).
- Discord/Slack use channel allowlists (`channels.discord.guilds.*.channels`, `channels.slack.channels`).
- Group DMs (Discord/Slack) are still controlled by `dm.groupEnabled` + `dm.groupChannels`.
- Default is `groupPolicy: "allowlist"`; if no allowlist is configured, group messages are blocked.
@@ -691,10 +703,12 @@ Example: two WhatsApp accounts → two agents:
{ agentId: "home", match: { provider: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { provider: "whatsapp", accountId: "biz" } }
],
whatsapp: {
accounts: {
personal: {},
biz: {},
channels: {
whatsapp: {
accounts: {
personal: {},
biz: {},
}
}
}
}
@@ -761,9 +775,9 @@ Controls how chat commands are enabled across connectors.
Notes:
- Text commands must be sent as a **standalone** message and use the leading `/` (no plain-text aliases).
- `commands.text: false` disables parsing chat messages for commands.
- `commands.native: "auto"` (default) turns on native commands for Discord/Telegram and leaves Slack off; unsupported providers stay text-only.
- Set `commands.native: true|false` to force all, or override per provider with `discord.commands.native`, `telegram.commands.native`, `slack.commands.native` (bool or `"auto"`). `false` clears previously registered commands on Discord/Telegram at startup; Slack commands are managed in the Slack app.
- `commands.bash: true` enables `! <cmd>` to run host shell commands (`/bash <cmd>` also works as an alias). Requires `tools.elevated.enabled` and allowlisting the sender in `tools.elevated.allowFrom.<provider>`.
- `commands.native: "auto"` (default) turns on native commands for Discord/Telegram and leaves Slack off; unsupported channels stay text-only.
- Set `commands.native: true|false` to force all, or override per channel with `channels.discord.commands.native`, `channels.telegram.commands.native`, `channels.slack.commands.native` (bool or `"auto"`). `false` clears previously registered commands on Discord/Telegram at startup; Slack commands are managed in the Slack app.
- `commands.bash: true` enables `! <cmd>` to run host shell commands (`/bash <cmd>` also works as an alias). Requires `tools.elevated.enabled` and allowlisting the sender in `tools.elevated.allowFrom.<channel>`.
- `commands.bashForegroundMs` controls how long bash waits before backgrounding. While a bash job is running, new `! <cmd>` requests are rejected (one at a time).
- `commands.config: true` enables `/config` (reads/writes `clawdbot.json`).
- `commands.debug: true` enables `/debug` (runtime-only overrides).
@@ -791,53 +805,55 @@ Set `web.enabled: false` to keep it off by default.
}
```
### `telegram` (bot transport)
### `channels.telegram` (bot transport)
Clawdbot starts Telegram only when a `telegram` config section exists. The bot token is resolved from `TELEGRAM_BOT_TOKEN` or `telegram.botToken`.
Set `telegram.enabled: false` to disable automatic startup.
Multi-account support lives under `telegram.accounts` (see the multi-account section above). Env tokens only apply to the default account.
Clawdbot starts Telegram only when a `channels.telegram` config section exists. The bot token is resolved from `TELEGRAM_BOT_TOKEN` or `channels.telegram.botToken`.
Set `channels.telegram.enabled: false` to disable automatic startup.
Multi-account support lives under `channels.telegram.accounts` (see the multi-account section above). Env tokens only apply to the default account.
```json5
{
telegram: {
enabled: true,
botToken: "your-bot-token",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
groups: {
"*": { requireMention: true },
"-1001234567890": {
allowFrom: ["@admin"],
systemPrompt: "Keep answers brief.",
topics: {
"99": {
requireMention: false,
skills: ["search"],
systemPrompt: "Stay on topic."
channels: {
telegram: {
enabled: true,
botToken: "your-bot-token",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
groups: {
"*": { requireMention: true },
"-1001234567890": {
allowFrom: ["@admin"],
systemPrompt: "Keep answers brief.",
topics: {
"99": {
requireMention: false,
skills: ["search"],
systemPrompt: "Stay on topic."
}
}
}
}
},
historyLimit: 50, // include last N group messages as context (0 disables)
replyToMode: "first", // off | first | all
streamMode: "partial", // off | partial | block (draft streaming; separate from block streaming)
draftChunk: { // optional; only for streamMode=block
minChars: 200,
maxChars: 800,
breakPreference: "paragraph" // paragraph | newline | sentence
},
actions: { reactions: true, sendMessage: true }, // tool action gates (false disables)
mediaMaxMb: 5,
retry: { // outbound retry policy
attempts: 3,
minDelayMs: 400,
maxDelayMs: 30000,
jitter: 0.1
},
proxy: "socks5://localhost:9050",
webhookUrl: "https://example.com/telegram-webhook",
webhookSecret: "secret",
webhookPath: "/telegram-webhook"
},
historyLimit: 50, // include last N group messages as context (0 disables)
replyToMode: "first", // off | first | all
streamMode: "partial", // off | partial | block (draft streaming; separate from block streaming)
draftChunk: { // optional; only for streamMode=block
minChars: 200,
maxChars: 800,
breakPreference: "paragraph" // paragraph | newline | sentence
},
actions: { reactions: true, sendMessage: true }, // tool action gates (false disables)
mediaMaxMb: 5,
retry: { // outbound retry policy
attempts: 3,
minDelayMs: 400,
maxDelayMs: 30000,
jitter: 0.1
},
proxy: "socks5://localhost:9050",
webhookUrl: "https://example.com/telegram-webhook",
webhookSecret: "secret",
webhookPath: "/telegram-webhook"
}
}
}
```
@@ -848,148 +864,152 @@ Draft streaming notes:
- `/reasoning stream` streams reasoning into the draft, then sends the final answer.
Retry policy defaults and behavior are documented in [Retry policy](/concepts/retry).
### `discord` (bot transport)
### `channels.discord` (bot transport)
Configure the Discord bot by setting the bot token and optional gating:
Multi-account support lives under `discord.accounts` (see the multi-account section above). Env tokens only apply to the default account.
Multi-account support lives under `channels.discord.accounts` (see the multi-account section above). Env tokens only apply to the default account.
```json5
{
discord: {
enabled: true,
token: "your-bot-token",
mediaMaxMb: 8, // clamp inbound media size
allowBots: false, // allow bot-authored messages
actions: { // tool action gates (false disables)
reactions: true,
stickers: true,
polls: true,
permissions: true,
messages: true,
threads: true,
pins: true,
search: true,
memberInfo: true,
roleInfo: true,
roles: false,
channelInfo: true,
voiceStatus: true,
events: true,
moderation: false
},
replyToMode: "off", // off | first | all
dm: {
enabled: true, // disable all DMs when false
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["1234567890", "steipete"], // optional DM allowlist ("open" requires ["*"])
groupEnabled: false, // enable group DMs
groupChannels: ["clawd-dm"] // optional group DM allowlist
},
guilds: {
"123456789012345678": { // guild id (preferred) or slug
slug: "friends-of-clawd",
requireMention: false, // per-guild default
reactionNotifications: "own", // off | own | all | allowlist
users: ["987654321098765432"], // optional per-guild user allowlist
channels: {
general: { allow: true },
help: {
allow: true,
requireMention: true,
users: ["987654321098765432"],
skills: ["docs"],
systemPrompt: "Short answers only."
channels: {
discord: {
enabled: true,
token: "your-bot-token",
mediaMaxMb: 8, // clamp inbound media size
allowBots: false, // allow bot-authored messages
actions: { // tool action gates (false disables)
reactions: true,
stickers: true,
polls: true,
permissions: true,
messages: true,
threads: true,
pins: true,
search: true,
memberInfo: true,
roleInfo: true,
roles: false,
channelInfo: true,
voiceStatus: true,
events: true,
moderation: false
},
replyToMode: "off", // off | first | all
dm: {
enabled: true, // disable all DMs when false
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["1234567890", "steipete"], // optional DM allowlist ("open" requires ["*"])
groupEnabled: false, // enable group DMs
groupChannels: ["clawd-dm"] // optional group DM allowlist
},
guilds: {
"123456789012345678": { // guild id (preferred) or slug
slug: "friends-of-clawd",
requireMention: false, // per-guild default
reactionNotifications: "own", // off | own | all | allowlist
users: ["987654321098765432"], // optional per-guild user allowlist
channels: {
general: { allow: true },
help: {
allow: true,
requireMention: true,
users: ["987654321098765432"],
skills: ["docs"],
systemPrompt: "Short answers only."
}
}
}
},
historyLimit: 20, // include last N guild messages as context
textChunkLimit: 2000, // optional outbound text chunk size (chars)
maxLinesPerMessage: 17, // soft max lines per message (Discord UI clipping)
retry: { // outbound retry policy
attempts: 3,
minDelayMs: 500,
maxDelayMs: 30000,
jitter: 0.1
}
},
historyLimit: 20, // include last N guild messages as context
textChunkLimit: 2000, // optional outbound text chunk size (chars)
maxLinesPerMessage: 17, // soft max lines per message (Discord UI clipping)
retry: { // outbound retry policy
attempts: 3,
minDelayMs: 500,
maxDelayMs: 30000,
jitter: 0.1
}
}
}
```
Clawdbot starts Discord only when a `discord` config section exists. The token is resolved from `DISCORD_BOT_TOKEN` or `discord.token` (unless `discord.enabled` is `false`). Use `user:<id>` (DM) or `channel:<id>` (guild channel) when specifying delivery targets for cron/CLI commands; bare numeric IDs are ambiguous and rejected.
Clawdbot starts Discord only when a `channels.discord` config section exists. The token is resolved from `DISCORD_BOT_TOKEN` or `channels.discord.token` (unless `channels.discord.enabled` is `false`). Use `user:<id>` (DM) or `channel:<id>` (guild channel) when specifying delivery targets for cron/CLI commands; bare numeric IDs are ambiguous and rejected.
Guild slugs are lowercase with spaces replaced by `-`; channel keys use the slugged channel name (no leading `#`). Prefer guild ids as keys to avoid rename ambiguity.
Bot-authored messages are ignored by default. Enable with `discord.allowBots` (own messages are still filtered to prevent self-reply loops).
Bot-authored messages are ignored by default. Enable with `channels.discord.allowBots` (own messages are still filtered to prevent self-reply loops).
Reaction notification modes:
- `off`: no reaction events.
- `own`: reactions on the bot's own messages (default).
- `all`: all reactions on all messages.
- `allowlist`: reactions from `guilds.<id>.users` on all messages (empty list disables).
Outbound text is chunked by `discord.textChunkLimit` (default 2000). Discord clients can clip very tall messages, so `discord.maxLinesPerMessage` (default 17) splits long multi-line replies even when under 2000 chars.
Outbound text is chunked by `channels.discord.textChunkLimit` (default 2000). Discord clients can clip very tall messages, so `channels.discord.maxLinesPerMessage` (default 17) splits long multi-line replies even when under 2000 chars.
Retry policy defaults and behavior are documented in [Retry policy](/concepts/retry).
### `slack` (socket mode)
### `channels.slack` (socket mode)
Slack runs in Socket Mode and requires both a bot token and app token:
```json5
{
slack: {
enabled: true,
botToken: "xoxb-...",
appToken: "xapp-...",
dm: {
channels: {
slack: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["U123", "U456", "*"], // optional; "open" requires ["*"]
groupEnabled: false,
groupChannels: ["G123"]
},
channels: {
C123: { allow: true, requireMention: true, allowBots: false },
"#general": {
allow: true,
requireMention: true,
allowBots: false,
users: ["U123"],
skills: ["docs"],
systemPrompt: "Short answers only."
}
},
historyLimit: 50, // include last N channel/group messages as context (0 disables)
allowBots: false,
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["U123"],
replyToMode: "off", // off | first | all
actions: {
reactions: true,
messages: true,
pins: true,
memberInfo: true,
emojiList: true
},
slashCommand: {
enabled: true,
name: "clawd",
sessionPrefix: "slack:slash",
ephemeral: true
},
textChunkLimit: 4000,
mediaMaxMb: 20
botToken: "xoxb-...",
appToken: "xapp-...",
dm: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["U123", "U456", "*"], // optional; "open" requires ["*"]
groupEnabled: false,
groupChannels: ["G123"]
},
channels: {
C123: { allow: true, requireMention: true, allowBots: false },
"#general": {
allow: true,
requireMention: true,
allowBots: false,
users: ["U123"],
skills: ["docs"],
systemPrompt: "Short answers only."
}
},
historyLimit: 50, // include last N channel/group messages as context (0 disables)
allowBots: false,
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["U123"],
replyToMode: "off", // off | first | all
actions: {
reactions: true,
messages: true,
pins: true,
memberInfo: true,
emojiList: true
},
slashCommand: {
enabled: true,
name: "clawd",
sessionPrefix: "slack:slash",
ephemeral: true
},
textChunkLimit: 4000,
mediaMaxMb: 20
}
}
}
```
Multi-account support lives under `slack.accounts` (see the multi-account section above). Env tokens only apply to the default account.
Multi-account support lives under `channels.slack.accounts` (see the multi-account section above). Env tokens only apply to the default account.
Clawdbot starts Slack when the provider is enabled and both tokens are set (via config or `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN`). Use `user:<id>` (DM) or `channel:<id>` when specifying delivery targets for cron/CLI commands.
Bot-authored messages are ignored by default. Enable with `slack.allowBots` or `slack.channels.<id>.allowBots`.
Bot-authored messages are ignored by default. Enable with `channels.slack.allowBots` or `channels.slack.channels.<id>.allowBots`.
Reaction notification modes:
- `off`: no reaction events.
- `own`: reactions on the bot's own messages (default).
- `all`: all reactions on all messages.
- `allowlist`: reactions from `slack.reactionAllowlist` on all messages (empty list disables).
- `allowlist`: reactions from `channels.slack.reactionAllowlist` on all messages (empty list disables).
Slack action groups (gate `slack` tool actions):
| Action group | Default | Notes |
@@ -1000,16 +1020,18 @@ Slack action groups (gate `slack` tool actions):
| memberInfo | enabled | Member info |
| emojiList | enabled | Custom emoji list |
### `signal` (signal-cli)
### `channels.signal` (signal-cli)
Signal reactions can emit system events (shared reaction tooling):
```json5
{
signal: {
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["+15551234567", "uuid:123e4567-e89b-12d3-a456-426614174000"],
historyLimit: 50 // include last N group messages as context (0 disables)
channels: {
signal: {
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["+15551234567", "uuid:123e4567-e89b-12d3-a456-426614174000"],
historyLimit: 50 // include last N group messages as context (0 disables)
}
}
}
```
@@ -1018,36 +1040,38 @@ Reaction notification modes:
- `off`: no reaction events.
- `own`: reactions on the bot's own messages (default).
- `all`: all reactions on all messages.
- `allowlist`: reactions from `signal.reactionAllowlist` on all messages (empty list disables).
- `allowlist`: reactions from `channels.signal.reactionAllowlist` on all messages (empty list disables).
### `imessage` (imsg CLI)
### `channels.imessage` (imsg CLI)
Clawdbot spawns `imsg rpc` (JSON-RPC over stdio). No daemon or port required.
```json5
{
imessage: {
enabled: true,
cliPath: "imsg",
dbPath: "~/Library/Messages/chat.db",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "user@example.com", "chat_id:123"],
historyLimit: 50, // include last N group messages as context (0 disables)
includeAttachments: false,
mediaMaxMb: 16,
service: "auto",
region: "US"
channels: {
imessage: {
enabled: true,
cliPath: "imsg",
dbPath: "~/Library/Messages/chat.db",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "user@example.com", "chat_id:123"],
historyLimit: 50, // include last N group messages as context (0 disables)
includeAttachments: false,
mediaMaxMb: 16,
service: "auto",
region: "US"
}
}
}
```
Multi-account support lives under `imessage.accounts` (see the multi-account section above).
Multi-account support lives under `channels.imessage.accounts` (see the multi-account section above).
Notes:
- Requires Full Disk Access to the Messages DB.
- The first send will prompt for Messages automation permission.
- Prefer `chat_id:<id>` targets. Use `imsg chats --limit 20` to list chats.
- `imessage.cliPath` can point to a wrapper script (e.g. `ssh` to another Mac that runs `imsg rpc`); use SSH keys to avoid password prompts.
- `channels.imessage.cliPath` can point to a wrapper script (e.g. `ssh` to another Mac that runs `imsg rpc`); use SSH keys to avoid password prompts.
Example wrapper:
```bash
@@ -1129,9 +1153,9 @@ streaming, final replies) across providers unless already present.
If `messages.responsePrefix` is unset, no prefix is applied by default.
Set it to `"auto"` to derive `[{identity.name}]` for the routed agent (when set).
WhatsApp inbound prefix is configured via `whatsapp.messagePrefix` (deprecated:
WhatsApp inbound prefix is configured via `channels.whatsapp.messagePrefix` (deprecated:
`messages.messagePrefix`). Default stays **unchanged**: `"[clawdbot]"` when
`whatsapp.allowFrom` is empty, otherwise `""` (no prefix). When using
`channels.whatsapp.allowFrom` is empty, otherwise `""` (no prefix). When using
`"[clawdbot]"`, Clawdbot will instead use `[{identity.name}]` when the routed
agent has `identity.name` set.
@@ -1474,9 +1498,9 @@ Block streaming:
Defaults to `{ idleMs: 1000 }` and inherits `minChars` from `blockStreamingChunk`
with `maxChars` capped to the provider text limit. Signal/Slack/Discord default
to `minChars: 1500` unless overridden.
Provider overrides: `whatsapp.blockStreamingCoalesce`, `telegram.blockStreamingCoalesce`,
`discord.blockStreamingCoalesce`, `slack.blockStreamingCoalesce`, `signal.blockStreamingCoalesce`,
`imessage.blockStreamingCoalesce`, `msteams.blockStreamingCoalesce` (and per-account variants).
Provider overrides: `channels.whatsapp.blockStreamingCoalesce`, `channels.telegram.blockStreamingCoalesce`,
`channels.discord.blockStreamingCoalesce`, `channels.slack.blockStreamingCoalesce`, `channels.signal.blockStreamingCoalesce`,
`channels.imessage.blockStreamingCoalesce`, `channels.msteams.blockStreamingCoalesce` (and per-account variants).
- `agents.defaults.humanDelay`: randomized pause between **block replies** after the first.
Modes: `off` (default), `natural` (8002500ms), `custom` (use `minMs`/`maxMs`).
Per-agent override: `agents.list[].humanDelay`.
@@ -1585,7 +1609,7 @@ Tool groups (shorthands) work in **global** and **per-agent** tool policies:
- `allowFrom`: per-provider allowlists (empty = disabled)
- `whatsapp`: E.164 numbers
- `telegram`: chat ids or usernames
- `discord`: user ids or usernames (falls back to `discord.dm.allowFrom` if omitted)
- `discord`: user ids or usernames (falls back to `channels.discord.dm.allowFrom` if omitted)
- `signal`: E.164 numbers
- `imessage`: handles/chat ids
- `webchat`: session ids or usernames