Run AI coding agents on your computer, from your phone.
Patchbay bridges Telegram to AI coding agents running on your own machine — Claude Code, the Claude Agent SDK, Aider, OpenCode, and more — letting you develop software, manage infrastructure, and run autonomous agents from a mobile messaging app while your real workstation does the actual work.
Most "AI coding from your phone" tools mean chatting with an LLM that has no access to your real code, your real tools, or your real environment — fine for asking questions, useless for actual work. Patchbay inverts that: the real machine in another room does the real work, the phone just drives.
The architecture leverages Telegram's forum-mode group topics — persistent threaded conversations that already provide the routing primitives multi-project work needs. Each topic binds to a project directory and an agent session. Topic #myproject is an agent session in ~/Developer/myproject. Topic #another is one in ~/Developer/another. Switching between them is one tap. Each topic has full filesystem access, full tool access, and full process control on the actual workstation where the code lives.
Patchbay is the transport: a Python service that listens for Telegram messages, routes each to the right project, agent, and harness, runs the turn, and streams the response back. It survives its own self-edits, recovers from its own crashes, and is agnostic to which agent runs the turn.
Concrete examples of work driven from a phone with the workstation in another room:
- Long-form drafting on the move. A topic bound to a research/writing agent reads work history and drafts long-form documents on demand — cover letters, project briefs, design memos — written to a synced markdown vault, ready to review on landing.
- Repo work from anywhere. A topic bound to a project directory accepts plain-language change requests. The agent pulls logs, finds bugs, fixes code, runs tests, commits, deploys, and replies with the diff. The phone never holds a checkout; the workstation does.
- Inbox triage on a commute. A topic bound to an email-triage agent batches overnight inbox into priority buckets, flags items that need a human, drafts replies for the rest. Tap, dictate edits, send.
- Self-healing during downtime. Crash-loop detection runs autonomously. Three crashes within five minutes triggers a self-heal session that reads the traceback, finds the bug, writes the fix, validates it, and restarts. A Telegram message lands with the diff after the fact.
- Repo provisioning by message. A single message provisions a new GitHub repo — branch protection, default-branch convention, lint hooks, initial scaffold — all via API. Done in seconds without leaving Telegram.
The unifying property: the phone never holds the work. The workstation does. The phone is just the keyboard.
Each Telegram forum topic maps to an independent agent session. Multiple topics run in parallel, each with its own project directory, harness backend, and session state. Sessions persist across messages and auto-expire after configurable inactivity.
bridge.py Entrypoint -- Telegram handlers, commands, lifecycle
patchbay/ Core package
config.py Environment variables, paths, constants, logging
sessions.py Session persistence, sanitization, pending messages
parser.py Agent CLI output parsing (JSON array, NDJSON, single-object)
quota.py Quota/rate-limit detection and queue handoff
activity.py Structured JSON-lines activity logging
projects.py Chat-to-project directory mapping
self_heal.py Crash-loop / corrupt-session repair dispatcher
singleton.py Single-instance lock (avoids 409 getUpdates conflicts)
harness/ Pluggable agent backends
base.py Protocol + TurnEvent types + ChannelHandle protocol
claude_cli.py Claude Code CLI subprocess
claude_sdk.py Claude Agent SDK (typed messages, mid-turn push)
claude_sdk_channel.py Long-lived ClaudeSDKClient for inflight push
aider.py Aider with chat-history-file resume
opencode.py sst/opencode JSON event protocol
pi.py badlogicgames/pi multi-model agent
validate.py Pre-flight validation (syntax, imports, parser smoke tests)
run.sh Entry point with crash-loop detection and self-healing
A single Telegram chat would force the user to pick "what project am I working on" with every message. By making each forum topic an independent session, the user picks the project once when they enter the topic; routing is implicit thereafter. Multi-project work becomes tab-switching. Group permissions and notifications work per topic, so silencing or pinning a project is one tap.
The bridge does not care which agent runs the turn. The Harness protocol (patchbay/harness/base.py) is a streaming-event interface that all backends conform to: Claude Code CLI, Claude Agent SDK, Aider, OpenCode, badlogicgames/pi. New harnesses can be added by implementing one class. Capabilities (resume, mid-turn push, MCP, tool streaming) are advertised via HarnessCapabilities so the bridge can degrade gracefully when a backend doesn't support a feature.
Patchbay is frequently edited by an agent running through itself, which means a bad self-edit could brick the bridge. Three or more crashes in five minutes triggers an autonomous Claude Code session that reads the traceback, diagnoses the bug, writes the fix, validates it, and restarts. Self-heal incidents are logged to activity.jsonl so they are visible in the soak-test dashboard.
Before every bridge start, validate.py runs syntax checks, import checks, and parser smoke tests on the current source. If any fail, run.sh rolls back to a .bridge-known-good.py snapshot and starts that instead. This means a syntactically broken self-edit cannot stop the bridge from running.
Two bridge processes polling Telegram's getUpdates simultaneously will trigger 409 conflicts, log them 1000 times each, and never exit cleanly. patchbay/singleton.py holds an exclusive lock on .bridge.lock. If a stale PID is holding it (process is dead), the new bridge steals it and continues. If a live PID holds it, the new bridge waits for it to exit, then takes over. No babysitting required during launchd restarts.
Test suite is 708 tests across 47 test files covering the bridge, the parser, every harness, all command handlers, the self-heal path, the singleton lock, and chaos cases (partial JSON, slow drip, hangs, OOM-style exits). Pre-push hook runs the full suite. No CI on the remote — quality gates are local.
- Multi-harness — Claude Code CLI, Claude Agent SDK, Aider, OpenCode, badlogicgames/pi. Switch per topic via
/harness <name>. - Multi-project routing — Each Telegram topic binds to a project directory via
chat_projects.json. Each topic can target a different codebase. - Per-topic agent identity — Topics can load identity files (e.g.
SOUL.md,IDENTITY.md,AGENTS.md) into the agent's system prompt to specialize behavior per persona. - Crash recovery — Self-healing crash-loop detection: 3+ crashes in 5 minutes triggers an autonomous repair session.
- Pre-flight validation —
validate.pychecks syntax, imports, and parser behavior before every bridge start. On failure, rolls back to a known-good snapshot. - Quota handoff — When the agent hits a rate limit, the task is written to a queue file for background processing.
- Rich messaging — Markdown rendering, code blocks, photo and image handling with captions.
- Pending message batching — Messages arriving while the agent is processing are queued and batched into a single follow-up invocation.
- Mid-turn push (cc-sdk only) — On harnesses that support streaming input, follow-up messages are injected into the active turn at the next natural break — no queue, no friction reply.
- Remote control — Start
claude remote-controlsessions from Telegram for direct CLI access. - Session persistence — Sessions resume across messages and bridge restarts.
Anyone with a Mac and 30 minutes should be able to follow this end-to-end.
- macOS (uses launchd for process management)
- Python 3.13+
- uv for dependency management
- An agent CLI installed locally (any of: Claude Code, Aider, OpenCode, pi)
- Message @BotFather on Telegram, run
/newbot, follow the prompts. - Save the bot token he gives you. This goes in
.envasTELEGRAM_BOT_TOKEN. - Get your Telegram user ID by messaging @userinfobot. This goes in
.envasALLOWED_USER_IDS(comma-separated if multiple).
- Telegram → New Group → add your bot.
- Group settings → "Topics" → enable. (This converts the group into a forum.)
- Make the bot an admin with permission to read all messages and post in topics.
- Find the chat ID by sending a message and reading
bridge.logonce patchbay is running, or by using a tool like @RawDataBot.
git clone https://github.com/synodic-studio/patchbay-relay.git
cd patchbay-relay
uv sync
# Configure environment
cp .env.example .env
# Edit .env: set TELEGRAM_BOT_TOKEN, ALLOWED_USER_IDS, CLAUDE_PATH, CLAUDE_WORKING_DIR
# Optional: pre-bind topics to projects
cp chat_projects.example.json chat_projects.json
# Edit chat_projects.json with your real chat-and-thread IDs and project dirs./run.sh
# In another terminal:
tail -f logs/bridge.logSend a message in any topic. The bridge logs the chat-and-thread ID; copy it into chat_projects.json, set the project directory, and the next message routes to that project.
# Update paths in com.synodic.claude-telegram-bridge.plist (look for YOURUSER placeholders)
cp com.synodic.claude-telegram-bridge.plist ~/Library/LaunchAgents/com.synodic.patchbay-relay.plist
launchctl load ~/Library/LaunchAgents/com.synodic.patchbay-relay.plistThe plist sets KeepAlive: SuccessfulExit=false so the bridge auto-restarts on crash, with a 30-second ThrottleInterval backstop to prevent rapid-fire restart storms. run.sh does pre-flight validation; if validate.py fails, it rolls back to a known-good snapshot.
| Command | Description |
|---|---|
/start |
Show command list and your Telegram user ID |
/clearnew |
Start a fresh session (conversation history lost) |
/setproject |
Bind topic to a project directory |
/project |
Show current project and agent for this topic |
/harness <name> |
Switch the agent backend for this topic |
/kill |
Kill the active agent subprocess (session preserved) |
/restart |
Restart the bridge process |
/remote_control |
Start or stop a claude remote-control session |
/ping |
Liveness check with active session status |
The bridge is frequently edited by an agent running through itself. Five layers prevent self-edits from bricking it:
validate.py— Standalone validation: syntax check, import check, parser smoke testsrun.shpre-flight — Runsvalidate.pybefore starting; rolls back to known-good on failure- Crash loop detection — 3+ crashes in 5 minutes triggers an autonomous self-heal session
/restartgate — Validates before restarting; blocks restart on failureThrottleInterval: 30in launchd — backstop against rapid respawn
uv run pytest tests/ -q # run tests (708 tests)
uv run ruff check . # lint
uv run python validate.py # pre-flight smoke tests
uv run pytest tests/ --cov --cov-report=term-missing # with coveragePre-push hook runs the full test suite. There is no CI on the remote — quality gates are local.
MIT