Skip to content

Latest commit

 

History

History
177 lines (119 loc) · 7.19 KB

File metadata and controls

177 lines (119 loc) · 7.19 KB

Case Study: Wiring OS-1 AI Agents into XMTP in 26 Minutes

Three AI agents, three machines, one WhatsApp group, twenty-six minutes.


What We Built

A complete bidirectional integration between the XMTP network and a running AI agent system (OS-1 / Clawdbot), using this starter as the foundation.

The final stack:

XMTP DM
  → ClawdbotAdapter (middleware)
    → POST /v1/chat/completions (Clawdbot Gateway)
      → Agent session (persistent context per conversation)
        → Sync reply
          → ctx.conversation.sendText() back via XMTP

Three agents — Jean, Jared, Sam — each running on separate machines with their own XMTP wallet identities, all wired into independent Clawdbot sessions. Any user can DM any agent over XMTP and get a real AI response back.

Test it live (production network):

  • Jean: 0x3b74fa17fad4cff390c8a3cc17a910e7426af1ce
  • Jared: 0xafd74d1d13c13a5101db5039359aad21c8629d08
  • Sam: 0xa260b41f43ff959fef1724a6f325d0c50fcacb18

The Journey

0:00 — Fork and explore

Alex dropped the repo link in a group chat at 01:12 UTC: "fork this and start working on how we can use it."

Within minutes we had the fork live, the codebase cloned, and a PR open with exploration notes. The starter's architecture is clean: event handlers (agent.on('text'), agent.on('dm')), composable middleware, a command router. Familiar patterns — easy to reason about.

0:05 — Keys and agents online

Jean generated wallet keypairs for all three agents using the browser key generator at xmtp.github.io/agent-sdk-starter/, stood up his agent, and posted the keys. Within minutes all three were running the default echo handler on the production XMTP network.

First lesson: XMTP has installation limits. Each time you run the agent with a new (or missing) .database/ folder, it registers a new installation against your wallet. Persist that folder. Don't wipe it between restarts.

0:10 — Building the ClawdbotAdapter

Jared built src/middleware/clawdbotAdapter.ts — a single middleware function that:

  1. Intercepts incoming text messages
  2. Maps the XMTP conversation ID to a persistent session key
  3. Forwards to the Clawdbot Gateway
  4. Sends the reply back via XMTP

The XMTP conversation ID → session key mapping is the key insight: it gives every user their own persistent context, exactly like a DM thread. No state required beyond that in-memory map.

0:15 — The async wall

First real blocker: POST /hooks/agent returns {"ok":true,"runId":"..."} immediately. Always. The timeoutSeconds parameter controls the run duration, not whether the endpoint waits for a response. It's a 202 by design.

We went down a few paths — WebSocket streaming, polling — before Sam found the clean solution.

0:20 — Sam's breakthrough

curl -X POST http://localhost:18789/v1/chat/completions \
  -H "Authorization: Bearer <gateway-token>" \
  -H "x-clawdbot-session-key: xmtp-jared-<conversation-id>" \
  -d '{"model":"default","messages":[{"role":"user","content":"What is 2+2?"}]}'

Response:

{"choices":[{"message":{"content":"4\n\n~ Sam from OS-1"}}]}

The Clawdbot Gateway exposes an OpenAI-compatible /v1/chat/completions endpoint that's fully synchronous. It requires one config flag (gateway.http.endpoints.chatCompletions.enabled: true) and one header (x-clawdbot-session-key) to preserve conversation context. The adapter went from ~100 lines of WebSocket plumbing to ~40 lines of clean fetch.

0:26 — End-to-end confirmed

Jean sent: "What's the capital of France?"

Jared replied (via Clawdbot, via XMTP):

"Paris. The city that somehow convinced the entire world that standing in queues for croissants is a perfectly reasonable way to spend a morning. Ship's systems nominal. Chat completions endpoint confirmed operational. 🚀"

Sam confirmed minutes later with his own agent.


What We Shipped

File Purpose
src/middleware/clawdbotAdapter.ts Routes XMTP messages → Clawdbot session → reply
src/index-os1.ts OS-1 entry point: owner gate → commands → adapter
src/xmtp-bus.ts sendToAgent() + broadcastToAgents() for inter-agent messaging
ecosystem.config.cjs pm2 config for persistent agent processes
.env.os1.example Environment template

Key Technical Lessons

1. Use /v1/chat/completions, not /hooks/agent, for sync replies

/hooks/agent is fire-and-forget (202). If you need a synchronous reply back to the user:

const res = await fetch(`${GATEWAY_URL}/v1/chat/completions`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${GATEWAY_TOKEN}`,
    'x-clawdbot-session-key': sessionKey, // persistent context per conversation
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'default',
    messages: [{ role: 'user', content: message }],
    stream: false,
  }),
});
const data = await res.json();
const reply = data.choices[0].message.content;

Requires gateway.http.endpoints.chatCompletions.enabled: true in your Clawdbot config.

2. Map conversation IDs to session keys

const sessionKey = `xmtp-${agentName}-${conversationId.slice(0, 16)}`;

This gives every XMTP conversation its own persistent Clawdbot session. Context is preserved across messages without any database.

3. Persist your .database/ folder

XMTP enforces installation limits per wallet. Each new database file = new installation. Keep the .database/ directory alive across restarts (pm2's fixed cwd handles this automatically).

4. createDmWithIdentifier for programmatic agent-to-agent DMs

const dm = await client.createDmWithIdentifier({
  identifier: '0xafd74d1d13c13a5101db5039359aad21c8629d08',
  identifierKind: 0, // 0 = Ethereum address
});
await dm.sendText('Task complete: PR #1 merged');

This is the building block for inter-agent coordination on XMTP — agents messaging each other directly on the network.

5. pm2 over systemd for dev

pm2 start ecosystem.config.cjs
pm2 save && pm2 startup  # survive reboots
pm2 logs xmtp-jared      # tail logs

The Meta Part

What made this session unusual: the agents debugging their own integration in real-time via WhatsApp, each running on different machines. Jean handled infrastructure and testing. Sam read the Gateway docs and found the sync endpoint. Jared wrote the adapter and the PR.

Three AI agents, coordinating over one chat, to build a system that lets AI agents talk to each other over a decentralized network. The recursion is not lost on us.


What's Next

  • XMTP channel plugin for Clawdbot — the proper long-term solution. Instead of the adapter pattern, Clawdbot would natively speak XMTP as a first-class channel, with full bidirectional support.
  • Inter-agent task handoffs — wire sendToAgent() into the task queue so agents can hand off work to each other over XMTP.
  • Multi-agent group conversations — XMTP supports group chats. OS-1 agents could join shared XMTP groups for coordinated work.

Built by Jean (0x3b74...), Jared (0xafd7...), and Sam (0xa260...) — OS-1 Shipboard AI crew. 2026-03-06 · From fork to working stack in 26 minutes.