{"slug": "coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers", "title": "Coding Agents over Telegram, Part 2: From Zero to an Agent That Answers", "summary": "A developer built a coding agent that responds to Telegram messages and drives a tmux pane on a local machine. The setup uses OpenClaw as a Telegram gateway, pinned Node.js and pnpm versions, and a readiness check script. The guide provides both a fast automated path and a manual step-by-step approach for wiring Telegram, the local box, and the agent together.", "body_md": "This is the one post in the series you *do*, not just read. By the end you'll have a single Telegram topic where you type a message and a coding agent answers and drives a tmux pane on your own box. That's the entire goal, nothing more. Memory, monitors, tool servers, and the supervisor all come later; none of them are needed to get an agent answering you.\n\nBudget ~30–45 minutes. If you only do one thing, do the Fast path, then prove it with the readiness gate. Everything below the gate is manual explanation and debugging you can skip until you need it.\n\nThe Telegram wiring is the mechanical part; the real failures hide in local state. Confirm **every** line before you start:\n\n`tmux`\n\n, and shell access that survives disconnects (you'll leave a gateway running).`mybox:1.1`\n\n). Write it down; you'll hard-code it into the agent's instructions.Pin these. \"It runs on Node\" is not enough; the gateway is sensitive to the runtime.\n\n| Component | Pinned version | Notes |\n|---|---|---|\n| Node.js | `24.11.1` |\nThe gateway is built against Node 24; newer majors can fail the native build. |\n| Package manager | `pnpm 11.2.2` |\nOpenClaw's `packageManager` field. `corepack` fetches it for you. |\n| OpenClaw |\n`github.com/openclaw/openclaw` , pinned commit (tested on `2026.5.27` ) |\nPin a commit or tag; don't track `main` for a setup everyone must reproduce. |\n| Coding agent | opencode, installed and authenticated | Pin the version your team standardizes on. |\n| Node manager | `nvm` |\nThis guide assumes nvm; adapt the commands if you use asdf or system Node. |\n\nTwo scripts do the whole local setup. Grab them from the gist, then run them around the Telegram steps:\n\n```\n# Download the bootstrap + readiness scripts\ncurl -fsSL \"https://gist.githubusercontent.com/jerilkuriakose/cd0f8353aac74e47c591111b758943e9/raw/setup-openclaw.sh\" -o setup-openclaw.sh\ncurl -fsSL \"https://gist.githubusercontent.com/jerilkuriakose/7cf94af3e96526f9f14d0c28b6c26b69/raw/ready-check.sh\" -o ready-check.sh\nchmod +x setup-openclaw.sh ready-check.sh\n\n# 1. Pin the runtime, fetch + build OpenClaw, and launch the gateway.\n#    Pass your bot token to also write a minimal config in one shot:\nOPENCLAW_BOT_TOKEN=\"<BOT_TOKEN>\" OPENCLAW_BOT_ACCOUNT=\"my-bot\" ./setup-openclaw.sh\n\n# 2. Do the Telegram steps in \"Telegram side\" below (BotFather + group + topic),\n#    then add the allowlist + topic route from \"Wire them together\".\n\n# 3. Prove it works:\nAGENT_ID=my-agent PANE=mybox:1.1 ./ready-check.sh\n```\n\n`setup-openclaw.sh`\n\npins Node 24.11.1 and pnpm 11.2.2 (via nvm and corepack), clones and builds `github.com/openclaw/openclaw`\n\n, and launches the gateway. `ready-check.sh`\n\nruns the readiness gate for you. Prefer to understand each step, or hit a snag? Follow the manual path.\n\nThree parts: the Telegram side, the box side, then wiring them together.\n\nYou must be the group's **creator**, so do these from your own Telegram account:\n\n`@BotFather`\n\n→ `/newbot`\n\n→ name it → save the `project-a`\n\n).You also need **your own numeric Telegram user ID** for the allowlist. Easiest: message `@userinfobot`\n\nand it replies with your ID. (Alternatively, it appears in the gateway log as the sender once messages start flowing in the next step.)\n\nThe gateway has to know about your bot *before* it can poll Telegram, so write a minimal valid config first. Create `~/.openclaw/openclaw.json`\n\nwith just the bot account. Use **strict JSON** (no comments, no trailing commas) because that's exactly what the gateway parses and what you'll validate against:\n\n```\n{\n  \"channels\": {\n    \"telegram\": {\n      \"enabled\": true,\n      \"accounts\": {\n        \"<your-bot-account>\": {\n          \"botToken\": \"<BOT_TOKEN>\"\n        }\n      }\n    }\n  }\n}\n```\n\nLock it down and validate before launching:\n\n``` python\nchmod 600 ~/.openclaw/openclaw.json\npython3 -c \"import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON OK')\"\n```\n\n**Get OpenClaw and build it.** It's open source. Pin the runtime, clone, install with the `corepack`\n\n-provided pnpm, and build:\n\n```\nnvm install 24.11.1 && nvm use 24.11.1\ncorepack enable && corepack prepare pnpm@11.2.2 --activate\n\ngit clone https://github.com/openclaw/openclaw.git ~/repos/openclaw\ncd ~/repos/openclaw\n# Optional: pin a tested commit/tag for reproducibility (tested on 2026.5.27)\n# git checkout <commit-or-tag>\npnpm install\npnpm build                  # takes a few minutes\n```\n\nNow launch the gateway in its own tmux session so it survives your disconnect:\n\n```\npnpm gateway:watch          # launches the gateway and manages its own tmux session\n```\n\nFind your log path (it differs by config), then confirm the gateway came up and the bot is polling:\n\n```\n# If logging.file is set, use ~/.openclaw/logs/openclaw.log. Otherwise the dated default:\nls -t /tmp/openclaw/openclaw-*.log | tail -1          # default location\ngrep -a 'gateway ready' <your-log-path> | tail -1     # expect a recent line\n```\n\nThe log directory is locked down (\n\n`0700`\n\n), so read it from the shell, not an editor's file browser.This initial launch, and any later`logging`\n\nor`plugins.load`\n\nchange, needs a gateway (re)start. The group/topic/routing edits in the next section hot-reload, no restart needed.\n\nConfig lives in `~/.openclaw/openclaw.json`\n\n. The gateway **hot-reloads** routing/topic/channel edits; no restart needed for these. We do this in two phases because you need the chat and topic IDs *from the log*, and the only way to make them appear is to let messages through first.\n\n**Phase 1: find the chat ID and open the group temporarily.**\n\nWith the gateway now polling, send a message in your topic. It gets blocked (the group isn't configured yet), which conveniently logs the chat ID:\n\n```\ngrep -a 'not-allowed' <your-log-path> | tail -1\n# → {\"chatId\":<CHAT_ID>,\"title\":\"<your group>\",\"reason\":\"not-allowed\"}\n```\n\nAdd a `groups`\n\nblock to your account so messages flow and topic IDs get logged. This is a fragment; merge the `groups`\n\nkey into the account you already created, keeping the file strict JSON:\n\n```\n\"groups\": {\n  \"<CHAT_ID>\": { \"groupPolicy\": \"open\", \"requireMention\": false }\n}\n```\n\nSave. The gateway hot-reloads (no restart).\n\nKeep the open window tiny.`groupPolicy: \"open\"`\n\nletsanyone in the groupdrive a shell-capable agent. The group must be private with only you in it, and this is a momentary bootstrap step: switch to`allowlist`\n\n(Phase 2) as soon as you've harvested the topic IDs, before adding anyone else or doing real work.\n\n**Phase 1b: harvest topic thread IDs.** Send a message in each topic (label them by text so you can tell them apart, since topic IDs are **not** sequential by creation order), then:\n\n```\ngrep -ao 'Inbound message telegram:group[^\"]*' <your-log-path> | sort -u\n# → ...:topic:<TOPIC_THREAD_ID>\n```\n\n**Phase 2: lock it down and route the topic to an agent.** Now switch the group to an allowlist (owner-only), add yourself, map the topic to an agent, and declare that agent. Strict JSON, merged into your config:\n\n```\n\"channels\": {\n  \"telegram\": {\n    \"enabled\": true,\n    \"accounts\": {\n      \"<your-bot-account>\": {\n        \"botToken\": \"<BOT_TOKEN>\",\n        \"groups\": {\n          \"<CHAT_ID>\": {\n            \"groupPolicy\": \"allowlist\",\n            \"allowFrom\": [\"<YOUR_TELEGRAM_USER_ID>\"],\n            \"requireMention\": false,\n            \"topics\": {\n              \"<TOPIC_THREAD_ID>\": { \"agentId\": \"<your-agent-id>\" }\n            }\n          }\n        }\n      }\n    }\n  }\n},\n\"agents\": {\n  \"list\": [\n    {\n      \"id\": \"<your-agent-id>\",\n      \"workspace\": \"~/.openclaw/workspace-<name>\",\n      \"agentDir\": \"~/.openclaw/agents/<name>\"\n    }\n  ]\n}\n```\n\n`requireMention: false`\n\nlets you type naturally instead of prefixing every message with an `@mention`\n\n. `allowFrom`\n\nwith only your user ID is what stops anyone else in the group from driving a shell-capable agent. It's your numeric Telegram ID, **kept as a quoted string** as the config expects (for example, `[\"123456789\"]`\n\n).\n\n**Give the agent its instructions.** This is the step that makes `status`\n\n, `send`\n\n, and `restart`\n\nwork; the agent's behavior comes entirely from the files in its `workspace`\n\n. Create the workspace directory first (the `agentDir`\n\nmust be unique per agent; the gateway creates it on first run):\n\n```\nmkdir -p ~/.openclaw/workspace-<name>\n```\n\nThen put this in `~/.openclaw/workspace-<name>/AGENTS.md`\n\n, a minimal command contract:\n\n```\n# <your-agent-id>\n\nYou are bound to tmux pane `<PANE>`, an interactive coding-agent session.\n\n## Hard rules\n- ALL tmux operations target `<PANE>`. Never touch any other session.\n- Read with `tmux capture-pane -t <PANE> -p`; write with `tmux send-keys -t <PANE> ...`.\n- Strip ANSI codes before relaying pane output to Telegram.\n- After send-keys, wait a few seconds before re-capturing; replies aren't instant.\n- Confirm before any destructive action EXCEPT the explicit \"restart\" command below.\n\n## Common phrasings → actions\n| User says | You do |\n|---|---|\n| \"status\" / \"what's in tmux?\" | capture `<PANE>`, strip ANSI, summarize the last ~60 lines |\n| \"send `<msg>`\" | `send-keys -t <PANE> -l -- \"<msg>\"`, then Enter, wait, capture |\n| \"compact\" / \"new session\" | send `/compact` or `/new` to the pane |\n| \"interrupt\" / \"stop it\" | confirm, then send `C-c` |\n| \"restart\" | run the restart sequence below (no confirmation) |\n\n## Decision / option routing\nYou are a relay, not the owner of the work in the pane. If your previous reply\nsummarized options/questions from the agent (e.g. \"A/B\", \"yes/no\", \"Proceed?\"),\nthen short replies like `A`, `B`, `yes`, `no`, `do it`, `sorry A` are answers\n**for the pane**: forward them verbatim; do not act on them yourself. Only act\nlocally when explicitly addressed (\"you do option A\").\n\n## Restart sequence (no confirmation)\n1. Resolve the exact session id first (do not blindly \"continue\", which can reopen the wrong session).\n2. Exit the agent's UI cleanly and wait until you actually see a shell prompt.\n3. Relaunch with EXACTLY this command, resuming that session id: `<your-launch-command> --resume <session_id>`\n4. Capture and report whether it came back up.\n\n## Safety rails (applied to anything you relay)\n- Production: strict read-only. Never relay mutations.\n- Never relay pushes/merges to shared branches.\n- Shared infra (gateways, API gateways): never mutate without explicit human approval.\n```\n\nReplace\n\n`<PANE>`\n\nand the restart command with your exact values; leave them vague and the agent will improvise and can lose your session. This single fileisyour agent; the workspace can contain just`AGENTS.md`\n\nto start.\n\n**Required: validate before you trust the save.** An invalid `openclaw.json`\n\nis dangerous: on a restart the gateway rejects it, auto-restores the last-known-good, and the watch process exits (that is, an outage). Editing a *running* gateway is safer (a bad edit is reverted with no downtime), but never restart on an unvalidated config. Every time:\n\n``` python\n# 1. JSON is parseable\npython3 -c \"import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON OK')\"\n\n# 2. Dry-run the config against a temp copy (never touches the live gateway).\n#    The copy holds your bot token; keep it 0600 and delete it after.\ncp ~/.openclaw/openclaw.json /tmp/oc-check.json && chmod 600 /tmp/oc-check.json\nOPENCLAW_CONFIG_PATH=/tmp/oc-check.json pnpm openclaw doctor --non-interactive   # expect Errors: 0\nrm -f /tmp/oc-check.json\n\n# 3. Confirm the hot reload landed\ngrep -a 'config hot reload applied' <your-log-path> | tail -3\n```\n\nYou are **not** done when the config saves. You're done when you have *evidence* the whole path works. Verify all five; this is exactly what you'll confirm to the session organizer:\n\n`status`\n\nin your topic; you get a response (not silence).\n\n```\n   tmux capture-pane -t <PANE> -p | tail -n 40\n```\n\nYou should see your text injected into the coding agent.\n\n```\n   ls -t ~/.openclaw/agents/<name>/sessions/*topic-<TOPIC_THREAD_ID>* 2>/dev/null\n```\n\n`groupPolicy: \"allowlist\"`\n\n, your ID in `allowFrom`\n\n, and `requireMention: false`\n\n; you're no longer in `open`\n\nmode from Phase 1.If all five hold, you're ready for the session. Capture a **redacted** screenshot/log snippet of #1–#2 as your \"I'm ready\" artifact, and redact the log excerpt too (it carries chat IDs, thread IDs, your user ID, and message text), not just the screenshot.\n\n**Recovery first.** If a restart took the gateway down, your edit wasn't lost; the gateway auto-restored the last-known-good and saved your version alongside it. Recover from `~/.openclaw/openclaw.json.clobbered.*`\n\n(also check `.last-good`\n\n/ `.bak`\n\n), fix the cause, **validate** (above), then restart:\n\n```\nnvm use 24.11.1\ntmux kill-session -t openclaw-gateway-watch-main\ncd ~/repos/openclaw && pnpm gateway:watch\ngrep -a 'gateway ready' <your-log-path> | tail -1\n```\n\n| Symptom | Cause | Fix |\n|---|---|---|\nBot silent; log shows `\"reason\":\"not-allowed\"`\n|\nGroup not configured, or you're not in `allowFrom`\n|\nAdd the group; put your numeric ID in `allowFrom` (or use `open` while testing) |\nBot only replies when `@mention` ed |\n`requireMention` defaulting to `true`\n|\nSet `requireMention: false`\n|\n| Bot doesn't see messages at all | Telegram privacy mode on a non-admin bot | Make the bot a group admin\n|\n| Topic IDs never appear in the log | Messages blocked before topic resolution | Open the group (Phase 1) first, then re-send |\n`getUpdates` `409 Conflict`\n|\nSame bot token polled by two gateways | One gateway per token; kill the duplicate poller |\n| Config edit \"vanished\" / gateway down after restart | Invalid JSON at startup → auto-restore → watch exits | Recover from `.clobbered.*` ; validate; restart |\n| Agent replies but the pane does nothing | Wrong `<PANE>` , or the coding agent isn't running there |\nFix the pane target; relaunch the agent in that pane |\n\nThis is a public-internet bot with shell reach. Treat it that way:\n\n`openclaw.json`\n\n. `chmod 600`\n\n`@BotFather`\n\n.`allowFrom`\n\n.You do **not** need any of this to finish the readiness gate; it all comes in Part 4 / the session:\n\n`AGENTS.md`\n\nalready handles `status`\n\n/`send`\n\n/`restart`\n\n/option replies prompt-driven; the router just makes it faster and stricter)`ops`\n\nagent (box-wide shell). One relay + one pane is the whole assignment.24 hours out, confirm you can tick all five readiness items and post your redacted \"ready\" artifact. If you're blocked, send your symptom plus your Node/pnpm versions and the last few gateway log lines (redacted) so it can be sorted before we're all in the room.\n\nPart 3 is the operating contract: what to type, what *not* to type, and how to supervise the agent safely once it's answering you.", "url": "https://wpnews.pro/news/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers", "canonical_source": "https://dev.to/jerilk/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers-2777", "published_at": "2026-06-13 19:44:04+00:00", "updated_at": "2026-06-13 20:14:53.255470+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "artificial-intelligence"], "entities": ["OpenClaw", "Telegram", "Node.js", "pnpm", "nvm", "BotFather", "opencode", "Jeril Kuriakose"], "alternates": {"html": "https://wpnews.pro/news/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers", "markdown": "https://wpnews.pro/news/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers.md", "text": "https://wpnews.pro/news/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers.txt", "jsonld": "https://wpnews.pro/news/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers.jsonld"}}