Heterogeneous AI agents talking to each other in a Telegram group — with real loop-suppression, not just "put two bots in a chat."
Claude Code, Codex, and Gemini as independent full-stack bots, coordinated over a Telegram-native envelope protocol (A2A-TG) with generation-counted loop guards. Always-on, self-hosted, owner-gated.
English | 简体中文
On the A2A name. The A2A protocol was originally proposed by Google and is now a Linux Foundation project. This repository ships A2A-TG — an IM-scenario envelope inspired by A2A with generation-based loop suppression and chat-scoped idempotency. A2A-TG is not compatible with official A2A and is not affiliated with the official project. See the A2A-TG v1 spec.
Remote Control = your phone watches the terminal. Channels = the terminal receives phone messages. This project = heterogeneous agents collaborating in a group chat, and the chat IS the terminal.
You: @cc-alpha Analyze this API design
Alpha: [deep analysis, writes to shared context]
You: @cc-beta Write integration tests based on Alpha's analysis
Beta: [reads Alpha's analysis from shared context, writes tests]
You: @cc-gamma Review both — any gaps?
Gamma: [reads everything, reviews]
You: @cc-delta Ship it — commit and push
Delta: [reads full context, commits]
4 agents, 1 group, shared memory, zero noise. The same workflow that takes 4 terminal windows on your desk — now fits in your pocket.
Four different jobs this project does. Each entry point is independent — you do not need to read the whole README to use one mode.
| If you want to… | Start here | Core tech |
|---|---|---|
| Control one Claude Code from your phone | Quick Start | Single bot, Agent SDK |
| Run N parallel Claude sessions with shared memory (War Room) | Parallel Sessions → Multi-Instance Deployment | @mention dispatch + pluggable shared context (SQLite/Redis) |
| Let Claude and Codex actively talk to each other in a group | A2A-TG: Heterogeneous agents in one group chat | A2A-TG envelope + 5-layer loop guard |
| Read the protocol / embed A2A-TG in your own bot | A2A-TG v1 spec | HTTP/JSON envelope, generation cap |
On your desktop you run 4-5 Claude Code windows simultaneously. Now do the same from your phone:
TG Bot 1 (🟣) ──→ CC Instance 1 ──┐
TG Bot 2 (🔵) ──→ CC Instance 2 ──┤── Shared: CLAUDE.md + MCP memory + ~/.claude/
TG Bot 3 (🟢) ──→ CC Instance 3 ──┤
TG Bot 4 (🟡) ──→ CC Instance 4 ──┘
Each bot runs an independent CC process with its own session. Not a thin API client — full Claude Code with all native tools (Bash, Read, Write, Edit, Glob, Grep, WebFetch), skills, hooks, and MCP servers. All instances share the same memory layer (CLAUDE.md, memory-store, project settings) — what you tell one, the others already know. No memory fragmentation, no sync overhead.
Setup takes 30 seconds per instance: create a bot with @BotFather, copy a config, start a process. See Multi-Instance Deployment below.
Want different personalities? Point each bot's cwd to a directory with its own CLAUDE.md. CC loads global rules from ~/.claude/CLAUDE.md + per-bot persona from the workspace — just like OpenClaw's SOUL.md, but with CC's full skill/hook/MCP stack behind it.
~/.claude/CLAUDE.md ← shared rules (memory, safety, workflow)
~/bots/researcher/CLAUDE.md ← "You are a deep research analyst..."
~/bots/reviewer/CLAUDE.md ← "You are a senior code reviewer..."
~/bots/writer/CLAUDE.md ← "You are a content strategist..."
Walk away from your desk. Open Telegram. /new starts a fresh session. /resume 3 picks up where you left off. /peek 5 reads a session without touching it. /model switches models on the fly. Full session lifecycle from your phone — no terminal required.
The session picker shows the last thing you said as the title — not a truncated UUID. Tap a button or type /resume 3. Sessions from all sources (this bot, terminal CLI, other bridges) appear in one unified list.
Input has always been bidirectional: text, photos, documents, voice all flow to CC. Now output is too:
- Screenshots: CC takes a screenshot → image appears in your TG chat automatically
- Files: CC creates or references a file → bridge detects the path and sends it as a TG attachment
- Long code: Output >4000 chars with >60% code → sent as a file attachment with preview summary
The bridge captures images from SDK tool results (base64 data from Read/peekaboo/screenshot tools) and scans CC's response text for file paths. No manual copy-paste, no "where did you save it?" — files just appear in the chat.
Reply to cancel: Task taking too long? Send
/cancelto abort. Need context from a previous message? Reply to it — the quoted text is automatically included as context.
Put @claude-bot and @codex-bot in the same Telegram group. Ask Claude to review code — Codex reads the reply via shared context and offers its own take automatically. Built-in loop guards and circuit breakers prevent runaway bot-to-bot conversations. For DM cross-checking, bots communicate directly via MCP/CLI — no relay needed.
Put all 4 CC bots in one Telegram group. Each bot stays silent until @mentioned — no crosstalk, no chaos. But every bot can read what the others said via a pluggable shared context store (SQLite by default, Redis as an option for multi-bot / Docker setups — see Storage Backend Comparison). You orchestrate — they execute.
Two collaboration modes in one project:
- A2A mode (CC + Codex group): bots auto-respond to each other with loop guards — for brainstorming and debate
- War Room mode (multi-CC group): @mention only — for coordinated parallel execution
For softer group-chat behavior, allowlist the room with shared.discussChatIds. Allowlisted rooms default to Discuss mode: each bot can read an unmentioned human message and independently decide whether it has something useful to add. A bot that is explicitly @mentioned or replied to must answer; the others still self-select. Bots that opt out stay silent; bots that opt in send normal text only. Use /discuss off to quiet a room explicitly, and /discuss on to re-enable it. The bridge suppresses progress bubbles in this mode but keeps Telegram's native typing indicator, so the room stays quiet without becoming opaque.
Done discussing? /export dumps the entire cross-bot conversation as a Markdown file — full audit trail, every bot's contribution timestamped.
macOS LaunchAgent or Docker keeps the bridge running in the background. Sessions persist in SQLite across restarts and reboots — pick up where you left off after a reboot, a network drop, or a flight. Code and credentials never leave your machine. Owner-only access by default.
Not a toy — built for all-day use:
- Send retry with backoff: Exponential retry (3 attempts, 1s→2s→4s + jitter), auto-parse Telegram 429 rate limits, HTML→plaintext fallback on parse errors
- Sliding-window rate limiter: Configurable per-chat throttle (default 10 req/60s), auto-cleanup of expired windows
- FlushGate message batching: 800ms aggregation window (max 5 buffered), prevents rapid-fire messages from flooding the chat
- Graceful shutdown: 25-second drain timeout for active queries, force-abort hung tasks, progress message cleanup
- Live streaming preview: Real-time editMessage updates (2s throttle, 20-char min delta), tool call collapsing ("Bash x5" instead of 5 lines)
- Polling conflict recovery: Automatic detection and backoff when multiple processes poll the same bot token
Why this matters: Claude's official tools give you one session, tied to a terminal. OpenClaw gives you one bot per session, isolated memory. This project gives you N parallel sessions with shared memory, persistent state, and full CC capabilities — the same productive workflow you have on desktop, now available everywhere you have Telegram.
Prerequisites: Bun runtime, a Telegram bot token (from @BotFather), and at least one backend CLI: Claude Code, Codex, or Gemini CLI.
git clone https://github.com/AliceLJY/telegram-ai-bridge.git
cd telegram-ai-bridge
bun install
bun run setup --backend claude # interactive wizard: prompts for owner ID, bot token, etc.
bun run check --backend claude
bun run start --backend claudeRun
setupon its own — it's interactive. It's a prompt-driven wizard that waits for your input and writesconfig.jsonfor you, so don't paste the whole block at once.What
checkverifies (and what it doesn't): it validates config shape — required fields present, token format looks plausible. It does not confirm the backend CLI is installed and logged in, or that the token is real — a well-formed but fake token passescheckand only fails at runtime. Make sure the Prerequisites above are actually in place.Prefer a non-interactive flow? Run
bun run bootstrap --backend claudeto generate aconfig.jsontemplate, then edit it by hand (ownerTelegramId,telegramBotToken,tasksDb) beforecheck. See the Configuration section below.
Want parallel agents? Add a second bot in 30 seconds — see Multi-Instance Deployment.
Run multiple bots for parallel workflows:
@cc-alpha→ Claude Code instance 1 (primary)@cc-beta→ Claude Code instance 2 (parallel tasks)@cc-gamma→ Claude Code instance 3 (parallel tasks)@your-codex-bot→ Codex (different backend)
Each Claude instance shares memory automatically. No configuration needed — CC's memory lives in ~/.claude/, not in the bot.
Supported backends:
| Backend | SDK | Status |
|---|---|---|
claude |
Claude Code (via Agent SDK) | Recommended |
codex |
Codex CLI (via Codex SDK) | Recommended |
gemini |
Gemini Code Assist API | The "quiet scribe" — see note below |
Gemini's niche in this setup. The Gemini backend is best used as an overnight note-taker in a multi-agent group: let Claude and Codex brainstorm, and have Gemini read along and summarize the conversation on a schedule. It is the least chatty of the three backends, which turns out to be a fit for that role. Nothing in the code enforces a read-only mode — you shape the behavior through the per-bot
CLAUDE.md/ prompt. It runs through Gemini Code Assist API, not a full CLI terminal, so capabilities are narrower than Claude Code or Codex.
Core rule: One bot = one process = one independent agent. Run as many as you need.
The bridge is transparent. Your TG bot inherits whatever skills, MCP servers, and hooks your local CC has. If CC can browse the web, generate images, or query databases in terminal — it can do the same through Telegram. The bridge adds session management; the capabilities come from CC itself.
Sessions are sticky: messages continue the current session until you explicitly change it.
| Command | Description |
|---|---|
/help |
Show all commands with descriptions |
/new |
Start a new session |
/cancel |
Abort the currently running task |
/sessions |
List recent sessions |
/peek <id> |
Read-only preview a session |
/resume <#|id> |
Resume by sequence number or session ID |
/model |
Pick a model for the current bot |
/status |
Show backend, model, cwd, and session |
/discuss status|on|off |
Control opt-in Discuss mode for allowlisted group chats |
/dir |
Switch working directory |
/tasks |
Show recent task history |
/verbose 0|1|2 |
Change progress verbosity |
/cron |
Manage scheduled tasks |
/export |
Export group shared context as Markdown file |
/doctor |
Run health check |
/a2a |
Show A2A bus status, peer health, and loop guard stats |
Claude Code now ships Remote Control (Feb 2026) and a Telegram channel plugin (Mar 2026). Both let you talk to Claude from your phone. Neither gives you session management, multi-backend support, or agent-to-agent collaboration.
| Key differentiator | Remote Control | Channels | OpenClaw | This project |
|---|---|---|---|---|
| Parallel sessions | — | — | 1 bot = 1 session | N bots, shared memory |
| Session management (new/resume/peek) | — | — | — | ✅ Full lifecycle |
| Image & file output relay | Terminal only | — | — | ✅ Auto-sent to chat |
| War Room (multi-agent) | — | — | — | ✅ @mention + shared context |
| Multi-backend (Claude/Codex/Gemini) | Claude only | Claude only | Provider-locked | ✅ All three |
| Always-on daemon | Terminal must stay open | Session-tied | Gateway | ✅ LaunchAgent / Docker |
| Production reliability | — | — | — | ✅ Retry, rate-limit, drain |
What official tools do better: Remote Control streams full terminal output. Channels relay tool-approval dialogs natively. This project optimizes for a different job: persistent, multi-agent session management entirely from Telegram.
Full comparison (26 features)
| Feature | Remote Control | Channels (TG plugin) | OpenClaw | This project |
|---|---|---|---|---|
| Parallel sessions (multi-instance) | — | — | 1 bot = 1 session | N bots, N parallel CC instances, shared memory |
| Create new sessions from phone | — | — | — | /new |
| Browse & resume past sessions | — | — | — | /sessions /resume /peek |
| Switch models on the fly | — | — | Per-bot config | /model with inline buttons |
| Claude + Codex + Gemini backends | Claude only | Claude only | Provider-locked | All three, per-chat switchable |
| Tool approval from phone | Partial (limited UI) | Yes | Yes | Inline buttons: Allow / Deny / Always / YOLO |
| War Room (multi-agent command center) | — | — | — | @mention dispatch + pluggable shared context (SQLite/Redis) |
| Multi-agent group collaboration | — | — | — | A2A bus + shared context |
| Cross-agent collaboration | — | — | Gateway channels | A2A broadcast (groups) + MCP/CLI (DMs) |
| Real-time progress streaming | Terminal output only | — | Yes | Live text preview (editMessage streaming) + tool icons + 3 verbosity levels |
| Rapid message batching | N/A | — | — | FlushGate: 800ms window, auto-merge |
| Photo / document / voice input | — | Text only | Yes | Auto-download + reference in prompt |
| Image / file output relay | Terminal only | — | — | Screenshots & files auto-sent to TG chat |
| Cancel running task | Ctrl+C in terminal | — | — | /cancel — abort from phone |
| Message reply context | N/A | — | — | Reply to any message → quoted text as context |
| Smart quick-reply buttons | — | — | — | Yes/No + numbered options (1. 1、 1) formats) |
| Runs as background daemon | Terminal must stay open | Session must be open | Yes (Gateway) | LaunchAgent / Docker |
| Survives network interruptions | 10-min timeout kills session | Tied to session lifecycle | Gateway reconnect | SQLite + Redis persistence |
| Memory shared across instances | N/A | N/A | Per-bot isolated | All instances share CLAUDE.md + MCP memory |
| Per-bot persona | N/A | N/A | SOUL.md per bot | Per-bot CLAUDE.md workspace + shared global rules |
| Group context compression | N/A | N/A | N/A | 3-tier: recent full / middle truncated / old keywords |
| Shared context backend | N/A | N/A | N/A | SQLite / JSON / Redis (pluggable) |
| Task audit trail | — | — | — | SQLite: status, cost, duration, approval log |
| Loop guard for bot-to-bot | N/A | N/A | N/A | 5-layer: generation cap + AI self-decline + no-rebroadcast + idempotency + circuit breaker |
| Production reliability | — | — | — | Exponential retry, rate-limit, FlushGate batching, graceful drain |
| Stable release | Yes | Research preview | Yes | Yes (v4.1) |
Migrating from OpenClaw?
Every OpenClaw feature has a direct equivalent — most of them are just CC running natively behind the bridge:
| OpenClaw feature | How this project handles it |
|---|---|
| IM integration (Telegram/WhatsApp) | grammy Telegram bot + Claude Code Agent SDK — runs full CC, not an API wrapper |
| Multi-agent routing | A2A bus (auto-debate) + War Room (@mention dispatch) |
| Skills | CC native skills (~/.claude/skills/) — no conversion needed |
| Memory system | CC native (CLAUDE.md + MCP memory like memory-store) — shared across all instances |
| Cron / scheduled tasks | CC native cron — runs inside the agent, results delivered to TG |
| Tool execution (bash/fs/web) | CC native tools — Bash, Read, Write, Edit, Glob, Grep, WebFetch, etc. |
| External agents (ACP) | CC subagents + MCP servers |
| Hooks | CC native hooks (~/.claude/settings.json) |
| Web UI | Telegram IS the UI — inline buttons, notifications, multi-device, zero deployment |
| SOUL.md persona | Per-bot CLAUDE.md workspace + shared global rules |
| Workspace memory | Per-project CLAUDE.md + MCP memory — CC loads both automatically |
The difference: OpenClaw reimplements these features on top of an API. This project runs actual Claude Code — every feature CC has, you get for free.
Telegram bots cannot see each other's messages — this is a platform-level limitation. When you put Claude and Codex in the same group, neither can read the other's replies.
This project works around it with a pluggable shared context store. Each bot writes its reply after responding. When another bot is @mentioned, it reads the shared context and includes the other bot's replies in its prompt.
You: @claude Review this code
CC: [reviews code, writes reply to shared store]
You: @codex Do you agree with CC's review?
Codex: [reads CC's reply from shared store, gives opinion]
No copy-pasting needed. Built-in limits (30 messages / 3000 tokens / 20-minute TTL) prevent context bloat.
| Backend | Dependencies | Concurrency | Best For |
|---|---|---|---|
sqlite (default) |
None (built-in) | WAL mode, single-writer | Single bot, low concurrency |
json |
None (built-in) | Atomic write (tmp+rename) | Zero-dependency deployment |
redis |
ioredis |
Native concurrency + TTL | Multi-bot, Docker environment |
Set sharedContextBackend in config.json:
{
"shared": {
"sharedContextBackend": "redis",
"redisUrl": "redis://localhost:6379"
}
}Note: Bots only respond when explicitly @mentioned or replied to. They don't auto-reply to each other.
Beyond passive shared context, A2A-TG lets bots actively respond to each other. When one bot replies to a user, the A2A-TG bus broadcasts the envelope over loopback HTTP to sibling bots. Each sibling independently decides whether to chime in — and crucially, it does not re-broadcast its own reply, so the chain terminates by design.
You: @claude What's the best way to handle retries?
Claude: [responds with retry pattern advice]
↓ A2A-TG broadcast (generation=1)
Codex: [reads Claude's reply, adds: "I'd also suggest exponential backoff..."]
✗ Codex's reply is NOT broadcast further — chain ends here
The official A2A protocol is designed for web services discovering each other via Agent Cards and exchanging long-running Tasks over HTTPS. telegram-ai-bridge runs agents in group chats, where peers are few and pre-configured, turns are short and high-frequency, and the dominant failure mode is ping-pong loops.
A2A-TG keeps the spirit (agent-to-agent peer communication, envelope with correlation/idempotency, TTL) but adds what IM actually needs:
generation— a turn counter with a hard cap (>= 2is rejected). Official A2A has no equivalent.- Chat-scoped idempotency — fingerprints are keyed on
(chat_id, sender, content), not on a web-service task ID. - Loopback-only transport — peers live on
127.0.0.1; there is no internet-facing endpoint and no OAuth dance.
Full field-by-field definition, compatibility matrix, and reserved-hooks list: A2A-TG v1 spec.
{
"protocol_version": "a2a-tg/v1",
"message_id": "<time-ordered id>",
"idempotency_key": "<unique per envelope>",
"sender": "claude",
"chat_id": -1001234567890,
"generation": 1,
"content": "...",
"ttl_seconds": 300
}Source of truth: a2a/envelope.js. As of v1.1 the on-wire tag is a2a-tg/v1 (self-identifying, distinct from official A2A). The validator still accepts the legacy a2a/v1 tag during a two-minor-version compatibility window and logs a one-time deprecation warning per legacy tag, so running bot instances keep talking to each other mid-rollout (see spec §1, §9).
- Generation cap —
validateEnvelope()rejectsgeneration >= 2. User prompts are generation 0, a bot's first reply is generation 1, any further rebroadcast is blocked at the wire. - AI self-decline — each bot's prompt allows returning
[NO_RESPONSE]when it has nothing useful to add; the bridge skips the TG send. - No-rebroadcast policy — A2A-triggered replies are written to shared context and sent to Telegram, but are not re-broadcast through the A2A-TG bus. This breaks the ping-pong chain at the source. Reference:
bridge.js:311. - Idempotency dedup — SHA-256 fingerprint of
(chat_id, sender, content)with 300s TTL rejects duplicate envelopes. - Peer circuit breaker — a peer that fails 3 times in a row is marked unavailable; a half-open probe resets it on recovery.
loop-guard.jsalso keepscooldownMs/maxResponsesPerWindow/windowMsas reserved hooks (not currently wired). The no-rebroadcast policy already covers loop prevention for the current architecture — the fields are preserved as extension points if a future mode ever re-enables chain replies.
A2A-TG only works in group chats. Private/DM conversations are never broadcast — this is enforced at both the inbound filter and the outbound broadcaster. Two people DMing two different bots on the same account cannot leak into each other's context.
{
"shared": {
"a2aEnabled": true,
"a2aPorts": { "claude": 18810, "codex": 18811 }
}
}Each bot instance listens on its assigned loopback port. Peers are auto-discovered from a2aPorts (excluding self). /a2a from Telegram shows live stats — bus status, peer health, loop-guard counters.
For all local LaunchAgent instances plus optional mini targets:
./scripts/status-all.sh
A2A_STATUS_URLS='mini-claude=http://mini.local:18810/a2a/status' ./scripts/status-all.shSeveral projects solve some part of "make multiple AI agents work together." They tend to pick one axis and specialize. The table below is scoped to the multi-agent orchestration lane (not CC-remote-control, which is a different lane covered earlier).
| Project | Heterogeneous agents | Dedicated protocol layer | IM as primary UI | Self-hosted |
|---|---|---|---|---|
| golutra | Yes (manual) | GUI pipe, human-in-the-loop | Desktop GUI | Yes |
| claude-code-studio | CC only (homogeneous) | Redis + filesystem watcher | Web UI | Yes |
| telegram-ai-bridge (this project) | Yes (CC + Codex + Gemini) | A2A-TG envelope + loop guards | Telegram | Yes |
The point is not that the others are worse — they are built for different tasks. golutra's strength is precise human routing; claude-code-studio's is deep homogeneous CC choreography. This project's strength is heterogeneous auto-coordination inside an IM surface you already carry in your pocket.
This bridge runs full Claude Code / Codex with your local credentials, so it is worth being explicit about what it does and does not protect against.
- Owner gating protects the trigger, not the content.
ownerTelegramIdcontrols who can invoke a bot. It does not sanitize the content of replies, shared context, or A2A-TG envelopes. Anyone already in an authorized group chat can see whatever the bots say. - Group chats write to shared storage. Every bot reply in a group is written to the shared-context store (SQLite / JSON / Redis). Do not add the bots to a group you do not control — conversations persist on your disk, and any bot in the group can read them when next @mentioned.
- A2A-TG broadcasts are loopback-only and group-scoped. Envelopes never leave
127.0.0.1, and the inbound/outbound filters rejectchat_id > 0(DMs). Two people DMing two bots cannot leak into each other's context. bypassPermissionsdisables tool approval prompts. With this mode enabled, the bot executes Bash / Write / Edit tools without asking. That is convenient for personal use on your own machine; it is dangerous if anyone else can reach the bot. Keep it off unless you understand the blast radius.- Secrets in config.
config.jsonis.gitignore'd.bun run configredacts secrets when printing. Do not share bridge logs raw — they can contain tool outputs with sensitive paths. - Upstream trust. The bridge inherits whatever your local Claude Code / Codex / Gemini can do — MCP servers, hooks, skills. If you install an untrusted skill or MCP, the bot inherits the risk.
Telegram bot
→ start.js
→ config.json
→ bridge.js
→ executor (direct | local-agent)
→ backend adapter (claude | codex | gemini)
→ local credentials and session files
Each bot instance keeps its own Telegram token, SQLite DBs, credential directory, and model settings.
Configuration
bun run bootstrap --backend claude generates a starter config.json. Or copy config.example.json.
{
"shared": {
"ownerTelegramId": "123456789",
"cwd": "/Users/you",
"httpProxy": "",
"defaultVerboseLevel": 1,
"executor": "direct",
"tasksDb": "tasks.db",
"sharedContextBackend": "sqlite",
"sharedContextDb": "shared-context.db",
"redisUrl": "",
"streamPreviewEnabled": true
},
"backends": {
"claude": {
"enabled": true,
"telegramBotToken": "...",
"sessionsDb": "sessions.db",
"model": "claude-sonnet-4-7",
"permissionMode": "default"
},
"codex": {
"enabled": true,
"telegramBotToken": "...",
"sessionsDb": "sessions-codex.db",
"model": ""
},
"gemini": {
"enabled": false,
"telegramBotToken": "",
"sessionsDb": "sessions-gemini.db",
"model": "gemini-2.5-pro",
"oauthClientId": "",
"oauthClientSecret": "",
"googleCloudProject": ""
}
}
}config.json is gitignored. Sessions run until completion — no hard timeout (a soft watchdog logs after 15 minutes without aborting).
Inspect resolved config: bun run config --backend claude (secrets redacted).
Backend Notes
Claude:
- Requires local login state under
~/.claude/ - Supports
permissionMode:defaultorbypassPermissions
Codex:
- Requires local login state under
~/.codex/ - Optional
modeloverride; empty string uses Codex defaults
Gemini:
- Positioned as the "quiet scribe" in a multi-agent group — a good role for overnight summarization, not for front-line coding
- Requires
~/.gemini/oauth_creds.json,oauthClientId,oauthClientSecret - Uses Gemini Code Assist API mode, not full CLI terminal control
- Behavior is shaped through prompt/workspace
CLAUDE.md, not a code-level read-only switch
Run N parallel Claude Code instances, each with its own Telegram bot:
1. Create a bot — message @BotFather on Telegram, get a token.
2. Create a config file — copy and customize:
cp config.json config-2.json
# Edit config-2.json: use a brand-new bot token from @BotFather (each instance needs its own),
# and change sessionsDb / tasksDb. Never reuse a token already polled by another running
# instance or repo — two processes sharing one token both get Telegram 409 Conflict.{
"shared": {
"ownerTelegramId": "YOUR_ID",
"tasksDb": "tasks-2.db"
},
"backends": {
"claude": {
"enabled": true,
"telegramBotToken": "NEW_TOKEN_FROM_BOTFATHER",
"sessionsDb": "sessions-2.db",
"model": "claude-opus-4-7",
"permissionMode": "bypassPermissions"
}
}
}3. Start it:
bun run start --backend claude --config config-2.json4. (Optional) Register as LaunchAgent for auto-start:
./scripts/install-launch-agent.sh --backend claude --instance 2 --config config-2.json --install
bun run check-configs config.example.json config-2.jsonSee the LaunchAgent section below for plist setup.
What's shared vs isolated:
Shared (automatic) Isolated (per-instance) ~/.claude/(CLAUDE.md, memory, skills, hooks)Telegram bot token MCP servers (memory-store, etc.) SQLite sessions DB Project settings & rules SQLite tasks DB Git repos & file system Log files
macOS LaunchAgent
Generate and install:
./scripts/install-launch-agent.sh --backend claude --install
./scripts/install-launch-agent.sh --backend codex --install
./scripts/install-log-rotation.sh --installThe wrapper runs bun run check before bun run start, so bad config fails fast.
Logs are written under ~/Library/Logs/telegram-ai-bridge/ and the rotation agent copy-truncates them daily at 03:00.
Default labels: com.telegram-ai-bridge, com.telegram-ai-bridge-codex, com.telegram-ai-bridge-gemini.
launchctl print gui/$(id -u)/com.telegram-ai-bridge
launchctl kickstart -k gui/$(id -u)/com.telegram-ai-bridge
tail -f ~/Library/Logs/telegram-ai-bridge/bridge.logIf you see 409 Conflict, another process is polling the same bot token.
Docker
docker build -t telegram-ai-bridge .
docker run -d \
--name tg-ai-bridge-claude \
-v $(pwd)/config.json:/app/config.json:ro \
-v ~/.claude:/root/.claude \
telegram-ai-bridge --backend claudeSwap credential mount and --backend for other backends. See docker-compose.example.yml for a Compose starter.
Project Structure
start.js— CLI entry forstart,bootstrap,check,setup,configconfig.js— Config loader and setup wizardbridge.js— Telegram bot runtimesessions.js— SQLite session persistencediscuss-mode.js— Discuss mode send/silent contract, control command semantics, and probe gatinggroup-context-pipeline.js— Canonical group-message context reduction and renderingtelegram-command-routing.js— Telegram slash-command target parsing, including mention-first control commandsstreaming-preview.js— Live text preview via editMessage (throttled, with degradation)progress.js— Progress messages and typing-only indicatorssend-retry.js— Outbound delivery retry with error classification and HTML fallbackfile-ref-protect.js— Prevents Telegram auto-linking filenames as domains (.md, .go, .py etc.)shared-context.js— Cross-bot shared context entry pointshared-context/— Pluggable backends (SQLite / JSON / Redis)a2a/— Agent-to-agent communication bus, loop guard, peer healthadapters/— Backend integrationslaunchd/— LaunchAgent template for macOSscripts/— Install wrapper and runtime launcherdocker-compose.example.yml— Compose starter
Execution Modes
direct— runs the backend adapter in-process (default)local-agent— communicates with a local agent subprocess over JSONL stdio
Set in config.json at shared.executor, or override with BRIDGE_EXECUTOR.
Three ways to make AI agents talk to each other — different protocols, different scenarios:
| Layer | Protocol | How | Scenario |
|---|---|---|---|
| Terminal | MCP | Built-in codex mcp-server + claude mcp serve, zero code |
CC ↔ Codex direct calls in your terminal |
| Telegram Group | A2A-TG v1 (this project) | Loopback HTTP envelope bus with generation-based loop guards | Multiple heterogeneous bots in one group, chiming in |
| Telegram DM | MCP / CLI | Bots call each other directly via terminal config | Direct cross-bot communication, no bridge needed |
| Server | Official A2A v0.3.0 | openclaw-a2a-gateway (archived — A2A now built into OpenClaw) | Web-service agents across servers |
MCP vs A2A: MCP is a tool-calling protocol (I invoke your capability). A2A is a peer communication protocol (I talk to you as an equal). CC calling Codex via MCP is using Codex as a tool — not two agents chatting.
Official A2A vs A2A-TG: Official A2A is a Linux Foundation project (originally proposed by Google) for web-service-to-web-service interop. A2A-TG is this repository's IM-scenario envelope inspired by A2A — different scope, different transport, different loop model. Not interchangeable. See A2A-TG v1 spec §7.
Claude Code and Codex each have a built-in MCP server mode. Register them with each other and they can call each other directly — no bridge, no Telegram, no custom code:
# In Claude Code: register Codex as MCP server
claude mcp add codex -- codex mcp-server
# In Codex: register Claude Code as MCP server (in ~/.codex/config.toml)
[mcp_servers.claude-code]
type = "stdio"
command = "claude"
args = ["mcp", "serve"]Groups use A2A auto-broadcast. DMs go through MCP/CLI direct communication. See sections above.
For OpenClaw agents communicating across servers via the official A2A v0.3.0 protocol (Linux Foundation project, originally proposed by Google). A2A is now built into OpenClaw as a native plugin — the standalone gateway has been archived. See openclaw-a2a-gateway for historical reference.
Code attribution: the a2a/ directory in this repository (envelope, idempotency store, peer-health manager) started as a simplified port of openclaw-a2a-gateway (MIT license) and has since diverged into the A2A-TG shape. Original copyright and license text are preserved.
bun testGitHub Actions runs the same suite on every push and pull request.
Part of the 小试AI open-source AI workflow:
| Project | Description |
|---|---|
| recallnest | MCP memory workbench (LanceDB + Jina v5) |
| content-publisher | Image generation + layout + WeChat publishing |
| openclaw-tunnel | Docker ↔ host CLI bridge (/cc /codex /gemini) |
| digital-clone-skill | Build digital clones from corpus data |
| claude-code-studio | Multi-session collaboration platform for Claude Code |
| cc-empire | Complete Claude Code workflow scaffold |
| tg-bridge-channel | Sister bridge using Claude Agent View background sessions (channel/pool engine) |
MIT