| 1 | # Configuration |
| 2 | |
| 3 | DeepSeek TUI reads configuration from a TOML file plus environment variables. |
| 4 | At process startup it also loads a workspace-local `.env` file when present. |
| 5 | Use the tracked `.env.example` as the template; copy it to `.env`, then edit |
| 6 | only the provider and safety knobs you need. |
| 7 | |
| 8 | ## Where It Looks |
| 9 | |
| 10 | Default config path: |
| 11 | |
| 12 | - `~/.deepseek/config.toml` |
| 13 | |
| 14 | Overrides: |
| 15 | |
| 16 | - CLI: `deepseek --config /path/to/config.toml` |
| 17 | - Env: `DEEPSEEK_CONFIG_PATH=/path/to/config.toml` |
| 18 | |
| 19 | If both are set, `--config` wins. Environment variable overrides are applied after the file is loaded. |
| 20 | |
| 21 | ### Per-project overlay (#485) |
| 22 | |
| 23 | When the TUI starts in a workspace that contains a |
| 24 | `<workspace>/.deepseek/config.toml` file, the values declared in that |
| 25 | file are merged on top of the global config. This lets a repo lock its |
| 26 | own provider, model, sandbox policy, or approval policy without |
| 27 | touching the user's `~/.deepseek/config.toml`. Pass |
| 28 | `--no-project-config` to skip the overlay for one launch. |
| 29 | |
| 30 | Supported keys in the project overlay (top-level fields only): |
| 31 | |
| 32 | | Key | Effect | |
| 33 | |---|---| |
| 34 | | `provider` | switch backend (e.g. `"nvidia-nim"` for an enterprise repo) | |
| 35 | | `model` | override `default_text_model` | |
| 36 | | `api_key` | use a per-repo key (typically read from `.env`, **not committed**) | |
| 37 | | `base_url` | point at a self-hosted endpoint | |
| 38 | | `reasoning_effort` | force `"high"` / `"max"` for a complex repo | |
| 39 | | `approval_policy` | `"never"` / `"on-request"` / `"untrusted"` for opinionated repos | |
| 40 | | `sandbox_mode` | `"read-only"` / `"workspace-write"` / `"danger-full-access"` | |
| 41 | | `mcp_config_path` | per-repo MCP server set | |
| 42 | | `notes_path` | keep notes in-repo | |
| 43 | | `max_subagents` | clamp concurrency for a constrained repo (clamped to 1..=20) | |
| 44 | | `allow_shell` | gate shell tool access on `false` | |
| 45 | |
| 46 | The overlay is intentionally narrow — it covers the fields a repo |
| 47 | maintainer is most likely to want to standardize across contributors. |
| 48 | Other settings (skills_dir, hooks, capacity, retry, etc.) stay |
| 49 | user-global. If your repo needs more, file an issue describing the |
| 50 | specific use case. |
| 51 | |
| 52 | The `deepseek` facade and `deepseek-tui` binary share the same config file for |
| 53 | DeepSeek auth and model defaults. `deepseek auth set --provider deepseek` (and |
| 54 | the legacy `deepseek login --api-key ...` alias) saves the key to |
| 55 | `~/.deepseek/config.toml`, and `deepseek --model deepseek-v4-flash` is forwarded |
| 56 | to the TUI as `DEEPSEEK_MODEL`. |
| 57 | |
| 58 | For hosted or self-hosted DeepSeek V4 providers, set `provider = "nvidia-nim"`, |
| 59 | `"fireworks"`, `"sglang"`, or `"vllm"` or pass `deepseek --provider <name>`. The facade |
| 60 | saves provider credentials to the shared user config and forwards the resolved |
| 61 | key, base URL, provider, and model to the TUI process. Use |
| 62 | `deepseek auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY"` or |
| 63 | `deepseek auth set --provider fireworks --api-key "YOUR_FIREWORKS_API_KEY"` to |
| 64 | save hosted-provider keys through the facade. SGLang and vLLM are self-hosted and can run |
| 65 | without an API key by default. |
| 66 | |
| 67 | Third-party OpenAI-compatible gateways that need extra request headers can set |
| 68 | `http_headers = { "X-Model-Provider-Id" = "your-model-provider" }` at the top |
| 69 | level or under a provider table such as `[providers.deepseek]`. When configured, |
| 70 | DeepSeek TUI sends those custom headers on model API requests. The equivalent |
| 71 | environment override is `DEEPSEEK_HTTP_HEADERS`, using comma-separated |
| 72 | `name=value` pairs such as |
| 73 | `X-Model-Provider-Id=your-model-provider,X-Gateway-Route=dev`. `Authorization` |
| 74 | and `Content-Type` are managed by the client and are not overridden by this |
| 75 | setting. |
| 76 | |
| 77 | To bootstrap MCP and skills directories at their resolved paths, run `deepseek-tui setup`. |
| 78 | To only scaffold MCP, run `deepseek-tui mcp init`. |
| 79 | |
| 80 | Note: setup, doctor, mcp, features, sessions, resume/fork, exec, review, and eval |
| 81 | are subcommands of the `deepseek-tui` binary. The `deepseek` dispatcher exposes a |
| 82 | distinct set of commands (`auth`, `config`, `model`, `thread`, `sandbox`, |
| 83 | `app-server`, `mcp-server`, `completion`) and forwards plain prompts to |
| 84 | `deepseek-tui`. |
| 85 | |
| 86 | ## Profiles |
| 87 | |
| 88 | You can define multiple profiles in the same file: |
| 89 | |
| 90 | ```toml |
| 91 | api_key = "PERSONAL_KEY" |
| 92 | default_text_model = "deepseek-v4-pro" |
| 93 | |
| 94 | [profiles.work] |
| 95 | api_key = "WORK_KEY" |
| 96 | base_url = "https://api.deepseek.com" |
| 97 | |
| 98 | [profiles.nvidia-nim] |
| 99 | provider = "nvidia-nim" |
| 100 | api_key = "NVIDIA_KEY" |
| 101 | base_url = "https://integrate.api.nvidia.com/v1" |
| 102 | default_text_model = "deepseek-ai/deepseek-v4-pro" |
| 103 | |
| 104 | [profiles.fireworks] |
| 105 | provider = "fireworks" |
| 106 | default_text_model = "accounts/fireworks/models/deepseek-v4-pro" |
| 107 | |
| 108 | [profiles.sglang] |
| 109 | provider = "sglang" |
| 110 | base_url = "http://localhost:30000/v1" |
| 111 | default_text_model = "deepseek-ai/DeepSeek-V4-Pro" |
| 112 | |
| 113 | [profiles.vllm] |
| 114 | provider = "vllm" |
| 115 | base_url = "http://localhost:8000/v1" |
| 116 | default_text_model = "deepseek-ai/DeepSeek-V4-Pro" |
| 117 | ``` |
| 118 | |
| 119 | Select a profile with: |
| 120 | |
| 121 | - CLI: `deepseek --profile work` |
| 122 | - Env: `DEEPSEEK_PROFILE=work` |
| 123 | |
| 124 | If a profile is selected but missing, DeepSeek TUI exits with an error listing available profiles. |
| 125 | |
| 126 | ## Environment Variables |
| 127 | |
| 128 | These override config values: |
| 129 | |
| 130 | - `DEEPSEEK_API_KEY` |
| 131 | - `DEEPSEEK_BASE_URL` |
| 132 | - `DEEPSEEK_HTTP_HEADERS` (custom model request headers, comma-separated `name=value` pairs) |
| 133 | - `DEEPSEEK_PROVIDER` (`deepseek|nvidia-nim|openrouter|novita|fireworks|sglang|vllm`) |
| 134 | - `DEEPSEEK_MODEL` or `DEEPSEEK_DEFAULT_TEXT_MODEL` |
| 135 | - `NVIDIA_API_KEY` or `NVIDIA_NIM_API_KEY` (preferred when provider is `nvidia-nim`; falls back to `DEEPSEEK_API_KEY`) |
| 136 | - `NVIDIA_NIM_BASE_URL`, `NIM_BASE_URL`, or `NVIDIA_BASE_URL` |
| 137 | - `NVIDIA_NIM_MODEL` |
| 138 | - `FIREWORKS_API_KEY` |
| 139 | - `FIREWORKS_BASE_URL` |
| 140 | - `SGLANG_BASE_URL` |
| 141 | - `SGLANG_MODEL` |
| 142 | - `SGLANG_API_KEY` (optional; many localhost SGLang servers do not require auth) |
| 143 | - `VLLM_BASE_URL` |
| 144 | - `VLLM_MODEL` |
| 145 | - `VLLM_API_KEY` (optional; many localhost vLLM servers do not require auth) |
| 146 | - `DEEPSEEK_LOG_LEVEL` or `RUST_LOG` (`info`/`debug`/`trace` enables lightweight verbose logs) |
| 147 | - `DEEPSEEK_SKILLS_DIR` |
| 148 | - `DEEPSEEK_MCP_CONFIG` |
| 149 | - `DEEPSEEK_NOTES_PATH` |
| 150 | - `DEEPSEEK_MEMORY` (`1|on|true|yes|y|enabled` turns user memory on) |
| 151 | - `DEEPSEEK_MEMORY_PATH` |
| 152 | - `DEEPSEEK_ALLOW_SHELL` (`1`/`true` enables) |
| 153 | - `DEEPSEEK_APPROVAL_POLICY` (`on-request|untrusted|never`) |
| 154 | - `DEEPSEEK_SANDBOX_MODE` (`read-only|workspace-write|danger-full-access|external-sandbox`) |
| 155 | - `DEEPSEEK_MANAGED_CONFIG_PATH` |
| 156 | - `DEEPSEEK_REQUIREMENTS_PATH` |
| 157 | - `DEEPSEEK_MAX_SUBAGENTS` (clamped to `1..=20`) |
| 158 | - `DEEPSEEK_TASKS_DIR` (runtime task queue/artifact storage, default `~/.deepseek/tasks`) |
| 159 | - `DEEPSEEK_ALLOW_INSECURE_HTTP` (`1`/`true` allows non-local `http://` base URLs; default is reject) |
| 160 | - `DEEPSEEK_CAPACITY_ENABLED` |
| 161 | - `DEEPSEEK_CAPACITY_LOW_RISK_MAX` |
| 162 | - `DEEPSEEK_CAPACITY_MEDIUM_RISK_MAX` |
| 163 | - `DEEPSEEK_CAPACITY_SEVERE_MIN_SLACK` |
| 164 | - `DEEPSEEK_CAPACITY_SEVERE_VIOLATION_RATIO` |
| 165 | - `DEEPSEEK_CAPACITY_REFRESH_COOLDOWN_TURNS` |
| 166 | - `DEEPSEEK_CAPACITY_REPLAN_COOLDOWN_TURNS` |
| 167 | - `DEEPSEEK_CAPACITY_MAX_REPLAY_PER_TURN` |
| 168 | - `DEEPSEEK_CAPACITY_MIN_TURNS_BEFORE_GUARDRAIL` |
| 169 | - `DEEPSEEK_CAPACITY_PROFILE_WINDOW` |
| 170 | - `DEEPSEEK_CAPACITY_PRIOR_CHAT` |
| 171 | - `DEEPSEEK_CAPACITY_PRIOR_REASONER` |
| 172 | - `DEEPSEEK_CAPACITY_PRIOR_V4_PRO` |
| 173 | - `DEEPSEEK_CAPACITY_PRIOR_V4_FLASH` |
| 174 | - `DEEPSEEK_CAPACITY_PRIOR_FALLBACK` |
| 175 | - `NO_ANIMATIONS` (`1|true|yes|on` forces `low_motion = true` and |
| 176 | `fancy_animations = false` at startup, regardless of the saved |
| 177 | settings; see [`docs/ACCESSIBILITY.md`](./ACCESSIBILITY.md)). |
| 178 | - `SSL_CERT_FILE` — corporate-proxy / TLS-inspecting MITM users |
| 179 | point this at a PEM bundle (or single DER cert) and the cert(s) |
| 180 | get added alongside the platform's system trust store. Failures |
| 181 | log a warning and continue — the existing system roots still |
| 182 | apply. |
| 183 | |
| 184 | ### Instruction sources (`instructions = [...]`, #454) |
| 185 | |
| 186 | Add a list of additional system-prompt sources that get |
| 187 | concatenated, in declared order, alongside the auto-loaded |
| 188 | `AGENTS.md`: |
| 189 | |
| 190 | ```toml |
| 191 | instructions = [ |
| 192 | "./AGENTS.md", |
| 193 | "~/.deepseek/global.md", |
| 194 | "~/team/agents-shared.md", |
| 195 | ] |
| 196 | ``` |
| 197 | |
| 198 | Rules: |
| 199 | |
| 200 | - Paths run through `expand_path` so `~` and env vars work. |
| 201 | - Each file is capped at 100 KiB; oversized files are |
| 202 | truncated with a `[…elided]` marker rather than skipped. |
| 203 | - Missing files are skipped with a tracing warning so a stale |
| 204 | entry doesn't fail the launch. |
| 205 | - Project config (`<workspace>/.deepseek/config.toml`) |
| 206 | **replaces** the user array wholesale rather than merging. |
| 207 | If you want both, list `~/global.md` inside the project |
| 208 | array. Set `instructions = []` in the project to clear the |
| 209 | user list for that repo. |
| 210 | |
| 211 | ### `/hooks` listing |
| 212 | |
| 213 | Run `/hooks` (or `/hooks list`) inside the TUI to see every |
| 214 | configured lifecycle hook grouped by event, including each |
| 215 | hook's name, command preview, timeout, and condition. The |
| 216 | `[hooks].enabled` flag's state is shown at the top so it's |
| 217 | obvious when hooks are globally suppressed. Hooks are |
| 218 | configured under `[[hooks.hooks]]` entries — see the existing |
| 219 | hook-system documentation for the full schema. |
| 220 | |
| 221 | ### Composer stash (`/stash`, Ctrl+S) |
| 222 | |
| 223 | Press **Ctrl+S** in the composer to park the current draft to |
| 224 | `~/.deepseek/composer_stash.jsonl`. `/stash list` shows parked |
| 225 | drafts with one-line previews and timestamps; `/stash pop` |
| 226 | restores the most recently parked draft (LIFO); `/stash clear` |
| 227 | wipes the file. Capped at 200 entries; multiline drafts |
| 228 | round-trip intact. |
| 229 | |
| 230 | ## Settings File (Persistent UI Preferences) |
| 231 | |
| 232 | DeepSeek TUI also stores user preferences in: |
| 233 | |
| 234 | - `~/.config/deepseek/settings.toml` |
| 235 | |
| 236 | Notable settings include `auto_compact` (default `false`), which opts into |
| 237 | replacement-style summarization only near the active model limit. The default |
| 238 | V4 path preserves the stable message prefix for cache reuse; use manual |
| 239 | `/compact` or enable `auto_compact` only when you explicitly want automatic |
| 240 | replacement compaction. You can inspect or update these from the TUI with |
| 241 | `/settings` and `/config` (interactive editor). |
| 242 | |
| 243 | Common settings keys: |
| 244 | |
| 245 | - `theme` (default, dark, light, whale) |
| 246 | - `auto_compact` (on/off, default off) |
| 247 | - `paste_burst_detection` (on/off, default on): fallback rapid-key paste |
| 248 | detection for terminals that do not emit bracketed-paste events. This is |
| 249 | independent of terminal bracketed-paste mode. |
| 250 | - `show_thinking` (on/off) |
| 251 | - `show_tool_details` (on/off) |
| 252 | - `locale` (`auto`, `en`, `ja`, `zh-Hans`, `pt-BR`; default `auto`): UI chrome |
| 253 | locale. `auto` checks `LC_ALL`, `LC_MESSAGES`, then `LANG`; unsupported or |
| 254 | missing locales fall back to English. This does not force model output |
| 255 | language. |
| 256 | - `cost_currency` (`usd`, `cny`; default `usd`): currency used by the footer, |
| 257 | context panel, `/cost`, `/tokens`, and long-turn notification summaries. The |
| 258 | aliases `rmb` and `yuan` normalize to `cny`. |
| 259 | - `default_mode` (agent, plan, yolo; legacy `normal` is accepted and normalized to `agent`) |
| 260 | - `max_history` (number of submitted input history entries; cleared drafts are |
| 261 | also kept locally for composer history search) |
| 262 | - `default_model` (model name override) |
| 263 | |
| 264 | Only `agent`, `plan`, and `yolo` are visible modes in the UI. For compatibility, |
| 265 | older settings files with `default_mode = "normal"` still load as `agent`, and |
| 266 | the hidden `/normal` slash command switches to `Agent`. |
| 267 | |
| 268 | Localization scope is tracked in [LOCALIZATION.md](LOCALIZATION.md). The v0.7.6 |
| 269 | core pack covers high-visibility TUI chrome only; provider/tool schemas, |
| 270 | personality prompts, and full documentation remain English unless explicitly |
| 271 | translated later. |
| 272 | |
| 273 | Readability semantics: |
| 274 | |
| 275 | - Selection uses a unified style across transcript, composer menus, and modals. |
| 276 | - Footer hints use a dedicated semantic role (`FOOTER_HINT`) so hint text stays readable across themes. |
| 277 | - The footer includes a compact `coherence` chip that describes how stable and |
| 278 | focused the current session is right now. Possible states are `healthy`, |
| 279 | `crowded`, `refreshing`, `verifying`, and `resetting`; these are derived from |
| 280 | capacity and compaction events without exposing internal formulas in normal UI. |
| 281 | |
| 282 | ### Token Quantities and Drivers |
| 283 | |
| 284 | DeepSeek V4 prefix caching makes token labels matter. These quantities are kept |
| 285 | separate: |
| 286 | |
| 287 | | Quantity | Meaning | Allowed to drive | |
| 288 | |---|---|---| |
| 289 | | Active request input estimate | Conservative estimate of the next request's live system prompt and transcript payload. | Header/footer context percent, hard-cycle trigger, opt-in Flash seam trigger, and emergency overflow preflight. | |
| 290 | | Reserved response headroom | The requested `max_tokens` budget plus safety headroom. v0.7.5 keeps normal turns at `262144` output tokens and adds `1024` safety tokens for context-window checks. | Hard-cycle and emergency overflow budget checks only. | |
| 291 | | Cumulative API usage | Provider-reported input plus output tokens summed across completed API calls; multi-tool turns may count the same stable prefix more than once. | Session usage and approximate cost telemetry only. | |
| 292 | | Prompt cache hit/miss | Provider cache telemetry for the most recent call when available. | Cache-hit display and cost estimation only; never compaction, seam, or cycle triggers. | |
| 293 | | Context percent | Active request input estimate divided by the model context window. | Display only; it mirrors the active-input basis used by context safeguards. | |
| 294 | | Cost estimate | Approximate spend from provider usage and configured DeepSeek rates. | Display only. | |
| 295 | |
| 296 | For the default V4 path, hard cycles fire when active input reaches the smaller |
| 297 | of the configured cycle threshold (`768000`) and the model window minus reserved |
| 298 | response headroom. Replacement compaction remains opt-in (`auto_compact = false` |
| 299 | by default), the Flash seam manager remains opt-in (`[context].enabled = false`), |
| 300 | and the capacity controller remains disabled unless configured. |
| 301 | |
| 302 | ### Command Migration Notes |
| 303 | |
| 304 | If you are upgrading from older releases: |
| 305 | |
| 306 | - Old: `/deepseek` |
| 307 | New: `/links` (aliases: `/dashboard`, `/api`) |
| 308 | - Old: `/set model deepseek-reasoner` |
| 309 | New: `/config` and edit the `model` row to `deepseek-v4-pro` or `deepseek-v4-flash` |
| 310 | - Old: visible `Normal` mode or `default_mode = "normal"` |
| 311 | New: use `Agent` / `default_mode = "agent"`; legacy `normal` still maps to `agent` |
| 312 | - Old: discover `/set` in slash UX/help |
| 313 | New: use `/config` for editing and `/settings` for read-only inspection |
| 314 | |
| 315 | ## Key Reference |
| 316 | |
| 317 | ### Core keys (used by the TUI/engine) |
| 318 | |
| 319 | - `provider` (string, optional): `deepseek` (default), `deepseek-cn`, `nvidia-nim`, `openrouter`, `novita`, `fireworks`, `sglang`, or `vllm`. `deepseek-cn` uses DeepSeek's mainland China endpoint (`https://api.deepseeki.com`); `nvidia-nim` targets NVIDIA's NIM-hosted DeepSeek endpoints through `https://integrate.api.nvidia.com/v1`; `fireworks` targets `https://api.fireworks.ai/inference/v1`; `sglang` targets a self-hosted OpenAI-compatible endpoint, defaulting to `http://localhost:30000/v1`; `vllm` targets a self-hosted vLLM OpenAI-compatible endpoint, defaulting to `http://localhost:8000/v1`. |
| 320 | - `api_key` (string, required): must be non-empty (or set `DEEPSEEK_API_KEY`). |
| 321 | - `base_url` (string, optional): defaults to `https://api.deepseek.com` for DeepSeek's OpenAI-compatible Chat Completions API, `https://api.deepseeki.com` for `provider = "deepseek-cn"`, or the provider-specific endpoint for hosted/self-hosted providers. `https://api.deepseek.com/v1` is also accepted for SDK compatibility; use `https://api.deepseek.com/beta` only for DeepSeek beta features such as strict tool mode, chat prefix completion, and FIM completion. |
| 322 | - `default_text_model` (string, optional): defaults to `deepseek-v4-pro` for DeepSeek, `deepseek-ai/deepseek-v4-pro` for NVIDIA NIM, `accounts/fireworks/models/deepseek-v4-pro` for Fireworks, and `deepseek-ai/DeepSeek-V4-Pro` for SGLang. Current public DeepSeek IDs are `deepseek-v4-pro` and `deepseek-v4-flash`, both with 1M context windows and thinking mode enabled by default. Legacy `deepseek-chat` and `deepseek-reasoner` remain compatibility aliases for `deepseek-v4-flash`. Provider-specific mappings translate `deepseek-v4-pro` / `deepseek-v4-flash` to each provider's model ID where supported. Use `/models` or `deepseek models` to discover live IDs from your configured endpoint. `DEEPSEEK_MODEL` overrides this for a single process. |
| 323 | - `reasoning_effort` (string, optional): `off`, `low`, `medium`, `high`, or `max`; defaults to the configured UI tier. DeepSeek Platform receives top-level `thinking` / `reasoning_effort` fields. NVIDIA NIM receives equivalent settings through `chat_template_kwargs`. |
| 324 | - `allow_shell` (bool, optional): defaults to `true` (sandboxed). |
| 325 | - `approval_policy` (string, optional): `on-request`, `untrusted`, or `never`. Runtime `approval_mode` editing in `/config` also accepts `on-request` and `untrusted` aliases. |
| 326 | - `sandbox_mode` (string, optional): `read-only`, `workspace-write`, `danger-full-access`, `external-sandbox`. |
| 327 | - `managed_config_path` (string, optional): managed config file loaded after user/env config. |
| 328 | - `requirements_path` (string, optional): requirements file used to enforce allowed approval/sandbox values. |
| 329 | - `max_subagents` (int, optional): defaults to `10` and is clamped to `1..=20`. |
| 330 | - `subagents.*` (optional): per-role/type model defaults for `agent_spawn` and |
| 331 | related sub-agent tools. Explicit tool `model` values win, then role/type |
| 332 | overrides, then the parent runtime model. Supported convenience keys are |
| 333 | `default_model`, `worker_model`, `explorer_model`, `awaiter_model`, |
| 334 | `review_model`, `custom_model`, and `max_concurrent`. The |
| 335 | `[subagents] max_concurrent` value overrides top-level `max_subagents` and is |
| 336 | also clamped to `1..=20`. `[subagents.models]` accepts lower-case role or type |
| 337 | keys such as `worker`, `explorer`, `general`, `explore`, `plan`, and |
| 338 | `review`. Values must normalize to a supported DeepSeek model id before an |
| 339 | agent is spawned. |
| 340 | - `skills_dir` (string, optional): defaults to `~/.deepseek/skills` (each skill is a directory containing `SKILL.md`). Workspace-local `.agents/skills` or `./skills` are preferred when present; the runtime also discovers global agentskills.io-compatible `~/.agents/skills` and the broader Claude-ecosystem `~/.claude/skills`. |
| 341 | - `mcp_config_path` (string, optional): defaults to `~/.deepseek/mcp.json`. |
| 342 | It is visible in `/config` and can be changed from the TUI. The new path is |
| 343 | used immediately by `/mcp`, but rebuilding the model-visible MCP tool pool |
| 344 | requires restarting the TUI. |
| 345 | - `notes_path` (string, optional): defaults to `~/.deepseek/notes.txt` and is used by the `note` tool. |
| 346 | - `[memory].enabled` (bool, optional): defaults to `false`. When `true`, |
| 347 | the TUI loads the user memory file into a `<user_memory>` prompt block, |
| 348 | enables `# foo` quick-capture in the composer, surfaces the `/memory` |
| 349 | slash command, and registers the `remember` tool. The same toggle is |
| 350 | available via `DEEPSEEK_MEMORY=on`. |
| 351 | - `memory_path` (string, optional): defaults to `~/.deepseek/memory.md`. |
| 352 | Used by the user-memory feature when enabled — see |
| 353 | [`MEMORY.md`](MEMORY.md) for the full feature surface (`# foo` |
| 354 | composer prefix, `/memory` slash command, `remember` tool, opt-in |
| 355 | toggle). |
| 356 | - `snapshots.*` (optional): side-git workspace snapshots for file rollback: |
| 357 | - `[snapshots].enabled` (bool, default `true`) |
| 358 | - `[snapshots].max_age_days` (int, default `7`) |
| 359 | - snapshots live under `~/.deepseek/snapshots/<project_hash>/<worktree_hash>/.git` and never use the workspace's own `.git` directory |
| 360 | - `context.*` (optional): append-only Flash seam manager, currently opt-in. |
| 361 | Thresholds use the active request input estimate, not lifetime summed API |
| 362 | usage: |
| 363 | - `[context].enabled` (bool, default `false`) |
| 364 | - `[context].verbatim_window_turns` (int, default `16`) |
| 365 | - `[context].l1_threshold` (int, default `192000`) |
| 366 | - `[context].l2_threshold` (int, default `384000`) |
| 367 | - `[context].l3_threshold` (int, default `576000`) |
| 368 | - `[context].cycle_threshold` (int, default `768000`) |
| 369 | - `[context].seam_model` (string, default `deepseek-v4-flash`) |
| 370 | - `retry.*` (optional): retry/backoff settings for API requests: |
| 371 | - `[retry].enabled` (bool, default `true`) |
| 372 | - `[retry].max_retries` (int, default `3`) |
| 373 | - `[retry].initial_delay` (float seconds, default `1.0`) |
| 374 | - `[retry].max_delay` (float seconds, default `60.0`) |
| 375 | - `[retry].exponential_base` (float, default `2.0`) |
| 376 | - `capacity.*` (optional): runtime context-capacity controller. This is opt-in |
| 377 | because its active interventions can rewrite the live transcript. |
| 378 | - `[capacity].enabled` (bool, default `false`) |
| 379 | - `[capacity].low_risk_max` (float, default `0.50`) |
| 380 | - `[capacity].medium_risk_max` (float, default `0.62`) |
| 381 | - `[capacity].severe_min_slack` (float, default `-0.25`) |
| 382 | - `[capacity].severe_violation_ratio` (float, default `0.40`) |
| 383 | - `[capacity].refresh_cooldown_turns` (int, default `6`) |
| 384 | - `[capacity].replan_cooldown_turns` (int, default `5`) |
| 385 | - `[capacity].max_replay_per_turn` (int, default `1`) |
| 386 | - `[capacity].min_turns_before_guardrail` (int, default `4`) |
| 387 | - `[capacity].profile_window` (int, default `8`) |
| 388 | - `[capacity].deepseek_v3_2_chat_prior` (float, default `3.9`) |
| 389 | - `[capacity].deepseek_v3_2_reasoner_prior` (float, default `4.1`) |
| 390 | - `[capacity].deepseek_v4_pro_prior` (float, default `3.5`) |
| 391 | - `[capacity].deepseek_v4_flash_prior` (float, default `4.2`) |
| 392 | - `[capacity].fallback_default_prior` (float, default `3.8`) |
| 393 | - `[notifications].method` (string, optional): `auto`, `osc9`, `bel`, or |
| 394 | `off`. Defaults to `auto`. The TUI fires this on completed (successful) |
| 395 | turns whose elapsed time meets `threshold_secs`; failed and cancelled |
| 396 | turns are silent. `auto` resolves to `osc9` for `iTerm.app`, `Ghostty`, |
| 397 | and `WezTerm` (detected via `$TERM_PROGRAM`). Otherwise the fallback is |
| 398 | `bel` on macOS / Linux and `off` on Windows (where BEL maps to the |
| 399 | system error chime — see the [Notifications](#notifications) section |
| 400 | for the full rationale, #583). |
| 401 | - `[notifications].threshold_secs` (int, optional): defaults to `30`. |
| 402 | Only completed turns whose elapsed time meets or exceeds this fire a |
| 403 | notification. |
| 404 | - `[notifications].include_summary` (bool, optional): defaults to |
| 405 | `false`. When `true`, the notification body includes the elapsed |
| 406 | duration and the turn's cost in the configured display currency. |
| 407 | - `tui.alternate_screen` (string, optional): `auto`, `always`, or `never`. `auto` disables the alternate screen in Zellij; `--no-alt-screen` forces inline mode. Set `never` or run with `--no-alt-screen` when you want real terminal scrollback. |
| 408 | - `tui.mouse_capture` (bool, optional, default `true` on non-Windows terminals when the alternate screen is active; `false` on Windows and inside JetBrains JediTerm — PyCharm/IDEA/CLion/etc. — where mouse-event escapes leak into the input stream as garbled text, see #878 / #898): enable internal mouse scrolling, transcript selection, and right-click context actions. TUI-owned drag selection copies only user/assistant transcript text. Set this to `false` or run with `--no-mouse-capture` for raw terminal selection; set it to `true` or run with `--mouse-capture` to opt in anywhere it's defaulted off. |
| 409 | - `tui.terminal_probe_timeout_ms` (int, optional, default `500`): startup terminal-mode probe timeout in milliseconds. Values are clamped to `100..=5000`; timeout emits a warning and aborts startup instead of hanging indefinitely. |
| 410 | - `tui.osc8_links` (bool, optional, default `true`): emit OSC 8 escape sequences around URLs in transcript output so terminals that support them (iTerm2, Terminal.app 13+, Ghostty, Kitty, WezTerm, Alacritty, recent gnome-terminal/konsole) render them as Cmd+click hyperlinks. Terminals without OSC 8 support render the plain URL and ignore the escape. Set `false` for terminals that misrender the sequence; selection/clipboard output always strips the escapes. |
| 411 | - `hooks` (optional): lifecycle hooks configuration (see `config.example.toml`). |
| 412 | - `features.*` (optional): feature flag overrides (see below). |
| 413 | |
| 414 | ### User memory |
| 415 | |
| 416 | User memory is split across one top-level path setting and one opt-in |
| 417 | toggle table: |
| 418 | |
| 419 | ```toml |
| 420 | memory_path = "~/.deepseek/memory.md" |
| 421 | |
| 422 | [memory] |
| 423 | enabled = true |
| 424 | ``` |
| 425 | |
| 426 | Notes: |
| 427 | |
| 428 | - `memory_path` stays at the top level beside `notes_path` and |
| 429 | `skills_dir`; it is not nested under `[memory]`. |
| 430 | - `DEEPSEEK_MEMORY_PATH` overrides the file path from the environment. |
| 431 | - `DEEPSEEK_MEMORY=on` (also `1`, `true`, `yes`, `y`, or `enabled`) |
| 432 | flips the feature on without editing `config.toml`. |
| 433 | - The feature is inert when disabled: no file is injected, `# foo` |
| 434 | falls through to normal message submission, and the model does not |
| 435 | see the `remember` tool. |
| 436 | - See [`MEMORY.md`](MEMORY.md) for examples and the full `/memory` |
| 437 | command surface. |
| 438 | |
| 439 | ### Notifications |
| 440 | |
| 441 | The TUI can emit a desktop notification (OSC 9 escape or plain BEL) when a turn **completes successfully** and took longer than a threshold, so you can tab away while a long task runs. Failed or cancelled turns are intentionally silent — the notification is a "your task is ready" cue, not a generic ping. Configuration lives under `[notifications]`: |
| 442 | |
| 443 | ```toml |
| 444 | [notifications] |
| 445 | method = "auto" # auto | osc9 | bel | off |
| 446 | threshold_secs = 30 # only notify when the turn took >= this many seconds |
| 447 | include_summary = false # include elapsed time + cost in the notification body |
| 448 | ``` |
| 449 | |
| 450 | Method semantics: |
| 451 | |
| 452 | - `auto` (default) — picks `osc9` for `iTerm.app`, `Ghostty`, and `WezTerm` (detected via `$TERM_PROGRAM`). On macOS and Linux it falls back to `bel`. **On Windows the fallback is `off`** instead of `bel`, because the Windows audio stack maps `\x07` to the `SystemAsterisk` / `MB_OK` chime — the same sound application error popups use, so a successful-turn notification ends up sounding like an error (#583). |
| 453 | - `osc9` — emit `\x1b]9;<msg>\x07`. Inside tmux the sequence is wrapped in DCS passthrough so it reaches the outer terminal. |
| 454 | - `bel` — emit a single `\x07` byte. Use this on Windows only if you actively want the chime back. |
| 455 | - `off` — disable post-turn notifications entirely. |
| 456 | |
| 457 | Windows users who run inside a known OSC-9 terminal (e.g. WezTerm on Windows) keep getting OSC-9 notifications; the `off` fallback only applies when no recognised `TERM_PROGRAM` is detected. |
| 458 | |
| 459 | ### Parsed but currently unused (reserved for future versions) |
| 460 | |
| 461 | These keys are accepted by the config loader but not currently used by the interactive TUI or built-in tools: |
| 462 | |
| 463 | - `tools_file` |
| 464 | |
| 465 | ## Feature Flags |
| 466 | |
| 467 | Feature flags live under the `[features]` table and are merged across profiles. |
| 468 | Defaults are enabled for built-in tooling, so you only need to set entries you |
| 469 | want to force on or off. |
| 470 | |
| 471 | ```toml |
| 472 | [features] |
| 473 | shell_tool = true |
| 474 | subagents = true |
| 475 | web_search = true # enables canonical web.run plus the compatibility web_search alias |
| 476 | apply_patch = true |
| 477 | mcp = true |
| 478 | exec_policy = true |
| 479 | ``` |
| 480 | |
| 481 | You can also override features for a single run: |
| 482 | |
| 483 | - `deepseek-tui --enable web_search` |
| 484 | - `deepseek-tui --disable subagents` |
| 485 | |
| 486 | Use `deepseek-tui features list` to inspect known flags and their effective state. |
| 487 | |
| 488 | ## Local Media Attachments |
| 489 | |
| 490 | Use `@path/to/file` in the composer to add local text file or directory context |
| 491 | to the next message. Use `/attach <path>` for local image/video media paths, or |
| 492 | `Ctrl+V` to attach an image from the clipboard. DeepSeek's public Chat |
| 493 | Completions API currently accepts text message content, so media attachments are |
| 494 | sent as explicit local path references instead of native image/video payloads. |
| 495 | Attachment rows appear above the composer before submit; move to the start of |
| 496 | the composer, press `↑` to select an attachment row, then press `Backspace` or |
| 497 | `Delete` to remove it without editing the placeholder text by hand. |
| 498 | |
| 499 | ## Managed Configuration and Requirements |
| 500 | |
| 501 | DeepSeek TUI supports a policy layering model: |
| 502 | |
| 503 | 1. user config + profile + env overrides |
| 504 | 2. managed config (if present) |
| 505 | 3. requirements validation (if present) |
| 506 | |
| 507 | By default on Unix: |
| 508 | - managed config: `/etc/deepseek/managed_config.toml` |
| 509 | - requirements: `/etc/deepseek/requirements.toml` |
| 510 | |
| 511 | Requirements file shape: |
| 512 | |
| 513 | ```toml |
| 514 | allowed_approval_policies = ["on-request", "untrusted", "never"] |
| 515 | allowed_sandbox_modes = ["read-only", "workspace-write"] |
| 516 | ``` |
| 517 | |
| 518 | If configured values violate requirements, startup fails with a descriptive error. |
| 519 | |
| 520 | See `docs/capacity_controller.md` for formulas, intervention behavior, and telemetry. |
| 521 | |
| 522 | ## Notes On `deepseek-tui doctor` |
| 523 | |
| 524 | `deepseek-tui doctor` follows the same config resolution rules as the rest of the |
| 525 | TUI. That means `--config` / `DEEPSEEK_CONFIG_PATH` are respected, and MCP/skills |
| 526 | checks use the resolved `mcp_config_path` / `skills_dir` (including env overrides). |
| 527 | |
| 528 | To bootstrap missing MCP/skills paths, run `deepseek-tui setup --all`. You can |
| 529 | also run `deepseek-tui setup --skills --local` to create a workspace-local |
| 530 | `./skills` dir. |
| 531 | |
| 532 | `deepseek-tui doctor --json` prints a machine-readable report that skips the |
| 533 | live API connectivity probe. Top-level keys: `version`, `config_path`, |
| 534 | `config_present`, `workspace`, `api_key.source`, `base_url`, |
| 535 | `default_text_model`, `mcp`, `skills`, `tools`, `plugins`, `sandbox`, |
| 536 | `platform`, `api_connectivity`, `capability`. CI consumers should rely on `api_key.source` |
| 537 | (`env`/`config`/`missing`) rather than parsing the human-readable `doctor` |
| 538 | text. |
| 539 | |
| 540 | The `capability` key contains per-provider capability info derived from |
| 541 | static knowledge (release docs, API guides) rather than live API probes. |
| 542 | Top-level sub-keys: `resolved_provider`, `resolved_model`, `context_window`, |
| 543 | `max_output`, `thinking_supported`, `cache_telemetry_supported`, |
| 544 | `request_payload_mode`, and `deprecation`. When the resolved model is a known |
| 545 | legacy alias (e.g. `deepseek-chat`, `deepseek-reasoner`), the `deprecation` |
| 546 | sub-object carries `alias`, `replacement`, and `notice` fields. |
| 547 | |
| 548 | Use `capability.context_window` and `capability.max_output` for context-window |
| 549 | budgeting in CI scripts. Use `capability.thinking_supported` to decide whether |
| 550 | to configure reasoning effort. Use `capability.deprecation` to warn users about |
| 551 | legacy model aliases. |
| 552 | |
| 553 | ## Setup status, clean, and extension dirs |
| 554 | |
| 555 | `deepseek-tui setup` accepts a few flags beyond the existing `--mcp`, |
| 556 | `--skills`, `--local`, `--all`, and `--force`: |
| 557 | |
| 558 | - `--status` — print a compact one-screen status (api key, base URL, model, |
| 559 | MCP/skills/tools/plugins counts, sandbox, `.env` presence). Read-only and |
| 560 | network-free; safe to run in CI. If `.env` is missing and `.env.example` is |
| 561 | present in the workspace, the status output points at `cp .env.example .env`. |
| 562 | - `--tools` — scaffold `~/.deepseek/tools/` with a `README.md` describing the |
| 563 | self-describing frontmatter convention (`# name:` / `# description:` / |
| 564 | `# usage:`) and an `example.sh` that follows it. The directory is |
| 565 | intentionally not auto-loaded; wire individual scripts into the agent via |
| 566 | MCP, hooks, or skills. |
| 567 | - `--plugins` — scaffold `~/.deepseek/plugins/` with a `README.md` and an |
| 568 | `example/PLUGIN.md` placeholder using the same frontmatter shape as |
| 569 | `SKILL.md`. Plugins are not loaded automatically either; reference them |
| 570 | from a skill or MCP wrapper when you want them active. |
| 571 | - `--all` now scaffolds MCP + skills + tools + plugins together. |
| 572 | - `--clean` — list `~/.deepseek/sessions/checkpoints/latest.json` and |
| 573 | `offline_queue.json` if they exist. Pass `--force` to actually remove them. |
| 574 | This never touches real session history or the task queue. |
| 575 | |
| 576 | `--status` and `--clean` are mutually exclusive with the scaffold flags. |
| 577 | |
| 578 | ## Why the engine strips XML/`[TOOL_CALL]` text |
| 579 | |
| 580 | DeepSeek TUI sends and receives tool calls only over the API tool channel |
| 581 | (structured `tool_use` / `tool_call` items). The streaming loop in |
| 582 | `crates/tui/src/core/engine.rs` recognizes a fixed set of fake-wrapper start |
| 583 | markers — `[TOOL_CALL]`, `<deepseek:tool_call`, `<tool_call`, `<invoke `, |
| 584 | `<function_calls>` — and scrubs them from visible assistant text without ever |
| 585 | turning them into structured tool calls. When a wrapper is stripped, the loop |
| 586 | emits one compact `status` notice per turn so the user can see why their |
| 587 | visible text shrank. Treat any change that re-enables text-based tool |
| 588 | execution as a regression; the protocol-recovery tests in |
| 589 | `crates/tui/tests/protocol_recovery.rs` lock the contract. |
| 590 |