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>
* feat(tui): add `notification_condition` override + assistant text in body (#820)
`[notifications]` already controls method (auto/osc9/bel/off), the
`threshold_secs` gate, and the `include_summary` body. Some users
want a simpler high-level switch — "always notify on every turn" or
"never notify" — without having to know the lower-level fields.
This adds a single optional `[tui].notification_condition` field:
- `"always"` — notify on every successful turn (no duration
threshold). The configured `[notifications].method` and
`include_summary` flag are still respected.
- `"never"` — suppress all turn-completion notifications.
- omitted — fall back to the existing `[notifications]` defaults
(the v0.8.15 behavior is unchanged).
The OSC 9 / BEL body now also carries the assistant's reply text,
sanitized and truncated to 360 characters, with a fallback to the
latest assistant message in `api_messages` when the streaming buffer
was empty (e.g. a tool-only turn). When `include_summary = true`,
the elapsed/cost line is appended on a new line.
Drive-by: drop the unused `Method::from_str` helper (the new code
match-arms over the typed `NotificationMethod` enum, so the parser
helper had no remaining callers).
Differences from upstream #820:
- Keeps the `[notifications]` section in `config.example.toml`
(with `notification_condition` documented as an opt-in override)
rather than deleting the existing block. This avoids breaking
configs that already set `[notifications].method` etc.
- Drops the unrelated `#[allow(dead_code)]` on
`schema_migration::registry`.
- Threads `Option<CostEstimate>` through the helper (the cost
surface changed from `Option<f64>` since #820 was authored).
Tests:
- `notification_settings_*` (3) — `always` keeps the configured
method, `never` returns `None`, missing override falls back.
- `completed_turn_notification_*` (4) — streaming text wins, falls
back to latest assistant message, default placeholder, and 360-char
truncation with `...`.
Integrates #820.
Co-authored-by: zero <1603852@qq.com>
Co-authored-by: zerx-lab <161401688+zerx-lab@users.noreply.github.com>
* style: fmt — collapse short test message helper calls
---------
Co-authored-by: zero <1603852@qq.com>
Co-authored-by: zerx-lab <161401688+zerx-lab@users.noreply.github.com>
Replace the model's first-message language detection with a
deterministic `## Environment` block at the top of the workspace-
static portion of the system prompt. The block lists:
- lang: resolved BCP-47 tag (`en`, `zh-Hans`, `ja`, `pt-BR`)
- platform: `std::env::consts::OS`
- shell: `$SHELL` (or `unknown`)
- pwd: workspace path
`base.md`'s `## Language` directive now points the model at the
`lang` field instead of asking it to guess from the user's first
turn. When `lang` is missing or ambiguous the existing detect-from-
writing fallback still applies.
The block is injected at "step 2.25" in the prompt builder — after
mode prompt + project context, before the configured `instructions`
files and the skills section — so it lives in the same workspace-
static cache layer as the surrounding blocks. All four inputs
(locale tag, platform, shell, pwd) are resolved once per session
and stay byte-stable across turns, preserving prefix-cache hits.
`render_environment_block` is intentionally I/O-free: callers pass
the resolved locale tag in `PromptSessionContext.locale_tag` /
`EngineConfig.locale_tag`. `resolve_locale(...)` is invoked once
when constructing the engine config in `tui::ui`, `runtime_threads`,
and `run_exec_agent`.
Tests:
- `render_environment_block_lists_supplied_locale_and_workspace`
- `environment_block_is_inserted_into_system_prompt`
Verified:
- `cargo test -p deepseek-tui --bin deepseek-tui` (2224 passed)
- `cargo test -p deepseek-tui-core --test snapshot --locked`
- `cargo clippy -p deepseek-tui --all-targets -- -D warnings`
- `cargo fmt --all -- --check`
Integrates #813.
Co-authored-by: lloydzhou <lloydzhou@qq.com>