High-level component map and data flow for the Navi backend.
┌─────────────────────────────────────────────────────────┐
│ Client (browser) │
│ WebSocket /ws/sessions/{id} REST /sessions/* │
└──────────────┬──────────────────────────┬───────────────┘
│ WS frames │ HTTP
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ FastAPI (navi/main.py) │
│ ┌──────────────────┐ ┌───────────────────────────┐ │
│ │ websocket.py │ │ routes/sessions.py │ │
│ │ orchestrator │ │ routes/messages.py │ │
│ │ stop endpoint │ │ routes/agents.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 │ │ (PostgreSQL) │
│(Ollama)│ │ + user tools│ │ │
└────────┘ └──────────────┘ └────────────────────────┘
│
┌─────────┴──────────┐
│ MemoryStore │
│ (PostgreSQL + pgvector) │
└────────────────────┘
{type: "message", content: "..."} over WebSocket.AgentSessionOrchestrator creates a SessionRun, subscribes a queue, and launches the agent task.agent.run_stream(session_id, content).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):
llm.stream_complete() → yields ThinkingDelta, TextDelta, tool call requests.ToolContext, yields ToolStarted → sub-agent events → ToolEvent.finish_reason == stop: yields StreamEnd, runs post-turn workers. e. Saves session to DB.SessionRun to all subscriber queues.Defined in navi/tools/_internal/base.py.
Legacy path: ContextVars (current_session_id, current_event_sink, current_stop_event, current_model, current_user_id, current_user_role, current_user_info) are still present for backward compatibility.
Current path: Agent builds a ToolContext dataclass explicitly and passes it into every tool's execute() call. This removes hidden dependencies and makes tool execution deterministic and testable:
| Field | Type | Purpose | ||
|---|---|---|---|---|
session_id |
`str \ | None` | Session ID for per-session state (SSH pool, scratchpad, todo) | |
event_sink |
`Queue \ | None` | Queue where subagent events are written; parent drains it in real time | |
stop_event |
`Event \ | None` | Cooperative stop signal checked before each LLM call | |
model |
`list[str] \ | str \ | None` | Current profile model — tools that call the LLM read this |
user_id |
`str \ | None` | Authenticated user ID | |
user_role |
str |
Role (user or admin) — admins bypass sandbox restrictions |
||
user_info |
`dict \ | None` | Full user profile for context injection |
navi/core/registry.py)build_default_registries() is the composition root. It:
ToolRegistry, registers all built-in tools.tools/ directory.ProfileRegistry, registers all profiles from navi/profiles/.BackendRegistry, registers LLM backend. If OLLAMA_BACKENDS_FILE is set, uses FallbackOllamaBackend (multi-server with blacklisting); otherwise uses the single OllamaBackend.SpawnAgentTool and SwitchProfileTool (need references to other registries).spawn_tool._backend_registry after backends are built (avoids circular dep).Called once at startup from navi/api/deps.py.
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 for details.