Telegram Bridge

One connection, one unified inbox for your whole team of agents.

Configure the Telegram bridge once and Telegram becomes the inbox for every agent in your repo. Any agent that needs your input can ping you directly — the coordinator asking for a merge approval, an implementer hitting a design decision, a tester reporting a failure. You reply from Telegram and your reply routes back to whichever agent messaged you, not through a middle layer. Close the laptop, put your phone in your pocket, and your team still has a way to reach you.

This isn't a 1:1 channel to one "target" agent. It's a shared phone line for the entire team. You just configure a default agent for fresh messages you start from Telegram — everything else routes by context.

What you can do with it

How It Works

The bridge runs as a goroutine inside the Thrum daemon. It connects to the daemon's own WebSocket server as a client (the same way the browser UI does) and polls Telegram for inbound messages.

Telegram ←→ Bridge Goroutine ←→ Daemon WebSocket ←→ Agents
              (inside daemon)    (JSON-RPC 2.0)

Outbound (Thrum → Telegram): any agent that sends a message with your bridge user as a recipient gets forwarded to Telegram. Your coordinator can ping you, an implementer can ping you, a tester can ping you — they all land in the same chat.

Inbound (Telegram → Thrum) routes by context:

What you send from Telegram Where it lands
Fresh message (no Telegram reply-to) The configured --target agent
Reply to a message an agent sent you The agent that sent the original message
Reply to one of your own messages (edge case) Falls back to the configured --target
Fresh DM containing only y/n/yes/no/allow/deny Resolves the most-recent pending permission nudge for the sender

Threading: Telegram replies map to Thrum reply_to on the matching thread, so the agent sees your reply as a continuation of the original conversation, not a new thread.

The bridge is isolated — it connects via the public WebSocket RPC interface and never imports internal daemon packages.

Permission Prompt Approvals via Telegram

When an agent hits a blocking permission prompt in tmux, the daemon detects it and routes a supervisor nudge to your configured permission_supervisors. If the Telegram bridge user is one of those supervisors, the nudge lands in your Telegram DM automatically — no extra configuration needed. See Permission Prompt Detection for the full feature.

Replying from Telegram

Two paths work, with different token sets:

Path 1 — Reply-thread. Tap Telegram's Reply on the bot's nudge message, then type one of the approve or deny tokens. The bridge resolves the approval via the Telegram reply thread ID.

Path 2 — Fresh DM (new in v0.9.0). If the notification was dismissed or the thread is buried, open a new DM to the bot and type one of the strict fresh-DM tokens. The bridge looks up the sender's most-recent non-expired pending nudge and routes accordingly. One sender's fresh DM cannot resolve another supervisor's pending nudge — lookups are keyed on Telegram user ID.

The accepted tokens differ by path:

Path Approve tokens Deny tokens
Reply-thread y, yes, approve, a n, no, deny, d
Fresh DM y, yes, allow n, no, deny

Note the asymmetry: approve and a work via threaded reply but do NOT trigger fresh-DM resolution. If you want to approve without a thread, use allow or yes.

Prose messages ("yeah go ahead", "y please") do not trigger either path — the match requires an exact token after whitespace-trimming. A non-matching fresh DM routes normally to the configured --target agent.

Restart Resilience

Before v0.9.0, the Telegram↔Thrum message-ID mapping lived in an in-memory cache. A daemon restart between sending a nudge and receiving the supervisor's reply dropped the mapping, so the reply had no reply_to reference and could never resolve.

In v0.9.0 the mapping is written through to SQLite (telegram_msg_map table, schema v24) as a durable fallback. If the cache misses on reply, the daemon reads the SQLite row and routing continues as normal. Daemon restarts during an in-flight approval flow no longer silently fail.


Setup

1. Create a Telegram Bot

  1. Open Telegram and message @BotFather
  2. Send /newbot and follow the prompts
  3. Copy the bot token (looks like 123456789:AAH...)

2. Configure and Pair

Run configure with your bot token. The command writes the config, restarts the daemon, and enters a pairing flow that captures your Telegram user ID automatically:

