Master catalog of every mechanism, feature, behavior, and configurable flag in the Navi project.
Use this document before designing a new feature to check whether an existing mechanism can be reused, extended, or should be replaced.
| Column | Meaning |
|---|---|
| Mechanic | Short name. Click the source file link to see the implementation. |
| Description | What it does and when it triggers. |
| Config / Flags | .env variables or config.json profile fields that control it. |
| Files | Primary implementation file(s). |
| Docs | ✅ = documented in docs/. ❌ = not documented anywhere yet. ⚠️ = partially documented. |
navi/core/agent.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Streaming entry point | run_stream() — yields AgentEvent objects in real time. Loads session, runs planning (if enabled), tool loop, workers. |
profile.max_iterations, profile.llm_backend, profile.model, profile.temperature |
agent.py |
✅ |
| Non-streaming entry point | run() — same loop but returns plain string. No planning phase, no events. |
Same as above | agent.py |
✅ |
| Streaming guard wrapper | Wraps llm.stream_complete() with two safety layers: (1) polls stop_event every second during prefill so the Stop button works even when the model emits no chunks, and (2) hard first_chunk_timeout/chunk_timeout deadlines that close the HTTP connection to Ollama so GPU load drops. |
LLM_STREAM_FIRST_CHUNK_TIMEOUT, LLM_STREAM_CHUNK_TIMEOUT |
agent.py |
✅ |
| Subagent thinking stall detector | Monitors subagent streaming; if only thinking output is emitted for 60 s or 12 000 chars without text/tool calls, aborts the subagent to prevent endless internal-token loops on local models. |
Hard-coded _SUBAGENT_THINKING_STALL_SECONDS=60.0, _SUBAGENT_THINKING_STALL_CHARS=12000 |
agent.py |
❌ |
| Cooperative stop | Checks current_stop_event (asyncio.Event) before each LLM call, during streaming, and after tool execution. Uses clean generator close — never task.cancel(). |
None | agent.py |
✅ |
| First-message forced planning | Planning phase always runs on the first user message in a session regardless of profile.planning_enabled. |
profile.planning_enabled (only affects subsequent turns) |
agent.py |
✅ |
| Profile reload mid-session | After each tool execution batch, checks DB for profile ID change (e.g. from switch_profile). If changed, reloads profile, tools, schemas, and backend for next iteration. |
None | agent.py |
✅ |
| Pre-turn context compression | Before assistant reply, checks session.context_token_count against threshold and compresses if exceeded. |
CONTEXT_COMPRESSION_ENABLED, OLLAMA_NUM_CTX, CONTEXT_COMPRESSION_THRESHOLD |
agent.py |
✅ |
| Mid-turn context compression | On iterations > 0, estimates tokens and triggers compression with keep_recent_messages=max(12, CONTEXT_KEEP_RECENT*2). For long autonomous loops where the entire conversation is one turn. |
Same as above + CONTEXT_KEEP_RECENT |
agent.py |
❌ |
| Context size check with output reserve | Raises ContextTooLargeError if estimated input tokens exceed OLLAMA_NUM_CTX - OUTPUT_RESERVE_TOKENS. Images counted at 500 tokens each. |
OLLAMA_NUM_CTX, OUTPUT_RESERVE_TOKENS |
agent.py |
⚠️ |
| Local token estimation | Conservative estimate: chars // 4 + imgs * 500. Used for preflight context size checks. |
None | agent.py |
❌ |
| Anti-stall detection | Tracks two signals: (1) consecutive iterations with no todo status change, (2) identical tool call signatures. When either hits threshold, injects a hard warning system message. | profile.anti_stall_enabled, profile.anti_stall_threshold |
agent.py |
✅ |
| Adaptive replan on failure | Detects newly-failed todo steps after each tool batch and queues a re-planning system message for the next iteration. | profile.adaptive_replan_enabled |
agent.py |
✅ |
| Goal anchoring | Injects [Goal anchor] system message with original request + todo state every N iterations. |
profile.goal_anchoring_enabled, profile.goal_anchoring_interval |
agent.py |
✅ |
| Todo status snapshot | Captures frozenset of (task_text, status) before each iteration so anti-stall can detect progress. |
None | agent.py |
❌ |
| Todo failed-steps tracking | Captures frozenset of (index, text) for failed steps, used by adaptive replan. |
None | agent.py |
❌ |
| Todo progress message injection | Injects compact system reminder with current todo state and discipline notes at start of every iteration. | None | agent.py |
❌ |
| Memory facts deduplication | Tracks _injected_fact_ids across a single run_stream call so the same memory fact is not injected twice in one turn. |
None | agent.py |
❌ |
| Context injection collection (parallel) | Fires _collect_context_injections and _memory_facts_msg concurrently before each turn. |
profile.context_providers |
agent.py |
❌ |
| MCP server group expansion | Resolves profile.mcp_servers: * expands to all tools for that server; named groups resolve via mcp_manager.resolve_group. |
profile.mcp_servers |
agent.py |
⚠️ |
| User-enabled tools merge | Loads extra tool names from tools/enabled.json and appends to profile's enabled_tools. |
settings.tools_dir |
agent.py |
⚠️ |
| Recall message wrapping | When is_recall=True, prefixes user message with [Scheduled recall — execute this task]\n\n. |
None | agent.py |
❌ |
| Per-tool-call event sink | Creates asyncio.Queue for each tool call so subagents can emit events back to parent in real time. |
None | agent.py |
✅ |
| Display vs context message splitting | Accepts separate display_message (shown in UI) and user_message (sent to LLM, may contain injected hints). |
None | agent.py |
✅ |
| Post-turn workers | Runs registered workers sequentially after StreamEnd. |
None | agent.py |
✅ |
navi/core/agent.py run_ephemeral)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Ephemeral execution | Runs tool loop without persistent session, temporary in-memory context. Returns (result_text, completed_normally). |
max_iterations param, timeout_seconds param |
agent.py |
✅ |
| Inherit system prompt | When inherit_system_prompt=True, prepends parent's profile.system_prompt as base layer, then subagent specialization on top. |
inherit_system_prompt param |
agent.py |
✅ |
| Context transfer priming | If context_transfer provided, injects it as synthetic user/assistant exchange before task message. |
context_transfer param |
agent.py |
❌ |
| Wall-clock timeout | Monitors elapsed time; aborts and returns [Sub-agent timed out] if exceeded. |
timeout_seconds param (default 300.0) |
agent.py |
✅ |
| Subagent planning phase | Optionally runs full 3-phase planning before tool loop for subagents. | profile.subagent_planning_enabled |
agent.py |
✅ |
| Parent session ID passthrough | Sets session ContextVar to parent's ID so session-aware tools resolve paths correctly. | parent_session_id param |
agent.py |
✅ |
| Dedicated subagent tool list | Uses profile.subagent_tools if non-empty; falls back to profile.enabled_tools. |
profile.subagent_tools |
agent.py |
✅ |
| ContextVar restoration | Saves/restores current_session_id, current_model, current_user_id, current_user_role, current_user_info in finally block. |
None | agent.py |
✅ |
navi/core/planning.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| 3-phase planning engine | Orchestrates Phase 1 (analysis), Phase 2 (review), Phase 3 (execution plan) as async generator. | profile.planning_phase1_enabled, profile.planning_phase2_enabled, profile.planning_phase3_enabled, profile.planning_mandatory, profile.planning_enabled |
planning.py |
✅ |
| Phase 1 — Task analysis | LLM call reformulates task, identifies subtasks, unknowns, resources. Can output DIRECT to skip planning. |
profile.think_enabled, profile.planning_phase1_enabled |
planning.py |
✅ |
| Phase 2 — Structured review | One critique pass when planning_phase2_enabled=True and Phase 1 outputs REFLECT: yes. Returns Critic/Pragmatist/Detailer/Plan Adjustments. |
profile.planning_phase2_enabled |
planning.py |
✅ |
| Phase 3 — Execution plan | Produces milestones + numbered steps with executor assignments (TOOL:, AGENT:, SELF). Enforces comma-test splitting. |
profile.planning_phase3_enabled |
planning.py |
✅ |
| Auto-populate todo from plan | Parses Phase 3 steps and calls todo.set_tasks() to initialize session todo list. |
None | planning.py |
✅ |
| Plan step parser | Regex extracts numbered step lines from **Steps:** section. |
None | planning.py |
❌ |
| Planning debug data logging | Accumulates per-phase outputs, tokens, timestamps into _dbg dict and yields PlanningDebugData. |
None | planning.py |
❌ |
| Knowledge store rules | Hard-coded prompt rules distinguishing memory vs MCP knowledge servers vs docs. |
None | planning.py |
❌ |
navi/core/context_builder.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| System prompt caching | Caches built system prompt per profile ID to avoid rebuilding on every turn. Provides invalidate_system_prompt_cache(). |
None | context_builder.py |
✅ |
| Persona + profile construction | Prepends global NAVI_PERSONA to profile's system_prompt, separated by ---. |
NAVI_PERSONA_FILE |
context_builder.py |
✅ |
| Cross-profile awareness | Appends ## Available profiles block listing all other profiles with descriptions. |
None | context_builder.py |
⚠️ |
| Memory summary message | Injects ## What I remember about the user if memory store has a summary. |
None | context_builder.py |
✅ |
| Memory facts message | Searches memory facts from user message. Skips messages ≤20 chars or <2 words. Limits: 1 fact for <50 chars, 2 for ≤150, 3 otherwise. Deduplicates. | None | context_builder.py |
❌ |
| Context provider injection | Injects global providers unconditionally, profile-named providers only if listed in profile.context_providers. |
profile.context_providers |
context_builder.py |
⚠️ |
| Goal anchor builder | Constructs [Goal anchor] system message with original request + todo lines. |
None | context_builder.py |
❌ |
| Security policy message | Injects [Security policy] based on current_user_role: admin = full access; user = sandbox + terminal allowlist. |
TERMINAL_ALLOWED_COMMANDS |
context_builder.py |
❌ |
| User context message | Builds [User context] from current_user_info (display_name, email, locale, etc.). |
None | context_builder.py |
❌ |
| MCP context message | Combines MCP server instructions from handshake with overlay instructions from mcp_servers.d/*.json. |
profile.mcp_servers |
context_builder.py |
❌ |
| Iteration budget message | Appends [Iteration N/M — K after this one] with escalating urgency when ≤2 or ≤5 remaining. |
profile.iteration_budget_enabled |
context_builder.py |
✅ |
| Session context injection | Appends session ID and exact session_files_dir/{session_id}/ path. |
SESSION_FILES_DIR |
context_builder.py |
❌ |
navi/core/compressor.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Threshold-based trigger | Returns True when context_tokens >= max_context_tokens * threshold. |
CONTEXT_COMPRESSION_THRESHOLD |
compressor.py |
✅ |
| Turn-based partitioning | Groups messages into turns. Keeps last keep_recent turns verbatim; older go to summarization. Tool call groups never split. |
CONTEXT_KEEP_RECENT |
compressor.py |
❌ |
| Mid-turn fallback partitioning | For long autonomous loops where entire conversation is one turn: keeps current request + newest N messages verbatim, summarizes older messages from same turn. | keep_recent_messages param |
compressor.py |
❌ |
| Summary input formatter | Renders messages as plain text for summarizer: preserves summaries, notes image counts, renders tool calls compactly, collects base64 images for vision models. | None | compressor.py |
❌ |
| Summary input truncate | Hard cap of 24 000 chars on formatted input sent to summarizer LLM. | Hard-coded _MAX_SUMMARY_INPUT_CHARS=24000 |
compressor.py |
❌ |
| LLM-based summarization | Calls LLM with structured summarization prompt and replaces old messages with is_summary=True user message. |
CONTEXT_SUMMARY_TEMPERATURE, CONTEXT_SUMMARY_MAX_TOKENS |
compressor.py |
✅ |
navi/core/tool_executor.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Tool name resolution | Resolves exact names, then falls back through 3 MCP alias heuristics: (1) bare suffix match, (2) dash→underscore, (3) old underscore format. | None | tool_executor.py |
❌ |
| Sequential execution | Gathers all tool calls with asyncio.gather and returns (tool_msgs, image_msgs). |
None | tool_executor.py |
❌ |
| Streaming execution | Same as sequential but yields ToolEvent objects alongside messages for UI rendering. |
None | tool_executor.py |
❌ |
| Middleware hooks | Calls before_execute and after_execute on all registered middlewares around each tool call. |
None | tool_executor.py |
❌ |
| Image message generation | If tool result has metadata.is_image + metadata.base64, synthesizes vision user message for next LLM call. |
None | tool_executor.py |
❌ |
navi/core/ai_helper.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Single-turn wrapper | Thin reusable wrapper over LLMBackend for tools needing quick LLM call. Uses current_model ContextVar with fallback. |
default_model param, temperature param (default 0.1) |
ai_helper.py |
❌ |
ask() with timeout |
Non-streaming call with 120-second asyncio.wait_for timeout. |
Hard-coded 120 |
ai_helper.py |
❌ |
ask_json() |
Calls ask() then parses JSON, handling markdown code fences automatically. |
None | ai_helper.py |
❌ |
| Token usage emission | Emits AIHelperTokensUsed into current_event_sink for parent session metrics. |
None | ai_helper.py |
❌ |
| JSON extractor | Strips code fences, tries direct parse, then bracket-matching to find outermost [] or {}. |
None | ai_helper.py |
❌ |
navi/core/orchestrator.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Orchestrator stub | Placeholder class for future multi-agent orchestration. Raises NotImplementedError if instantiated. |
None | orchestrator.py |
❌ |
navi/core/event_bus.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Async pub/sub | Type-specific subscription plus catch-all. Publishes by creating asyncio.Task per subscriber and gathering with return_exceptions=True. |
None | event_bus.py |
❌ |
| Global singleton | Lazy-initialized default bus via get_event_bus(); replaceable via set_event_bus(). |
None | event_bus.py |
❌ |
navi/core/registry.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
ToolRegistry |
Holds built-in, user-defined, and external/MCP tools. Supports hot-reload without touching builtins. | settings.tools_dir |
registry.py |
✅ |
ProfileRegistry |
Holds agent profiles. Supports lookup and in-memory replacement. | None | registry.py |
✅ |
BackendRegistry |
Holds LLM backend instances (Ollama, OpenAI, fallback). | OLLAMA_BACKENDS_FILE, OLLAMA_HOST, OPENAI_API_KEY, etc. |
registry.py |
✅ |
build_default_registries() |
Composition root. Discovers backends, creates AIHelper, registers all tools, loads user tools, wires cross-references. |
settings.tools_dir, settings.context_providers_dir |
registry.py |
✅ |
navi/tools/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| FilesystemTool | Read/write/append/edit/list/find/move/copy/delete/exists/mkdir + AI query/smart_edit + grep/diff. Path restrictions via allowlist. | FS_ALLOWED_PATHS, FS_ALLOWED_PATHS_LIST |
filesystem.py |
✅ |
| TerminalTool | Run shell commands. Unrestricted for admins; sandbox + allowlist for users. | TERMINAL_ALLOWED_COMMANDS, TERMINAL_USER_ALLOWED_COMMANDS |
terminal.py |
✅ |
| SshExecTool | SSH exec and SCP file transfer. Connection pool per-session with 20-min TTL. | SSH_HOSTS_FILE |
ssh_exec.py |
✅ |
| CodeExecTool | Run Python in subprocess sandbox. Non-admin sandboxed to user_data/<user_id>/. |
None | code_exec.py |
✅ |
| ImageViewTool | Load image from path/URL → resize to 1024px, JPEG, return base64 for LLM. | None | image_view.py |
✅ |
| MemoryTool | Save/search/forget/list user facts. Dual search: semantic (cosine) + ILIKE fallback. | None | memory.py |
✅ |
| TodoTool | Session-scoped task tracker. Set/view/update/clear. Auto-populated from planning. | None | todo.py |
✅ |
| ScratchpadTool | Session-scoped working notes. Write/append/read/clear per section. | None | scratchpad.py |
✅ |
| SpawnAgentTool | Spawn isolated subagent with own tool loop. Supports inherit_system_prompt, briefing, profile_id. |
None | spawn_agent.py |
✅ |
| SwitchProfileTool | Switch active profile for session. Blocks is_subagent_only profiles. |
None | switch_profile.py |
✅ |
| ListProfilesTool | List all profiles with descriptions. Shows [subagent only] tag. |
None | list_profiles.py |
✅ |
| ReflectTool | 3 parallel AI calls (Critic/Pragmatist/Detailer) to challenge assumptions. | None | reflect.py |
✅ |
| ShareFileTool | Copy file into session directory, return public download link. | PUBLIC_URL, SESSION_FILES_DIR, SHARE_FILE_MAX_SIZE_MB |
share_file.py |
✅ |
| ContentPublishTool | Register session file for inline viewing in chat. | SESSION_FILES_DIR |
content_publish.py |
✅ |
| ToolManualTool | Return manuals/{tool}.md or auto-generate from schema. |
MANUALS_DIR |
tool_manual.py |
✅ |
| ListToolsTool | Return tools enabled for a profile, including MCP group expansion. | tools/enabled.json |
list_tools.py |
✅ |
| ReloadToolsTool | Hot-reload user tools, context providers, and MCP servers without restart. | settings.tools_dir, settings.context_providers_dir |
reload_tools.py |
✅ |
| ScheduleRecallTool | Schedule headless callback (once/recurring/immediate). | None | schedule_recall.py |
✅ |
| ManageRecallTool | Cancel/skip/list scheduled recalls. | None | manage_recall.py |
✅ |
| McpStatusTool | List MCP servers, connection status, exposed tools. | None | mcp_status.py |
✅ |
| CreateMcpServerTool | Scaffold new MCP server directory with boilerplate. | None | create_mcp_server.py |
✅ |
| TestMcpToolTool | Execute single MCP tool call in isolation for diagnostics. | None | test_mcp_tool.py |
✅ |
navi/tools/_internal/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
Tool ABC |
Abstract base. Self-describes via name, description, parameters. |
None | base.py |
✅ |
ToolResult |
Standard return container: success, output, error, metadata. |
None | base.py |
✅ |
| ContextVars | Async-safe vars set before each tool call: current_session_id, current_event_sink, current_stop_event, current_model, current_user_id, current_user_role, current_user_info. |
None | base.py |
✅ |
| Tool loader | Discovers module-level (name, description, parameters, execute) and class-based tools. Errors isolated per file. |
None | loader.py |
✅ |
ToolMiddleware |
Pre/post execute hooks for logging/metrics/rate limiting. | None | middleware.py |
❌ |
LoggingMiddleware |
Logs every tool execution with duration and result summary. | None | logging_middleware.py |
❌ |
| Time parser | Parses natural-language times: ISO, relative (2d 6h), in 3 hours, tomorrow at 09:00. |
None | time_parser.py |
✅ |
navi/mcp/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
McpClient |
Official MCP SDK wrapper. stdio or SSE transport. | None | client.py |
⚠️ |
McpServerConfig |
Pydantic model for server config. Auto-migrates legacy mcp_servers.json → mcp_servers.d/. |
mcp_servers.d/ directory |
config.py |
⚠️ |
McpManager |
Pool of McpClient instances. Lifecycle management, group resolution, tool listing. |
config_path |
manager.py |
⚠️ |
McpTool proxy |
Tool subclass forwarding to MCP server. Namespaced as mcp__<server>__<tool>. |
None | tools.py |
✅ |
navi/core/session.py, navi/core/pg_session_store.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Session model | Dual-buffer design: messages (display, never compressed) and context (LLM input, compressible). |
None | session.py |
✅ |
InMemorySessionStore |
Dict-backed ephemeral store for testing. | None | session.py |
✅ |
PgSessionStore |
PostgreSQL-backed store. Auto-DDL, JSON serialization, full-text search. | DATABASE_URL |
pg_session_store.py |
✅ |
| Session file storage | Per-session directory on disk. Safe filename sanitization, forbidden extension blocking, orphan cleanup. | SESSION_FILES_DIR, SESSION_FILES_MAX_SIZE_MB, SHARE_FILE_MAX_SIZE_MB |
session_files.py |
✅ |
| Content store | Registers session files for inline viewing via DB metadata. | PUBLIC_URL |
content_store.py |
⚠️ |
navi/memory/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
MemoryStore |
Composite: EmbeddingMixin + FactMixin + SummaryMixin + SessionStateMixin. Lazy-initializes pool, auto-creates tables. |
DATABASE_URL, EMBEDDING_MODEL, EMBEDDING_DIMENSIONS |
store.py |
✅ |
EmbeddingMixin |
Generates embeddings via LLM backend. Single or batch. Backfills missing. | EMBEDDING_MODEL, EMBEDDING_OLLAMA_HOST, EMBEDDING_OLLAMA_API_KEY |
_embeddings.py |
✅ |
FactMixin |
CRUD + dual search: vector (cosine, cutoff 0.3) then ILIKE fallback. Upsert, delete, list, count, categories. | None | _facts.py |
✅ |
SummaryMixin |
Per-user narrative summaries. Deterministic PK via zlib.crc32. |
None | _summary.py |
✅ |
SessionStateMixin |
Tracks which sessions already processed for fact extraction. Prevents duplicates. | None | _session_state.py |
✅ |
| Fact extraction | Post-session background extraction from transcripts. LLM parses JSON, upserts facts with confidence, regenerates summary. | Temperature 0.1, summary temperature 0.3, _MAX_TRANSCRIPT_CHARS=12000 |
extractor.py |
✅ |
| DDL builder | Conditionally generates DDL based on pgvector availability. Creates tables, indexes, constraints. | EMBEDDING_DIMENSIONS |
_ddl.py |
⚠️ |
| Backfill embeddings script | Batch-generates embeddings for existing facts without them. 8 per batch, 2s sleep. | DATABASE_URL, EMBEDDING_MODEL |
backfill_embeddings.py |
✅ |
| Migrate pgvector script | Adds missing pgvector columns and indexes to existing tables. | DATABASE_URL |
migrate_pgvector.py |
✅ |
navi/context_providers/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
ContextProviderRegistry |
Loads built-in + user providers. Validates exports. Supports hot-reload. | CONTEXT_PROVIDERS_DIR |
_loader.py |
✅ |
public_url provider |
Injects server's public URL into LLM context as system message. | PUBLIC_URL |
public_url.py |
✅ |
navi/workers/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Worker base class | Abstract base for post-response background tasks. Receives WorkerContext, may mutate session, return events. |
None | base.py |
⚠️ |
CompressionWorker |
Post-turn compression. Replaces old context with summary, resets token count, appends marker. | CONTEXT_COMPRESSION_ENABLED, CONTEXT_COMPRESSION_THRESHOLD, CONTEXT_KEEP_RECENT, CONTEXT_SUMMARY_TEMPERATURE, CONTEXT_SUMMARY_MAX_TOKENS |
compressor.py |
✅ |
| Worker auto-discovery | Scans navi/workers/*.py and auto-instantiates non-abstract Worker subclasses. |
None | __init__.py |
❌ |
navi/store/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
KvStore |
PostgreSQL-backed key-value persistence scoped by (user_id, session_id, scope, key). Auto-creates table/index. |
DATABASE_URL |
__init__.py |
✅ |
navi/api/websocket.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Streaming protocol | Full-duplex: client sends message, server emits stream_start, thinking_delta, stream_delta, tool_call, stream_end, etc. |
_HEARTBEAT_INTERVAL=20.0, _MAX_REPLAY_EVENTS=500, _MAX_IMAGES=10, _MAX_IMAGE_BYTES=5MB |
websocket.py |
✅ |
| Stop session | POST /sessions/{id}/stop sets stop_event cooperatively. |
None | websocket.py |
✅ |
| Reconnect/replay | On connect, if run active, replays buffered events before live stream. | _MAX_REPLAY_EVENTS=500 |
websocket.py |
✅ |
| Image upload validation | Max 10 images, 5MB each. Strips data:...;base64, prefix. |
_MAX_IMAGES=10, _MAX_IMAGE_BYTES=5242880 |
websocket.py |
✅ |
| Image context annotation | Appends note telling model that N images are already in multimodal context. | None | websocket.py |
❌ |
| File context annotation | Appends [Uploaded files on disk: ...] to user content. |
None | websocket.py |
⚠️ |
| Concurrent run guard | Rejects new messages if _runs or _busy_sessions already contains session ID. |
None | websocket.py |
✅ |
| Heartbeat keepalive | Sends heartbeat every 20 seconds during idle. |
_HEARTBEAT_INTERVAL=20.0 |
websocket.py |
✅ |
| User ContextVar propagation | Sets current_user_id, current_user_role, current_user_info from resolved User before agent run. |
None | websocket.py |
❌ |
| Recall update forwarding | Subscribes to RecallUpdate events and forwards to open WebSockets for affected session. |
None | websocket.py |
✅ |
navi/api/routes/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| List profiles | GET /agents/profiles — filters is_admin_only for non-admins. |
None | agents.py |
✅ |
| List system prompts | GET /agents/prompts — returns fully built prompt per profile. |
None | agents.py |
✅ |
| List tools | GET /agents/tools — all registered tools with schemas. |
None | agents.py |
✅ |
| List MCP servers | GET /agents/mcp_servers — servers, groups, instructions, tools. |
None | agents.py |
✅ |
| OAuth login redirect | PKCE + state, stores platform metadata for Android. | GNAUTH_CLIENT_ID, GNAUTH_REDIRECT_URI |
auth.py |
✅ |
| OAuth callback | Exchanges code, fetches user, upserts navi_users, encrypts tokens, sets cookie or redirects to mobile bridge. |
GNAUTH_CLIENT_ID, GNAUTH_CLIENT_SECRET, NAVI_AUTH_ENCRYPTION_KEY |
auth.py |
✅ |
| Mobile auth bridge | HTML bridge page with Chrome Intent URL auto-redirect. | None | auth.py |
✅ |
| Session CRUD | Create, list (paginated/search/sort), get, pin, delete. | DATABASE_URL |
sessions.py |
✅ |
| Session context/planning (debug) | GET /sessions/{id}/context and /planning — admin only. |
None | sessions.py |
✅ |
| Session file upload | Multipart upload with size/type validation. | SESSION_FILES_MAX_SIZE_MB |
sessions.py |
✅ |
| Session file download | Inline or attachment. Path-traversal guarded. | None | sessions.py |
✅ |
| Generate session name | Auto-generates display name from user messages via LLM. | None | sessions.py |
✅ |
| Recall endpoints | Get, cancel, skip recalls for session. | None | sessions.py |
✅ |
| Admin session management | List all sessions, full details, bypass-ownership delete. | None | admin.py |
✅ |
| Admin user management | List, detail, role update. | None | admin.py |
✅ |
| Admin memory view | All memory facts with pagination/search. Requires navi.memory.read_all. |
None | admin.py |
✅ |
| Admin profile toggle | Toggles is_admin_only and persists to profile_overrides table. |
None | admin.py |
✅ |
| Admin profile detail/update | GET/PUT profile config, writes back to disk, updates in-memory registry. | None | admin.py |
✅ |
| Admin Ollama blacklist clear | Clears dead-server and dead-model blacklists. | None | admin.py |
✅ |
| Admin MCP config | Bulk get/put, per-server CRUD. | None | admin.py |
✅ |
| Admin MCP reconnect | Drops old client, unregisters tools, connects fresh, re-registers. | None | admin.py |
✅ |
| Admin MCP status/test | Status listing and isolated tool execution. | None | admin.py |
✅ |
| Admin profile MCP mapping | GET/PUT mcp_servers dict per profile. |
None | admin.py |
✅ |
| Admin recall listing | All scheduled recalls with pagination/filtering. | None | admin.py |
✅ |
| Gnexus-auth webhook | Receives user.blocked/archived/deleted, auth.global_logout, session.revoked, client.roles_changed. |
None | webhooks.py |
✅ |
navi/api/deps.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Lazy singleton initialization | get_session_store(), get_memory_store(), get_kv_store(), get_scheduler(), get_registries(), etc. Module-level caching. |
DATABASE_URL, EMBEDDING_OLLAMA_HOST, EMBEDDING_OLLAMA_API_KEY |
deps.py |
⚠️ |
| MCP manager & tool registration | get_mcp_manager() lazily initializes and loads all servers. Clears and re-registers mcp__* tools. |
None | deps.py |
❌ |
| Embedding backend wiring | Dedicated OllamaBackend for embeddings (if EMBEDDING_OLLAMA_HOST set) or falls back to main chat backend. Injected into memory store. |
EMBEDDING_OLLAMA_HOST, EMBEDDING_OLLAMA_API_KEY, EMBEDDING_MODEL |
deps.py |
⚠️ |
navi/core/scheduler.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| Recall scheduling | Inserts pending recall into session_recalls. One pending per session via partial unique index. |
None | scheduler.py |
✅ |
| Recall cancellation | Marks pending recalls as cancelled. |
None | scheduler.py |
✅ |
| Recall skip | Advances recurring recall by interval_seconds using GREATEST(trigger_at, now). |
None | scheduler.py |
✅ |
| Recall listing | Filter by session_id, user_id, with admin override. | None | scheduler.py |
✅ |
| Pending recall queries | get_pending_recalls(before), get_next_trigger_at(), get_pending_session_ids(). |
None | scheduler.py |
✅ |
| Background loop | recall_scheduler_loop() polls for due recalls, fires up to 3 concurrently (semaphore), sleeps until next trigger. |
None | scheduler.py |
✅ |
| Headless fire | _fire_recall() defers if WebSocket run active, loads session, sets ContextVars, registers headless _AgentRun, streams events, handles success/failure/MaxIterationsReached, reschedules recurring, sends session_sync. |
None | scheduler.py |
✅ |
navi/main.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| FastAPI app setup | Creates app, includes routers, mounts static dirs. | None | main.py |
❌ |
| CORS middleware | Allows all origins, credentials, methods, headers. | None | main.py |
❌ |
| Static file mounting | /assets, /images, /content-viewers, /content, /debug, /debug/eval, /admin. |
None | main.py |
❌ |
| Startup lifecycle | Ensures auth tables, content store tables, initializes registries, connects MCP, applies profile overrides, checks embedding health, starts file cleanup + recall scheduler. Retries table creation 5× with 2s sleep for Docker races. | None | main.py |
❌ |
| Shutdown lifecycle | Closes SSH connections, disconnects MCP servers, cancels scheduler task. | None | main.py |
❌ |
navi/config.py)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
Settings model |
Pydantic-settings from .env. Covers LLM, embedding, web search, sandboxing, SSH, DB, logging, tools, session files, public URL, Gmail, OAuth, cookies, timeouts, compression, persona. |
~60 env vars | config.py |
✅ |
| Persona file loader | @model_validator reads navi_persona_file into navi_persona if inline value empty. |
NAVI_PERSONA_FILE |
config.py |
✅ |
| Computed path/command lists | Parses comma-separated strings into lists. | FS_ALLOWED_PATHS, TERMINAL_ALLOWED_COMMANDS, etc. |
config.py |
✅ |
navi/auth/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
| User model | Pydantic User with id, email, profile, role, permissions, has_permission() helper. |
None | auth/__init__.py |
✅ |
| Auth DDL & boot-time migrations | Creates navi_users, user_auth_sessions, migrates missing columns. |
None | auth/_ddl.py |
✅ |
GAuthClient singleton |
Shared state/PKCE stores, builds per-redirect_uri GAuthClient. |
GNAUTH_CLIENT_ID, GNAUTH_CLIENT_SECRET |
auth/client.py |
✅ |
| Token encryption | Fernet encrypts/decrypts OAuth tokens before DB storage. | NAVI_AUTH_ENCRYPTION_KEY |
auth/encrypt.py |
✅ |
| Current user resolution | Reads cookie, decrypts token, refreshes if expired, fetches from gnexus-auth, upserts navi_users, caches in conn.state.user. |
None | auth/deps.py |
✅ |
| Permission guards | require_user (401), require_admin (403), require_permission(permission) (403). |
None | auth/deps.py |
✅ |
| Session access control | check_session_access(): legacy sessions (user_id=None) admin-only; owned sessions accessible to owner/admin/explicit permission. |
None | auth/deps.py |
✅ |
navi/profiles/)| Mechanic | Description | Config / Flags | Files | Docs |
|---|---|---|---|---|
AgentProfile model |
Full agent config: identity, LLM settings, tools, thinking mechanics, planning flags, subagent config, visibility flags. | All fields in config.json |
profiles/base.py |
✅ |
| Profile loader | Auto-discovers subdirs with config.json + system_prompt.txt. Validates keys, loads optional subagent_system_prompt.txt, migrates planning_reflect_enabled → planning_phase2_enabled. |
None | profiles/loader.py |
✅ |
| Profile saver | Writes config.json, system_prompt.txt, optional subagent_system_prompt.txt. |
None | profiles/loader.py |
✅ |
| Runtime profile overrides | profile_overrides table persists is_admin_only changes across restarts. Loaded at startup. |
None | profiles/_overrides.py |
❌ |
These mechanisms have no documentation in docs/:
agent.py) — prefill polling + hard timeoutsagent.py) — aborts subagent after 60s/12k chars of pure thinkingagent.py) — _injected_fact_ids per turnagent.py) — compression during autonomous loops with doubled keep-recentagent.py) — concurrent context providers + memorycontext_builder.py) — dynamic sandbox/allowlist messagecontext_builder.py) — injects user profile datacontext_builder.py) — combines handshake + overlay instructionscontext_builder.py) — skip short messages, limit resultscontext_builder.py) — per-profile cachecontext_builder.py) — constructs goal anchor messagecontext_builder.py) — injects session files pathcompressor.py) — turn grouping for compressioncompressor.py) — same-turn compression for long loopscompressor.py) — message rendering for summarizercompressor.py) — 24k char captool_executor.py) — 3 heuristic fallbackstool_executor.py) — before/after executetool_executor.py) — synthesizes vision message from tool resulttool_executor.py) — yields ToolEvent alongside messagesai_helper.py)ask() timeout (ai_helper.py) — 120s hard limitask_json() (ai_helper.py)ai_helper.py)ai_helper.py)orchestrator.py)event_bus.py)websocket.py) — 10 images, 5MB eachwebsocket.py) — note about inline imageswebsocket.py) — uploaded files listwebsocket.py)websocket.py)deps.py) — lazy init + clear/re-registerworkers/__init__.py)ToolMiddleware ABC (tools/_internal/middleware.py)LoggingMiddleware (tools/_internal/logging_middleware.py)profiles/_overrides.py) — DB table for admin togglesplanning.py)planning.py)planning.py)agent.py subagent)agent.py subagent)main.py)main.py)main.py) — table creation retries, MCP connect, override applymain.py)agent.py)agent.py)context_builder.py)context_builder.py)"I want to build X — which existing mechanics might help?"
| Desired Feature | Reusable Mechanic(s) |
|---|---|
| Rate-limit tool calls | ToolMiddleware hooks (before_execute/after_execute) |
| Log tool metrics | LoggingMiddleware (already exists) |
| Inject dynamic context per session | Context providers (ContextProviderRegistry) |
| Inject dynamic context per turn | ContextBuilder._collect_context_injections + context providers |
| Sandbox file access for users | FS_ALLOWED_PATHS + _check_path() in FilesystemTool |
| Sandbox shell commands for users | TERMINAL_ALLOWED_COMMANDS + dangerous pattern blocklist |
| Track task progress across turns | TodoTool + planning auto-populate |
| Detect model looping | Anti-stall detector + todo snapshot comparison |
| Auto-replan on failures | Adaptive replan + todo failed-steps tracking |
| Compress long conversations | CompressionWorker + compress_context() |
| Summarize old turns | compress_context() with LLM-based summarization |
| Schedule future work | RecallScheduler + schedule_recall / manage_recall tools |
| Run headless agent | Agent.run_stream() with is_recall=True |
| Stream events to client | _AgentRun queue + WebSocket protocol |
| Delegate to specialist agent | SpawnAgentTool + run_ephemeral() |
| Switch agent personality mid-chat | SwitchProfileTool + profile reload mid-session |
| Store per-session state across restarts | KvStore (session_store table) |
| Persist user facts across sessions | MemoryStore + FactMixin + extractor |
| Search memory semantically | EmbeddingMixin + pgvector cosine distance |
| Hot-reload without restart | ReloadToolsTool + ToolRegistry.reload_user_tools() |
| Multi-server LLM fallback | FallbackOllamaBackend + blacklist clearing |
| Auth with external OAuth | GAuthClient + _resolve_user() + token refresh |
| Permission-based access control | require_permission() + check_session_access() |
| Auto-generate tool docs | ToolManualTool (manuals/ → auto-generate fallback) |
| Add new capability without code | CreateMcpServerTool + MCP proxy tools |
| Monitor external services | McpStatusTool + McpManager.get_all_tools() |
| Run isolated Python code | CodeExecTool with sandbox |
| Transfer files to/from remote | SshExecTool with connection pooling |
| Process images for LLM | ImageViewTool preprocessing pipeline |
| Validate planning assumptions | ReflectTool (Critic/Pragmatist/Detailer) |
| Control planning depth | Planning flags: planning_phase1/2/3_enabled, planning_mandatory |
| Prevent model drift | Goal anchoring + iteration budget injection |
| Stop long-running generation | Cooperative stop via current_stop_event |
| Handle model prefill hangs | Streaming guard wrapper |
| Handle model streaming stalls | Chunk timeout in streaming guard |
| Abort stuck subagents | Subagent thinking stall detector |
| Cache expensive prompts | System prompt caching in ContextBuilder |
| Parallelize independent LLM calls | asyncio.gather pattern (used in context injection, reflect tool) |
| Handle JSON from unreliable LLM | AIHelper.ask_json() + bracket-matching extractor |
| Track token usage | AIHelperTokensUsed event emission |
| Replay events on reconnect | WebSocket replay mechanism |
| Keep connection alive | WebSocket heartbeat |
| Prevent concurrent runs | Concurrent run guard |
| Handle mobile auth | Mobile auth bridge with Chrome Intent |
| Persist admin toggles | profile_overrides table |
| Auto-apply schema migrations | DDL builder + boot-time retries |
| Backfill missing data | backfill_embeddings.py pattern |
| Clean up old session files | Startup file cleanup loop |
Generated 2026-05-16. To update after adding a new mechanic: add a row to the appropriate section and update the cross-reference index if relevant.