# Profiles

Profiles define the agent's identity, tools, and behaviour for a specific domain.

## Profile definition (`navi/profiles/base.py`)

Each profile is loaded from a directory under `navi/profiles/<id>/`:
- `config.json` — all fields below
- `system_prompt.txt` — domain-specific instructions
- `subagent_system_prompt.txt` — injected into subagents spawned from this profile (optional)

---

## All `config.json` fields

### Identity

| Key | Type | Default | Description |
|---|---|---|---|
| `id` | str | **required** | Unique profile identifier (matches directory name) |
| `name` | str | **required** | Human-readable name shown in UI |
| `description` | str | **required** | Longer description shown in profile picker |
| `short_description` | str | `""` | One-line summary injected into every profile's system prompt (cross-profile awareness) |
| `full_description` | dict | `{}` | Structured dict: `specialization`, `when_to_use`, `key_tools` keys |

### LLM

| Key | Type | Default | Description |
|---|---|---|---|
| `llm_backend` | str | `"ollama"` | Backend key: `"ollama"`, `"openai"` |
| `model` | str or list[str] | `["gemma4:31b-cloud"]` | Model priority list — first available wins. String is accepted and auto-wrapped. |
| `temperature` | float | `0.7` | Sampling temperature for main loop calls |
| `max_iterations` | int | `10` | Hard cap on tool-calling iterations per turn |
| `top_k` | int \| None | `None` | Ollama sampling top_k |
| `top_p` | float \| None | `None` | Ollama sampling top_p |
| `num_thread` | int \| None | `None` | CPU threads for local inference. `None` = Ollama default |

### Tools

| Key | Type | Default | Description |
|---|---|---|---|
| `enabled_tools` | list[str] | **required** | Tool names available in the main loop |
| `subagent_tools` | list[str] | `[]` | Tools available to sub-agents spawned from this profile. Falls back to `enabled_tools` (full list) if empty. **Also acts as a whitelist for MCP tools** — only `mcp__<server>__<tool>` entries listed here are exposed to the sub-agent. If the list is non-empty and contains no `mcp__` entries, the sub-agent receives no MCP tools at all. |

`spawn_agent` may receive an optional `profile_id`. If omitted, the subagent uses the parent session's current profile. If provided, the subagent uses the selected profile's model, prompt, planning flags, and `subagent_tools`/`enabled_tools` fallback.

### MCP tools in sub-agents

When `subagent_tools` is non-empty, `mcp_servers` is filtered so that only MCP tools whose full name (`mcp__<server>__<tool>`) appears in `subagent_tools` are available to the sub-agent. This prevents a profile's main MCP servers from leaking into restricted sub-agent contexts. To grant a sub-agent access to a specific MCP tool, add it explicitly to `subagent_tools`, e.g. `mcp__navi_web__web_search`.

### Thinking mechanics

| Key | Type | Default | Description |
|---|---|---|---|
| `think_enabled` | bool | `true` | Pass `think=True` to LLM on every call (extended reasoning). Disable for latency-sensitive profiles. |
| `iteration_budget_enabled` | bool | `true` | Inject remaining iteration count into context so the model knows when to wrap up. |
| `goal_anchoring_enabled` | bool | `true` | Inject a goal-reminder system message every N iterations to prevent drift. |
| `goal_anchoring_interval` | int | `5` | N for goal anchoring. |
| `anti_stall_enabled` | bool | `true` | Detect looping without todo progress and inject a hard warning. |
| `anti_stall_threshold` | int | `8` | Consecutive iterations without progress before stall warning fires. |
| `step_validation_enabled` | bool | `false` | Reserved flag — todo validation is unconditional in the current implementation. |
| `adaptive_replan_enabled` | bool | `false` | When a todo step is marked failed, trigger a re-planning pass. Depends on `step_validation_enabled`. |

### Planning

The planning pipeline runs before the main tool-calling loop and produces a structured execution plan. It has three phases:

- **Phase 1 — Analysis**: reformulates the task, identifies subtasks and unknowns. Can output `DIRECT` to skip to execution immediately.
- **Phase 2 — Structured review**: one LLM call critiques the Phase 1 analysis through Critic / Pragmatist / Detailer sections and emits Plan Adjustments. Runs only when Phase 1 signals `REFLECT: yes`.
- **Phase 3 — Execution plan**: assigns each subtask to `TOOL / AGENT / SELF` and uses adaptive plan depth.

| Key | Type | Default | Description |
|---|---|---|---|
| `planning_enabled` | bool | `false` | Run the planning pipeline on every user message (not just the first). First-message planning always runs regardless of this flag. |
| `planning_mandatory` | bool | `false` | `true` — the `DIRECT` shortcut is never offered to the model; all three phases always run. `false` — the model can output `DIRECT` in Phase 1 to skip straight to execution. First-message planning is always forced regardless of this flag. |
| `planning_phase1_enabled` | bool | `true` | Enable Phase 1 (task analysis). When disabled, Phase 3 runs without analysis context. |
| `planning_phase2_enabled` | bool | `false` | Enable Phase 2 structured review. Adds one LLM call only when Phase 1 signals `REFLECT: yes`. |
| `planning_phase3_enabled` | bool | `true` | Enable Phase 3 (structured execution plan). When disabled, only Phase 1 (analysis) runs. |

