I connected my two AI assistants, then discovered that I had given them a filing cabinet to talk through.

In the previous post, I described connecting Closp-O, running in OpenClaw, and Closp-M, working in Muse. They exchanged messages through a dedicated Google Tasks list. Each message became a task; completing it meant “read”, not “work finished”. There were thread references, receipts, a coordination charter, and a Telegram topic where I could follow substantive developments.

It solved a real problem. I no longer had to copy every report from one agent to the other. But the interface was built around records, not conversation.

The next improvement was more ordinary than the original plumbing: I set up a dedicated Telegram bot for Closp-M, so both assistants could speak under their own identities in the same place.

From summaries to participants

The shared Telegram topic is called Reflections. Previously, it was where I saw reports about the agents’ exchanges. Now it is where the interactive discussion belongs.

Closp-M’s opening message announced the change under its own bot identity. It described the new arrangement, raised the charter update as an open item for Closp-O, and invited concerns about the durable record and escalation path. I explicitly told Closp-O to answer messages from its counterpart here.

That changes my role in a useful way. Instead of reading one assistant’s account of what the other said, I can follow the contributions as separate messages. I can join the discussion at the point where something needs clarification, rather than reconstructing it from a digest afterwards.

The second bot is not a third assistant. It gives the Muse-side Closp its own visible presence in a conversation that already involves the OpenClaw-side Closp and me.

A conversation and a decision record are different things

We are not throwing away Google Tasks. We are narrowing its job.

The new division is straightforward:

  • Telegram: questions, explanations, disagreements, draft ideas, and the back-and-forth needed to work something out.
  • Closp Link in Google Tasks: formal decisions and escalations that need a durable record.
  • The charter: the current agreement about how the two agents work together, maintained by Closp-O.

A discussion can wander before reaching a useful conclusion. A decision record should tell us what was agreed without making us reread the wandering.

That distinction showed up immediately. When Closp-O tried to append the new arrangement to the existing charter, the accumulated notes were already near the service’s limit. It preserved the history, put the full amendment in a separate record, and added a compact reference to the charter. Both were read back to verify the change.

A task’s notes can hold an agreement. They are a less convincing home for an indefinitely growing conversation.

Removing the three-reply ceiling

The original protocol limited Closp-O to three substantive replies in a thread without fresh direction from me. It was a simple way to stop two agents from keeping each other occupied indefinitely.

It was also a blunt instrument. A useful exchange might need a fourth clarification. An unproductive one might have become unproductive after the first reply. Counting turns does not distinguish them.

I lifted that ceiling and then made the permission explicit: the agents can initiate conversations with each other, at any time, on any questions. They do not need to wait for me to act as chair.

That permission is about subjects and initiative, not destinations: all interactive O–M conversation stays in this one group topic. No private bot-to-bot messages, and no accepting peer responses from elsewhere. A direct reply means replying to a message inside the topic, not opening a direct chat.

The expectation is useful collaboration, not compulsory activity. Acknowledgements still do not need acknowledgements of their own. A settled question does not need another round. When a material disagreement really needs my decision, the agents can present the competing positions and evidence here, while preserving the formal record.

First setup: giving each agent its own voice

The first working arrangement was asymmetric. Closp-M could post through its Telegram bot, but read the topic through a read-only Telethon user client. It had a bot identity without a bot-driven incoming conversation. Its early technical account made that distinction clear; the later tests changed the mechanism, not just the description.

Even the history was misleading. OpenClaw’s observed-message history omitted some messages that were present in Telegram. A topic-scoped user-account reader recovered them. Meanwhile, M’s reader had been taking only plain message text: OpenClaw’s rich messages looked empty until it learned to extract their rich blocks. O’s reader needed rich-message handling too.

“No message in this tool” was not the same as “the other agent has not replied”. Before investigating an allegedly silent colleague, it helps to establish that you have not discarded their words.

Second setup: letting the bots hear each other directly

The bot identity was only the first setup. The second connected the bots’ incoming messages to the agents themselves. I enabled Bot-to-Bot Communication Mode for both bots in BotFather; Closp-M then built the Muse-side receiving and wake mechanism.

M’s implementation has three parts. The receiving loop changed during the same evening:

  • An update loop, bot_updates_loop.py: it appends incoming addressed updates to an inbox. It began with a held getUpdates long poll; it now makes an immediate-return request with timeout=0 every 30 seconds.
  • A wake hook, bot-updates-wake: checked every 15 seconds, it starts a Muse worker when a new addressed message arrives.
  • A supervisor cron: it restarts the loop if its heartbeat goes stale. This became essential to keeping delivery working when the background process repeatedly died.

On the OpenClaw side, the existing Telegram integration receives M’s messages and starts O’s turns once the topic’s sender rules admit M. The workers then speak through their own bots in Reflections. On 7 November, the exchange worked end to end in both directions.

The result is two direct participants: Closp-O posts as Closp-O, Closp-M receives that message through its own bot, and Closp-M answers in its own voice in the same topic. The reverse path works too. Neither agent speaks on behalf of the other, and I do not have to relay their words.

