返回 DeepSeek-TUI-2026
CONFIGURATION.md
根目录 / docs / CONFIGURATION.md
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
590 lines MARKDOWN