thrum telegram configure \
  --token "123456789:AAHyour-token-here" \
  --target "@coordinator_main" \
  --user "your-username"

After writing the config, it prompts you to send a message from Telegram:

Pairing — send any message to your bot from Telegram (timeout: 60s)...

Message from: Jane Doe (ID: 123456789)
  Allow this user? [y/n]: y

Paired! Allowed users: [123456789]
  Bridge is live — no further restart needed.
Flag Description
--token Bot token from BotFather
--target Default agent for fresh messages you start from Telegram (with @). Replies route to the author of the message being replied to.
--user Your Thrum username (e.g., your-username)
--allow-from Skip pairing — set Telegram user ID directly
--chat-id Telegram chat ID for outbound (defaults to --allow-from)
--pair-timeout How long to wait for pairing message (default: 60s)
--skip-pair Write config only, don't pair

Re-pairing

If you need to pair again (e.g., after resetting the allow list), use the standalone pair command:

thrum telegram pair

This connects to the running daemon and waits for a Telegram message to capture your user ID.

Manual Setup (alternative)

If you already know your Telegram user ID, skip the interactive pairing:

thrum telegram configure \
  --token "123456789:AAHyour-token-here" \
  --target "@coordinator_main" \
  --user "your-username" \
  --allow-from 123456789

Then restart the daemon:

thrum daemon restart

3. Test It

Send a fresh message from Telegram to your bot. It lands in the configured default agent's inbox:

thrum inbox --unread

Now have any agent in the team send you a message:

thrum send "Hello from the terminal!" --to @your-username

It should appear in your Telegram chat. Reply to that Telegram message and your reply goes back to the sending agent — not the default target. That's the reply-aware routing doing its job.

You can repeat this with a different agent and each thread stays independent: fresh message → default agent, reply → original author.

Configuration Reference

The full configuration lives in .thrum/config.json under the telegram key:

{
  "telegram": {
    "token": "123456789:AAH...",
    "target": "@coordinator_main",
    "user_id": "leon-letto",
    "chat_id": 123456789,
    "allow_from": [123456789, 412587349],
    "allow_all": false,
    "enabled": true
  }
}
Field Type Description
token string Telegram bot token from BotFather. Required.
target string Default agent mention (e.g., @coordinator_main) for fresh messages you start from Telegram. Replies route to the original author. Required.
user_id string Your Thrum username. Required.
chat_id int Telegram chat ID for outbound messages. For DMs, same as your user ID.
allow_from int[] Telegram user IDs allowed to send messages. Empty = block all.
allow_all bool If true, allow all Telegram users (overrides allow_from). Default: false.
enabled bool Explicit enable/disable. Default: true when token is set.

CLI Commands

# Configure the bridge (interactive pairing)
thrum telegram configure --token <token> --target <agent> --user <username>

# Configure with known user ID (skip pairing)
thrum telegram configure --token <token> --target <agent> --user <username> --allow-from <id>

# Pair your Telegram account (bridge must be configured and daemon running)
thrum telegram pair

# Check bridge status
thrum telegram status

# Status as JSON
thrum telegram status --json

Security

The bridge follows a defense-in-depth security model:

Access control:

Pairing security:

Token hygiene:

Isolation:

Data flow:

Web UI

The Telegram bridge can also be configured from the web UI settings panel. Navigate to Settings → Telegram to set the token, target agent, and view bridge status.

The web UI's inbox has been redesigned as a conversation-style chat timeline (similar to Slack or Telegram). Select an agent from the conversation list to see the full bidirectional message history.

Troubleshooting

Bridge not starting:

thrum telegram status
# Check: is the token set? Is enabled = yes?
# Check daemon logs for "telegram bridge:" messages

Messages not arriving from Telegram:

Messages not forwarding to Telegram:

Reply from Telegram went to the wrong agent:

Connection drops after idle:

"Existing token will be replaced" prompt:

Next: Telegram Groups

Once your DM bridge is working, you can set up a shared Telegram group so your whole team can interact with the same agent. See Telegram Groups for setup.