Skip to main content

Same Brain, Every Channel

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

The Five Channels

Electron UI

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

Terminal

The wolffish command — an interactive session or a one-shot prompt, attached to the running agent. Markdown rendered in ANSI, approvals answered with a keystroke, files as paths in both directions. The only channel that works on a headless server.

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.

Phone

The mobile companion, paired to this desktop over an end-to-end encrypted tunnel. Not a message stream but a second view of the whole app — and a message sent from it runs a real turn here.

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.

The TurnSink Interface

Each channel implements the TurnSink interface — the contract for receiving pipeline output:
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 per conversation, not globally. A single conversation is one ordered transcript — its own turns queue behind each other so they never interleave — but turns for different conversations run in parallel, whichever channel they arrive on. Each turn runs in its own isolated scope with its own per-turn state, so concurrent turns never corrupt each other.
A conversation’s own second message waits for its first to finish (preserving transcript order and preventing races in that conversation’s memory writes and context assembly), while an unrelated conversation on any channel proceeds immediately. Your morning WhatsApp thread and a long in-app task no longer block each other. Every turn also broadcasts its lifecycle — started, done, canceled, error — back to the desktop app, which is what drives the conversations rail’s live status chips for in-app, Telegram, and WhatsApp runs alike.
A new message on a busy chat is queued behind the running turn, not merged into it — Telegram and WhatsApp park it and run it next, in order (see each channel’s Queued Messages). The TurnRunner still supports preempting a conversation’s own in-flight turn by aborting it first — which is exactly what /stop does — but a plain message no longer triggers that, and nothing ever preempts a different conversation.

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, cli, telegram, whatsapp, or mobile)
  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 anywhere can be continued anywhere (see below)
  • 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.

Every Conversation Is Continuable Everywhere

Conversations are fully two-way across channels — nothing is read-only:
  • Phone → desktop, live. A conversation that started on Telegram or WhatsApp opens in the app as a live chat: the composer works, and you continue it with a real keyboard. If a turn is running on the phone right now — or an automation is working the conversation — the desktop reflects it as it happens: the reply streams into the feed as it’s written, the Stop button genuinely stops that run whichever device started it, and a message you type mid-run queues and sends itself when the run lands. Opening the window in the middle of a run still shows it as live. Messages that land on your phone appear in place, and voice notes survive the round-trip untouched.
  • Automations too. A finished automation or procedure run isn’t a sealed record — continue it in the app to ask the follow-up question, and it becomes a live conversation again.
  • Desktop → phone. /resume on either channel opens a numbered picker over every conversation Wolffish has — all channels, newest first, 25 to a page with next, each row tagged with its origin. Scheduled automation runs are hidden from it by default (a per-channel settings toggle), while staying in /delete and the app.
  • Replies land where they belong. When Wolffish messages a channel out of band — an automation reporting a finished job, an in-app conversation dropping you a note — that chat re-points itself at the sending conversation, so your reply continues exactly the conversation that messaged you.
  • Projects travel too. /project on either channel starts a conversation inside one of your projects — briefed with its standing instructions and files, exactly like a project chat opened in the app. The binding lives on the conversation itself, so continuing it anywhere keeps the project context.
In the app, the conversations rail and History mark each conversation with a small origin badge — the terminal, Telegram, WhatsApp, the phone, an automation, or a procedure run — so a mixed list stays legible. In-app chats carry no badge: the app is the default. Conversations that belong to a project or were run by an automation or procedure also wear that source’s emoji on their rail chip.

File Delivery

When Wolffish produces a file — a PDF, a converted video, a spreadsheet — delivering it is a deliberate act by the agent: its send_file tool. Nothing auto-delivers. A tool writing a file to disk never sends it by itself, and paths mentioned in tool output are never sniffed and auto-uploaded — if the agent doesn’t send it, you don’t receive it, and the agent’s operating contract requires it to send the result the moment a file-producing task is done. send_file renders natively on every surface:

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 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:
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.
Workflow-mode runs are the one addition on top of this: on Telegram and WhatsApp they always report deterministic progress — the phase plan when the run starts, a line as each phase completes, and a closing summary with totals — while verbose adds a landing line per agent (name, model, duration, tool calls). In the app the same information lives on the workflow card.

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

Terminal CLI

The whole app in a terminal — and on a server.

Telegram Channel

Set up and use the Telegram bot.

WhatsApp Channel

Set up and use WhatsApp messaging.