Talk to your agents from Telegram
Turning loops into something you can steer: progress pings, blocking questions, and a remote control — all from a Telegram chat in your pocket.
In Prompts became loops I argued that the unit of work had shifted. A prompt used to be a one-shot: you ask, it answers, done. Now the unit of work is a loop — the agent keeps going, takes steps, checks results, and continues until the goal is met. Coding agents like Claude Code already live this way.
But loops have an awkward gap. Once an agent is off running for minutes or hours, you're no longer at the keyboard. And sooner or later the loop needs you: a decision, a missing credential, a "yes, ship it." If you're away, the loop stalls — or worse, the agent guesses. The loop runs without you, but it can't reach you.
So I built a tiny skill to close that gap: telegram-bridge. It lets an agent talk to me on Telegram while it works — send progress, ask blocking questions and wait for my answer, and — the part I like most — let me drive it from my phone. The loop keeps looping; I steer it from wherever I am.
What it does, in one screen
Three primitives, all stdlib Python (no installs, no SDK):
# progress — fire and forget
telegram.py notify "Tests green. Deploying to staging."
# blocking question — posts to Telegram, WAITS for your reply, prints it back
telegram.py ask "Migration will drop the legacy column. Proceed? (yes/no)"
# listen — wait for your next message and hand it to the agent as a new prompt
telegram.py listen
That's the whole surface. The agent calls notify/ask when it has something to say; it runs listen in the background to receive instructions from you.
The trick that keeps it cheap
Here's the part that matters if you've ever watched an agent burn tokens idling.
A naive "let me check Telegram every few seconds" loop is the model polling — and every tick re-bills the entire conversation context. That's expensive and pointless.
listen flips it. The polling is done by a background shell process doing a long-poll against Telegram's API — pure network, zero model tokens while it waits. The model is asleep. The instant a message arrives, the process exits and hands the text to the agent, which wakes up once, does the work, relaunches listen, and goes back to sleep.
Token cost scales with the number of messages you send, not with how long you're away. Idle is free.
This is the loops idea taken one step further: the loop doesn't just run on its own — it parks for free and resumes on a human signal, and that signal can come from your pocket.
A dispatcher you didn't have to build
The first version handled one agent. Then the obvious question: what if I'm running several agents at once? One refactoring a backend, one writing docs, one babysitting CI. They'd all share one bot, and Telegram's update queue is consume-once — so they'd race for your messages. Chaos.
The fix is a broker: a single background process that owns the Telegram connection and routes each message to the right agent. It's a dispatcher — except you don't write it, configure it, or even think about it. It's plug & play and on by default:
- The first command auto-starts the broker (detached, survives the session).
- Each agent auto-names itself after its project directory.
- Every outbound message is auto-tagged
[backend] …,[docs] …so you always know who's talking. - The broker shuts itself down when nobody's around.
You never run a setup step. It just works.
How you target a session from your phone
[backend] ✅ migration applied, running tests
[docs] ❓ generate the API reference now? reply to confirm
- Reply to a message → the answer goes to that agent. Zero typing of names.
- Prefix
docs: regenerate the changelog→ routes by name. - Send
/sessions→ a tap-to-pick inline keyboard of live agents. stopstops the one you're talking to;stop allstops everybody.
And because messages are tagged and routed, when a second agent shows up the broker drops you a one-time heads-up explaining how to switch — so there's nothing to memorize.
Side note for non-Claude setups: the whole thing is ~400 lines of stdlib Python driven by exit codes (0= message on stdout,4= stop,3= timeout). Any headless agent or script that can shell out can use it as a Telegram control plane — a ready-made dispatcher for agents that don't have one.
It also persists its read offset, so if the broker restarts, messages you sent while it was briefly down are delivered when it comes back instead of vanishing.
Where this is genuinely useful
- Long unattended runs. Kick off a multi-hour refactor, walk away, get pinged only when it finishes or needs a call. Answer from a café.
- Human-in-the-loop without babysitting. Dangerous step? The agent asks and blocks until you say go — no guessing, no sitting at the terminal.
- Steering from your phone. New idea at dinner? Message the bot; the agent picks it up as its next prompt, exactly as if you'd typed it in the app.
- Fleet control. Several agents in parallel, one chat, each message routed to the right one — a dispatcher you didn't build.
- Security by default. An allowlist means a stranger who finds the bot sees nothing and can't drive anything; only your chat id can.
Getting started
# install into Claude Code (or any skills-compatible agent)
npx skills add https://github.com/jcordon5/claude-telegram-bridge.git
Create a bot with @BotFather, drop the token + your chat id into the skill's .env, and tell your agent: "work on this and ping me on Telegram if you get stuck — and let me drive you from there." That's it.
It pairs naturally with the loop mindset I wrote about in Prompts became loops: if your prompts have already become loops, this is how you put a remote control in your hand. The agent runs the loop; you steer it from your pocket.