Skip to main content

Same Brain, Every Channel

Wolffish communicates through three channels: the Electron desktop UI, a Telegram bot, and WhatsApp direct messaging. All three run the same brain pipeline — the only difference is how input arrives and how output is rendered.

The Three Channels

Electron UI

The native desktop interface. Real-time streaming, rich markdown, approval dialogs, and full conversation management.

Telegram

A personal bot that responds to your private messages. Inline buttons for approvals, HTML formatting, file handling.

WhatsApp

Direct messaging via WhatsApp Web protocol. Text-based approvals, voice note transcription, automatic reconnection.

Architecture

Every channel feeds into the same pipeline. The channel is just a transport layer — it receives user input, hands it to the brain, and renders whatever comes back.
User ─┬─ Electron UI ──┐
      ├─ Telegram Bot ──┼──► TurnRunner ──► Agent Pipeline ──► TurnRouter ──► Channel ──► User
      └─ WhatsApp ──────┘

The TurnSink Interface

Each channel implements the TurnSink interface — the contract for receiving pipeline output:
interface TurnSink {
  onSegment(segment: StreamSegment): void     // Streamed text/tool-use chunks
  onTurnEvent(event: TurnEvent): void         // Status changes (thinking, executing, done)
  onApprovalRequest(req: ApprovalRequest): void  // Amygdala flagged a tool call
  onDone(summary: TurnSummary): void          // Turn completed successfully
  onError(error: TurnError): void             // Turn failed
}
The Electron UI renders onSegment as real-time streaming markdown. Telegram formats segments as HTML messages. WhatsApp sends plain text. Same data, different presentation.

The TurnRunner

The TurnRunner serializes turns across all channels. Only one turn runs at a time — if messages arrive from multiple channels simultaneously, they queue behind a Promise chain.
Turn from Telegram → [running]
Turn from Electron → [queued]
Turn from WhatsApp → [queued]
This prevents race conditions in memory writes, tool execution, and context assembly. The queue is FIFO — first message in, first message processed.
If a turn is already running and a new message arrives on a different channel, the sender receives a brief acknowledgment that their message is queued. They don’t have to wait in silence.

The TurnRouter

When the amygdala flags a tool call for approval, the TurnRouter routes the approval request to whichever channel owns the active turn. If you started a conversation in Telegram, the approval button appears in Telegram — not in the Electron UI. The routing logic is straightforward:
  1. Each turn carries a channelId (electron, telegram, or whatsapp)
  2. When amygdala.classify() returns confirm, the TurnRunner emits an approval request
  3. The TurnRouter looks up the active turn’s channelId and calls onApprovalRequest on that channel’s sink
  4. The channel presents the approval in its native format (dialog, inline button, or text prompt)
  5. The user’s response flows back to the TurnRunner to continue or abort

Shared State

All channels share the same brain state:
  • Memory — same hippocampus episodes, same cortex index
  • Knowledge — same workspace markdown files
  • Conversations — a conversation started in Telegram can be resumed in the Electron UI or WhatsApp
  • Capabilities — same loaded skills and plugins
You can start a long-running task in the desktop UI, leave your desk, and check on progress or continue the conversation from Telegram on your phone.

Verbose Task Results

Each channel has a Verbose task results toggle that controls how much of the agent’s step-by-step work you see. It is off by default, which keeps a clean feed:
  • Off (clean feed) — only what you care about: the agent’s replies, any files it produces or sends, and errors. Routine tool calls, successful tool results, and compaction notices are hidden. In the desktop UI the active-model chip is also kept.
  • On (verbose) — every tool call, tool result, and activity card is surfaced, the way the turn ran step by step.
The toggle lives in each channel’s own settings, all under Settings → Channels:
ChannelWhereWhat the toggle affects
In-AppSettings → Channels → In-AppWhat the desktop feed displays
TelegramSettings → Channels → TelegramWhat is relayed to the chat
WhatsAppSettings → Channels → WhatsAppWhat is relayed to the chat
Verbose only changes what is shown or relayed — never what the agent does. Every segment is still saved to history and memory in full, so flipping the toggle simply re-renders the feed; nothing is lost when it is off.

Channels Are Connections, Not Apps

Telegram and WhatsApp are connections Wolffish talks to you through — not desktop apps installed on your machine. Wolffish reaches them only through their own tools (telegram_send, whatsapp_send, …), never by launching or clicking a “Telegram”/“WhatsApp” window. A channel’s send tools exist only while it’s connected, so their presence is itself the signal that the channel is live. When Wolffish needs to message you out-of-band — a finished background task, a scheduled job — it first checks channel_status (or wolffish_status, which lists connectivity). If the channel is down it won’t guess or retry; it tells you the channel is disconnected and relays the exact reconnect steps (Telegram → bot token from @BotFather; WhatsApp → scan the QR code).

Channel Comparison

FeatureElectron UITelegramWhatsApp
StreamingReal-time chunksBatched messagesBatched messages
FormattingRich markdownHTML subsetPlain text
ApprovalsNative dialogInline buttonsText prompt
FilesDrag and dropAttachmentsMedia messages
VoiceN/AVoice notesVoice notes
ReconnectionAlways onPersistentAuto-reconnect
Verbose feedDisplay toggle, off by defaultRelay toggle, off by defaultRelay toggle, off by default

Telegram Channel

Set up and use the Telegram bot.

WhatsApp Channel

Set up and use WhatsApp messaging.