# Coding Agents over Telegram, Part 2: From Zero to an Agent That Answers

> Source: <https://dev.to/jerilk/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers-2777>
> Published: 2026-06-13 19:44:04+00:00

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.

Budget ~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.

The Telegram wiring is the mechanical part; the real failures hide in local state. Confirm **every** line before you start:

`tmux`

, and shell access that survives disconnects (you'll leave a gateway running).`mybox:1.1`

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

| Component | Pinned version | Notes |
|---|---|---|
| Node.js | `24.11.1` |
The gateway is built against Node 24; newer majors can fail the native build. |
| Package manager | `pnpm 11.2.2` |
OpenClaw's `packageManager` field. `corepack` fetches it for you. |
| OpenClaw |
`github.com/openclaw/openclaw` , pinned commit (tested on `2026.5.27` ) |
Pin a commit or tag; don't track `main` for a setup everyone must reproduce. |
| Coding agent | opencode, installed and authenticated | Pin the version your team standardizes on. |
| Node manager | `nvm` |
This guide assumes nvm; adapt the commands if you use asdf or system Node. |

Two scripts do the whole local setup. Grab them from the gist, then run them around the Telegram steps:

```
# Download the bootstrap + readiness scripts
curl -fsSL "https://gist.githubusercontent.com/jerilkuriakose/cd0f8353aac74e47c591111b758943e9/raw/setup-openclaw.sh" -o setup-openclaw.sh
curl -fsSL "https://gist.githubusercontent.com/jerilkuriakose/7cf94af3e96526f9f14d0c28b6c26b69/raw/ready-check.sh" -o ready-check.sh
chmod +x setup-openclaw.sh ready-check.sh

# 1. Pin the runtime, fetch + build OpenClaw, and launch the gateway.
#    Pass your bot token to also write a minimal config in one shot:
OPENCLAW_BOT_TOKEN="<BOT_TOKEN>" OPENCLAW_BOT_ACCOUNT="my-bot" ./setup-openclaw.sh

# 2. Do the Telegram steps in "Telegram side" below (BotFather + group + topic),
#    then add the allowlist + topic route from "Wire them together".

# 3. Prove it works:
AGENT_ID=my-agent PANE=mybox:1.1 ./ready-check.sh
```

`setup-openclaw.sh`

pins Node 24.11.1 and pnpm 11.2.2 (via nvm and corepack), clones and builds `github.com/openclaw/openclaw`

, and launches the gateway. `ready-check.sh`

runs the readiness gate for you. Prefer to understand each step, or hit a snag? Follow the manual path.

Three parts: the Telegram side, the box side, then wiring them together.

You must be the group's **creator**, so do these from your own Telegram account:

`@BotFather`

→ `/newbot`

→ name it → save the `project-a`

).You also need **your own numeric Telegram user ID** for the allowlist. Easiest: message `@userinfobot`

and it replies with your ID. (Alternatively, it appears in the gateway log as the sender once messages start flowing in the next step.)

The gateway has to know about your bot *before* it can poll Telegram, so write a minimal valid config first. Create `~/.openclaw/openclaw.json`

with 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:

```
{
  "channels": {
    "telegram": {
      "enabled": true,
      "accounts": {
        "<your-bot-account>": {
          "botToken": "<BOT_TOKEN>"
        }
      }
    }
  }
}
```

Lock it down and validate before launching:

``` python
chmod 600 ~/.openclaw/openclaw.json
python3 -c "import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON OK')"
```

**Get OpenClaw and build it.** It's open source. Pin the runtime, clone, install with the `corepack`

-provided pnpm, and build:

```
nvm install 24.11.1 && nvm use 24.11.1
corepack enable && corepack prepare pnpm@11.2.2 --activate

git clone https://github.com/openclaw/openclaw.git ~/repos/openclaw
cd ~/repos/openclaw
# Optional: pin a tested commit/tag for reproducibility (tested on 2026.5.27)
# git checkout <commit-or-tag>
pnpm install
pnpm build                  # takes a few minutes
```

