A community-driven reliability release. Plan-mode safety, paste-Enter
auto-submit, slash-menu skills coverage, the deepseek-cn endpoint
preset, and a handful of platform / streaming / gateway-compat fixes,
plus a small PTY-driven QA harness.
See CHANGELOG.md for the full annotated change list with credits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five rustdoc warnings (all pre-existing or surfaced by recent merges)
that the CI \`RUSTDOCFLAGS=-Dwarnings cargo doc\` gate would have
caught:
- \`backend.rs\`: intra-doc link to \`OpenSandboxBackend\` couldn't
resolve without a use; qualify with the module path.
- \`markdown_render.rs\`: backtick-quote the literal \`[text](url)\`
example so rustdoc doesn't try to follow it as an actual link.
- \`anchor.rs\`: rewrite the \`<text>\` / \`<n>\` placeholder syntax in
doc comments so they aren't parsed as malformed HTML tags.
- \`config.rs\`: wrap the bare api-docs URL in \`<>\` so rustdoc treats
it as a hyperlink instead of warning.
Plus rustfmt cleanup of two recently-merged files (\`commands/config.rs\`,
\`localization.rs\`) where the inserted lines weren't yet through
\`cargo fmt\`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When OpenRouter is pointed at a custom base_url, keep explicit model values verbatim instead of remapping DeepSeek aliases to OpenRouter catalog IDs.
Add config coverage for both the dispatcher config crate and the TUI config loader, while preserving existing provider alias behavior such as NVIDIA NIM.
Closes#857
DeepSeek API returns gzip/brotli compressed SSE responses. Without
auto-decompression enabled, reqwest's rustls backend passed compressed
bytes directly to UTF-8 parsing, causing garbled output () for Chinese
thinking content.
Add "gzip" and "brotli" features to reqwest to enable automatic content
encoding decompression (Accept-Encoding: gzip, br).
Fixes#954
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refactors the thinking-block lifecycle in `run_event_loop` into three named helpers — `start_streaming_thinking_block`, `finalize_current_streaming_thinking`, `stash_reasoning_buffer_into_last_reasoning` — and calls `finalize_current_streaming_thinking` from the engine-error handler so a thinking block that's still active when the stream errors gets drained into the transcript instead of being discarded.
This addresses one of the failure modes in #861 ("thinking collapse — thinking blocks freeze, truncate silently, or drop reasoning_content"): the case where a transient stream error mid-thinking left the partial reasoning orphaned in `StreamingState`.
Thanks to @reidliu41 — extracting the named helpers is the kind of refactor that pays off the next time we have to touch this lifecycle.
When a workspace has no context file anywhere up the parent chain, `load_project_context_with_parents` now writes a short `.deepseek/instructions.md` (project tree + summary) on first launch. This replaces the previous per-turn filesystem-scan fallback in `prompts.rs`, whose output varied turn-to-turn and broke KV prefix-cache stability for the system prompt.
The auto-generated file is plainly labelled and the user can edit or delete it freely; it's only created when there is no existing context file to respect.
Thanks to @lloydzhou — making the cache surface stable through a real on-disk artifact is exactly the right move for prefix-cache hit rate.
Some terminal emulators (macOS Terminal.app, Windows ConHost) briefly report stale dimensions via `crossterm::terminal::size()` after a resize. ratatui's `draw()` calls `autoresize()` internally, queries the backend, and shrinks the viewport back to the stale dimension — leaving the newly-expanded area filled with stale content from the previous frame (the duplicate-panel symptom users have reported).
The fix adds `force_size` / `clear_forced_size` to `ColorCompatBackend` and forces the resize-event size for the post-resize draw, then clears the forcing for subsequent frames. Same class of fix as #582 but covers an additional emulator family.
Thanks to @ArronAI007 for tracking down the autoresize→stale-size→short-buffer interaction.
Slash command toggles between the dark and light `UiTheme` presets without round-tripping through `/config`. Useful for quickly matching the editor or terminal theme.
Thanks to @MengZ-super — small, scoped, exactly the right surface for a quick toggle.
Two snapshot-store improvements that together address #975 (≈30 GB+ disk consumed by stale snapshot temp files):
1. **Startup cleanup** of `tmp_pack_*` files left by interrupted git pack operations — a side-repo crash or sigkill mid-snapshot can leave hundreds of these orphaned in `.deepseek/snapshots/objects/pack/`.
2. **Prune unreachable objects** during the regular snapshot prune cycle so loose objects from rolled-back snapshots don't accumulate.
Closes#975. Thanks to @axobase001 — the 60-minute staleness threshold is the right balance between cleanup eagerness and not interfering with an actively-running git pack.
Prefixes Windows shell commands with `chcp 65001 >/dev/null & ` so subprocess output is UTF-8 instead of the system ANSI code page (e.g. GBK on Chinese-locale machines). The `display_command` helper strips the prefix so transcripts and approval prompts show the original command.
Closes#982. Thanks to @chnjames for the fix and the test update — the prefix-strip in `display_command` is exactly the right symmetric move.
Plan mode's tool context elevated the shell sandbox to `WorkspaceWrite`
with the workspace itself as a writable root. Reads were correct, but a
shell call like `python -c "open('foo','w').write('x')"` succeeded
because the sandbox literally permitted writes inside the workspace —
which is exactly what the user reported in #1077: "Plan mode … uses
shell to run python to change files."
Plan mode is investigation, not modification. Switch to
`SandboxPolicy::ReadOnly`: no writes anywhere on the filesystem, no
network. The model-facing tool surface still includes shell so
read-only commands (`ls`, `git log`, `grep`, `cat`, `cargo metadata`,
…) keep working; the per-platform sandbox (seatbelt on macOS, landlock
on Linux) blocks the rest.
Refactored the per-mode policy decision into
`tool_setup::sandbox_policy_for_mode(mode, workspace)` so it has one
unit-testable home. Updated the network-denied hint to surface the
actual contract ("read-only sandbox — no writes, no network") instead
of the previous wording that only mentioned network.
Tests:
- New `sandbox_policy_for_mode_returns_correct_policy_per_mode` asserts
the helper returns `ReadOnly` / `WorkspaceWrite{network: true}` /
`DangerFullAccess` for Plan / Agent / YOLO respectively.
- Existing `engine_elevates_sandbox_policy_for_mode` test extended to
confirm Plan mode is `ReadOnly` (not just network-denied),
`has_full_disk_write_access()` is false, and `get_writable_roots`
enumerates zero entries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two pieces:
**#1073 fix.** When a paste burst is currently being assembled, or when
the burst's Enter-suppression window is still open after a flush, the
trailing newline of the paste was firing `submit_input()` and the
in-flight burst buffer was getting destroyed by `clear_after_explicit_paste()`.
The PasteBurst module already exposed `newline_should_insert_instead_of_submit`
and `append_newline_if_active` for exactly this case, but no caller had
been wired up. Added `App::handle_composer_enter`, which checks the
suppression state and either appends `\n` to the burst buffer or inserts
it directly into the composer text — no submit. The `KeyCode::Enter`
arm in the composer event loop now dispatches through that helper.
Reproduces the Windows/PowerShell symptom from the report:
multi-line paste ending with `\n` no longer auto-submits AND the text
no longer leaks into the now-empty composer.
Four unit tests cover: active-burst Enter, post-flush window Enter,
normal Enter outside the window, and Enter with paste-burst detection
disabled (suppression must be off).
**PTY QA harness.** New `crates/tui/tests/support/qa_harness/` wraps
`portable-pty` (already a runtime dep) and `vt100` (new dev-dep) into
a small surface for scenarios that need a real PTY: spawn a binary,
send keys/paste/resize, parse the ANSI stream into a frame, assert
on visible text + filesystem state. The harness seals `$HOME` so
scenarios cannot read the developer's real `~/.deepseek/` and points
the base URL at 127.0.0.1:1 so no live request escapes. README under
`support/qa_harness/README.md` documents how to add a scenario.
Initial scenarios in `crates/tui/tests/qa_pty.rs`: smoke boot,
keystroke round-trip, and bracketed/unbracketed paste-with-trailing-
newline regression guards for #1073. The unbracketed scenario does
not deterministically reproduce the bug on macOS (single-syscall
PTY writes keep the burst continuously active), but the unit tests
above cover the path conclusively; the PTY test stands as a
regression guard for the visible-text invariant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the close-by-default admission rules with a find-value-first
posture. The new section in AGENTS.md says: every contribution has
value somewhere; harvest commits/files/ideas yourself rather than
asking contributors to split or resubmit; always credit; keep the
trust boundary on sandbox/providers/publishing/global-prompts but
own the work of getting there.
Bad-faith / prompt-injection contributions: close and block.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sets the `deepseek-cn` provider preset's default `base_url` to the official host (`https://api.deepseek.com`) per [api-docs.deepseek.com](https://api-docs.deepseek.com/). Keeps recognizing `api.deepseeki.com` in URL heuristics and chat-client normalization so existing configs continue to work, and updates the `doctor` strict-tool-mode endpoint hint, docs, and examples accordingly.
Closes#1079. Thanks to @Jefsky for the fix.
Aligns the slash-menu cache with `discover_in_workspace`, so global `~/.deepseek/skills` and `~/.claude/skills` skills appear alongside project-local `.agents/skills` in the slash menu, `/skills` listing, and `/skill <name>` lookups. Also strips surrounding YAML quotes from `SKILL.md` frontmatter values, so a skill declared as `name: "hud"` registers as `hud` and matches prefix lookup.
Closes#1068. Thanks to @AlphaGogoo / @Duducoco for the fix and the test.
Summary:
- Keep default auto alternate-screen mode inside the TUI so transcript scrolling stays app-owned unless users explicitly opt out.
- Queue terminal resume events when the engine channel is full, avoiding stranded paused terminal state after interactive tool cancellation or bursts.
- Scope crash-checkpoint recovery to the resolved launch workspace instead of the shell cwd.
- Add runtime deepseek_version to the prompt environment block so agents can distinguish installed runtime identity from a stale checkout.
Test plan:
- cargo test -p deepseek-tui --locked on a simulated merge with current main
- cargo fmt --all -- --check
- git diff --check
- Existing PR CI was green for lint, version drift, Linux/macOS/Windows tests, npm wrapper smoke, and GitGuardian.
Summary:
- Use Reverse for job timestamp sorting to avoid negation overflow edge cases.
- Make secret redaction UTF-8 safe while preserving the previous short-secret threshold.
- Update remaining setup and doctor guidance to use the supported deepseek dispatcher name.
Test plan:
- cargo test -p deepseek-config list_values_redacts --locked
- cargo test -p deepseek-core --locked
- cargo test -p deepseek-tui doctor_endpoint_tests --locked
- cargo fmt --all -- --check
- git diff --check
Supersedes #957.
Summary:
- Stop treating -v as an npm wrapper version fallback.
- Keep wrapper fallback for --version and -V.
- Add a Node regression test for wrapper version flag detection.
Test plan:
- npm test from npm/deepseek-tui
- git diff --check origin/main...HEAD
Supersedes #959.
Summary:
- Add a mouse-only jump-to-latest affordance when the transcript is scrolled away from the live tail.
- Hide the control at the tail or when mouse capture is disabled.
- Add tests for visibility and click behavior.
Maintainer verification on current origin/main:
- cargo test -p deepseek-tui jump_to_latest --locked
- cargo fmt --all -- --check
- git diff --check origin/main...HEAD
Closes#971
Summary:
- Truncate oversized memory prompt content at the previous valid UTF-8 character boundary.
- Preserve the existing memory cap and truncation marker behavior.
- Add regression coverage for accented and emoji content crossing the byte cutoff.
Maintainer verification on current origin/main:
- cargo test -p deepseek-tui memory --locked
- cargo fmt --all -- --check
- git diff --check origin/main...HEAD
Summary:
- Treat Ctrl+H as Backspace in the help overlay filter.
- Document the terminal erase behavior inline.
- Add regression coverage for the help filter widening after Ctrl+H.
Maintainer verification on current origin/main:
- cargo test -p deepseek-tui ctrl_h_widens_match_set --locked
- cargo fmt --all -- --check
- git diff --check origin/main...HEAD
Fixes#958
Summary:
- Skip empty auto-created sessions when continuing a workspace.
- Recover a saved session ending in an unanswered user prompt as an editable draft instead of auto-resubmitting it.
- Preserved and repaired the local Qthresh poisoned session records by moving them to ~/.deepseek/sessions/recovery-backups/qthresh-2026-05-07-auto-poison.
Test plan:
- cargo test -p deepseek-tui latest_session_for_workspace_skips_empty_auto_created_session --locked
- cargo test -p deepseek-tui apply_loaded_session_restores_dangling_user_tail_as_retry_draft --locked
- cargo test -p deepseek-tui --locked
- cargo fmt --all -- --check
- git diff --check
- git diff --check origin/main...HEAD
- cargo clippy -p deepseek-tui --all-targets --all-features --locked -- -D warnings
Closes#988
Closes #944\n\n## Summary\n- mark Docker/GHCR publishing as experimental while the package is not publicly readable\n- align installer and release docs with the live npm/Scoop state\n- keep package-channel verification explicit for release triage\n\n## Test plan\n- ruby -e 'require "yaml"; YAML.load_file(".github/workflows/release.yml"); puts "release.yml ok"'\n- cargo test -p deepseek-tui-cli update::tests::test_asset_matching_accepts_binary_assets_and_rejects_checksums --locked\n- cargo fmt --all -- --check\n- git diff --check origin/main...HEAD\n- CI: Version drift, Lint, Test (ubuntu-latest), Test (macos-latest), Test (windows-latest), npm wrapper smoke
Closes#899.
Detects light terminal profiles from COLORFGBG, keeps the existing dark theme as the fallback, and maps DeepSeek dark-palette cells to readable light surfaces in the existing ColorCompatBackend.
Local verification:
- cargo fmt --all -- --check
- cargo test -p deepseek-tui palette --all-features
- cargo test -p deepseek-tui color_compat --all-features
- cargo build
CI: all required checks passed on #931.
Integrates source PR #525 by @shentoumengxin.
Adds `/anchor` for critical user facts that should survive compaction via `.deepseek/anchors.md`. Keeps `/pin` unregistered so the resident-context `/pin` lane remains available, and renders anchors as structured bullets in compaction summaries instead of raw separator text.
Local verification:
- cargo fmt --all -- --check
- cargo test -p deepseek-tui anchor --all-features
- cargo build
CI: all required checks passed on #930.
Co-authored-by: ZZHAsus <3075047037@qq.com>