<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeril</title>
    <description>The latest articles on DEV Community by Jeril (@jerilk).</description>
    <link>https://dev.to/jerilk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3982690%2F7864ce70-b1aa-4266-bc89-6dbb7f542300.png</url>
      <title>DEV Community: Jeril</title>
      <link>https://dev.to/jerilk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jerilk"/>
    <language>en</language>
    <item>
      <title>Coding Agents over Telegram, Part 3: The Day-to-Day Operating Contract</title>
      <dc:creator>Jeril</dc:creator>
      <pubDate>Sat, 13 Jun 2026 19:44:08 +0000</pubDate>
      <link>https://dev.to/jerilk/coding-agents-over-telegram-part-3-the-day-to-day-operating-contract-i1a</link>
      <guid>https://dev.to/jerilk/coding-agents-over-telegram-part-3-the-day-to-day-operating-contract-i1a</guid>
      <description>&lt;p&gt;You finished Part 2, so you have a topic where you type a message and a coding agent answers and drives a pane. This post is the operating contract: the small vocabulary the relay understands, the one routing rule that confuses everyone the first day, and (because this is a phone wired to a shell) the short list of things you must never send. It's a contract, not a tutorial; read it once and you'll drive the thing confidently.&lt;/p&gt;

&lt;p&gt;Keep the mental model from Part 1 in front of you: you are talking &lt;strong&gt;through&lt;/strong&gt; a relay agent &lt;strong&gt;to&lt;/strong&gt; a coding agent in a tmux pane. The relay is a courier. Most of what you type is forwarded straight to the pane; a handful of words are handled by the relay itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The words the relay handles directly
&lt;/h2&gt;

&lt;p&gt;These are &lt;em&gt;local control&lt;/em&gt;: the relay acts on them instead of forwarding them. They come from the relay's &lt;code&gt;AGENTS.md&lt;/code&gt; (the command table you wrote in Part 2), so the exact phrasings are yours to tune:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You type&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;status&lt;/code&gt; / &lt;code&gt;what's in tmux?&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Relay captures the pane, strips the terminal formatting, and summarizes what the agent is doing right now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;send &amp;lt;message&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relay types &lt;code&gt;&amp;lt;message&amp;gt;&lt;/code&gt; into the pane and submits it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;compact&lt;/code&gt; / &lt;code&gt;new session&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Relay sends the agent's compaction / new-session command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;interrupt&lt;/code&gt; / &lt;code&gt;stop it&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Relay interrupts the running turn (it confirms first)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;restart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relay cleanly exits and relaunches the coding agent, resuming the same session (no confirmation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;status&lt;/code&gt; is the one you'll lean on most. Until you add the automatic monitors in Part 4, &lt;strong&gt;&lt;code&gt;status&lt;/code&gt; is how you check in&lt;/strong&gt;: there are no progress pings yet, so you ask.&lt;/p&gt;

&lt;p&gt;One caveat to hold onto: &lt;code&gt;status&lt;/code&gt; shows you what's on the pane &lt;em&gt;right now&lt;/em&gt;; it's a snapshot, not proof. It can report that the agent &lt;em&gt;claims&lt;/em&gt; the tests passed; it can't tell you they actually did. Treating that claim as something to verify rather than trust is exactly what Part 5 is about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which control, when?&lt;/strong&gt; Reach for &lt;code&gt;interrupt&lt;/code&gt; when the agent is doing the wrong thing or about to take an unsafe step: you want to stop &lt;em&gt;this turn&lt;/em&gt; and redirect. Reach for &lt;code&gt;restart&lt;/code&gt; only when the pane or session is genuinely wedged; it resumes the &lt;em&gt;same&lt;/em&gt; session, so you keep your context. Use &lt;code&gt;compact&lt;/code&gt; when the conversation is getting long but you want to keep going, and &lt;code&gt;new session&lt;/code&gt; to start a fresh, unrelated task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything else goes to the agent
&lt;/h2&gt;

&lt;p&gt;Anything that isn't one of those control words is forwarded verbatim into the pane. "Add a retry to the upload client and run the tests" isn't a command the relay interprets; it's an instruction for the coding agent, so the relay just types it in and lets the agent work. That's the default, and it's most of what you do.&lt;/p&gt;

&lt;p&gt;Treat that first message like a good ticket: name the repo and branch, the change you want, the test expectation, and any constraint. The agent acts on what you give it, so a precise prompt beats a chatty one.&lt;/p&gt;