Now launch the gateway in its own tmux session so it survives your disconnect:

```
pnpm gateway:watch          # launches the gateway and manages its own tmux session
```

Find your log path (it differs by config), then confirm the gateway came up and the bot is polling:

```
# If logging.file is set, use ~/.openclaw/logs/openclaw.log. Otherwise the dated default:
ls -t /tmp/openclaw/openclaw-*.log | tail -1          # default location
grep -a 'gateway ready' <your-log-path> | tail -1     # expect a recent line
```

The log directory is locked down (

`0700`

), so read it from the shell, not an editor's file browser.This initial launch, and any later`logging`

or`plugins.load`

change, needs a gateway (re)start. The group/topic/routing edits in the next section hot-reload, no restart needed.

Config lives in `~/.openclaw/openclaw.json`

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

**Phase 1: find the chat ID and open the group temporarily.**

With 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:

```
grep -a 'not-allowed' <your-log-path> | tail -1
# → {"chatId":<CHAT_ID>,"title":"<your group>","reason":"not-allowed"}
```

Add a `groups`

block to your account so messages flow and topic IDs get logged. This is a fragment; merge the `groups`

key into the account you already created, keeping the file strict JSON:

```
"groups": {
  "<CHAT_ID>": { "groupPolicy": "open", "requireMention": false }
}
```

Save. The gateway hot-reloads (no restart).

Keep the open window tiny.`groupPolicy: "open"`

letsanyone 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`

(Phase 2) as soon as you've harvested the topic IDs, before adding anyone else or doing real work.

**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:

```
grep -ao 'Inbound message telegram:group[^"]*' <your-log-path> | sort -u
# → ...:topic:<TOPIC_THREAD_ID>
```

**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:

```
"channels": {
  "telegram": {
    "enabled": true,
    "accounts": {
      "<your-bot-account>": {
        "botToken": "<BOT_TOKEN>",
        "groups": {
          "<CHAT_ID>": {
            "groupPolicy": "allowlist",
            "allowFrom": ["<YOUR_TELEGRAM_USER_ID>"],
            "requireMention": false,
            "topics": {
              "<TOPIC_THREAD_ID>": { "agentId": "<your-agent-id>" }
            }
          }
        }
      }
    }
  }
},
"agents": {
  "list": [
    {
      "id": "<your-agent-id>",
      "workspace": "~/.openclaw/workspace-<name>",
      "agentDir": "~/.openclaw/agents/<name>"
    }
  ]
}
```

`requireMention: false`

lets you type naturally instead of prefixing every message with an `@mention`

. `allowFrom`

with 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"]`

).

**Give the agent its instructions.** This is the step that makes `status`

, `send`

, and `restart`

work; the agent's behavior comes entirely from the files in its `workspace`

. Create the workspace directory first (the `agentDir`

must be unique per agent; the gateway creates it on first run):

```
mkdir -p ~/.openclaw/workspace-<name>
```

Then put this in `~/.openclaw/workspace-<name>/AGENTS.md`

, a minimal command contract:

```
# <your-agent-id>

You are bound to tmux pane `<PANE>`, an interactive coding-agent session.

## Hard rules
- ALL tmux operations target `<PANE>`. Never touch any other session.
- Read with `tmux capture-pane -t <PANE> -p`; write with `tmux send-keys -t <PANE> ...`.
- Strip ANSI codes before relaying pane output to Telegram.
- After send-keys, wait a few seconds before re-capturing; replies aren't instant.
- Confirm before any destructive action EXCEPT the explicit "restart" command below.

## Common phrasings → actions
| User says | You do |
|---|---|
| "status" / "what's in tmux?" | capture `<PANE>`, strip ANSI, summarize the last ~60 lines |
| "send `<msg>`" | `send-keys -t <PANE> -l -- "<msg>"`, then Enter, wait, capture |
| "compact" / "new session" | send `/compact` or `/new` to the pane |
| "interrupt" / "stop it" | confirm, then send `C-c` |
| "restart" | run the restart sequence below (no confirmation) |

## Decision / option routing
You are a relay, not the owner of the work in the pane. If your previous reply
summarized options/questions from the agent (e.g. "A/B", "yes/no", "Proceed?"),
then short replies like `A`, `B`, `yes`, `no`, `do it`, `sorry A` are answers
**for the pane**: forward them verbatim; do not act on them yourself. Only act
locally when explicitly addressed ("you do option A").

## Restart sequence (no confirmation)
1. Resolve the exact session id first (do not blindly "continue", which can reopen the wrong session).
2. Exit the agent's UI cleanly and wait until you actually see a shell prompt.
3. Relaunch with EXACTLY this command, resuming that session id: `<your-launch-command> --resume <session_id>`
4. Capture and report whether it came back up.

## Safety rails (applied to anything you relay)
- Production: strict read-only. Never relay mutations.
- Never relay pushes/merges to shared branches.
- Shared infra (gateways, API gateways): never mutate without explicit human approval.
```

