From 464dabdc16337ef46bf3164f356848051e4be9d9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 2 Jan 2026 01:11:04 +0100 Subject: [PATCH] docs: default discord reactions to on --- docs/configuration.md | 31 ++++++++++++++++++-- docs/discord.md | 6 ++-- src/agents/clawdis-tools.ts | 4 +-- src/config/config.ts | 57 ++++++++++++++++++++++++++++++++++--- 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index c7ca94330..b23df2acc 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -114,6 +114,7 @@ Controls how inbound messages behave when an agent run is already active. whatsapp: "interrupt", telegram: "interrupt", discord: "queue", + imessage: "interrupt", webchat: "queue" } } @@ -180,13 +181,37 @@ Configure the Discord bot by setting the bot token and optional gating: requireMention: true, // require @bot mentions in guilds mediaMaxMb: 8, // clamp inbound media size historyLimit: 20, // include last N guild messages as context - enableReactions: false // allow agent-triggered reactions + enableReactions: true // allow agent-triggered reactions } } ``` Clawdis reads `DISCORD_BOT_TOKEN` or `discord.token` to start the provider (unless `discord.enabled` is `false`). Use `user:` (DM) or `channel:` (guild channel) when specifying delivery targets for cron/CLI commands. +### `imessage` (imsg CLI) + +Clawdis spawns `imsg rpc` (JSON-RPC over stdio). No daemon or port required. + +```json5 +{ + imessage: { + enabled: true, + cliPath: "imsg", + dbPath: "~/Library/Messages/chat.db", + allowFrom: ["+15555550123", "user@example.com", "chat_id:123"], + includeAttachments: false, + mediaMaxMb: 16, + service: "auto", + region: "US" + } +} +``` + +Notes: +- Requires Full Disk Access to the Messages DB. +- The first send will prompt for Messages automation permission. +- Prefer `chat_id:` targets. Use `imsg chats --limit 20` to list chats. + ### `agent.workspace` Sets the **single global workspace directory** used by the agent for file operations. @@ -284,7 +309,7 @@ Z.AI models are available as `zai/` (e.g. `zai/glm-4.7`) and require - `every`: duration string (`ms`, `s`, `m`, `h`); default unit minutes. Omit or set `0m` to disable. - `model`: optional override model for heartbeat runs (`provider/model`). -- `target`: optional delivery channel (`last`, `whatsapp`, `telegram`, `discord`, `none`). Default: `last`. +- `target`: optional delivery channel (`last`, `whatsapp`, `telegram`, `discord`, `imessage`, `none`). Default: `last`. - `to`: optional recipient override (E.164 for WhatsApp, chat id for Telegram). - `prompt`: optional override for the heartbeat body (default: `HEARTBEAT`). @@ -718,7 +743,7 @@ Template placeholders are expanded in `routing.transcribeAudio.command` (and any | `{{GroupMembers}}` | Group members preview (best effort) | | `{{SenderName}}` | Sender display name (best effort) | | `{{SenderE164}}` | Sender phone number (best effort) | -| `{{Surface}}` | Surface hint (whatsapp|telegram|discord|webchat|…) | +| `{{Surface}}` | Surface hint (whatsapp|telegram|discord|imessage|webchat|…) | ## Cron (Gateway scheduler) diff --git a/docs/discord.md b/docs/discord.md index 7bd17715f..475525aa6 100644 --- a/docs/discord.md +++ b/docs/discord.md @@ -24,7 +24,7 @@ Status: ready for DM and guild text channels via the official Discord bot gatewa 7. Optional DM allowlist: reuse `discord.allowFrom` with user ids (`1234567890` or `discord:1234567890`). Use `"*"` to allow all DMs. 8. Optional guild allowlist: set `discord.guildAllowFrom` with `guilds` and/or `users` to gate who can invoke the bot in servers. 9. Optional guild context history: set `discord.historyLimit` (default 20) to include the last N guild messages as context when replying to a mention. Set `0` to disable. -10. Optional reactions: set `discord.enableReactions = true` to allow the agent to react to Discord messages via the `clawdis_discord` tool. +10. Reactions (default on): set `discord.enableReactions = false` to disable agent-triggered reactions via the `clawdis_discord` tool. Note: Discord does not provide a simple username → id lookup without extra guild context, so prefer ids or `<@id>` mentions for DM delivery targets. @@ -49,7 +49,7 @@ Note: Discord does not provide a simple username → id lookup without extra gui requireMention: true, mediaMaxMb: 8, historyLimit: 20, - enableReactions: false + enableReactions: true } } ``` @@ -59,7 +59,7 @@ Note: Discord does not provide a simple username → id lookup without extra gui - `requireMention`: when `true`, messages in guild channels must mention the bot. - `mediaMaxMb`: clamp inbound media saved to disk. - `historyLimit`: number of recent guild messages to include as context when replying to a mention (default 20, `0` disables). -- `enableReactions`: allow agent-triggered reactions via the `clawdis_discord` tool (default `false`). +- `enableReactions`: allow agent-triggered reactions via the `clawdis_discord` tool (default `true`). ## Reactions When `discord.enableReactions = true`, the agent can call `clawdis_discord` with: diff --git a/src/agents/clawdis-tools.ts b/src/agents/clawdis-tools.ts index 38cf1c32a..2b3a37647 100644 --- a/src/agents/clawdis-tools.ts +++ b/src/agents/clawdis-tools.ts @@ -1437,7 +1437,7 @@ function createDiscordTool(): AnyAgentTool { label: "Clawdis Discord", name: "clawdis_discord", description: - "React to Discord messages. Requires discord.enableReactions=true in config.", + "React to Discord messages. Controlled by discord.enableReactions (default: true).", parameters: DiscordToolSchema, execute: async (_toolCallId, args) => { const params = args as Record; @@ -1445,7 +1445,7 @@ function createDiscordTool(): AnyAgentTool { if (action !== "react") throw new Error(`Unknown action: ${action}`); const cfg = loadConfig(); - if (!cfg.discord?.enableReactions) { + if (cfg.discord?.enableReactions === false) { throw new Error( "Discord reactions are disabled (set discord.enableReactions=true).", ); diff --git a/src/config/config.ts b/src/config/config.ts index 803d16a62..6eb1ab2f5 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -102,7 +102,7 @@ export type HookMappingConfig = { messageTemplate?: string; textTemplate?: string; deliver?: boolean; - channel?: "last" | "whatsapp" | "telegram" | "discord"; + channel?: "last" | "whatsapp" | "telegram" | "discord" | "signal" | "imessage"; to?: string; thinking?: string; timeoutSeconds?: number; @@ -171,7 +171,7 @@ export type DiscordConfig = { mediaMaxMb?: number; /** Number of recent guild messages to include for context (default: 20). */ historyLimit?: number; - /** Allow agent-triggered Discord reactions (default: false). */ + /** Allow agent-triggered Discord reactions (default: true). */ enableReactions?: boolean; }; @@ -198,6 +198,25 @@ export type SignalConfig = { mediaMaxMb?: number; }; +export type IMessageConfig = { + /** If false, do not start the iMessage provider. Default: true. */ + enabled?: boolean; + /** imsg CLI binary path (default: imsg). */ + cliPath?: string; + /** Optional Messages db path override. */ + dbPath?: string; + /** Optional default send service (imessage|sms|auto). */ + service?: "imessage" | "sms" | "auto"; + /** Optional default region (used when sending SMS). */ + region?: string; + /** Optional allowlist for inbound handles or chat_id targets. */ + allowFrom?: Array; + /** Include attachments + reactions in watch payloads. */ + includeAttachments?: boolean; + /** Max outbound media size in MB. */ + mediaMaxMb?: number; +}; + export type QueueMode = "queue" | "interrupt"; export type QueueModeBySurface = { @@ -205,6 +224,7 @@ export type QueueModeBySurface = { telegram?: QueueMode; discord?: QueueMode; signal?: QueueMode; + imessage?: QueueMode; webchat?: QueueMode; }; @@ -450,8 +470,15 @@ export type ClawdisConfig = { every?: string; /** Heartbeat model override (provider/model). */ model?: string; - /** Delivery target (last|whatsapp|telegram|discord|signal|none). */ - target?: "last" | "whatsapp" | "telegram" | "discord" | "signal" | "none"; + /** Delivery target (last|whatsapp|telegram|discord|signal|imessage|none). */ + target?: + | "last" + | "whatsapp" + | "telegram" + | "discord" + | "signal" + | "imessage" + | "none"; /** Optional delivery override (E.164 for WhatsApp, chat id for Telegram). */ to?: string; /** Override the heartbeat prompt body (default: "HEARTBEAT"). */ @@ -476,6 +503,7 @@ export type ClawdisConfig = { telegram?: TelegramConfig; discord?: DiscordConfig; signal?: SignalConfig; + imessage?: IMessageConfig; cron?: CronConfig; hooks?: HooksConfig; bridge?: BridgeConfig; @@ -570,6 +598,7 @@ const QueueModeBySurfaceSchema = z telegram: QueueModeSchema.optional(), discord: QueueModeSchema.optional(), signal: QueueModeSchema.optional(), + imessage: QueueModeSchema.optional(), webchat: QueueModeSchema.optional(), }) .optional(); @@ -616,6 +645,7 @@ const HeartbeatSchema = z z.literal("telegram"), z.literal("discord"), z.literal("signal"), + z.literal("imessage"), z.literal("none"), ]) .optional(), @@ -675,6 +705,7 @@ const HookMappingSchema = z z.literal("telegram"), z.literal("discord"), z.literal("signal"), + z.literal("imessage"), ]) .optional(), to: z.string().optional(), @@ -903,6 +934,24 @@ const ClawdisSchema = z.object({ mediaMaxMb: z.number().positive().optional(), }) .optional(), + imessage: z + .object({ + enabled: z.boolean().optional(), + cliPath: z.string().optional(), + dbPath: z.string().optional(), + service: z + .union([ + z.literal("imessage"), + z.literal("sms"), + z.literal("auto"), + ]) + .optional(), + region: z.string().optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + includeAttachments: z.boolean().optional(), + mediaMaxMb: z.number().positive().optional(), + }) + .optional(), bridge: z .object({ enabled: z.boolean().optional(),