Thread sessions
By default (BOT_MODE=bridge) the container runs a small Node service, bridge/, instead of the Discord channel plugin. It uses discord.js and the Claude Agent SDK and gives every Discord thread its own Claude Code session.
Using it
Section titled “Using it”- @mention the bot in a listened channel (
DISCORD_CHANNELS). The bot creates a thread from your message and starts a new session in it. - Write in the thread to continue that session. No @mention is needed in the thread.
- Start another thread for another task. Each thread has its own context, and threads run in parallel.
Reactions on your message show what happened:
| Reaction | Meaning |
|---|---|
| 👀 | A new turn started. |
| ➕ | Claude was already working in this thread; your message joined the running turn. |
| ⏳ | All slots are busy (BRIDGE_MAX_CONCURRENT); the turn starts when one frees up. |
Commands inside a session thread:
| Command | Effect |
|---|---|
!stop |
Interrupts the running turn. |
!session |
Shows the thread’s Claude session ID. |
!model / !effort |
Shows the thread’s model and effort. |
!model sonnet |
Uses another model in this thread: opus, fable, sonnet, haiku or a full ID like claude-sonnet-5-5. A running turn switches right away. |
!effort medium |
Effort for this thread from the next message: low, medium, high, xhigh, max. |
!model reset / !effort reset |
Back to the server default. |
!model default sonnet / !effort default medium |
Changes the default for every thread without its own setting. … default reset goes back to CLAUDE_MODEL / CLAUDE_EFFORT. |
!usage |
Estimated spend of the thread, today (per person) and the last 7 days, plus plan usage. |
!help |
Lists the commands. |
!usage, !help and the default forms also work directly in a listened channel and in the status channel, without starting a session.
Model and usage
Section titled “Model and usage”Each thread runs on the server default model and effort (CLAUDE_MODEL / CLAUDE_EFFORT, or what !model default / !effort default set) unless the thread has its own (!model … in it). Defaults changed from Discord are stored in settings.json in the state dir and survive restarts.
Usage is shown in four places:
- Footer: after each answer the bot posts a small grey line such as
Opus 5.5 · high · 14 steps · 3m 12s · ~$0.38 · 5h 42%: model, effort, tool steps, time, estimated cost of that turn and the 5-hour plan window.BRIDGE_FOOTER=falseturns it off. !usage: a reply with this thread’s total, today’s total per person, the last 7 days and the plan windows with their reset times.- Pinned status message: set
BRIDGE_STATUS_CHANNELto a channel ID. The bot posts one message there, pins it, and edits it when a turn starts or ends and every 5 minutes: default model, plan usage bars, which threads are working, and today’s / this week’s spend. The message ID is kept instatus.json; delete the message and the bot posts a new one. - Bot status: the bot’s Discord status shows e.g.
Opus 5.5 · 5h 42% · 1 working.BRIDGE_PRESENCE=falseturns it off.
Where the numbers come from:
- Cost is the Agent SDK’s
total_cost_usdfor each turn, at API list prices. With an API key it matches the bill; with a Claude subscription it is only an estimate of how much work was done. Totals are kept for 90 days inusage.jsonin the state dir. A turn’s cost is counted for the person whose message started it. - Plan usage (5-hour and 7-day windows) is only available with a subscription login. It comes from the rate-limit info Claude Code receives with each answer, and from the SDK’s experimental
/usagedata when the login allows it. It updates whenever the bot answers, so between answers it shows the last known value; a window whose reset time has passed is shown as 0 %.
How it works
Section titled “How it works”#channel ── @bot msg ──► new thread ──► new session (UUID stored for the thread)thread A ── msg ───────► session A ┐thread B ── msg ───────► session B ├─ run in parallel, up to BRIDGE_MAX_CONCURRENTthread C ── msg ───────► waits ⏳ ┘- Sessions: the thread ID → session ID map is stored in
~/.claude/discord-bridge/sessions.json(theclaude-homevolume). The first turn creates the session with that ID; later turns resume it, also after a restart. - Turns: within one thread, turns run one at a time. A message sent while Claude is working is passed into the running turn.
- Replies: Claude’s text is posted to the thread, split into 2000-character messages. Claude can attach files with its
send_filestool. - Attachments you post are saved to
/workspace/.discord-inbox/<thread id>/and their paths are given to Claude. - Permissions: sessions use
CLAUDE_PERMISSION_MODE(defaultauto). When a tool call needs approval, the bot posts Allow / Deny buttons in the thread. Only allowed users can press them; no answer withinBRIDGE_APPROVAL_TIMEOUT_Smeans deny. - Settings: sessions load user, project and local settings,
CLAUDE.mdfiles and memory like the CLI does. The Discord plugin is switched off inside sessions. - Shared workspace: all sessions work in
/workspace. Parallel threads editing the same files can conflict, so give separate tasks to separate repos or branches.
Who can use it
Section titled “Who can use it”- Only users in
DISCORD_CHANNEL_USERS(orDISCORD_ALLOWED_USERSwhen empty) can start sessions, talk in session threads or press approval buttons. Everyone else is ignored. - Only threads the bridge created under a listened channel are sessions. Other threads are ignored.
- DMs are ignored in bridge mode.
Resource use
Section titled “Resource use”Each running session is a Claude Code process using roughly 250 MB of memory or more. With the default 4 GB container limit, keep BRIDGE_MAX_CONCURRENT at 3–4. Parallel sessions also use Claude quota or API credits at the same time.
Switching back
Section titled “Switching back”Set BOT_MODE=plugin and restart to get the original single-session plugin setup. Both modes use the same bot, so never run both at once.