Files
codewhale/config.example.toml
T
Hunter Bown a68c8dc974 docs(notifications): only completed turns notify; add Key Reference + WezTerm-on-Windows test
Post-merge review feedback on #583 surfaced four small accuracy gaps:

1. The narrative docs in `docs/CONFIGURATION.md` and the inline comment
   in `config.example.toml` said the notification fires "when a turn
   takes longer than a threshold" — but the call site in
   `tui/ui.rs:928` is gated on `TurnOutcomeStatus::Completed`. Failed
   and cancelled turns are silent on purpose. Spell that out so users
   don't expect alerts on long failures.

2. The `notify_done` rustdoc still summarised `Auto` as "Osc9 for known
   terminals, Bel otherwise" — internally inconsistent with the new
   Windows-aware fallback documented one screen earlier on the
   `Method::Auto` enum and on `resolve_method`. Update the public
   rustdoc to point at the canonical resolution table on
   `resolve_method` and call out the `Off`-on-Windows branch.

3. The `## Key Reference` list in `docs/CONFIGURATION.md` had no entries
   for `[notifications].method`, `[notifications].threshold_secs`, or
   `[notifications].include_summary`. Other features with a dedicated
   subsection (e.g. `[memory].enabled`) are listed there too, so readers
   scanning the canonical key list could not discover the notification
   knobs. Added the three keys with cross-references to the
   Notifications subsection.

4. The Windows-only test only covered the unknown-`TERM_PROGRAM` →
   `Off` fallback. The positive path (known OSC-9 terminal still
   resolves to `Osc9`) was only tested via `iTerm.app`, which is a
   macOS-only program — Windows CI would still pass if the `WezTerm`
   arm of the match disappeared. Added
   `auto_detect_picks_osc9_for_wezterm_on_windows` so the
   WezTerm-on-Windows compatibility guarantee is exercised on the
   Windows runner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 13:38:21 -05:00

