GoClaw Replacement: Direct Bot Migration
Status: Planning
Started: 2026-05-04
Prerequisite: Pre-production — no live users to migrate
Why
GoClaw's tenant isolation model is fundamentally incompatible with a SaaS that shares one Telegram bot token across all users. The current architecture requires:
- One GoClaw tenant per Supabase user (provisioned via fragile HTTP scripts)
- Three-layer identity mapping: Supabase user → GoClaw tenant → Telegram chat_id
- Static
X-User-Idheaders baked into per-tenant MCP server registrations - A separate PostgreSQL instance (pgvector) + Docker container just for GoClaw
- Four-hop message routing: Telegram → GoClaw → MCP → API
The Node.js API already contains all business logic as stateless service functions. GoClaw is a pass-through orchestrator that adds more complexity than value for a single-bot-token multi-user SaaS.
What Changes
Replace GoClaw with a channel-agnostic bot module at api/src/bot/. A ChannelAdapter interface abstracts Telegram, WhatsApp, and future channels. The LLM agent calls existing service functions directly — no MCP serialization, no tenant provisioning, no seeding scripts.
Architecture
api/src/bot/
├── index.ts # Adapter registry + boot
├── types.ts # ChannelAdapter, IncomingMessage, OutgoingMessage
├── agent.ts # LangChain tool-calling agent (Aimee)
├── tools.ts # Service functions wrapped as LangChain tools
├── memory.ts # Conversation state (Supabase-backed)
├── cron.ts # node-cron (briefing, reminders, weekly summary)
├── channels/
│ ├── telegram.ts # Telegraf adapter (first channel)
│ └── (whatsapp.ts) # Future channel
└── prompts/
├── system.ts # Merged SKILL + IDENTITY + SOUL
└── briefing.ts # Morning briefing prompt template
Message Flow
Telegram/WhatsApp → ChannelAdapter.onMessage()
→ resolve (channel, externalId) → user_id via messaging_users table
→ load conversation history
→ invoke LangChain agent with message + history + tools
→ agent calls service functions directly (no MCP)
→ agent returns response
→ ChannelAdapter.sendMessage() → formatted for channel
→ save conversation turn to chat_history
Phases
| Phase | Name | Depends On | Doc |
|---|---|---|---|
| 1 | Bot Core + Telegram Adapter | — | Foundation: types, Telegraf adapter, DB tables, pairing |
| 2 | Agent + Tools | Phase 1 | LangChain agent, tool wrappers, conversation memory |
| 3 | Cron Jobs | Phase 1 | Morning briefing, approval reminders, weekly summary |
| 4 | Cleanup + Cutover | Phases 1-3 verified | Remove GoClaw, MCP, tenant provisioning, seeding scripts |
Phase 3 can run in parallel with Phase 2 — both depend only on Phase 1.
What We Lose vs Gain
Lost (must rebuild or accept)
| Feature | GoClaw | Replacement | Effort |
|---|---|---|---|
| Conversation memory | Gemini embeddings + vector search | LangChain BufferWindowMemory + Supabase chat_history | Medium |
| Multi-agent delegation | liaison → research spawning | Single agent with all tools (research = scrape + search tools) | Low |
| Agent personas/skills | SKILL.md + IDENTITY.md + SOUL.md | Merged system prompt in prompts/system.ts | Low |
| Cron engine | Tenant-aware, in GoClaw DB | node-cron in API process | Low |
| Context window mgmt | Auto-truncation, 50-msg history | LangChain BufferWindowMemory | Medium |
| Admin UI | GoClaw dashboard | Logging; build admin later if needed | Low |
Gained
| Benefit | Impact |
|---|---|
| Simple multi-tenancy | One bot, chat_id → user_id is one DB query |
| Multi-channel ready | ChannelAdapter supports Telegram, WhatsApp, future channels |
| ~60% less infrastructure | Remove GoClaw container + its PostgreSQL |
| Direct function calls | No MCP serialization overhead |
| Single log stream | Two-hop (Telegram → API) instead of four |
| No vendor dependency | All open-source (Telegraf, LangChain) |
| Faster iteration | Edit code, not upload skills via HTTP API |
Key Decisions
- Bot in
api/src/bot/— same package, direct service calls, shared deployment - Channel-agnostic from day one —
ChannelAdapterinterface; adding a channel = one adapter file - Single agent — no multi-agent delegation at current scale
- Memory: BufferWindowMemory (last N messages) + Supabase. Vector search later if needed
- Don't touch content generation —
createPosts.ts,llm.ts,llm-models.tsunchanged - Keep
bot/directory during migration for prompt reference; delete after cutover