### Sub-agent planning

| Key | Type | Default | Description |
|---|---|---|---|
| `subagent_think_enabled` | bool \| None | `None` | Extended reasoning for sub-agents. `None` = inherit `think_enabled` from parent profile. |
| `subagent_planning_enabled` | bool | `false` | Sub-agents spawned from this profile also run the planning pipeline before their tool loop. |
| `context_providers` | list[str] | `[]` | Extra context providers to inject for this profile (by name). Global providers are always injected. |
| `mcp_servers` | dict | `{}` | MCP servers referenced by this profile. Format: `{"server_name": ["group1", "group2"]}` or `{"server_name": ["*"]}` for all tools. |
| `is_admin_only` | bool | `false` | If `true`, profile is hidden from non-admin users in the profile list. |
| `is_subagent_only` | bool | `false` | If `true`, profile can only be used via `spawn_agent`; `switch_profile` is blocked. Useful for narrow specialist agents that should never become the main session profile. |

---

## Active profiles

| ID | Name | Models (priority order) | Temp | Planning |
|---|---|---|---|---|
| `secretary` | Personal Secretary | gemma4:31b-cloud → gemma4:26b-a4b-it-q4_K_M | 0.65 | Yes |
| `server_admin` | Server Administrator | gemma4:31b-cloud → gemma4:26b-a4b-it-q4_K_M | 0.3 | Yes |
| `developer` | Developer | gemma4:31b-cloud → gemma4:26b-a4b-it-q4_K_M | 0.45 | Yes |
| `tool_developer` | Tool Developer | gemma4:31b-cloud → gemma4:26b-a4b-it-q4_K_M | 0.35 | Yes |
| `discuss` | Discussion | gemma4:31b-cloud → gemma4:26b-a4b-it-q4_K_M | 0.85 | No |
| `modeler_3d` | 3D Modeler | gemma4:26b-a4b-it-q4_K_M → gemma4:31b-cloud | 0.35 | Yes |
| `navi_code` | Navi Code | gemma4:26b-a4b-it-q4_K_M → gemma4:31b-cloud | 0.35 | Yes |

All profiles share a base tool set. User tools from `tools/enabled.json` are merged in at runtime.

### `navi_code`

Terminal-first local coding assistant. Designed for the Navi Code CLI and single-user local deployments:

- **Native tools:** `terminal`, `filesystem`, `code_exec`, `image_view`, `spawn_agent`, `todo`, `scratchpad`, `reflect`, `list_tools`, `tool_manual`, `reload_tools`.
- **MCP tools (via `navi-web`):** `mcp__navi-web__web_search`, `mcp__navi-web__web_view`, `mcp__navi-web__http_request`.
- **Excluded:** `share_file`, `content_publish`, `ssh_exec`, `gmail`.
- **Planning:** Phase 1 and Phase 3 enabled, Phase 2 disabled to reduce latency.
- **Safety:** the system prompt asks Navi to confirm destructive operations (`rm`, overwrites) before executing them.

Use it with `NAVI_DEFAULT_PROFILE_ID=navi_code` so `POST /sessions` without a `profile_id` creates a `navi_code` session automatically. See [`docs/navi_code.md`](navi_code.md) for the full local-terminal setup.

---

## System prompt construction

The LLM sees (injected fresh on every call, never stored in session):

```
{persona.txt content}

---

{profile system_prompt.txt content}
```

`persona.txt` — global layer: personality, CONTEXT FIRST principle, self-extension rules, scratchpad/todo/memory instructions, delegation rules.

`system_prompt.txt` — domain layer: tool priorities, workflow, safety rules for this profile.

---

## Adding a profile

1. Create directory `navi/profiles/my_profile/`
2. Add `config.json` (minimal example):
```json
{
  "id": "my_profile",
  "name": "My Profile",
  "description": "...",
  "short_description": "...",
  "model": ["gemma4:31b-cloud", "gemma4:26b-a4b-it-q4_K_M"],
  "temperature": 0.5,
  "max_iterations": 20,
  "enabled_tools": ["todo", "scratchpad", "mcp__navi_web__web_search", "filesystem"],
  "subagent_tools": ["todo", "filesystem", "terminal"],
  "planning_enabled": true,
  "planning_mandatory": false,
  "planning_phase1_enabled": true,
  "planning_phase2_enabled": false,
  "planning_phase3_enabled": true,
  "think_enabled": true,
  "iteration_budget_enabled": true,
  "goal_anchoring_enabled": true,
  "goal_anchoring_interval": 5,
  "anti_stall_enabled": true,
  "anti_stall_threshold": 8,
  "step_validation_enabled": false,
  "adaptive_replan_enabled": false,
  "subagent_planning_enabled": false
}
```
3. Add `system_prompt.txt` with domain-specific instructions.
4. Optionally add `subagent_system_prompt.txt`.
5. The profile is auto-discovered at startup — no registration needed.

---

## Profile switching

`switch_profile` tool updates `session.profile_id` in the DB. After each tool execution batch, `run_stream()` checks for a profile change and reloads profile + tools. Takes effect on the next LLM call.

Rules (in persona): don't switch for a single off-topic question; switch when the domain clearly changes; never switch back and forth repeatedly.
