Skip to content

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.

  1. @mention the bot in a listened channel (DISCORD_CHANNELS). The bot creates a thread from your message and starts a new session in it.
  2. Write in the thread to continue that session. No @mention is needed in the thread.
  3. 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.

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=false turns 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_CHANNEL to 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 in status.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=false turns it off.

Where the numbers come from:

  • Cost is the Agent SDK’s total_cost_usd for 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 in usage.json in 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 /usage data 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 %.
#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_CONCURRENT
thread C ── msg ───────► waits ⏳ ┘
  • Sessions: the thread ID → session ID map is stored in ~/.claude/discord-bridge/sessions.json (the claude-home volume). 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_files tool.
  • Attachments you post are saved to /workspace/.discord-inbox/<thread id>/ and their paths are given to Claude.
  • Permissions: sessions use CLAUDE_PERMISSION_MODE (default auto). When a tool call needs approval, the bot posts Allow / Deny buttons in the thread. Only allowed users can press them; no answer within BRIDGE_APPROVAL_TIMEOUT_S means deny.
  • Settings: sessions load user, project and local settings, CLAUDE.md files 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.
  • Only users in DISCORD_CHANNEL_USERS (or DISCORD_ALLOWED_USERS when 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.

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.

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.