Replace

`<PANE>`

and 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`

to start.

**Required: validate before you trust the save.** An invalid `openclaw.json`

is 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:

``` python
# 1. JSON is parseable
python3 -c "import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON OK')"

# 2. Dry-run the config against a temp copy (never touches the live gateway).
#    The copy holds your bot token; keep it 0600 and delete it after.
cp ~/.openclaw/openclaw.json /tmp/oc-check.json && chmod 600 /tmp/oc-check.json
OPENCLAW_CONFIG_PATH=/tmp/oc-check.json pnpm openclaw doctor --non-interactive   # expect Errors: 0
rm -f /tmp/oc-check.json

# 3. Confirm the hot reload landed
grep -a 'config hot reload applied' <your-log-path> | tail -3
```

You 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:

`status`

in your topic; you get a response (not silence).

```
   tmux capture-pane -t <PANE> -p | tail -n 40
```

You should see your text injected into the coding agent.

```
   ls -t ~/.openclaw/agents/<name>/sessions/*topic-<TOPIC_THREAD_ID>* 2>/dev/null
```

`groupPolicy: "allowlist"`

, your ID in `allowFrom`

, and `requireMention: false`

; you're no longer in `open`

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

**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.*`

(also check `.last-good`

/ `.bak`

), fix the cause, **validate** (above), then restart:

```
nvm use 24.11.1
tmux kill-session -t openclaw-gateway-watch-main
cd ~/repos/openclaw && pnpm gateway:watch
grep -a 'gateway ready' <your-log-path> | tail -1
```

| Symptom | Cause | Fix |
|---|---|---|
Bot silent; log shows `"reason":"not-allowed"`
|
Group not configured, or you're not in `allowFrom`
|
Add the group; put your numeric ID in `allowFrom` (or use `open` while testing) |
Bot only replies when `@mention` ed |
`requireMention` defaulting to `true`
|
Set `requireMention: false`
|
| Bot doesn't see messages at all | Telegram privacy mode on a non-admin bot | Make the bot a group admin
|
| Topic IDs never appear in the log | Messages blocked before topic resolution | Open the group (Phase 1) first, then re-send |
`getUpdates` `409 Conflict`
|
Same bot token polled by two gateways | One gateway per token; kill the duplicate poller |
| Config edit "vanished" / gateway down after restart | Invalid JSON at startup → auto-restore → watch exits | Recover from `.clobbered.*` ; validate; restart |
| Agent replies but the pane does nothing | Wrong `<PANE>` , or the coding agent isn't running there |
Fix the pane target; relaunch the agent in that pane |

This is a public-internet bot with shell reach. Treat it that way:

`openclaw.json`

. `chmod 600`

`@BotFather`

.`allowFrom`

.You do **not** need any of this to finish the readiness gate; it all comes in Part 4 / the session:

`AGENTS.md`

already handles `status`

/`send`

/`restart`

/option replies prompt-driven; the router just makes it faster and stricter)`ops`

agent (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.

Part 3 is the operating contract: what to type, what *not* to type, and how to supervise the agent safely once it's answering you.
