# @mosaicstack/discord-plugin Official Discord channel adapter for Mosaic Stack. ## Behavior - Runs independently of the bound agent's Claude, Codex, Pi, OpenCode, or future harness. - Routes authorized untagged messages in configured agent channels and replies in-channel. - Creates a public Discord thread when the bot is mentioned in a parent channel, or reuses the thread already attached to that same message. - Keeps follow-ups in existing threads without requiring repeated mentions. - Keeps `/approve` and `/stop ` on the current durable session. - Applies guild, parent-channel, user, pairing, role, and per-minute abuse limits before thread creation or gateway dispatch. - Authenticates to the gateway and signs ingress envelopes with the injected service token. ## Required configuration | Variable | Purpose | | --------------------------------------- | -------------------------------------------------------------------- | | `DISCORD_BOT_TOKEN` | Discord bot credential | | `DISCORD_SERVICE_TOKEN` | High-entropy plugin-to-gateway credential | | `DISCORD_SERVICE_USER_ID` | Provisioned Mosaic service principal | | `DISCORD_ALLOWED_GUILD_IDS` | Comma-separated guild allowlist | | `DISCORD_ALLOWED_CHANNEL_IDS` | Comma-separated parent-channel allowlist | | `DISCORD_ALLOWED_USER_IDS` | Comma-separated Discord user allowlist | | `DISCORD_INTERACTION_BINDINGS` | JSON channel→logical-agent bindings and paired-user roles | | `DISCORD_GATEWAY_URL` | Gateway base URL; defaults to the gateway's local development URL | | `DISCORD_MESSAGE_RATE_LIMIT_PER_MINUTE` | Authorized turns per guild/channel/user per minute; default `30` | | `DISCORD_THREAD_RATE_LIMIT_PER_MINUTE` | Mention-thread routes per guild/channel/user per minute; default `5` | Supply credentials through the approved runtime secret mechanism. Never commit tokens or binding data containing secrets. Example binding shape (identifiers are placeholders): ```json [ { "instanceId": "interaction-agent", "agentConfigId": "agent-config-id", "guildId": "guild-id", "channelId": "channel-id", "pairedUsers": { "discord-user-id": { "role": "operator", "mosaicUserId": "mosaic-user-id" } } } ] ``` Each binding's trusted `agentConfigId` must identify a provisioned database agent configuration whose name exactly matches its `instanceId`. Roles are `viewer`, `operator`, and `admin`. `viewer` cannot send agent turns. Runtime approval and stop operations require an `admin` pairing with a provisioned `mosaicUserId`. The bot needs Discord permissions to view/send messages in configured channels and create/send in public threads. A channel category is not an authorization boundary; threads inherit authorization only from their configured parent text channel. ## Shared contract The adapter implements `OfficialChannelAdapter` from `@mosaicstack/types`. `ChannelConversationRouteDto` carries only stable logical-agent/channel identity and a response target. Gateway durable-session and provider layers own runtime selection and handoff; Discord code must not import a harness SDK. ## Development ```bash pnpm --filter @mosaicstack/types build pnpm --filter @mosaicstack/discord-plugin typecheck pnpm --filter @mosaicstack/discord-plugin lint pnpm --filter @mosaicstack/discord-plugin test pnpm --filter @mosaicstack/discord-plugin build ```