417 lines
26 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ DeepSeek TUI Configuration ║
# ║ ║
# ║ Unofficial CLI for DeepSeek Platform - Not affiliated with DeepSeek Inc. ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
# See `docs/CONFIGURATION.md` for how config is loaded (profiles, env overrides, etc.).
# ─────────────────────────────────────────────────────────────────────────────────
# Active provider + DeepSeek defaults
# ─────────────────────────────────────────────────────────────────────────────────
# Choose which provider to use by default. Per-provider credentials live in the
# `[providers.*]` sections near the bottom of
# this file — keeping both stored at once means `/provider deepseek` and
# `/provider nvidia-nim` (or `--provider fireworks`, `/provider sglang`) toggle without having to
# re-enter keys. Top-level `api_key` / `base_url` are still read as DeepSeek
# defaults when `[providers.deepseek]` is absent (backward compatibility).
provider = "deepseek" # deepseek | nvidia-nim | openrouter | novita | fireworks | sglang
api_key = "YOUR_DEEPSEEK_API_KEY" # must be non-empty
base_url = "https://api.deepseek.com"
# base_url = "https://api.deepseeki.com" # China users
# base_url = "https://api.deepseek.com/beta" # DeepSeek beta features such as strict tool mode
# ─────────────────────────────────────────────────────────────────────────────────
# Default Models
# ─────────────────────────────────────────────────────────────────────────────────
# DeepSeek V4 family:
# deepseek-v4-pro — flagship reasoning model on DeepSeek Platform
# deepseek-v4-flash — fast, cost-efficient (legacy aliases: deepseek-chat, deepseek-reasoner)
# deepseek-ai/deepseek-v4-pro — NVIDIA NIM-hosted Pro model ID
# deepseek-ai/deepseek-v4-flash — NVIDIA NIM-hosted Flash model ID
# accounts/fireworks/models/deepseek-v4-pro — Fireworks AI Pro model ID
# deepseek-ai/DeepSeek-V4-Pro — SGLang self-hosted Pro model ID
# deepseek-ai/DeepSeek-V4-Flash — SGLang self-hosted Flash model ID
default_text_model = "deepseek-v4-pro"
# ─────────────────────────────────────────────────────────────────────────────────
# Thinking Mode (DeepSeek V4 reasoning effort)
# ─────────────────────────────────────────────────────────────────────────────────
# "off" — disables chain-of-thought (thinking.type = disabled)
# "low" — compat-maps to "high" server-side
# "medium" — compat-maps to "high" server-side
# "high" — reasoning_effort = high (DeepSeek default)
# "max" — reasoning_effort = max (deepest reasoning)
#
# Shift+Tab in the TUI cycles between off / high / max. The header shows the
# current tier as a ⚡ chip.
reasoning_effort = "max"
# ─────────────────────────────────────────────────────────────────────────────────
# Paths
# ─────────────────────────────────────────────────────────────────────────────────
skills_dir = "~/.deepseek/skills"
mcp_config_path = "~/.deepseek/mcp.json"
notes_path = "~/.deepseek/notes.txt"
memory_path = "~/.deepseek/memory.md"
# instructions = ["./AGENTS.md", "~/.deepseek/global.md"]
#
# Optional list of additional instruction files concatenated into the
# system prompt in declared order (#454). Useful for layering
# repo-specific rules on top of a global preferences file. Each entry
# is expanded so `~` and env vars work; missing files are skipped with
# a tracing warning. Files are capped at 100 KiB per entry.
#
# Project-level config (.deepseek/config.toml in the workspace) replaces
# the user-level array wholesale rather than merging — list `~/global.md`
# inside the project array if you want both. An explicit empty array
# (`instructions = []`) clears the user list for the current repo.
# ─────────────────────────────────────────────────────────────────────────────────
# User memory (#489) — opt-in. When enabled, the TUI reads memory_path on
# startup and injects its contents into the system prompt as a
# <user_memory> block, intercepts `# foo` typed in the composer to append
# the line as a timestamped bullet, and registers a `remember` tool the
# model can call to add durable notes itself.
# ─────────────────────────────────────────────────────────────────────────────────
[memory]
# enabled = true # turn the feature on (default: false)
# Override the env-var equivalent: `DEEPSEEK_MEMORY=on`
# Parsed but currently unused (reserved for future versions):
# tools_file = "./tools.json"
# ─────────────────────────────────────────────────────────────────────────────────
# Security
# ─────────────────────────────────────────────────────────────────────────────────
allow_shell = true
approval_policy = "on-request" # on-request | untrusted | never
sandbox_mode = "workspace-write" # read-only | workspace-write | danger-full-access | external-sandbox
# auto_allow entries match by command prefix, not raw string.
# See command_safety.rs for the prefix dictionary.
#
# Examples:
# auto_allow = ["git status"] # auto-approves: git status, git status -s, git status --porcelain
# # does NOT auto-approve: git push, git checkout
# auto_allow = ["cargo check", "npm run"]
#
# auto_allow = []
max_subagents = 10 # optional (1-20)
# Optional sub-agent tuning. max_concurrent overrides top-level max_subagents.
# [subagents]
# max_concurrent = 10
# Optional managed policy paths (defaults to /etc/deepseek/*.toml on unix):
# managed_config_path = "/etc/deepseek/managed_config.toml"
# requirements_path = "/etc/deepseek/requirements.toml"
# ─────────────────────────────────────────────────────────────────────────────────
# Per-provider credentials (peer providers — NIM is first-class, not a flag)
# ─────────────────────────────────────────────────────────────────────────────────
# Providers can be stored at once; `provider = "..."` (top of file) or
# `/provider deepseek` / `/provider nvidia-nim` / `/provider fireworks` switches between them without
# having to re-enter keys. Env vars override anything set here:
# DeepSeek: DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL
# NIM: NVIDIA_API_KEY (or NVIDIA_NIM_API_KEY), NIM_BASE_URL
# (or NVIDIA_NIM_BASE_URL / NVIDIA_BASE_URL), NVIDIA_NIM_MODEL
# Fireworks: FIREWORKS_API_KEY, FIREWORKS_BASE_URL
# SGLang: SGLANG_BASE_URL, SGLANG_MODEL, optional SGLANG_API_KEY
# DeepSeek Platform (https://platform.deepseek.com)
[providers.deepseek]
# api_key = "YOUR_DEEPSEEK_API_KEY"
# base_url = "https://api.deepseek.com"
# model = "deepseek-v4-pro"
# NVIDIA NIM-hosted DeepSeek V4 (https://build.nvidia.com)
[providers.nvidia_nim]
# api_key = "YOUR_NVIDIA_API_KEY"
# base_url = "https://integrate.api.nvidia.com/v1"
# model = "deepseek-ai/deepseek-v4-pro" # or deepseek-ai/deepseek-v4-flash
# Fireworks AI-hosted DeepSeek V4 (https://fireworks.ai)
[providers.fireworks]
# api_key = "YOUR_FIREWORKS_API_KEY"
# base_url = "https://api.fireworks.ai/inference/v1"
# model = "accounts/fireworks/models/deepseek-v4-pro"
# Self-hosted SGLang OpenAI-compatible server
[providers.sglang]
# api_key = "OPTIONAL_SGLANG_TOKEN"
# base_url = "http://localhost:30000/v1"
# model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
# ─────────────────────────────────────────────────────────────────────────────────
# Network Policy (#135)
# ─────────────────────────────────────────────────────────────────────────────────
# Per-domain allow/deny rules for outbound network calls made by the TUI's
# tools (`fetch_url`, `web_search`) and the MCP HTTP transport. Stdio MCP
# servers and direct LLM API calls are unaffected.
#
# Precedence: deny wins. A host listed in both `allow` and `deny` is denied.
#
# Host-matching rules:
# - Exact match: `api.deepseek.com` matches only `api.deepseek.com`.
# - Subdomain wildcard: an entry starting with `.` (e.g. `.example.com`)
# matches `api.example.com` and `a.b.example.com` but not the apex
# `example.com`. To cover both, list both. `*.example.com` is also accepted.
#
# Defaults are intentionally conservative: when this section is absent, no
# policy is enforced (mirrors pre-v0.7.0 behavior). To opt in:
#
# [network]
# default = "prompt" # allow | deny | prompt
# allow = ["api.deepseek.com", "github.com", ".githubusercontent.com"]
# deny = []
# audit = true # one line per call to ~/.deepseek/audit.log
# ─────────────────────────────────────────────────────────────────────────────────
# Skills (#140)
# ─────────────────────────────────────────────────────────────────────────────────
# Settings for the `/skill install <spec>` community-skill installer.
# * registry_url — curated index.json that resolves bare names to
# `github:owner/repo` specs. Override to point at
# a private fork or internal mirror.
# * max_install_size_bytes — per-skill uncompressed size cap. Tarballs that
# exceed this limit are rejected during validation.
# Default: 5 MiB.
#
# `/skill install` is gated by `[network]`. Make sure `github.com` and
# `raw.githubusercontent.com` are reachable (default `prompt` is fine — you'll
# be asked once and can persist) before running it.
#
# [skills]
# registry_url = "https://raw.githubusercontent.com/Hmbown/deepseek-skills/main/index.json"
# max_install_size_bytes = 5_242_880
# ─────────────────────────────────────────────────────────────────────────────────
# TUI
# ─────────────────────────────────────────────────────────────────────────────────
[tui]
alternate_screen = "auto" # auto | always | never
mouse_capture = true # true copies only transcript user/assistant text; false uses raw terminal selection/copy
terminal_probe_timeout_ms = 500 # optional startup terminal-mode timeout (100-5000ms)
osc8_links = true # emit OSC 8 escapes around URLs (Cmd+click in iTerm2/Ghostty/Kitty/WezTerm/Terminal.app 13+); set false for terminals that misrender
# ─────────────────────────────────────────────────────────────────────────────────
# Feature Flags
# ─────────────────────────────────────────────────────────────────────────────────
[features]
shell_tool = true
subagents = true
web_search = true # enables canonical web.run plus the compatibility web_search alias
apply_patch = true
mcp = true
exec_policy = true
# ─────────────────────────────────────────────────────────────────────────────────
# Retry Configuration
# ─────────────────────────────────────────────────────────────────────────────────
[retry]
enabled = true
max_retries = 3
initial_delay = 1.0
max_delay = 60.0
exponential_base = 2.0
# ─────────────────────────────────────────────────────────────────────────────────
# Context Compaction
# ─────────────────────────────────────────────────────────────────────────────────
# Auto-compaction is a saved UI setting edited with `/config` (`auto_compact`).
# There is no config-file `[compaction]` table yet; detailed thresholds are
# chosen by the TUI from the active model/context budget.
# Append-only Flash seams are experimental and opt-in while the v0.7.5
# context/cache audit validates prefix-cache behavior.
[context]
enabled = false
verbatim_window_turns = 16
# Thresholds are based on the active request input estimate, not lifetime
# summed API usage.
l1_threshold = 192000
l2_threshold = 384000
l3_threshold = 576000
# Hard cycle also reserves the normal 262144-token output budget plus 1024
# safety tokens against the model window.
cycle_threshold = 768000
seam_model = "deepseek-v4-flash"
# ─────────────────────────────────────────────────────────────────────────────────
# Capacity Controller (runtime pressure guardrails)
# ─────────────────────────────────────────────────────────────────────────────────
[capacity]
enabled = false
low_risk_max = 0.50
medium_risk_max = 0.62
severe_min_slack = -0.25
severe_violation_ratio = 0.40
refresh_cooldown_turns = 6
replan_cooldown_turns = 5
max_replay_per_turn = 1
min_turns_before_guardrail = 4
profile_window = 8
deepseek_v3_2_chat_prior = 3.9
deepseek_v3_2_reasoner_prior = 4.1
deepseek_v4_pro_prior = 3.5
deepseek_v4_flash_prior = 4.2
fallback_default_prior = 3.8
# ─────────────────────────────────────────────────────────────────────────────────
# Profile Example (for multiple environments)
# ─────────────────────────────────────────────────────────────────────────────────
# Select a profile with `deepseek --profile <name>` or `DEEPSEEK_PROFILE=<name>`.
[profiles.work]
api_key = "WORK_DEEPSEEK_API_KEY"
base_url = "https://api.deepseek.com"
[profiles.dev]
api_key = "DEV_DEEPSEEK_API_KEY"
allow_shell = true
[profiles.nvidia-nim]
provider = "nvidia-nim"
api_key = "YOUR_NVIDIA_API_KEY"
base_url = "https://integrate.api.nvidia.com/v1"
default_text_model = "deepseek-ai/deepseek-v4-pro"
# ─────────────────────────────────────────────────────────────────────────────────
# Desktop Notifications (OSC 9 / BEL on long agent-turn completion)
# ─────────────────────────────────────────────────────────────────────────────────
# Emits an escape sequence to the terminal when a turn **completes successfully**
# and took longer than `threshold_secs`. Failed or cancelled turns are
# intentionally silent. Useful when you tab away from the TUI and want an alert
# for "your task is ready".
#
# method = "auto" # auto | osc9 | bel | off
# auto: OSC 9 for iTerm.app / Ghostty / WezTerm.
# On macOS / Linux, falls back to BEL.
# On Windows, falls back to "off" — BEL maps to the
# system error chime (SystemAsterisk / MB_OK), which
# sounds like an error popup. Set method = "bel"
# explicitly to opt back in (#583).
# osc9: \x1b]9;<msg>\x07 (iTerm2-style; shows macOS notification)
# bel: plain \x07 beep
# off: disable entirely
# threshold_secs = 30 # only notify when the turn took >= this many seconds
# include_summary = false # include elapsed time + cost in the notification body
[notifications]
# method = "auto"
# threshold_secs = 30
# include_summary = false
# ─────────────────────────────────────────────────────────────────────────────────
# Workspace Snapshots (#137)
# ─────────────────────────────────────────────────────────────────────────────────
# Each turn the TUI takes a `pre-turn:<seq>` and `post-turn:<seq>` snapshot of
# your workspace into a side-git repo at:
#
# ~/.deepseek/snapshots/<project_hash>/<worktree_hash>/.git
#
# Your own `.git` is never touched — `--git-dir` and `--work-tree` are always
# set together when shelling out to git. Use `/restore N` (slash command) or
# the `revert_turn` tool to roll the working tree back. Conversation history
# is unaffected.
#
# Disk footprint: ~1-2 GB worst case for a 100 MB workspace × 12 turns/day,
# typically far less thanks to git's content-addressed storage. The session
# boot prunes anything older than `max_age_days` (default 7).
#
# [snapshots]
# enabled = true # Snapshot workspace pre/post each turn for /restore
# max_age_days = 7 # Older snapshots pruned at session start
# ─────────────────────────────────────────────────────────────────────────────────
# LSP Diagnostics (post-edit) (#136)
# ─────────────────────────────────────────────────────────────────────────────────
# After every successful file edit (`edit_file`, `apply_patch`, `write_file`),
# the engine asks an LSP server for diagnostics on the file and injects them
# as a synthetic system message before the next API call. This lets the agent
# see compile breaks immediately without round-tripping through the user.
#
# Enabled by default. Failure modes are non-blocking: a missing LSP binary,
# a crashed server, or a timeout simply skips the post-edit hook for that
# turn — the agent's work is never blocked.
#
# Built-in language → server defaults:
# rust → rust-analyzer
# go → gopls serve
# python → pyright-langserver --stdio
# typescript → typescript-language-server --stdio
# c, cpp → clangd
#
# Override the defaults via the `servers` table below.
[lsp]
# enabled = true
# poll_after_edit_ms = 5000
# max_diagnostics_per_file = 20
# include_warnings = false
# [lsp.servers]
# rust = ["rust-analyzer"]
# go = ["gopls", "serve"]
# ─────────────────────────────────────────────────────────────────────────────────
# Hooks (optional)
# ─────────────────────────────────────────────────────────────────────────────────
# Hooks run shell commands on lifecycle events (session start/end, tool calls, etc.).
# Configure as `[[hooks.hooks]]` under a `[hooks]` table.
#
# Available events: session_start, session_end, message_submit,
# tool_call_before, tool_call_after, mode_change, on_error, shell_env.
#
# `shell_env` (#456) is special: the hook runs immediately before each
# `exec_shell` invocation and its stdout is parsed as `KEY=VALUE\n` lines.
# Those vars are merged into the spawned process environment (later hooks
# override earlier ones). Use this for ephemeral credentials, per-skill
# PATH adjustments, or short-lived tokens. The resolved KEY names (NEVER
# values) are written to `~/.deepseek/audit.log` so each session can be
# reconciled later. Hook failure / timeout simply contributes no vars —
# it does not abort the shell call.
#
# [hooks]
# enabled = true
# default_timeout_secs = 30
#
# [[hooks.hooks]]
# event = "session_start"
# command = "echo 'DeepSeek TUI session started'"
#
# # Inject ephemeral creds into every shell call. Output one
# # KEY=VALUE per line on stdout (export prefix optional).
# [[hooks.hooks]]
# name = "aws-creds"
# event = "shell_env"
# command = "aws-vault export my-profile --format=env"
# # Optionally limit to specific tool names / categories:
# # condition = { type = "tool_category", category = "shell" }
# ─────────────────────────────────────────────────────────────────────────────────
# Runtime API (`deepseek serve --http`) (#561)
# ─────────────────────────────────────────────────────────────────────────────────
# Tuning knobs for the local HTTP/SSE daemon. The server binds to 127.0.0.1
# by default and is intended for local UIs (whalescale-desktop, dashboards,
# automation scripts). Today this section only controls the CORS allow-list;
# host/port/workers stay on `--host`, `--port`, and `--workers` flags.
#
# Built-in defaults always include:
# http://localhost:3000 http://127.0.0.1:3000
# http://localhost:1420 http://127.0.0.1:1420
# tauri://localhost
#
# Use `cors_origins` to add extra dev origins (e.g. Vite's default `:5173`).
# User entries STACK on top of the defaults — they do not replace them. The
# CLI flag `--cors-origin URL` (repeatable) and env var
# `DEEPSEEK_CORS_ORIGINS=url1,url2` resolve to the same merged list.
#
# [runtime_api]
# cors_origins = ["http://localhost:5173", "http://127.0.0.1:5173"]
# ─────────────────────────────────────────────────────────────────────────────────
# Requirements (admin constraints) example file
# ─────────────────────────────────────────────────────────────────────────────────
# allowed_approval_policies = ["on-request", "untrusted", "never"]
# allowed_sandbox_modes = ["read-only", "workspace-write"]