The Message Path
Every message follows this exact path through the system. No exceptions, no shortcuts.Step-by-Step
1. Message Received
The user sends a message from the chat UI. It arrives in the main process via IPC and enters the agent loop.2. Context Assembly (prefrontal)
This is the most important step — and it is lean by design. A fresh conversation’s system prompt is ~5,000 tokens (it used to be ~44,000). The prefrontal cortex assembles only:<identity>—soul.md+user.md, user-owned<device>— static device facts (~40 tokens)<prefrontal>— theagents.core.mdoperating contract (~1.4k tokens) plus the bounded learned-preferences digest frombasalganglia(~500 tokens)<capabilities>— a one-line-per-capability index: name, a ≤90-character description, tool count, and a[loaded]marker on capabilities that are callable right now. ~800 tokens; past 60 installed capabilities the unloaded remainder collapses to grouped counts, so the cost is O(1) in how much you install<memory_map>— a coverage map of what exists to be recalled: per-source record counts (rounded to coarse buckets), date ranges, knowledge-file topics, conversation and artifact counts. It is a map, never content, and it is cached per calendar day so it never churns the provider prompt cache<runtime>— slimmed; the live loop counters ride a volatile tail at the very end of the outbound message array, not the prompt- Conditional overlays: role (workflow master/agent — the master’s overlay includes its
<workflow_models>catalog of spawnable models), channel formatting (WhatsApp), and a<local_model>honesty overlay for locally-run models
memory_search for memory, tool_search for tools.
A debug snapshot of every assembled prompt is written to brain/prefrontal/.debug/ (capped at 50 files with rotation).
You can inspect exactly what the LLM received by reading the debug snapshot files. This is how you debug “why did Wolffish do that?” questions.
3. Tool Selection (cerebellum)
Full JSON schemas ship only for the core set — roughly 29–47 tools depending on what’s connected: tool-discovery, introspect, filesystem, shell, ask, utilities (send_file), web-search, secrets, system, workflow (master-gated), and the telegram/whatsapp/electron send tools while those channels are connected. That’s ~4k tokens of schemas, bringing a fresh request to ≈9.4k tokens total (vs ~94k before the lean redesign).
Everything else — github, google, notion, browser, computer-use, media and document tools, all MCP servers — is discoverable:
tool_search(query)— term search over capability names, descriptions, triggers, and tool names; the best match auto-loads and its tools are callable the same turn.tool_activate(capability)— explicit load by name.- Calling a known-but-unloaded tool directly just works (auto-activation). A truly unknown tool returns a deterministic error pointing at
tool_search.
pinnedCapabilities in config.json adds extra always-loaded capabilities (hand-edit; no UI).
The system prompt and tool schemas are pinned at turn start and reused across loop iterations — rebuilt only when this conversation’s toolset actually changes (a mid-turn tool_search activation, a skill created or edited, an MCP surface change). Worst case, base plus the 10 heaviest activations is ≈41.5k tokens — still under half the old floor.
4. LLM Call (thalamus)
The assembled context goes tothalamus.stream(), which:
- Checks
net.isOnline()for instant offline detection - Calls the Brain model — the one you explicitly selected from the chat composer’s model switch (and, in workflow mode, resolves each agent’s master-chosen model)
- On a transient error, retries the same cloud Brain on a backoff schedule; there’s no automatic cascade to another provider
- Returns a unified
StreamChunkasync generator
5. Response Streaming (broca)
broca receives the stream chunks and pipes them to the renderer via IPC for real-time display in the chat UI.
6. Response Parsing (wernicke)
wernicke parses the streamed response, normalizing across provider formats:
- DeepSeek: OpenAI-compatible
function_callobjects - Anthropic:
tool_usecontent blocks - OpenAI:
function_callobjects - Ollama: structured JSON in response
ToolCall type: { name, args, id }.
7. Tool Execution Loop (if tool calls detected)
Ifwernicke finds tool calls, the loop begins. There is no numeric iteration cap — the model reads its own loop position from the live runtime counters and owns termination:
- amygdala.classify() — Checks the tool call against danger patterns loaded from SKILL.md files. Three outcomes:
safe(proceed),confirm(show approval dialog),block(deny). - motor.execute() — Creates a
TASK-{id}.mdfile, logs the step, calls the plugin with retry logic (3x with 2s/6s/18s backoff). - cerebellum.executeTool() — Routes the call to the correct capability plugin.
- Results go back to the LLM for the next iteration.
Context Compaction
How the persisted rolling summary, verbatim tail, and in-turn safety net keep long conversations running without losing information.
8. Memory (hippocampus + basalganglia)
After the response is complete:hippocampusappends an origin-tagged summary of the turn (## HH:MM [heartbeat] — …) to today’s episode file (brain/hippocampus/episodes/YYYY-MM-DD.md)basalgangliarecords the outcome (success/failure/denial) to today’s feedback file- a fire-and-forget summarizer checks the saved conversation: when the unsummarized region exceeds ~120k characters, it folds everything before a turn boundary into one persisted summary (≤6k chars), keeping the last 8 messages verbatim. One summarization call per ~120k characters of growth, ever — and the original messages are never modified or deleted.