05a1032e00
Inject LSP diagnostics as a synthetic user message after every successful file edit (`edit_file`, `apply_patch`, `write_file`) so the agent sees compile breaks before its next reasoning step. Largest agent-quality lever in v0.7.0. Pieces: - `crates/tui/src/lsp/`: thin JSON-RPC stdio client (no `tower-lsp`), per-language registry, diagnostics renderer producing the `<diagnostics file="…">` block format. `LspManager` owns lazily spawned per-language transports keyed by `Language`. - `core/engine.rs`: hook on the success branch of the tool-result loop derives the edited file path(s) per tool, queries the LspManager with a 5 s timeout, and collects rendered blocks into `pending_lsp_blocks`. The queue is flushed as a `text` content block on the next request iteration so the model sees the diagnostics before it streams its next turn. - `[lsp]` config schema (`enabled`, `poll_after_edit_ms`, `max_diagnostics_per_file`, `include_warnings`, optional `servers` override) with built-in defaults for rust-analyzer, gopls, pyright, typescript-language-server, and clangd. - Failure modes are non-blocking by design: a missing LSP binary logs a one-time warning and skips the hook; a crashed server or poll timeout simply drops that turn's diagnostics. The agent's work is never blocked. Tests: 24 unit tests cover language detection, registry overrides, filter/sort/truncate behavior, and the rendered block format. Three engine-level tokio tests exercise the full path through a fake transport (no real LSP server is ever spawned in CI). Acceptance criteria (per #136): - Edit introducing a type error -> next request body contains `<diagnostics file="…">` block at the right line/col. - `[lsp] enabled = false` -> no diagnostics injected. - Snapshot test exercises full path with mock transport. - LSP binary not on PATH -> one-time warning, agent proceeds. - 5 s timeout, errors-only by default. - Transports spawn lazily on first edit per language. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
264 lines
17 KiB
TOML
264 lines
17 KiB
TOML
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
# ║ 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.deepseek]` and `[providers.nvidia_nim]` sections near the bottom of
|
|
# this file — keeping both stored at once means `/provider deepseek` and
|
|
# `/provider nvidia-nim` (or `--provider nvidia-nim`) 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
|
|
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
|
|
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"
|
|
|
|
# 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 = 5 # optional (1-20)
|
|
|
|
# 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)
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# Both providers can be stored at once; `provider = "..."` (top of file) or
|
|
# `/provider deepseek` / `/provider nvidia-nim` 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
|
|
|
|
# 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
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# 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
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# TUI
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
[tui]
|
|
alternate_screen = "auto" # auto | always | never
|
|
mouse_capture = true # true keeps wheel scrolling inside the TUI; false allows terminal-native drag selection/copy
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# 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.
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# Capacity Controller (runtime pressure guardrails)
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
[capacity]
|
|
enabled = true
|
|
low_risk_max = 0.34
|
|
medium_risk_max = 0.62
|
|
severe_min_slack = -0.25
|
|
severe_violation_ratio = 0.40
|
|
refresh_cooldown_turns = 2
|
|
replan_cooldown_turns = 5
|
|
max_replay_per_turn = 1
|
|
min_turns_before_guardrail = 2
|
|
profile_window = 8
|
|
deepseek_v3_2_chat_prior = 3.9
|
|
deepseek_v3_2_reasoner_prior = 4.1
|
|
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 finishes and took longer
|
|
# than `threshold_secs`. Useful when you tab away from the TUI and want an alert.
|
|
#
|
|
# method = "auto" # auto | osc9 | bel | off
|
|
# auto: OSC 9 for iTerm.app / Ghostty / WezTerm, BEL otherwise.
|
|
# 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
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# 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.
|
|
#
|
|
# [hooks]
|
|
# enabled = true
|
|
# default_timeout_secs = 30
|
|
#
|
|
# [[hooks.hooks]]
|
|
# event = "session_start"
|
|
# command = "echo 'DeepSeek TUI session started'"
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# Requirements (admin constraints) example file
|
|
# ─────────────────────────────────────────────────────────────────────────────────
|
|
# allowed_approval_policies = ["on-request", "untrusted", "never"]
|
|
# allowed_sandbox_modes = ["read-only", "workspace-write"]
|