Discord bot bridge for controlling and monitoring OpenCode sessions from a Discord server.
- Primary interaction mode is natural chat in control and managed session channels.
- Slash commands are fully supported for control/recovery workflows, but are no longer the primary day-to-day path.
- Full end-to-end setup for coding agents:
AGENT_BOOTSTRAP_PLAYBOOK.md.
- Requires
DISCORD_ENABLE_NATURAL_CHAT=trueand Message Content intent enabled in the Developer Portal. - In the control channel (
DISCORD_CONTROL_CHANNEL_IDorDISCORD_CONTROL_CHANNEL_NAME), talk to the bot normally. - You can say
list sessionsandlaunch session [n]directly. - Launched session channels are named from session title + session id suffix, auto-bound, and watch is enabled.
- In launched session channels, just chat normally with the bound OpenCode session.
- Slash commands are available; use them mainly for explicit control actions and recovery flows.
- Control Center channel: the main orchestration channel. Use it to discover sessions, launch/manage subchannels, and run administrative commands.
- Managed subchannel: a Discord text channel bound to one OpenCode session (
OpenCode session: ses_...in channel topic). - Routing behavior:
- Messages in Control Center go to the control session.
- Messages in a managed subchannel go to that subchannel's bound OpenCode session.
- Why this split exists: Control Center is for coordination and lifecycle; subchannels are for focused work per session.
/activate(run inside a managed subchannel):- Reloads the bound session in-place.
- Re-enables live streaming to that subchannel.
- Performs transcript catch-up from last delivered cursor (delta catch-up, not full replay).
/deactivate(run inside a managed subchannel):- Reloads the bound session in-place and interrupts in-flight work.
- Keeps the channel-session mapping intact.
- Pauses local live streaming in that subchannel until
/activateis run again.
- Use case: temporarily pause noisy streams in one subchannel without losing the session binding.
/help/sessionswith optionalmode,limit,minutes/use session:<id|[n]>/status/watch mode:on|off/activate/deactivate/interrupt/pending/perm request_id:<id> decision:once|always|reject [message]/question_reply request_id:<id> answers_json:[["A"],["B"]]/question_reject request_id:<id>/stopall/ask prompt:<message>/tail [count]
- Create a Discord app in Developer Portal.
- Create/generate bot token under Bot settings.
- Configure OAuth install link with scopes:
botapplications.commands
- In Bot -> Privileged Gateway Intents, enable Message Content Intent.
- Ensure bot has permissions to create/manage channels if using launch flow.
- Invite bot to your test server.
- Copy
.env.exampleto.envand fill values. - Optional: copy
bridge.config.example.jsontobridge.config.jsonfor local machine-specific settings.
cd .\path-to-your-dir\opencode_discord_bridge
npm install
npm run check
.\bootstrap.ps1 startTo inspect status/logs:
.\bootstrap.ps1 status
Get-Content .\.run\bridge.out.log -Wait
Get-Content .\.run\bridge.err.log -Wait- Use guild command registration (
DISCORD_GUILD_ID) during development for near-instant slash command updates. - Set
DISCORD_CONTROL_CHANNEL_ID(preferred) orDISCORD_CONTROL_CHANNEL_NAMEto define the main control channel. - Optional: set
DISCORD_CONTROL_SESSION_IDto pin a specific long-lived control session. - Set
DISCORD_ENABLE_NATURAL_CHAT=trueto enable message-based routing (control + launched session channels). - Set
DISCORD_NOTIFY_CHANNEL_IDS(preferred) orDISCORD_NOTIFY_CHANNEL_NAME(fallback) to post a "bridge restarted" message on startup. - Optional local config file (
bridge.config.json) fields:machineNamefor category naming prefixnotifyUserIdfor final response mentionsworkspaceDirandbridgeDirfor restart/attach command hints
- Keep bot token private and never commit
.env.
