Connect a Telegram bot to your conductor so you can talk to it from your phone. Each conductor pairs one-to-one with its own dedicated bot.
- A Telegram account
- A conductor already created (
agent-deck conductor setup <name>)
On Telegram:
- Message @BotFather ->
/newbot-> answer the prompts. - Copy the HTTP API token it gives you (looks like
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11). - Message @userinfobot -> it replies with your numeric Telegram user ID. Copy that too.
The bot has no commands yet; the conductor will register its own.
If you already have a conductor, re-run setup to add Telegram:
agent-deck conductor setup <name>Answer y at the Telegram prompt, paste the bot token and your user ID.
If creating a new conductor, the same wizard asks during initial setup.
agent-deck session start conductor-<name>This launches claude in a tmux pane with the Telegram plugin loaded per-session via --channels plugin:telegram@claude-plugins-official.
From your phone, message the bot:
/status
Within a few seconds the conductor should reply with the current fleet state:
[STATUS] Fleet summary
Running: 2 (frontend-app, api-fix)
Waiting: 1 (docs-pr — needs your call on the API rename)
Idle: 3
Error: 0
Verify exactly one poller is running:
pgrep -af "bun.*telegram" | wc -l
# Expected: 1 per active conductor with Telegram +------------------------------+
Telegram bot ------> | bridge.py daemon |
(1 bot = 1 conductor) | - polls Telegram getUpdates |
| - matches sender to user ID |
| - writes to conductor pane |
+--------+---------------------+
|
v
+------------------------------+
| conductor-<name> tmux pane |
| running `claude` with |
| --channels plugin:telegram |
| loaded for THIS session |
| only |
+------------------------------+
Two pieces do the work:
bridge.py— a Python daemon installed bysetup. It runs as a systemd/launchd service, polls Telegram, and feeds messages into the right tmux pane.- The
telegram@claude-plugins-officialplugin, loaded per-session via the session'schannelsfield. This is what lets Claude send messages back.
The token is stored in <conductor-dir>/.env (chmod 600).
agent-deck loads it via env_file in ~/.config/agent-deck/config.toml:
[conductors.work.claude]
config_dir = "~/.claude"
env_file = "~/.local/share/agent-deck/conductor/work/.env"Anyone who finds your bot's username can message it. The conductor refuses to act on messages from any user ID except yours. Treat the token as a secret anyway — anyone with it can impersonate the bot.
Bots cannot be shared between conductors.
Telegram's getUpdates is single-consumer.
Running two conductors against the same token produces 409 Conflict errors on every poll.
setup auto-disables enabledPlugins."telegram@claude-plugins-official" in ~/.claude/settings.json.
If it loads globally, every child session spawns a poller -> N pollers fighting for one token -> 409 errors.
The conductor session loads the plugin from its channels array, not from global enabledPlugins.
The env_file path in config.toml must match the conductor directory location.
If you renamed or moved the conductor dir, update the path.
Check that the bridge daemon is running:
pgrep -af bridge.pyCheck bridge logs:
tail -f ~/.local/share/agent-deck/conductor/bridge.logThe bridge daemon handles all conductors — one bridge process per machine multiplexes across N bots.
When you message a specific bot, the bridge routes it to the matching conductor.
Use the name: message prefix syntax to route explicitly when multiple conductors share a bridge.