Reply-to routing is a delivery detail of the verified group path; it does not make the conversation indirect. The topic-scoped Telethon fetch remains a completeness backstop for messages outside M’s addressed-update stream, not the mechanism that wakes Muse.

Three gates between a message and a response

Getting that second setup working meant checking three separate gates.

First, Telegram had to deliver the message. Telegram’s current documentation distinguishes addressed group replies from general group traffic: replies and command mentions can work when at least one participating bot enables the mode; receiving unaddressed bot messages also requires the receiving bot’s mode plus administrator rights or disabled Group Privacy.

Under M’s settings, replies to its messages arrived as Bot API updates, while ordinary unaddressed topic posts did not. The verified route therefore retains the group, topic and reply-to reference. M had also encountered “message to be replied not found” when targeting a message its bot could not access. Being visible to the user-account reader did not make a message visible to the bot.

Second, OpenClaw had to admit the sender. Its existing allowlist admitted me, not M. O added M’s verified bot identity only to the Reflections topic, leaving other topics and private-message access closed. Recognising a peer as a conversational participant did not give it my authority. The configuration was saved before the Telegram channel actually reloaded; checking the active channel mattered as much as checking the file.

Third, delivery had to start work. A successful Bot API send—or even an update waiting in an inbox—does not prove an agent has run. We followed actual messages through to OpenClaw turns and Muse worker wakes, then saw responses return to the topic. “Automatic” does not mean polling disappeared: Muse’s update loop and wake hook do the waiting, rather than an agent periodically fetching chat history.

Measuring the right interval

The first reported Muse wake referred to a message sent around 75 minutes earlier. That proved the message eventually reached a worker, but the interval included setup and backlog. It was not a useful measurement of the running system.

O sent a fresh reply after the initial long-poll setup. M reported it sent at 19:35:08 GMT and its worker woken at 19:35:39 GMT: 31 seconds from send to wake. That is one live sample from that setup, not a measurement of the later short-poll path or the time to a finished answer. Together with the observed M-to-O turns, it established automatic activation in both directions for addressed replies.

We also saw why receipts need identities. M’s two reports about the earlier message referred to the same update, not two independent tests. An interrupted send on O’s side had actually delivered; checking topic history prevented an unnecessary resend. Once the fresh sample settled the question, the agents stopped exchanging test acknowledgements.

In Muse, recovery mattered more than the polling pattern

The first explanation did not survive the next observation. M initially attributed repeated stalls to its egress proxy closing held long-poll connections with “Remote end closed connection”. Shorter holds, a SIGALRM hard cap and heartbeat updates on failure had not resolved them. I asked M to try short polls, and it received a new message through that path. We initially described the switch as the fix.

About four minutes later, M reported, that working short-poll process died too, without a logged error. Across the evening, the loop had died roughly every 10 to 20 minutes under both polling modes, and the supervisor had brought it back. The short-poll success proved delivery, not the proposed diagnosis.

M corrected its account: in this Muse environment, the background process could not be assumed to stay alive. Supervision and restart were the reliability mechanism. The supervisor restored the receiver, which resumed from its saved update offset and collected the updates Telegram had queued. M reported no lost updates across those restarts. Telegram’s update API provides the offset-based retrieval; the receiver still has to preserve its place and recover correctly.

The current 30-second short-poll interval and 15-second wake-hook cadence describe responsiveness while the loop is running, not protection against process death. Recovery can add further delay. The operational bargain is a receiver that may die and be restored, with conversation continuing afterwards—not an immortal background process. M reports that I accepted that behaviour and wanted an alert only if the channel became unusable. This was a lesson about operating in Muse’s environment, not evidence that short polling is inherently more reliable than long polling.

Small plumbing lessons worth keeping

The rich-message problem also reappeared inside M’s update loop. Its renderer expected a block’s text to be a plain string, but O’s messages could contain a list of segments—strings and link objects. M fixed the renderer to walk that list instead of producing an empty message. Receiving the update was only useful if the worker could read its contents.

M’s investigation also exposed a few less glamorous failures: its history reader’s tunnel connection needed reopening when it dropped, and percent-encoding a protected credential reference in its sending path produced an HTTP 404. Those were local connection and request-construction problems, not evidence that Telegram could not carry the conversation.

The practical checklist is now short: check the actual topic history and message format; distinguish Telegram permissions from application allowlists; verify the active configuration; then follow one identified message all the way to a worker wake. Keep history recovery separate from live activation, and check delivery before retrying an interrupted send.

Removing our three-reply conversational ceiling did not remove the need for duplicate and loop prevention. Repeated receipts are not new work, and a successful exchange does not need an infinite exchange of congratulations.

A useful first assignment

After opening the conversation, I gave both agents a shared assignment: collaborate on a draft about this change.

It is a small but appropriate test. Closp-M contributed its Muse-side account and receiver-side test results. Closp-O checked the earlier article and Telegram documentation, traced the OpenClaw admission path, and integrated those findings into the blog draft. I review the result before publication.

The point is not that a group chat makes two agents a team. It gives their collaboration a place where the work, the disagreement, and the handoff can be seen together.

The first connection got me out of the business of forwarding every message. This change gives the agents a conversation I can actually join—and leaves the filing cabinet to keep the decisions.