# Architecture

High-level component map and data flow for the Navi backend.

## Component diagram

```
┌─────────────────────────────────────────────────────────┐
│  Client (browser)                                       │
│  WebSocket /ws/sessions/{id}    REST /sessions/*        │
└──────────────┬──────────────────────────┬───────────────┘
               │ WS frames               │ HTTP
               ▼                         ▼
┌─────────────────────────────────────────────────────────┐
│  FastAPI (navi/main.py)                                 │
│  ┌──────────────────┐  ┌───────────────────────────┐   │
│  │  websocket.py    │  │  routes/sessions.py        │   │
│  │  websocket.py    │  │  routes/messages.py        │   │
│  │  _AgentRun       │  │  routes/agents.py          │   │
│  │  stop endpoint   │  │  routes/health.py          │   │
│  └────────┬─────────┘  └────────────┬──────────────┘   │
└───────────┼─────────────────────────┼───────────────────┘
            │                         │
            ▼                         ▼
┌──────────────────────────────────────────────────────────┐
│  Agent (navi/core/agent.py)                              │
│  run_stream() → AsyncGenerator[AgentEvent]               │
│  run()        → str  (non-streaming)                     │
│  run_ephemeral() → str  (subagent, no DB)                │
│                                                          │
│  ┌──────────────┐  ┌───────────────┐  ┌──────────────┐  │
│  │ Planning     │  │ Tool-calling  │  │  Workers     │  │
│  │ _run_planning│  │ loop          │  │ (compression)│  │
│  └──────────────┘  └───────────────┘  └──────────────┘  │
└───┬──────────────┬────────────────┬─────────────────────┘
    │              │                │
    ▼              ▼                ▼
┌────────┐  ┌──────────────┐  ┌────────────────────────┐
│ LLM    │  │  ToolRegistry│  │  SessionStore          │
│Backend │  │  (built-ins  │  │  (SQLite / in-memory)  │
│(Ollama)│  │  + user tools│  │                        │
└────────┘  └──────────────┘  └────────────────────────┘
                                        │
                              ┌─────────┴──────────┐
                              │   MemoryStore       │
                              │   (SQLite facts)    │
                              └────────────────────┘
```

## Request lifecycle (streaming)

1. Client sends `{type: "message", content: "..."}` over WebSocket.
2. `websocket_session()` creates `_AgentRun`, subscribes a queue, launches `_run_agent()` as a task.
3. `_run_agent()` calls `agent.run_stream(session_id, content)`.
4. `run_stream()`:
   a. Loads session + profile from store.
   b. Pre-turn: checks if context needs compression; compresses if threshold exceeded.
   c. **Planning phase** (if `profile.planning_enabled`): calls LLM once (non-streaming, no tools) to produce a step plan; injects plan as assistant message.
   d. **Tool-calling loop** (up to `max_iterations`):
      - Calls `llm.stream_complete()` → yields `ThinkingDelta`, `TextDelta`, tool call requests.
      - If tool calls: executes each tool, yields `ToolStarted` → sub-agent events → `ToolEvent`.
      - If `finish_reason == stop`: yields `StreamEnd`, runs post-turn workers.
   e. Saves session to DB.
5. Events are broadcast from `_AgentRun` to all subscriber queues.
6. `_stream_to_client()` drains the queue → sends JSON to WebSocket.

## Context vars (thread-safe, async-safe)

Defined in `navi/tools/base.py`. Set by `Agent` before each tool call; tools read them.

| ContextVar | Type | Purpose |
|---|---|---|
| `current_session_id` | `str \| None` | Session ID for tools needing per-session state (SSH pool, scratchpad) |
| `current_event_sink` | `Queue \| None` | Queue where subagent events are written; parent drains it in real time |
| `current_stop_event` | `Event \| None` | Set by `POST /sessions/{id}/stop`; agent checks before each LLM call |

## Registry wiring (`navi/core/registry.py`)

`build_default_registries()` is the composition root. It:
1. Creates `ToolRegistry`, registers all built-in tools.
2. Loads user tools from `tools/` directory.
3. Creates `ProfileRegistry`, registers all profiles from `navi/profiles/`.
4. Creates `BackendRegistry`, registers LLM backend. If `OLLAMA_BACKENDS_FILE` is set, uses `FallbackOllamaBackend` (multi-server with blacklisting); otherwise uses the single `OllamaBackend`.
5. Creates `SpawnAgentTool` and `SwitchProfileTool` (need references to other registries).
6. Patches `spawn_tool._backend_registry` after backends are built (avoids circular dep).

Called once at startup from `navi/api/deps.py`.

## Two-buffer session design

Each `Session` has two message lists:

- `messages` — full display history, **never modified by compression**. Used for UI history.
- `context` — what the LLM sees. May be replaced with a summary by the compressor.

See [`sessions.md`](sessions.md) for details.
