Single-bot, multi-agent Telegram routing for OpenClaw teams. All messages go to one coordinator who delegates to specialists via per-topic
systemPrompt.
OpenClaw doesn't support per-topic bindings (threadId in match.peer is rejected).
Creating 10 BotFather bots is overkill. Instead: one bot, one binding, smart routing via systemPrompts.
User writes in "DevOps" topic
→ Coordinator receives message
→ systemPrompt says: "Delegate to agent 'devops' using sessions_send"
→ Coordinator calls sessions_send(agentId="devops", message=...)
→ DevOps agent works in its own workspace
→ DevOps agent posts results back to topic 466 via sendMessage tool
pip install gasclawfrom pathlib import Path
from gasclaw.openclaw.team_config import (
AgentSpec, generate_team_config, write_team_workspaces, build_topic_routes
)
# Define your team
agents = [
AgentSpec(
id="coordinator", name="Tech Lead", emoji="🧑💻",
is_coordinator=True,
tools_allow=["exec", "read", "write", "sessions_send", "sessions_spawn"],
),
AgentSpec(
id="devops", name="DevOps Engineer", emoji="🚀",
system_prompt="You handle CI/CD, Docker, and infrastructure.",
tools_allow=["exec", "read", "write", "edit"],
),
AgentSpec(
id="backend-dev", name="Backend Developer", emoji="🔨",
system_prompt="You write code and implement features.",
tools_allow=["exec", "read", "write", "edit", "apply_patch"],
),
AgentSpec(
id="doctor", name="Doctor", emoji="🏥",
system_prompt="You monitor system health and fix issues.",
tools_allow=["exec", "read"],
),
]
# Map agents to Telegram forum topic IDs (create topics first via BotFather or API)
topic_map = {
"coordinator": "462",
"devops": "466",
"backend-dev": "464",
"doctor": "477",
}
# Generate openclaw.json
config = generate_team_config(
agents, topic_map,
bot_token="YOUR_BOT_TOKEN",
chat_id="-100YOUR_GROUP_ID",
owner_id="YOUR_TELEGRAM_USER_ID",
kimi_key="sk-your-key",
auth_token="random-hex-token",
openclaw_base=Path("~/.openclaw").expanduser(),
)
# Write it
import json
Path("~/.openclaw/openclaw.json").expanduser().write_text(json.dumps(config, indent=2))
# Create workspaces with SOUL.md and optional knowledge docs
write_team_workspaces(
agents, topic_map,
chat_id="-100YOUR_GROUP_ID",
openclaw_base=Path("~/.openclaw").expanduser(),
knowledge_dir=Path("/opt/knowledge"), # optional
)# presets/backend.yaml
name: backend
description: Backend software development team
coordination: hierarchical
coordinator:
id: coordinator
name: Tech Lead
emoji: "🧑💻"
tools:
allow: [exec, read, write, sessions_list, sessions_send, sessions_spawn]
agents:
- id: devops
name: DevOps Engineer
emoji: "🚀"
system_prompt: "You handle CI/CD and infrastructure."
tools:
allow: [exec, read, write, edit]
- id: backend-dev
name: Backend Developer
emoji: "🔨"
system_prompt: "You write code."
tools:
allow: [exec, read, write, edit, apply_patch]from gasclaw.openclaw.team_config import load_preset_agents, generate_team_config
agents = load_preset_agents(Path("presets/backend.yaml"))
config = generate_team_config(agents, topic_map, ...){
"bindings": [
{"agentId": "coordinator", "match": {"channel": "telegram"}}
],
"channels": {
"telegram": {
"network": {"autoSelectFamily": false},
"actions": {"sendMessage": true},
"groups": {
"-100YOUR_GROUP_ID": {
"groupPolicy": "open",
"topics": {
"1": {"requireMention": false},
"462": {"requireMention": false, "systemPrompt": "You are in the Tech Lead topic. This is YOUR topic. Handle coordination directly."},
"466": {"requireMention": false, "systemPrompt": "This is the DevOps Engineer topic. Delegate to agent \"devops\" using the sessions_send tool."},
"464": {"requireMention": false, "systemPrompt": "This is the Backend Developer topic. Delegate to agent \"backend-dev\" using the sessions_send tool."},
"477": {"requireMention": false, "systemPrompt": "This is the Doctor topic. Delegate to agent \"doctor\" using the sessions_send tool."}
}
}
}
}
},
"tools": {
"agentToAgent": {"enabled": true, "allow": ["coordinator", "devops", "backend-dev", "doctor"]}
}
}The coordinator gets a delegation table:
| Specialist (name / id) | Topic | Delegate via |
|------------------------|-------|--------------|
| DevOps Engineer (devops) | 466 | `sessions_send` |
| Backend Developer (backend-dev) | 464 | `sessions_send` |
| Doctor (doctor) | 477 | `sessions_send` |Plus instructions that sessions_send is a tool call (not a shell command).
If you provide a knowledge_dir, every agent gets a knowledge/ folder with condensed docs:
~/.openclaw/workspace-devops/knowledge/
├── openclaw-reference.md
├── gastown-reference.md
├── ais-reference.md
├── gasclaw-architecture.md
└── beads-reference.md
"1": {"requireMention": false}Never set "enabled": false on topic 1 — it silently drops all inbound messages
in forum groups because that's where users type by default.
"network": {"autoSelectFamily": false}Prevents IPv6 fallback issues that cause intermittent fetch fallback warnings
and dropped connections in containers.
"actions": {"sendMessage": true}Without this, agents can't post to specific topics via the sendMessage tool.
"bindings": [{"agentId": "coordinator", "match": {"channel": "telegram"}}]OpenClaw rejects peer.threadId in bindings. Don't try it — it causes
Invalid config: Unrecognized key "threadId" and breaks the gateway.
import urllib.request, json
def create_topic(bot_token, chat_id, name):
url = f"https://api.telegram.org/bot{bot_token}/createForumTopic"
data = json.dumps({"chat_id": chat_id, "name": name}).encode()
req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())["result"]["message_thread_id"]
# Create topics for each agent
topic_map = {}
for agent in agents:
topic_map[agent.id] = str(create_topic(TOKEN, CHAT_ID, f"{agent.emoji} {agent.name}"))Each agent gets its own Git-backed beads database:
for agent_id in coordinator devops backend-dev doctor; do
BD_DIR="/workspace/beads/$agent_id"
mkdir -p "$BD_DIR" && cd "$BD_DIR"
git init --initial-branch=main
git config user.email "${agent_id}@gasclaw.local"
mkdir -p .beads
echo '{"version":1,"agent":"'$agent_id'"}' > .beads/config.json
git add . && git commit -m "init beads for $agent_id"
doneAgents use BD_ROOT=/workspace/beads/<agent-id> bd new "title" --body "details" to log findings.
# In entrypoint.sh after starting the gateway:
python3 -c "
from pathlib import Path
from gasclaw.openclaw.team_config import load_preset_agents, generate_team_config, write_team_workspaces
agents = load_preset_agents(Path('/opt/presets/backend.yaml'))
topic_map = {} # load from team-topics.json or create via API
config = generate_team_config(agents, topic_map, bot_token='...', ...)
Path('~/.openclaw/openclaw.json').expanduser().write_text(
__import__('json').dumps(config, indent=2)
)
write_team_workspaces(agents, topic_map, '-100...', Path('~/.openclaw').expanduser(),
knowledge_dir=Path('/opt/knowledge'))
"
openclaw gateway run- PR: gastown-publish/gasclaw#309
- OpenClaw multi-agent docs: https://docs.openclaw.ai/concepts/multi-agent
- Telegram forum topics API: https://core.telegram.org/bots/api#createforumtopic