Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Patchbay

Run AI coding agents on your computer, from your phone.

Status: alpha, actively developed. Patchbay has run my studio for months. It is currently pi-only: the Claude Agent SDK backends (cc-sdk, cc-sdk-mop) were removed because Anthropic is ending subscription (Max) coverage for embedded SDK use, so pi (multi-model via litellm) is the viable engine now. The Harness protocol is kept as an internal seam for adding a future non-Claude backend, so this stays a harness-agnostic design with one backend live today. MIT-licensed; fork freely.

Patchbay bridges Telegram to a coding agent running on your own machine — currently pi, a multi-model agent — letting you develop software, manage infrastructure, and run autonomous agents from a mobile messaging app while your real workstation does the actual work.


Why this exists

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.

What it enables

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.
  • Survives its own bad edits. Because the bridge is edited from a phone through itself, a broken self-edit can't brick it: pre-flight validation rolls back to a known-good snapshot, and a crash loop backs off and logs the traceback for fix-forward.
  • 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.

Architecture

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.

flowchart TD
    TG["Telegram Bot API<br/>(long-polling)"] --> BR["bridge.py<br/>message router"]
    BR --> CP["chat_projects.json<br/>topic → directory + model"]
    BR --> H{"harness<br/>(per topic)"}
    H -->|pi| PI["pi<br/>(multi-model via litellm)"]
    PI --> OUT["response chunking<br/>+ typing indicators"]
    OUT --> SEND["Telegram Bot API<br/>send response"]
Loading

The Harness protocol allows other backends, but pi is the only one live today (see the status note above). New harnesses slot in at the harness node.

Module map

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)
  models.py               Per-chat pi model mapping (litellm aliases, default "small")
  efforts.py              Per-chat thinking-effort setting
  quota.py                Quota/rate-limit detection (is_quota_error)
  activity.py             Structured JSON-lines activity logging
  projects.py             Chat-to-project directory + harness mapping
  self_heal.py            In-process repair handlers (corrupt session, OOM retry)
  singleton.py            Single-instance lock (avoids 409 getUpdates conflicts)
  outbound.py             Outbound send/queue plumbing
  telegram_send.py        Telegram send helpers (chunking, markdown, media)
  text_split.py           Message splitting
  file_send.py            File / photo delivery
  markdown_config.py      Markdown rendering config
  log_filters.py          Log filtering
  logrotate.py            Log rotation
  runtime.py              Process runtime state
  reply_store.py          Reply-text cache
  commands/               Telegram command handlers
    lifecycle.py          /start, /clearnew, /kill, /restart, /ping
    project.py            /setproject, /project, /harness, /model, /effort, /remote_control
    observability.py      /usage, /health, /activity, /soak
    context.py            /context, /compact
    heartbeat.py          /heartbeat
    inquiry.py            Shared one-shot harness resolver (/context, /usage)
  harness/                Pluggable agent backends (pi live; protocol kept as a seam)
    base.py               Protocol + TurnEvent types + capability protocols
    pi.py                 badlogicgames/pi multi-model agent (the live backend)
    pi_session.py         Read context/usage from pi session transcripts
    context_estimate.py   Client-side token/context estimator (litellm-backed)
validate.py               Pre-flight validation (syntax, imports, parser smoke tests)
run.sh                    Entry point with crash-loop detection + known-good rollback

Design decisions

Forum topics as sessions, not chats

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.

Pluggable harness, single transport

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. Today there is exactly one live backend, badlogicgames/pi (pi, patchbay/harness/pi.py); the earlier Claude Agent SDK backends (cc-sdk, cc-sdk-mop) were removed when Anthropic ended embedded-subscription SDK use. The protocol is kept deliberately, so a future non-Claude framework 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. Note pi already gives multi-model coverage via litellm; the harness seam is for a different framework, not a different model.

Crash-loop detection + known-good rollback

Patchbay is frequently edited by an agent running through itself, which means a bad self-edit could brick the bridge. The safety net is validate.py pre-flight plus rollback to a .bridge-known-good.py snapshot (next section), so a broken self-edit can't stop the bridge from running. On top of that, three or more crashes in five minutes is detected as a loop: run.sh saves the crash tail to logs/crash-loop.log, backs off, and leaves it for fix-forward. (An earlier version spawned an autonomous Claude Code repair session here; that was removed once rollback proved to cover the bricking case, and because it depended on the Claude CLI.) Separately, in-process handlers in self_heal.py quarantine corrupt session files and recommend retries on OOM.

Pre-flight validation as known-good rollback

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.

