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:
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.
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
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.