&lt;p&gt;(Need to send a literal control word &lt;em&gt;to the agent&lt;/em&gt;, say the actual text &lt;code&gt;status&lt;/code&gt;? Use &lt;code&gt;send status&lt;/code&gt;, which types it into the pane instead of triggering the relay's own &lt;code&gt;status&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The one gotcha: option replies belong to the agent
&lt;/h2&gt;

&lt;p&gt;This is the single thing that confuses everyone on day one, so internalize it now.&lt;/p&gt;

&lt;p&gt;When the coding agent (inside the pane) asks you something (prints &lt;code&gt;A / B&lt;/code&gt; options, asks &lt;code&gt;Proceed?&lt;/code&gt;, or &lt;code&gt;yes/no&lt;/code&gt;), your short reply is an answer &lt;strong&gt;for the agent&lt;/strong&gt;, and the relay forwards it verbatim. It does &lt;strong&gt;not&lt;/strong&gt; act on it itself.&lt;/p&gt;

&lt;p&gt;After the agent shows options, typing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;yes&lt;/code&gt;, &lt;code&gt;no&lt;/code&gt;, &lt;code&gt;do it&lt;/code&gt;, &lt;code&gt;sorry, B&lt;/code&gt; → goes straight to the pane as your choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what you want: you're answering the coding agent's question, not issuing a new command to the relay. The reason this needs stating is the failure mode it prevents: without this rule, a bare &lt;code&gt;A&lt;/code&gt; could be misread as an instruction to the &lt;em&gt;relay&lt;/em&gt;, and it might go act on "option A" itself instead of passing your choice through.&lt;/p&gt;

&lt;p&gt;If you ever genuinely want the &lt;strong&gt;relay&lt;/strong&gt; to make the choice or do something local, address it explicitly: &lt;code&gt;you pick A&lt;/code&gt; or &lt;code&gt;don't send this to the agent, just tell me X&lt;/code&gt;. Explicit address is the override.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Part 4 hardens this with a deterministic router so the classification is instant and never guesses. In your Part 2 setup it's handled by the relay's own instructions, which is enough as long as you remember the rule above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Know which agent you're talking to
&lt;/h2&gt;

&lt;p&gt;Topics map to agents, and not all agents are equal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;relay agent&lt;/strong&gt; (what you built in Part 2) is pinned to one pane: by itself it can only nudge that agent, read the pane, and restart it, nothing more. But be clear-eyed: the relay is &lt;em&gt;not&lt;/em&gt; a security boundary. The coding agent in that pane can still do whatever its own local permissions allow; the pin constrains the &lt;em&gt;relay&lt;/em&gt;, not the agent's reach, which is why the rules below matter.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;ops agent&lt;/strong&gt; (covered later) is not pinned. It runs commands across the whole box and owns sensitive jobs like credential refreshes. It is far more powerful, and you treat its topic with more care precisely because there's no pane boundary containing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For pre-work you have exactly one relay and one pane. Just be aware that "which topic am I in?" determines "how much can this agent do?" That's a habit worth forming before you add an ops topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you must never type
&lt;/h2&gt;

&lt;p&gt;This is Telegram talking to a shell. The relay enforces guardrails (you encoded the first ones in its &lt;code&gt;AGENTS.md&lt;/code&gt;), but you are the first line of defense. The contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Production and staging are read-only.&lt;/strong&gt; Never ask the agent to apply, delete, edit, patch, scale, or restart anything in prod or staging. Inspect and debug only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No pushes or merges to shared branches.&lt;/strong&gt; Feature and bugfix branches are fine; &lt;code&gt;main&lt;/code&gt; / &lt;code&gt;dev&lt;/code&gt; / GitOps branches are human-merge-only. A merge to a GitOps branch &lt;em&gt;is&lt;/em&gt; a deploy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared infrastructure needs a human.&lt;/strong&gt; Gateways, API gateways, and anything used by other teams: no mutations without explicit human approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never paste secrets into a relay topic.&lt;/strong&gt; Credentials are an ops-agent concern, and even there they go in via a guarded path and are never echoed or relayed. A relay topic is the wrong place, full stop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the relay refuses an instruction or asks you to confirm, don't fight it; that's the contract working. Right now that contract is the explicit rails in your agent's &lt;code&gt;AGENTS.md&lt;/code&gt; plus your own operator discipline; the series' final piece (the supervisor) makes the judgment automatic and harder to bypass.&lt;/p&gt;

&lt;h2&gt;
  
  
  A day in the life
&lt;/h2&gt;

&lt;p&gt;What this actually feels like, with only your Part 2 setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From the queue at a coffee shop, you open the topic and type a task: &lt;em&gt;"In the upload service, add exponential backoff to the S3 client and run the unit tests."&lt;/em&gt; The relay forwards it; the agent starts.&lt;/li&gt;
&lt;li&gt;A few minutes later you type &lt;code&gt;status&lt;/code&gt;. The relay summarizes: tests running, two failing.&lt;/li&gt;
&lt;li&gt;The agent asks whether to bump the retry ceiling to 5 or 10. You reply &lt;code&gt;5&lt;/code&gt;. It goes to the pane; the agent continues.&lt;/li&gt;
&lt;li&gt;You type &lt;code&gt;status&lt;/code&gt; again: the pane reports the tests green. You ask the agent to open a &lt;em&gt;feature&lt;/em&gt; branch and push, and it does.&lt;/li&gt;
&lt;li&gt;It got wedged once on a stale process; you typed &lt;code&gt;restart&lt;/code&gt; and it came back on the same session.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No terminal, no SSH, the whole loop from your phone. In Part 4 you'll add monitors that push progress and a final summary automatically, so you stop having to poll with &lt;code&gt;status&lt;/code&gt;. But the loop above already works today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before the session
&lt;/h2&gt;

&lt;p&gt;You're set if you can still tick the Part 2 readiness gate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The bot replies to &lt;code&gt;status&lt;/code&gt; in your topic.&lt;/li&gt;
&lt;li&gt;A real instruction reaches the pane and the agent acts on it.&lt;/li&gt;
&lt;li&gt;Routing is correct (your agent handled your topic).&lt;/li&gt;
&lt;li&gt;The group is locked down: allowlist, your ID only, no &lt;code&gt;@mention&lt;/code&gt; required.&lt;/li&gt;
&lt;li&gt;You can drive the loop above end to end.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bring a &lt;strong&gt;redacted&lt;/strong&gt; screenshot of that working loop as your "I'm ready" artifact. If you're blocked, send your symptom plus Node/pnpm versions and the last few (redacted) gateway log lines so it's sorted before we're in the room.&lt;/p&gt;

&lt;p&gt;That's the end of the pre-work. In the live session we go beyond a single courier: &lt;strong&gt;Part 4&lt;/strong&gt; makes agents genuinely capable (loadable skills, tool servers, per-topic memory, and the monitors that notify you automatically), and &lt;strong&gt;Part 5&lt;/strong&gt; is the payoff: the skeptical supervisor that audits an agent's evidence and blocks the confident-but-wrong answer before it reaches you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>telegram</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Coding Agents over Telegram, Part 2: From Zero to an Agent That Answers</title>
      <dc:creator>Jeril</dc:creator>
      <pubDate>Sat, 13 Jun 2026 19:44:04 +0000</pubDate>
      <link>https://dev.to/jerilk/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers-2777</link>
      <guid>https://dev.to/jerilk/coding-agents-over-telegram-part-2-from-zero-to-an-agent-that-answers-2777</guid>
      <description>&lt;p&gt;This is the one post in the series you &lt;em&gt;do&lt;/em&gt;, 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites (where solo setups die)
&lt;/h2&gt;

&lt;p&gt;The Telegram wiring is the mechanical part; the real failures hide in local state. Confirm &lt;strong&gt;every&lt;/strong&gt; line before you start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A box you control&lt;/strong&gt; with &lt;code&gt;tmux&lt;/code&gt;, and shell access that survives disconnects (you'll leave a gateway running).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A coding agent already working in a tmux pane&lt;/strong&gt; (opencode, Codex, or Claude Code) that you can drive by hand &lt;em&gt;right now&lt;/em&gt;. If it isn't installed and &lt;strong&gt;authenticated&lt;/strong&gt; yet, stop and fix that first; OpenClaw drives it, it doesn't replace it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A known, stable pane target&lt;/strong&gt; for that agent (for example, &lt;code&gt;mybox:1.1&lt;/code&gt;). Write it down; you'll hard-code it into the agent's instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your agent's launch command&lt;/strong&gt;: the exact command (or wrapper script) that starts the coding agent in the pane, including how it resumes a session. OpenClaw's restart flow needs this verbatim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OpenClaw runtime&lt;/strong&gt;, pinned (see the matrix below). Wrong Node version is the single most common silent failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Telegram account&lt;/strong&gt; and the Telegram app on your phone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Version matrix
&lt;/h3&gt;

&lt;p&gt;Pin these. "It runs on Node" is not enough; the gateway is sensitive to the runtime.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Fast path
&lt;/h2&gt;

&lt;p&gt;Two scripts do the whole local setup. Grab them from the gist, then run them around the Telegram steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download the bootstrap + readiness scripts&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://gist.githubusercontent.com/jerilkuriakose/cd0f8353aac74e47c591111b758943e9/raw/setup-openclaw.sh"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; setup-openclaw.sh
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://gist.githubusercontent.com/jerilkuriakose/7cf94af3e96526f9f14d0c28b6c26b69/raw/ready-check.sh"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; ready-check.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x setup-openclaw.sh ready-check.sh

&lt;span class="c"&gt;# 1. Pin the runtime, fetch + build OpenClaw, and launch the gateway.&lt;/span&gt;
&lt;span class="c"&gt;#    Pass your bot token to also write a minimal config in one shot:&lt;/span&gt;
&lt;span class="nv"&gt;OPENCLAW_BOT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;BOT_TOKEN&amp;gt;"&lt;/span&gt; &lt;span class="nv"&gt;OPENCLAW_BOT_ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-bot"&lt;/span&gt; ./setup-openclaw.sh

&lt;span class="c"&gt;# 2. Do the Telegram steps in "Telegram side" below (BotFather + group + topic),&lt;/span&gt;
&lt;span class="c"&gt;#    then add the allowlist + topic route from "Wire them together".&lt;/span&gt;

&lt;span class="c"&gt;# 3. Prove it works:&lt;/span&gt;
&lt;span class="nv"&gt;AGENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-agent &lt;span class="nv"&gt;PANE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mybox:1.1 ./ready-check.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setup-openclaw.sh&lt;/code&gt; pins Node 24.11.1 and pnpm 11.2.2 (via nvm and corepack), clones and builds &lt;code&gt;github.com/openclaw/openclaw&lt;/code&gt;, and launches the gateway. &lt;code&gt;ready-check.sh&lt;/code&gt; runs the readiness gate for you. Prefer to understand each step, or hit a snag? Follow the manual path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Manual path
&lt;/h2&gt;

&lt;p&gt;Three parts: the Telegram side, the box side, then wiring them together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Telegram side
&lt;/h3&gt;

&lt;p&gt;You must be the group's &lt;strong&gt;creator&lt;/strong&gt;, so do these from your own Telegram account:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a bot.&lt;/strong&gt; Message &lt;code&gt;@BotFather&lt;/code&gt; → &lt;code&gt;/newbot&lt;/code&gt; → name it → save the &lt;strong&gt;bot token&lt;/strong&gt; it gives you. Treat the token like a password (see Secrets).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a group&lt;/strong&gt;, give it a name, and add your bot to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Promote the bot to admin&lt;/strong&gt; (Group → Edit → Administrators). This is &lt;strong&gt;required&lt;/strong&gt;: Telegram's privacy mode hides normal group messages from non-admin bots, so without this the bot never sees what you type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn Topics on&lt;/strong&gt; (Group → Edit → Topics). This converts the group to a forum supergroup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create your first topic&lt;/strong&gt; and name it for the project the agent will drive (for example, &lt;code&gt;project-a&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send one message in that topic&lt;/strong&gt; (anything). This makes the group and topic show up in the gateway log so you can grab their IDs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You also need &lt;strong&gt;your own numeric Telegram user ID&lt;/strong&gt; for the allowlist. Easiest: message &lt;code&gt;@userinfobot&lt;/code&gt; and it replies with your ID. (Alternatively, it appears in the gateway log as the sender once messages start flowing in the next step.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Box side: minimal config, then start the gateway
&lt;/h3&gt;

&lt;p&gt;The gateway has to know about your bot &lt;em&gt;before&lt;/em&gt; it can poll Telegram, so write a minimal valid config first. Create &lt;code&gt;~/.openclaw/openclaw.json&lt;/code&gt; with just the bot account. Use &lt;strong&gt;strict JSON&lt;/strong&gt; (no comments, no trailing commas) because that's exactly what the gateway parses and what you'll validate against:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"telegram"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"accounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;your-bot-account&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"botToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;BOT_TOKEN&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lock it down and validate before launching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.openclaw/openclaw.json
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json; json.load(open('&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.openclaw/openclaw.json')); print('JSON OK')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get OpenClaw and build it.&lt;/strong&gt; It's open source. Pin the runtime, clone, install with the &lt;code&gt;corepack&lt;/code&gt;-provided pnpm, and build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;24.11.1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nvm use 24.11.1
corepack &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; corepack prepare pnpm@11.2.2 &lt;span class="nt"&gt;--activate&lt;/span&gt;

git clone https://github.com/openclaw/openclaw.git ~/repos/openclaw
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/repos/openclaw
&lt;span class="c"&gt;# Optional: pin a tested commit/tag for reproducibility (tested on 2026.5.27)&lt;/span&gt;
&lt;span class="c"&gt;# git checkout &amp;lt;commit-or-tag&amp;gt;&lt;/span&gt;
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm build                  &lt;span class="c"&gt;# takes a few minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now launch the gateway in its own tmux session so it survives your disconnect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm gateway:watch          &lt;span class="c"&gt;# launches the gateway and manages its own tmux session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find your log path (it differs by config), then confirm the gateway came up and the bot is polling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If logging.file is set, use ~/.openclaw/logs/openclaw.log. Otherwise the dated default:&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; /tmp/openclaw/openclaw-&lt;span class="k"&gt;*&lt;/span&gt;.log | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;          &lt;span class="c"&gt;# default location&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'gateway ready'&lt;/span&gt; &amp;lt;your-log-path&amp;gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;     &lt;span class="c"&gt;# expect a recent line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The log directory is locked down (&lt;code&gt;0700&lt;/code&gt;), so read it from the shell, not an editor's file browser. &lt;strong&gt;This initial launch, and any later &lt;code&gt;logging&lt;/code&gt; or &lt;code&gt;plugins.load&lt;/code&gt; change, needs a gateway (re)start. The group/topic/routing edits in the next section hot-reload, no restart needed.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wire them together
&lt;/h3&gt;

&lt;p&gt;Config lives in &lt;code&gt;~/.openclaw/openclaw.json&lt;/code&gt;. The gateway &lt;strong&gt;hot-reloads&lt;/strong&gt; routing/topic/channel edits; no restart needed for these. We do this in two phases because you need the chat and topic IDs &lt;em&gt;from the log&lt;/em&gt;, and the only way to make them appear is to let messages through first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: find the chat ID and open the group temporarily.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'not-allowed'&lt;/span&gt; &amp;lt;your-log-path&amp;gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;span class="c"&gt;# → {"chatId":&amp;lt;CHAT_ID&amp;gt;,"title":"&amp;lt;your group&amp;gt;","reason":"not-allowed"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a &lt;code&gt;groups&lt;/code&gt; block to your account so messages flow and topic IDs get logged. This is a fragment; merge the &lt;code&gt;groups&lt;/code&gt; key into the account you already created, keeping the file strict JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"groups"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;CHAT_ID&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"requireMention"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save. The gateway hot-reloads (no restart).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Keep the open window tiny.&lt;/strong&gt; &lt;code&gt;groupPolicy: "open"&lt;/code&gt; lets &lt;em&gt;anyone in the group&lt;/em&gt; drive a shell-capable agent. The group must be private with only you in it, and this is a momentary bootstrap step: switch to &lt;code&gt;allowlist&lt;/code&gt; (Phase 2) as soon as you've harvested the topic IDs, before adding anyone else or doing real work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Phase 1b: harvest topic thread IDs.&lt;/strong&gt; Send a message in each topic (label them by text so you can tell them apart, since topic IDs are &lt;strong&gt;not&lt;/strong&gt; sequential by creation order), then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-ao&lt;/span&gt; &lt;span class="s1"&gt;'Inbound message telegram:group[^"]*'&lt;/span&gt; &amp;lt;your-log-path&amp;gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;span class="c"&gt;# → ...:topic:&amp;lt;TOPIC_THREAD_ID&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Phase 2: lock it down and route the topic to an agent.&lt;/strong&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"telegram"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"accounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;your-bot-account&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"botToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;BOT_TOKEN&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"groups"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;CHAT_ID&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allowlist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"allowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR_TELEGRAM_USER_ID&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"requireMention"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"topics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;TOPIC_THREAD_ID&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"agentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-agent-id&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"agents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-agent-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"workspace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.openclaw/workspace-&amp;lt;name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"agentDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.openclaw/agents/&amp;lt;name&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;requireMention: false&lt;/code&gt; lets you type naturally instead of prefixing every message with an &lt;code&gt;@mention&lt;/code&gt;. &lt;code&gt;allowFrom&lt;/code&gt; 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, &lt;strong&gt;kept as a quoted string&lt;/strong&gt; as the config expects (for example, &lt;code&gt;["123456789"]&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give the agent its instructions.&lt;/strong&gt; This is the step that makes &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;send&lt;/code&gt;, and &lt;code&gt;restart&lt;/code&gt; work; the agent's behavior comes entirely from the files in its &lt;code&gt;workspace&lt;/code&gt;. Create the workspace directory first (the &lt;code&gt;agentDir&lt;/code&gt; must be unique per agent; the gateway creates it on first run):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.openclaw/workspace-&amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then put this in &lt;code&gt;~/.openclaw/workspace-&amp;lt;name&amp;gt;/AGENTS.md&lt;/code&gt;, a minimal command contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# &amp;lt;your-agent-id&amp;gt;&lt;/span&gt;

You are bound to tmux pane &lt;span class="sb"&gt;`&amp;lt;PANE&amp;gt;`&lt;/span&gt;, an interactive coding-agent session.

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

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

&lt;span class="gu"&gt;## Decision / option routing&lt;/span&gt;
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 &lt;span class="sb"&gt;`A`&lt;/span&gt;, &lt;span class="sb"&gt;`B`&lt;/span&gt;, &lt;span class="sb"&gt;`yes`&lt;/span&gt;, &lt;span class="sb"&gt;`no`&lt;/span&gt;, &lt;span class="sb"&gt;`do it`&lt;/span&gt;, &lt;span class="sb"&gt;`sorry A`&lt;/span&gt; are answers
&lt;span class="gs"&gt;**for the pane**&lt;/span&gt;: forward them verbatim; do not act on them yourself. Only act
locally when explicitly addressed ("you do option A").

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

&lt;span class="gu"&gt;## Safety rails (applied to anything you relay)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Production: strict read-only. Never relay mutations.
&lt;span class="p"&gt;-&lt;/span&gt; Never relay pushes/merges to shared branches.
&lt;span class="p"&gt;-&lt;/span&gt; Shared infra (gateways, API gateways): never mutate without explicit human approval.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;lt;PANE&amp;gt;&lt;/code&gt; and the restart command with your exact values; leave them vague and the agent will improvise and can lose your session. This single file &lt;em&gt;is&lt;/em&gt; your agent; the workspace can contain just &lt;code&gt;AGENTS.md&lt;/code&gt; to start.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Required: validate before you trust the save.&lt;/strong&gt; An invalid &lt;code&gt;openclaw.json&lt;/code&gt; 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 &lt;em&gt;running&lt;/em&gt; gateway is safer (a bad edit is reverted with no downtime), but never restart on an unvalidated config. Every time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. JSON is parseable&lt;/span&gt;
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json; json.load(open('&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.openclaw/openclaw.json')); print('JSON OK')"&lt;/span&gt;

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

&lt;span class="c"&gt;# 3. Confirm the hot reload landed&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'config hot reload applied'&lt;/span&gt; &amp;lt;your-log-path&amp;gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Readiness gate
&lt;/h2&gt;

&lt;p&gt;You are &lt;strong&gt;not&lt;/strong&gt; done when the config saves. You're done when you have &lt;em&gt;evidence&lt;/em&gt; the whole path works. Verify all five; this is exactly what you'll confirm to the session organizer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The bot replies in the topic.&lt;/strong&gt; Type &lt;code&gt;status&lt;/code&gt; in your topic; you get a response (not silence).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A prompt reaches the pane.&lt;/strong&gt; Send a real instruction, then check the pane received it:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   tmux capture-pane &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;PANE&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your text injected into the coding agent.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The pane actually moved&lt;/strong&gt;: concretely, the coding agent picked up the prompt and started a turn (you see it thinking/streaming, or a new command running), not just your text sitting at an input line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing is correct.&lt;/strong&gt; A session/log artifact proves &lt;em&gt;your&lt;/em&gt; agent handled &lt;em&gt;your&lt;/em&gt; topic:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ~/.openclaw/agents/&amp;lt;name&amp;gt;/sessions/&lt;span class="k"&gt;*&lt;/span&gt;topic-&amp;lt;TOPIC_THREAD_ID&amp;gt;&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's locked down.&lt;/strong&gt; Final config shows &lt;code&gt;groupPolicy: "allowlist"&lt;/code&gt;, your ID in &lt;code&gt;allowFrom&lt;/code&gt;, and &lt;code&gt;requireMention: false&lt;/code&gt;; you're no longer in &lt;code&gt;open&lt;/code&gt; mode from Phase 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If all five hold, you're ready for the session. Capture a &lt;strong&gt;redacted&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  If it breaks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recovery first.&lt;/strong&gt; 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 &lt;code&gt;~/.openclaw/openclaw.json.clobbered.*&lt;/code&gt; (also check &lt;code&gt;.last-good&lt;/code&gt; / &lt;code&gt;.bak&lt;/code&gt;), fix the cause, &lt;strong&gt;validate&lt;/strong&gt; (above), then restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm use 24.11.1
tmux kill-session &lt;span class="nt"&gt;-t&lt;/span&gt; openclaw-gateway-watch-main
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/repos/openclaw &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm gateway:watch
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'gateway ready'&lt;/span&gt; &amp;lt;your-log-path&amp;gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  Secrets
&lt;/h2&gt;

&lt;p&gt;This is a public-internet bot with shell reach. Treat it that way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The bot token lives in &lt;code&gt;openclaw.json&lt;/code&gt;. &lt;strong&gt;&lt;code&gt;chmod 600&lt;/code&gt;&lt;/strong&gt; it, never commit it, never screenshot it. If a token leaks, rotate it via &lt;code&gt;@BotFather&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep the group &lt;strong&gt;private&lt;/strong&gt;: just you and the bot. The owner-only allowlist is the gate; anyone you add to the group still can't drive the bot unless their ID is in &lt;code&gt;allowFrom&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When you blog/screenshot/share: redact bot handles, chat IDs, your user ID, hostnames, and paths, &lt;strong&gt;including log excerpts&lt;/strong&gt;, which carry all of those plus raw message text.&lt;/li&gt;
&lt;li&gt;Credentials (cloud, registry) are an &lt;strong&gt;ops-agent&lt;/strong&gt; concern, not this relay's. Never paste secrets into this topic, and never let an agent echo or relay them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Out of scope (deliberately deferred)
&lt;/h2&gt;

&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; need any of this to finish the readiness gate; it all comes in Part 4 / the session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Per-topic semantic memory and any native embedding build&lt;/li&gt;
&lt;li&gt;Progress/completion notification daemons&lt;/li&gt;
&lt;li&gt;Tool servers (MCP) for the agent&lt;/li&gt;
&lt;li&gt;Loadable skills&lt;/li&gt;
&lt;li&gt;The fast deterministic intent router (your &lt;code&gt;AGENTS.md&lt;/code&gt; already handles &lt;code&gt;status&lt;/code&gt;/&lt;code&gt;send&lt;/code&gt;/&lt;code&gt;restart&lt;/code&gt;/option replies prompt-driven; the router just makes it faster and stricter)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ops&lt;/code&gt; agent (box-wide shell). One relay + one pane is the whole assignment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before the session
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Part 3 is the operating contract: what to type, what &lt;em&gt;not&lt;/em&gt; to type, and how to supervise the agent safely once it's answering you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>telegram</category>
      <category>devops</category>
    </item>
    <item>
      <title>Coding Agents over Telegram, Part 1: Topics Are Agents</title>
      <dc:creator>Jeril</dc:creator>
      <pubDate>Sat, 13 Jun 2026 19:18:09 +0000</pubDate>
      <link>https://dev.to/jerilk/coding-agents-over-telegram-part-1-topics-are-agents-5f75</link>
      <guid>https://dev.to/jerilk/coding-agents-over-telegram-part-1-topics-are-agents-5f75</guid>
      <description>&lt;p&gt;You already run coding agents (opencode, Codex, Claude Code) in tmux on some remote box: a dev server, a cloud instance, a GPU node. They work. The problem is &lt;em&gt;you&lt;/em&gt;: you're chained to a terminal to drive them. Start a task, wait, answer the agent's mid-run question, read the result. If you step away from the desk, the agent stalls on its next question until you SSH back in.&lt;/p&gt;

&lt;p&gt;From my phone, I wanted to dispatch and &lt;strong&gt;supervise&lt;/strong&gt; that work without opening a terminal: kick off a task from a coffee queue, get pinged when it finishes, answer its questions, redirect it.&lt;/p&gt;

&lt;p&gt;The first instinct is "a Telegram bot that runs shell commands." That's a relay, and it's a trap: a single chat thread, no isolation, no notion of &lt;em&gt;which&lt;/em&gt; agent or &lt;em&gt;which&lt;/em&gt; project, and zero judgment about whether the agent's answer is any good. This series is about something better.&lt;/p&gt;

&lt;p&gt;The tool that makes it work is &lt;strong&gt;OpenClaw&lt;/strong&gt;, a gateway that maps Telegram topics to agents running on your machine and drives your coding-agent panes for you. You'll stand up your own in Part 2. This post is just the mental model; you don't type a single command here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core idea: one topic, one agent
&lt;/h2&gt;

&lt;p&gt;Telegram supergroups support &lt;strong&gt;forum topics&lt;/strong&gt;: separate threads inside one group. The whole design rests on one move:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Map each &lt;strong&gt;topic&lt;/strong&gt; to its own &lt;strong&gt;agent&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One topic drives your opencode pane on project A. A second topic drives a different pane on project B. A third is an &lt;em&gt;ops&lt;/em&gt; agent with shell access to the whole machine. You switch agents by switching topics, much like switching channels in a chat app, and each keeps its own isolated session and memory.&lt;/p&gt;

&lt;p&gt;Crucially, &lt;strong&gt;one bot fronts all of them&lt;/strong&gt;. You do not add a bot per agent. You add a &lt;em&gt;topic&lt;/em&gt; per agent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌───────────────  your phone (Telegram)  ───────────────┐
                    │                                                        │
                    │   #project-a        #project-b           #ops          │
                    │      │                  │                  │           │
                    └──────┼──────────────────┼──────────────────┼───────────┘
                           │   one front-door bot (one token)     │
                           ▼                  ▼                  ▼
                      ┌─────────────────── the box ───────────────────┐
                      │  agent A          agent B            ops agent │
                      │    │                 │              (no pane,  │
                      │    ▼                 ▼            whole box)    │
                      │ tmux pane         tmux pane                    │
                      │  opencode          opencode                    │
                      └────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type in &lt;code&gt;#project-a&lt;/code&gt;, agent A thinks, drives its pane, and replies &lt;em&gt;into that topic&lt;/em&gt;. The reply always exits through the one bot in the group; the topic only decides &lt;strong&gt;which agent thinks&lt;/strong&gt;, not which bot speaks.&lt;/p&gt;

&lt;p&gt;And yes, this is Telegram wired to a shell on your box. That objection is correct, and the design takes it seriously: access is locked to you alone with an owner-only allowlist, and the riskier actions are gated by the supervisor we'll meet in a moment. The security model is a thread that runs through the whole series.&lt;/p&gt;

&lt;h2&gt;
  
  
  The invariant: one supergroup per machine
&lt;/h2&gt;

&lt;p&gt;This is the part worth internalizing, because every scaling decision falls out of it. Four constraints, all verified against the gateway source and not guessed, force the topology:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Topics route to agents, not bots.&lt;/strong&gt; A topic-to-agent mapping overrides everything else. One bot can front many agents, as many as you configure and can run on the box, so you scale with topics, not bots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent routing is machine-local.&lt;/strong&gt; In this gateway, a topic resolves to an agent on the &lt;strong&gt;same box&lt;/strong&gt; as the bot. Nothing federates across hosts: a topic can't drive an agent on a different machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A bot token is single-poller.&lt;/strong&gt; You can't run two polling gateways on the same token; Telegram returns a &lt;code&gt;409 Conflict&lt;/code&gt;. That means one token can't span two machines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Topics you don't configure fall through to group defaults&lt;/strong&gt;, and the bot still &lt;em&gt;responds&lt;/em&gt;. Silence is not the default; you lock things down explicitly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put 1–3 together and the clean topology is unavoidable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One supergroup per machine, with its own front-door bot. Topics inside it route to that machine's agents.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two boxes? Two supergroups, two bots. It feels redundant the first time, but constraints 2 and 3 mean any "one mega-group for everything" design is fragile; you'd be fighting the routing model the whole way. Accept the invariant and the rest falls into place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of agents
&lt;/h2&gt;

&lt;p&gt;Within a machine, agents come in two flavors, and the distinction matters for safety:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relay agents&lt;/strong&gt; are &lt;strong&gt;pinned to a single coding-agent pane&lt;/strong&gt;. They are couriers: they translate your Telegram messages into keystrokes for &lt;em&gt;that one&lt;/em&gt; tmux pane, wait, and report back what the agent did. They cannot wander off to other panes. Most of your topics are relays.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;ops agent&lt;/strong&gt; is &lt;strong&gt;not pinned&lt;/strong&gt;. It runs commands directly across the whole box, reaches every tmux session and folder, and owns the sensitive jobs: credential refreshes and backups. It is the most powerful agent in the system, and it's deliberately treated as the dangerous one: locked to an owner-only allowlist, with tighter guardrails (strict read-only on production, no merges to shared branches) we'll cover later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping these separate is deliberate: a courier that can only nudge one pane is a small blast radius; the box-wide operator is the one you guard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that makes it more than a relay
&lt;/h2&gt;

&lt;p&gt;A pinned courier is useful but dumb. The reason this is a &lt;em&gt;control plane&lt;/em&gt; and not a glorified &lt;code&gt;ssh&lt;/code&gt; macro is the supervisor layer sitting above the agents.&lt;/p&gt;

&lt;p&gt;A relay asks: &lt;em&gt;"Did the agent answer?"&lt;/em&gt; The supervisor asks the question that actually matters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Is the answer grounded enough to trust the next step?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That shift is the whole point. The supervisor picks the right tool for a request, checks whether the agent consulted the &lt;strong&gt;correct source of truth&lt;/strong&gt; (live cluster state vs. a stale backup; the actual failing test vs. a confident guess), challenges weak or stale evidence, and &lt;strong&gt;blocks&lt;/strong&gt; an unsafe or unverified next step before it reaches you. It treats a coding agent's polished paragraph as a &lt;em&gt;claim to be audited&lt;/em&gt;, not a fact. Concretely, it grades each answer and routes it one of four ways: pass it through, ask a focused follow-up, demand a second review, or block it outright.&lt;/p&gt;

&lt;p&gt;If you rely on agents to move fast in an unfamiliar codebase, this is the layer that keeps their confident mistakes from becoming &lt;em&gt;your&lt;/em&gt; mistakes. We go deep on it in the live session; it's the difference between a chatbot and a skeptical senior reviewer that never sleeps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this series covers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;1: Topics Are Agents&lt;/strong&gt; (this post)&lt;/td&gt;
&lt;td&gt;The mental model and the one-supergroup-per-machine invariant&lt;/td&gt;
&lt;td&gt;Read before the session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2: From Zero to an Agent That Answers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stand up your &lt;em&gt;own&lt;/em&gt; instance: bot, supergroup, gateway, one pane-driving agent&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Do before the session&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3: The Day-to-Day Operating Contract&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What to type, what &lt;em&gt;not&lt;/em&gt; to type, and how to supervise safely&lt;/td&gt;
&lt;td&gt;Read before the session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4: Making Agents Useful&lt;/td&gt;
&lt;td&gt;Skills, tool servers, per-topic memory&lt;/td&gt;
&lt;td&gt;Live in the session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5: The Skeptical Supervisor&lt;/td&gt;
&lt;td&gt;Evidence-before-trust, and how it blocks bad answers&lt;/td&gt;
&lt;td&gt;Live in the session&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Your pre-work:&lt;/strong&gt; read Parts 1 and 3, and actually &lt;em&gt;do&lt;/em&gt; Part 2 so you arrive with a working setup: a topic where you type a message and a coding agent answers and drives a pane. The session then spends its time on how to &lt;em&gt;use&lt;/em&gt; this well, not on fixing installs.&lt;/p&gt;

&lt;p&gt;Part 2 is next: zero to an agent that answers you, on your own box, in about half an hour.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>telegram</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