Single-instance lock, self-stealing

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.

640 tests, all in plain pytest

Test suite is 640 tests across 44 test files covering the bridge, the parser, the pi harness, all command handlers, the self-heal handlers, 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.

Features

  • Harness-agnostic (pi-only today) — the Harness protocol is retained as an internal seam; the one live backend is pi (badlogicgames/pi, multi-model via litellm). The cc-sdk/cc-sdk-mop backends were removed under Anthropic's embedded-subscription change.
  • 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-loop detection + known-good rollbackvalidate.py pre-flight rolls back to a known-good snapshot on a bad self-edit; 3+ crashes in 5 minutes backs off and logs the crash tail for fix-forward. (The old autonomous Claude-repair trigger was removed; rollback already covers the bricking case.)
  • Pre-flight validationvalidate.py checks syntax, imports, and parser behavior before every bridge start. On failure, rolls back to a known-good snapshot.
  • Rate-limit notice — When the agent hits a quota/rate limit, the turn returns a plain retry notice. (An earlier Forge background-resume handoff was removed with Forge's retirement.)
  • Rich messaging — Markdown rendering, code blocks, photo and image handling with captions.
  • Pending message batching — messages arriving mid-turn are queued and batched into a single follow-up invocation when the current turn finishes. (Mid-turn push was a cc-sdk streaming feature; with pi the debounce queue is the path.)
  • Remote control — Start claude remote-control sessions from Telegram for direct CLI access.
  • Session persistence — Sessions resume across messages and bridge restarts.

Self-host walkthrough

Anyone with a Mac and 30 minutes should be able to follow this end-to-end.

1. Prerequisites

  • macOS (uses launchd for process management)
  • Python 3.13+
  • uv for dependency management
  • pi on your PATH — the live harness (multi-model via litellm). uv sync installs the Python deps.

2. Get a Telegram bot

  1. Message @BotFather on Telegram, run /newbot, follow the prompts.
  2. Save the bot token he gives you. This goes in .env as TELEGRAM_BOT_TOKEN.
  3. Get your Telegram user ID by messaging @userinfobot. This goes in .env as ALLOWED_USER_IDS (comma-separated if multiple).

3. Make a forum-mode group

  1. Telegram → New Group → add your bot.
  2. Group settings → "Topics" → enable. (This converts the group into a forum.)
  3. Make the bot an admin with permission to read all messages and post in topics.
  4. Find the chat ID by sending a message and reading bridge.log once patchbay is running, or by using a tool like @RawDataBot.

4. Clone and configure

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

5. Smoke test

./run.sh
# In another terminal:
tail -f logs/bridge.log

Send 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.

6. Persist as a launchd service

# 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.plist

The 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.

7. Optional: macOS TCC watcher

Homebrew upgrades change Cellar binary paths, which silently revokes any TCC permissions (Full Disk Access, Calendar, Contacts) granted to the old binary — a known cause of mid-turn hangs for agents that touch those resources. scripts/tcc/ ships a separate optional launchd agent that watches for these resets and reports them. It does not start automatically with the bridge. See scripts/tcc/README.md for install and the brew-upgrade workflow.

Telegram commands

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

Self-edit safety

The bridge is frequently edited by an agent running through itself. Five layers prevent self-edits from bricking it:

  1. validate.py — Standalone validation: syntax check, import check, parser smoke tests
  2. run.sh pre-flight — Runs validate.py before starting; rolls back to known-good on failure
  3. Crash-loop detection — 3+ crashes in 5 minutes backs off and logs the crash tail (logs/crash-loop.log) for fix-forward
  4. /restart gate — Validates before restarting; blocks restart on failure
  5. ThrottleInterval: 30 in launchd — backstop against rapid respawn

Development

uv run pytest tests/ -q                                 # run tests (640 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 coverage

Pre-push hook runs the full test suite. There is no CI on the remote — quality gates are local.

Related projects

  • model-output-protocol — output-shaping layer between agent and user. Was wired as the cc-sdk-mop harness (removed with the Claude SDK backends); kept as a reference for when a harness needing output filtering returns.
  • patchbay-url-scheme-wrapper — Cloudflare Worker that rewrites custom URL schemes (obsidian://, x-apple-reminderkit://, ...) into tappable https:// links for Telegram. Point the bridge at your deployment with PATCHBAY_URL_WRAPPER=https://your-domain.example.

License

MIT

About

Patchbay Relay — run AI coding agents on your computer, from your phone. Telegram bridge to the Claude Agent SDK, cc-sdk-mop, or pi. Alpha; superseded by Hermes.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages