Surface the existing `deepseek update` self-update command in the
English and Chinese README command listings so users can discover it
without reading source. The `deepseek update` subcommand has shipped
since the dispatcher gained `crates/cli/src/update.rs`.
This integration takes only the README slice from #838. The startup
GitHub-Releases polling check from the same PR is intentionally
deferred until it can be made opt-in / config-backed without adding
unconditional network calls or perceptible startup latency.
Integrates #838.
Co-authored-by: leo119 <leo.hou0924@gmail.com>
Integrates #856 as a focused runtime API security slice.
Default local behavior remains unchanged. `/v1/*` routes require a token only when `--auth-token` or `DEEPSEEK_RUNTIME_TOKEN` is set, and `/health` remains public for readiness checks.
Co-authored-by: Zhuoran Deng <dengzhuoran9@gmail.com>
Integrates #879 safe lockfile dependency updates.
Regenerated the dependency bumps on current main so workspace crates stay on v0.8.15. The Dockerfile edits from #879 were intentionally left out because they pin stale Debian package versions and add an invalid trailing `MD []` instruction.
Co-authored-by: RinZ27 <222222878+RinZ27@users.noreply.github.com>
Integrates the useful custom HTTP header support from #881 onto current main.
- support root, provider-specific, and DEEPSEEK_HTTP_HEADERS overrides
- apply validated extra headers to model API requests while preserving protected Authorization and Content-Type defaults
- document the config shape in README, config.example.toml, and docs/CONFIGURATION.md
Co-authored-by: Desheng <8596814+dst1213@users.noreply.github.com>
@imakid reported on Windows PowerShell that clicking the OS restore
button (maximize → windowed) during a long task turns the entire
terminal black, unrecoverable until Ctrl+C. The "refreshing context"
chip in the footer indicates the freeze coincides with a compaction
summary call (engine state `CoherenceState::RefreshingContext`).
Hypothesis (no Windows dev box, awaiting @imakid confirmation):
* Symptom 1 — "transcript stops refreshing" — is the expected
no-stream window during the side-channel compaction summary call.
Tokens don't stream until the summary returns and the next assistant
turn resumes. That's not a bug, but the UI doesn't currently surface
the distinction well.
* Symptom 2 — "black screen on restore-from-maximized" — most likely
comes from a Windows ConHost transient: `crossterm::terminal::size()`
briefly returns stale (maximized) dimensions during the
maximize→windowed transition, while the `Event::Resize(w, h)`
payload itself already carries the correct post-restore size. The
current handler does `terminal.clear() + terminal.draw()` and
relies on ratatui's internal autoresize, which calls
`crossterm::terminal::size()` — meaning we paint a frame sized to
the stale dimensions into the post-restore viewport, leaving most
of the visible area unpainted (the user-reported black screen).
The change forwards the event-reported `(w, h)` to ratatui via
`Terminal::resize(Rect)` before the clear+draw, so the viewport
commit always uses the OS-truthful new size regardless of whether
`crossterm::terminal::size()` has caught up. This is a defensive
change everywhere — the event payload is authoritative on every
platform — and it specifically addresses the Windows ConHost stale-
size race.
Also widens the resize tracing event to include `coherence_state`
and `use_alt_screen` so the next user bug report includes the
context we'd ask for in triage.
Tests
=====
`chat_widget_renders_cleanly_after_resize_during_refreshing_context`
pins the renderer-side invariant: a resize cycle that arrives while
`coherence_state == RefreshingContext` must produce non-empty frames
at every cycled width, and must not mutate the engine's
coherence_state. The actual fix lives in the event-handler size
forwarding; the test guards the renderer's no-empty-buffer contract
so a future regression that gates layout on coherence state would be
caught immediately.
What this PR does NOT change
============================
* No platform-specific code path. The fix is universal — passing the
event-reported size to ratatui is correct everywhere; Windows just
happens to be where the bug manifests today.
* No change to the freeze symptom directly. If symptom 1 is the
expected compaction-summary no-stream window, the right
follow-up is a UX cue ("compacting context, please hold") rather
than a bugfix.
@imakid — please test by installing this branch:
cargo install --git https://github.com/Hmbown/DeepSeek-TUI.git \\
--branch fix/582-powershell-resize
Then run a long task, click the OS restore button mid-task, and
confirm whether the black-screen symptom is gone. If it still
reproduces, please run with `RUST_LOG=deepseek_tui=debug` and post
the resize lines from the log so we can see the dimensions
crossterm/ratatui actually saw.
Verification
============
* `cargo fmt --all -- --check` clean.
* `cargo clippy -p deepseek-tui --bin deepseek-tui --all-features
--locked -- -D warnings` clean.
* `cargo test -p deepseek-tui --bin deepseek-tui --locked` →
2029 passed, 2 ignored.
Refs #582.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dispatcher's top-level error handler prints `"error: {err}"`,
which is anyhow's bare Display. anyhow's Display only renders the
top-level context message and drops every cause beneath it. Users hit
"failed to parse config at <path>" with zero hint about the actual
TOML error (line/column, expected token, missing quote, BOM, etc.).
This is the gap reported in #767: the OP got
`error: failed to parse config at C:\Users\y1547\.deepseek\config.toml`
with nothing else, while a separate code path that uses a different
formatter shows a rich `Caused by: TOML parse error at line 1, column
20 ...` chain. Maintainer was unable to triage without the underlying
parse error.
Print the full chain by iterating `err.chain().skip(1)` after the
top-level message. Output for the issue's case becomes:
error: failed to parse config at C:\Users\y1547\.deepseek\config.toml
caused by: TOML parse error at line N, column M
| (snippet from toml-rs)
Tests: regression test pins the chain semantics so a future refactor
of the print path can't silently drop causes again.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
JetBrains' JediTerm — the terminal embedded in PyCharm, IDEA, CLion,
WebStorm, GoLand, etc. — advertises mouse support but forwards SGR
mouse-event escape sequences as raw input characters rather than
interpreting them. Users see the composer auto-fill with garbled
characters when they move the mouse over the TUI window. The
workaround was already a one-flag fix (`--no-mouse-capture` or
`[tui] mouse_capture = false` in config) but discovering it required
finding a maintainer comment on a related issue.
Auto-detect via `TERMINAL_EMULATOR=JetBrains-JediTerm` (the env var
JediTerm sets) and default `mouse_capture` off for that environment,
mirroring the existing Windows handling. Explicit `--mouse-capture`
or `[tui] mouse_capture = true` still wins, so power users who don't
hit the issue can opt back in.
Implementation:
- `default_mouse_capture_enabled` now takes `terminal_emulator: Option<&str>`
so the function is pure and trivially testable. The CLI entry point
reads the env var once and passes it through.
- `should_use_mouse_capture` keeps the same public signature; tests
call `should_use_mouse_capture_with` which takes the env explicitly,
removing test sensitivity to the host's actual TERMINAL_EMULATOR.
- Match is `eq_ignore_ascii_case` because JetBrains has occasionally
varied the casing across releases.
Tests:
- 4 new tests covering JetBrains default-off, case-insensitive match,
CLI override, and config-file override.
- Existing 6 mouse-capture tests retained, all passing.
- `cargo test -p deepseek-tui --bin deepseek-tui --all-features
terminal_mode_tests --locked` → 10/10 pass.
- `cargo clippy -p deepseek-tui --bins --all-features --locked --
-D warnings` clean.
- `cargo fmt --all -- --check` clean.
Docs in `docs/MODES.md` and `docs/CONFIGURATION.md` updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When the onboarding flow writes the API key under `~/.deepseek/`, the
parent-dir and config-file chmod are propagated as hard errors if they
fail. On Docker-on-Windows where the container mounts `$USERPROFILE\
.deepseek` to `/home/deepseek/.deepseek`, the bind-mounted NTFS volume
can't accept Unix chmod, so `set_permissions` returns EPERM and the
user sees `Failed to save API key: Failed to set permissions on
/home/deepseek/.deepseek` — even though the directory and the secret
file were already created successfully.
The chmod is a hardening pass: the dir already lives under the user's
home and the file is created with `O_CREAT | mode(0o600)` via
OpenOptions. On filesystems where Unix permissions don't apply at all
(Docker bind-mount of NTFS, network shares, FAT, certain CI volumes),
the host's native ACL model is doing access control regardless. So
demote the chmod to best-effort on Unix: warn loudly via
`tracing::warn!` for security-sensitive operators who run with
`RUST_LOG=warn`, then continue.
Three sites:
- `config::ensure_parent_dir` — parent-dir 0o700 hardening
- `config::write_config_file_secure` — file 0o600 hardening
- `secrets::FileKeyringStore::store_unlocked` — file 0o600 hardening
(the parent-dir chmod here was already best-effort via `let _ =`)
Tests:
- `cargo test -p deepseek-secrets --all-features --locked` → 16/16 pass
(including `file_store_round_trips_with_secure_perms` which still
asserts mode 0o600 on a normal Linux test FS)
- `cargo test -p deepseek-tui --bin deepseek-tui --all-features
save_api_key --locked` → 5/5 pass
- `cargo clippy -p deepseek-tui -p deepseek-secrets --all-features
--locked -- -D warnings` clean
- `cargo fmt --all -- --check` clean
The GitBash paste failure mode reported in the same issue is a terminal
quirk (GitBash on Windows doesn't reliably forward Ctrl+V to TUI apps);
PowerShell + Shift+Insert work, as the reporter discovered. Not in
scope here.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The skill registry already walks workspace-local `.claude/skills` for
Claude Code interop, plus global `~/.agents/skills` and
`~/.deepseek/skills`. Picking up the global `~/.claude/skills` brings
DeepSeek TUI in line with the broader Claude-ecosystem convention so
users can inherit skills installed for other Claude-compatible tools
without re-authoring them in DeepSeek's native layout.
Adds `claude_global_skills_dir()` mirroring `agents_global_skills_dir()`
and inserts it into `skills_directories()` between the agentskills.io
global and the DeepSeek-native global. Workspace candidates still win
on name conflicts; first-match-wins is preserved.
Tests:
- claude_global_skills_dir_returns_home_relative_path
- existing_skill_dirs_orders_globals_agents_then_claude_then_deepseek
- All 55 pre-existing skills tests still pass
Docs synced (README publishing-skills section, CONFIGURATION).
docs/COMPETITIVE_ANALYSIS.md already advertised this lookup; this
brings the implementation in line with the documented contract.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The parent agent's turn ends right after `agent_spawn` returns
`status: Running`, leaving children to finish in the background with no
path back into the parent's inference loop. The model has to be poked
by a human before it resumes the plan.
Wire a wakeup channel from `run_subagent_task` into the engine's turn
loop. When the model produces no more tool calls but direct children
are still running, the loop now blocks on the next completion (with
cancel and steer escape hatches), drains all pending sentinels, and
re-enters inference with the existing `<deepseek:subagent.done>`
elements injected as user messages. This fulfils the contract already
documented in `prompts/base.md` (lines 189-205): the parent is promised
it'll see the sentinel when a child finishes.
The async `agent_spawn` semantics from #239 are preserved — only direct
children fire on the channel (gated by `spawn_depth == 1`), so
grandchildren spawned recursively don't flood the parent.
Accepts SKILL.md files without YAML frontmatter by using the first Markdown H1 heading as the skill name. This lets compatible plain Markdown skill files show up instead of being skipped with parse warnings.\n\nI added a small maintainer test commit to keep the command tests hermetic and cover both the plain-heading fallback and missing-heading warning path.\n\nVerification:\n- cargo test -p deepseek-tui skills --all-features\n- cargo fmt --all -- --check\n- git diff --check xieshutao/main...HEAD\n- cargo clippy -p deepseek-tui --all-targets --all-features -- -D warnings\n- GitGuardian Security Checks passed
Adds a PowerShell Set-Clipboard fallback on Windows when arboard clipboard writes are unavailable, before falling back to OSC 52.\n\nVerification:\n- cargo fmt --all -- --check\n- git diff --check origin/main...HEAD\n- cargo test -p deepseek-tui clipboard --all-features\n- cargo clippy -p deepseek-tui --all-targets --all-features -- -D warnings\n- contributor verified the same focused Windows checks on stable-x86_64-pc-windows-msvc\n\nNote: local x86_64-pc-windows-gnu cross-check is blocked on this macOS machine by missing x86_64-w64-mingw32-gcc.
Prints the canonical resume command after a successful TUI exit when a session id is available.\n\nVerified with local focused tests plus green CI.\n\nCloses #682.
* fix(tui): cache @mention completions to fix cursor lag (#792)
Cache file-mention completion results to avoid re-walking the filesystem
on every keystroke. The cache is invalidated when the partial text after
@, cursor position, or input content changes.
Fixes#792
* fix: keep mention completion cache stable on cursor moves
* style: satisfy mention cache clippy
---------
Co-authored-by: Hunter Bown <hmbown@gmail.com>
* feat(tui): show skills in slash command autocomplete menu
- add SlashMenuEntry struct with name, description, is_skill fields
- cache skill names/descriptions in App to avoid per-keystroke disk I/O
- render slash popup as two-column layout with precise Unicode width truncation
- include skill names in Tab autocomplete and visible_slash_menu_entries
- refresh skill cache after install/uninstall
* style(tui): apply rustfmt to slash menu and widgets
* fix: complete skills with valid slash command form
---------
Co-authored-by: Hunter Bown <hmbown@gmail.com>
Previously, ModelRegistry::resolve() lowercased the requested model name
before looking it up in the alias map, and always returned the registry's
canonical (lowercase) model ID. This broke third-party API providers
that enforce case-sensitive model name matching.
Now when the resolved model ID differs from the requested name only in
case (eq_ignore_ascii_case), the requested casing is preserved.
Closes#729
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
percent_decode in web_run.rs builds the result via `out.push(b as char)`,
mapping each decoded byte to its Latin-1 codepoint. For percent-encoded
multi-byte UTF-8 sequences (e.g. %E4%B8%AA = 个) this produces visible
mojibake: bytes E4 B8 AA become three Latin-1 chars `个` and the final
String is the UTF-8 encoding of those codepoints, not the original
character.
The sister module web_search.rs::percent_decode (line 490) already uses
the correct pattern: collect bytes into Vec<u8>, then finalize with
String::from_utf8_lossy. Align web_run.rs with that implementation.
Affects DuckDuckGo redirect URLs containing non-ASCII paths, where
normalize_search_url -> percent_decode previously corrupted the decoded
URL before it was shown to the model and to the user.
Add a regression test covering percent-encoded CJK input, raw UTF-8
input, and mixed ASCII+UTF-8.
Co-authored-by: Elowen <xrnc@outlook.com>