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.
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 theTurnSink interface — the contract for receiving pipeline output:
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 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:- Each turn carries a
channelId(electron, cli, telegram, whatsapp, or mobile) - When
amygdala.classify()returnsconfirm, the TurnRunner emits an approval request - The TurnRouter looks up the active turn’s
channelIdand callsonApprovalRequeston that channel’s sink - The channel presents the approval in its native format (dialog, inline button, or text prompt)
- 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
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.
/resumeon either channel opens a numbered picker over every conversation Wolffish has — all channels, newest first, 25 to a page withnext, each row tagged with its origin. Scheduled automation runs are hidden from it by default (a per-channel settings toggle), while staying in/deleteand 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.
/projecton 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.
File Delivery
When Wolffish produces a file — a PDF, a converted video, a spreadsheet — delivering it is a deliberate act by the agent: itssend_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:
The Clean Feed
Each channel has a Show all tool activity 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) — the replies, the delivered files, the code edits and shell runs, the questions and approvals, the reasoning and the task lists, and errors. Only genuine mechanics stay out of sight — routine tool calls, successful tool results, and compaction notices. In the desktop UI the active-model chip is also kept.
- On — every tool call and tool result is surfaced as well, the way the turn ran step by step.
The switch was called Verbose task results until v1.0.288, on four different surfaces — a name describing its setting rather than what you would see. It is now Show all tool activity everywhere: desktop, phone, terminal, Telegram and WhatsApp. What a clean feed shows grew in the same release: a change in your project is not tool mechanics, so edits and commands draw their compact row whatever the switch says.
The switch 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
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.