Everything in One Request
Wolffish uses a stateless, self-contained-request model with every cloud provider. There are no persistent sessions, no thread IDs, no server-side conversation state. Each API call carries everything the model needs and is completely independent of the call before it. This isn’t a limitation — it’s the architecture’s most important property.How It Works
Every turn, the agent assembles the complete message array from scratch:- Prefrontal builds a lean system prompt: identity, device facts, the operating contract, the learned-preferences digest, a one-line-per-capability index, and a memory map — a map of what exists to be recalled, never the content itself. No memory dumps, no episode history, no skill bodies, no tool catalogs: the model retrieves what a turn needs through tools (
memory_search,tool_search). - The agent appends the conversation replay. For most conversations that’s the full history verbatim. Past ~120k characters of growth, older turns are folded into a persisted rolling summary and replay as that summary plus the last 8 messages verbatim — the request stays self-contained without re-sending every byte ever exchanged. See Context Compaction.
- Thalamus routes to the active provider, which transforms messages to the provider’s native format and sends a single HTTP request.
Within a Single Turn
The agent runs a tool-use loop: call the model, execute tools, append results, call the model again. Each iteration sends the growing message array back to the provider:Provider-Specific Request Format
Each provider receives the same logical content, transformed to its native API:
The transformation is invisible to the rest of the system. Thalamus takes one canonical format in and produces the provider-specific request out.
Prompt Caching
Sending the whole payload every call sounds expensive. It isn’t — because of prompt caching, and because Wolffish’s prompt prefix is byte-stable by construction: the memory map regenerates once per calendar day, tool activation is conversation-scoped (another conversation loading a capability never touches this one’s prefix), and the per-iteration counters render after the cache breakpoints. Measured across 50 conversations, provider cache hits run at ~99%.Anthropic
The Anthropic provider uses prompt caching with threecache_control breakpoints:
- System prompt — The lean prefrontal context, byte-stable across turns
- Tool definitions — Stable within a conversation
- Conversation history prefix — The second-to-last user turn, marking the boundary between stable history and the latest exchange
OpenAI
OpenAI applies its own automatic prefix caching transparently (50% input discount on cache hits). No opt-in is needed.DeepSeek
DeepSeek applies its own prefix caching with a 75% input discount on cache hits, making it the most cost-efficient provider for long conversations and multi-step agentic workflows.Ollama
Ollama runs locally and has no caching layer.Why Stateless
Three properties depend on the stateless design, and losing any of them would compromise the architecture.Model Switching
Thalamus calls whichever Brain model you selected — DeepSeek, Anthropic, OpenAI, or Ollama — and there’s no automatic cascade between them. But because the app owns the full message array, switching your Brain model mid-conversation just works: the new model gets the complete context on its very next turn, with no thread to migrate. (Workflow mode leans on the same property — each agent is handed a complete, self-contained context, on whatever model the master picked for it.) Thread-based APIs would make that impossible.Privacy
No provider retains conversation state between calls. Each request is isolated. If a provider is swapped mid-conversation, the previous provider has nothing. Your conversation history lives on your machine, not on someone else’s server.Context Control
Because the app rebuilds the request each turn, it controls exactly what the model sees. The rolling-summary fold, the verbatim tail, recovery-pointer stubs for stale bulky tool results, attachment trimming — all of it happens before the request leaves your machine, and all of it is reversible: the model can pull any folded turn back withconversation_read. Thread-based APIs accumulate everything the model has ever seen, and that control is lost.
Persistence Is App-Side Only
Conversations are persisted locally at~/.wolffish/workspace/brain/conversations/ as JSON files containing the full message history, streaming segments, tool timings, and attachments. Every message carries a stable id, and all writers persist through an id-keyed merge — so two copies of the same conversation (a channel message landing while an in-app turn runs on the same thread) reconcile message-by-message instead of last-writer-wins, and a stale copy can never shrink the transcript. The rolling summary lives in the same file ({summary, summarizedThroughMessage, summarizedThroughMessageId}) as a read-time lens — the id form pins the summary boundary to a specific message so merges can’t shift it, the original messages are never modified or deleted, and the History page always shows the complete transcript untouched. This is purely local — providers never see a conversation ID or resume from stored state.
The app is the source of truth. The providers are stateless compute.