Skip to content

Channel Overview

One agent. Twelve channels. Shared memory across all of them.

AI Butler isn’t 12 separate bots that happen to live in your messaging apps — it’s a single agent runtime with a single memory store that exposes itself through 12 different channel adapters. When you tell it something on the web chat in the morning, ask about it from Telegram on your commute, and follow up from a terminal SSH session that evening, you’re talking to the same agent the entire time. Same memory. Same schedules. Same tool calls. Same cost tracker.

ChannelStatusSetup complexityRich mediaVoiceStreaming
Web ChatReadyZero configFullMic inputWebSocket
Terminal REPLReadyZero configText onlyNoToken-by-token
Telegram🟡 BetaEasy (BotFather)FullVoice messagesEdit-based
Slack🟡 BetaMedium (App setup)FullAudio clipsEdit-based
Discord🟡 BetaMedium (Dev portal)FullVoice messagesEdit-based
WhatsApp🟡 BetaComplex (Meta Cloud API)FullVoice messagesEdit-based
Microsoft Teams🟡 BetaMedium (Bot Framework)Adaptive CardsNoNo
Google Chat🟡 BetaMedium (Service account)Cards v2NoNo
LINE🟡 BetaMedium (Messaging API)Flex MessagesNoNo
IRC🟡 BetaEasy (server/channel)Text onlyNoNo
Custom Webhook🟡 BetaEasy (HTTP endpoint)JSONNoNo
Nostr🟡 BetaEasy (relay + key)Text onlyNoNo

This is the feature that matters. Every other multi-channel bot framework makes you manually sync state, configure per-channel contexts, or ship separate conversations. AI Butler does the opposite: memory is global, channels are just transport.

On your laptop, in the web chat, you teach the agent about yourself:

AI Butler web chat showing the agent confirming 7 stored facts (name, location, role, current project, tech preferences, daily routine, interests) in a structured table with storage layer details

Later, from a completely different session — maybe a new browser tab, maybe Telegram on your phone, maybe a terminal REPL — you ask what the agent remembers about you:

Fresh chat in a different session showing the agent recalling every fact from the earlier conversation, plus flagging data inconsistencies it spotted and offering to clean them up

No context was carried over between the two sessions. The second chat is a brand-new WebSocket, with a brand-new session ID, and no prior messages. The agent reconstructed everything from the local SQLite store via memory.search. If you had sent the first message from Telegram and the second from the terminal REPL, the result would be identical — because memory lives in the database, not in any particular channel.

This is what “one agent, everywhere” means in practice. Every channel is a thin adapter that speaks its platform’s protocol; the agent and its memory are the same across all of them.

Each channel follows the same five-step pipeline:

User types on the platform
Channel adapter receives it (Telegram long-poll, Slack event, Discord gateway, etc.)
Adapter wraps it as a channel.Envelope { ID, Channel, AccountID, Text, Type, Attachments, Timestamp }
Router dispatches the envelope to the agent pipeline
Agent processes: capability check → tool calls → memory retrieval → model response
Response returns through the same adapter (chat.send, bot.sendMessage, gateway.reply, etc.)

The Envelope format is the single interface every channel implements. Once a message is in an envelope, the rest of the pipeline (memory, tools, capability gating, audit trail) doesn’t know or care which channel it came from. This is why adding a new channel is additive work — you don’t have to teach every feature about the new channel, just write an adapter that produces envelopes.

Because channels are first-class, the agent can route messages between them:

You (on Telegram): Send Sarah a message on WhatsApp saying I’ll be 20 minutes late

Butler: Sent to Sarah on WhatsApp: “Hi Sarah, quick heads up — I’ll be about 20 minutes late. See you soon.”

Under the hood the agent calls the channel.relay tool. The tool:

  1. Looks up “Sarah” in the contacts table to find her preferred channel and identifier
  2. Calls the WhatsApp channel’s send() method via the channel registry
  3. Returns the delivery status back to the originating Telegram thread

Requirements: both channels need to be active, and the recipient needs to be in your contacts. Contact management is a separate feature — see Memory (contacts live in the same SQLite store as everything else).

Activate a channel by listing it in settings.active_channels:

settings:
active_channels:
- webchat # always available, zero config
- terminal # always available, zero config
- telegram # beta — needs telegram_bot_token in vault
- slack # beta — needs slack_bot_token + slack_app_token
- discord # beta — needs discord_bot_token

Then ./aibutler run will start each adapter in a goroutine on boot. Each adapter’s startup is independent — if Telegram credentials are missing, the other channels still run and a warning logs the missing vault key.

Every channel follows the same pattern:

  1. Get credentials from the platform (bot token, app key, webhook URL, etc.)
  2. Store in the vault: aibutler vault set <key> <value>
  3. Add to active_channels in config.yaml
  4. Restart AI Butler

Individual channel guides walk through each step with the exact credential names and platform UIs:

Web Chat → Zero config, full visual tour of the 6-panel sidebar

Terminal REPL → Streaming REPL with slash commands

Telegram → BotFather token, long-polling, voice messages, Markdown

Slack → Bolt SDK, Socket Mode, channel + DM support

Discord → Gateway connection, voice + text, slash commands

WhatsApp → Meta Cloud API, business-tier requirements

Microsoft Teams → Bot Framework, Adaptive Cards

Google Chat → Service account auth, Cards v2

LINE → Messaging API, Flex Messages

IRC → Classic protocol, Libera/OFTC

Custom Webhook → Wrap any HTTP platform as a channel

Nostr → Decentralized, NIP-04 DMs

We’d love these. They’re not in v0.1 because:

  • Matrix — E2EE is the hard part, and getting Olm/Megolm right without a Go SDK is a lot of work. On the v0.2 roadmap.
  • XMPP — smaller modern user base; lower priority. Community contributions welcome.
  • Signal — no official bot API. The unofficial signal-cli bridge works but adds a large external dependency. Deferred.

If any of these are critical for you, open an issue and tell us which — the order of implementation follows demand.