Skip to content

chore(release): prepare v0.8.40#1823

Merged
Hmbown merged 38 commits into
mainfrom
work/v0.8.40-stability
May 21, 2026
Merged

chore(release): prepare v0.8.40#1823
Hmbown merged 38 commits into
mainfrom
work/v0.8.40-stability

Conversation

@Hmbown
Copy link
Copy Markdown
Owner

@Hmbown Hmbown commented May 20, 2026

v0.8.40 - Stability & Recovery

A focused stability release for the v0.8.38/v0.8.39 fallout: provider/model correctness, cross-platform terminal reliability, sub-agent/session robustness, runtime log hygiene, OCR for attached screenshots, and release infrastructure recovery.

Changes since v0.8.39

Thanks

Thanks to jayzhu (@zlh124) for the WSL2 startup report and clipboard-init fix in #1772/#1773.
Thanks to Paulo Aboim Pinto (@aboimpinto) for the Windows alt-screen logging report and fix in #1774/#1776, Home/End composer work in #1748/#1749, and per-process log filename follow-up in #1782/#1783.
Thanks to Zhongyue Lin (@LeoLin990405) for the provider model passthrough, reasoning replay, thinking-only turn, and Windows quoting fixes in #1740, #1743, #1742, and #1744.
Thanks to Nightt (@nightt5879) for the Ctrl+C prompt restore fix in #1764.
Thanks to Ling (@LING71671; commits as www17 <ivonrust@gmail.com>) for the configurable sub-agent API timeout in #1808.
Thanks to @knqiufan for the sub-agent file-write delegation work in #1833.
Thanks to @IIzzaya for the exact-alias-first slash-completion ordering idea in #1811.
Thanks to Bevis and the community reports that surfaced the compaction failure mode addressed in this release.

Current local preflight

  • ./scripts/release/check-versions.sh
  • cargo fmt --all
  • cargo check --workspace --all-targets --locked
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
  • cargo test --workspace --locked
  • cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui
  • cargo install --path crates/cli --locked --force
  • cargo install --path crates/tui --locked --force
  • deepseek --version -> deepseek 0.8.40 (faf5e07adb9b)
  • deepseek-tui --version -> deepseek-tui 0.8.40 (faf5e07adb9b)
  • deepseek doctor reports OCR: macOS Vision available -> image_ocr/read_file screenshot OCR enabled

CI

Remote CI is rerunning on faf5e07ad. The PR is currently open, non-draft, and mergeable; remaining release gate is waiting for GitHub checks to finish.

zlh124 and others added 16 commits May 18, 2026 23:06
…osts

On Linux, `arboard::Clipboard::new()` opens a blocking connect() to the
X11 Unix socket. When no X server is running (headless, WSL2 without
WSLg), the call hangs indefinitely. Because raw mode and the alternate
screen are already active at that point, Ctrl+C no longer generates
SIGINT and the event loop hasn't started yet — leaving the user with a
blank screen and no way to exit.

Move clipboard initialization from `ClipboardHandler::new()` (called
synchronously during App construction) to a lazy `ensure_clipboard()`
that runs on first read/write with a 500 ms timeout. If the X11
connection doesn't respond in time, the handler stays in fallback mode
and `write_text` falls through to the existing OSC 52 / pbcopy /
PowerShell paths.
…on Windows

Only DEEPSEEK_LOG_LEVEL should gate verbose CLI output. RUST_LOG controls
the tracing subscriber independently (file logging). On Windows stderr is
not redirected to the log file, so coupling the two causes tracing log
messages to leak into the TUI alt-screen, corrupting the display.

Closes #1774
…1714)

The CLI --model handoff only exports DEEPSEEK_MODEL, which apply_env_overrides
funneled into the DeepSeek-only root default_text_model slot. default_model()
then treated it as a normalizable weak default and fell back to a
DeepSeek/provider default for an unrecognized custom model on a non-DeepSeek
provider, so e.g. --provider openai --model MiniMax-M2.7 silently sent a
DeepSeek model to the endpoint.

Route DEEPSEEK_MODEL into the active provider's model slot for non-DeepSeek
providers (mirroring the existing OPENAI_MODEL branch), and return an explicit
non-DeepSeek-alias model verbatim from default_model(). DeepSeek/DeepSeekCN
behavior is unchanged. Adds a regression test next to
openai_provider_accepts_custom_model_and_base_url.

Refs #1714, #1739, #1736.
Address gemini-code-assist review on PR #1740 (MEDIUM, clarity): the
non-DeepSeek else-branch match listed ApiProvider::Deepseek /
DeepseekCN arms that are logically unreachable (handled by the if
branch above). Collapse to a single
'Deepseek | DeepseekCN => unreachable!(...)' arm so the intent is
explicit for future maintainers. No behavior change.

Refs #1714.
…rovider (#1739)

should_replay_reasoning_content_for_provider() returned false whenever
provider_accepts_reasoning_content(provider) was false (true for
ApiProvider::Openai) without checking the model. This single gate feeds
both build_for_provider (include_reasoning) and
sanitize_thinking_mode_messages, so a DeepSeek reasoning model on the
generic openai provider (DeepSeek-compatible endpoint) had all
reasoning_content stripped -> the DeepSeek thinking-mode API 400s
('reasoning_content in the thinking mode must be passed back'). This is
the over-aggressive half of ac01b22 (fix #1542).

Gate the early return on the model too:
!provider_accepts_reasoning_content(provider) && !requires_reasoning_content(model).
Known DeepSeek reasoning models replay regardless of provider; genuine
non-DeepSeek models on openai still strip (effort=off still wins). #1542
not regressed (provider_accepts_reasoning_content untouched).

Two pre-existing client.rs tests asserted the buggy case (deepseek-v4-pro
on Openai -> dropped); retargeted to gpt-4o to preserve their #1542
intent without encoding the bug. New positive/negative coverage in
chat.rs.

Refs #1739, #1694, #1542, #1736.
…#1743)

Address gemini-code-assist review on PR #1743:

- HIGH: should_replay_reasoning_content_for_provider was made model-aware
  in the previous commit, but handle_chat_completion_stream still computed
  is_reasoning_model = requires_reasoning_content(model) &&
  provider_accepts_reasoning_content(provider). On the openai provider +
  a DeepSeek model that was false during SSE parsing, so reasoning tokens
  were stored as content (not reasoning_content) and the next request
  still 400'd -- the fix was incomplete. Extract is_reasoning_model_for_stream()
  and route the stream call site through it; add an equivalence test
  locking it to the replay predicate so the two paths can't drift.
- MEDIUM: rename generic_openai_provider_drops_deepseek_reasoning_content
  -> generic_openai_provider_drops_reasoning_content_for_non_deepseek_models
  (now uses gpt-4o; old name was misleading).

Non-DeepSeek models on any provider are unaffected (#1542 not regressed).

Refs #1739, #1694, #1542.
When a model streams a turn with only a reasoning block (empty content,
no tool_calls -- e.g. gpt-oss via ollama's harmony->OpenAI shim mapping
to reasoning_content), has_sendable_assistant_content is false: the
if-only persist branch was skipped, NO event was emitted, and the turn
fell through to break. The UI spinner hung with no reply and no error.

Add an else-if at the same persist site that, only on a clean end
(tool_uses empty, turn_error.is_none(), not cancelled), warns and emits
an Event::status notice telling the user the turn ended and they can
retry. No assistant message is persisted (prior behavior preserved); no
retry/re-prompt/placeholder policy is added (out of scope). Guard
ordering extracted into a pure should_emit_thinking_only_status() helper
with a unit test, matching the existing should_hold_turn_for_subagents
style. Maintainer audit #1736 confirms #1727 is not shipped in v0.8.39
and release-blocking.

Refs #1727, #1736.
Address gemini-code-assist review on PR #1742 (MEDIUM): the status was
emitted at the assistant-persist site, but the same turn can still
CONTINUE for pending steers or sub-agent completions -- the user would
see a spurious 'turn ended without output' notice immediately before the
turn resumed.

Capture thinking_only_no_sendable at the persist site (no emission
there) and decide at the end of the tool_uses.is_empty() path, just
before the terminal break -- reachable only when there were no pending
steers, no sub-agent completions, and we were not holding for running
children. Extend should_emit_thinking_only_status with steers_pending
and holding_for_subagents (false if either), recomputed live at the
decision point as defense-in-depth. Unit test updated with the two new
no-emit cases.

Refs #1727.
git commit -m "feat: complete sub-pages" failed on Windows with
'pathspec sub-pages" did not match' because the quoted -m message was
split on spaces. Root cause is not a tokenizer bug: CommandSpec::shell()
builds 'cmd /C "chcp 65001 >/dev/null & <command>"' and std::process::Command
applies MSVCRT escaping (" -> \"); cmd.exe does not use MSVCRT parsing,
so the quoting is destroyed and git receives feat:/complete/sub-pages"
as separate pathspecs. The Unix sh -c path was already correct.

Add a cfg-split push_shell_args() replacing cmd.args() at the 3 std
spawn sites in shell.rs. On Windows, for the cmd /C <payload> shape
only, pass /C and payload via CommandExt::raw_arg so the string reaches
cmd.exe verbatim (as a terminal does); other programs keep normal
escaping. Non-Windows is a faithful pass-through (byte-for-byte
unchanged). portable_pty path intentionally untouched (out of scope).

The Unix path is provably unchanged (tested); the Windows raw_arg
runtime correctness is only verifiable on a Windows runner -- flagged in
the PR for Windows CI verification per the #1736 Windows policy.

Refs #1691, #1736.
)

Address gemini-code-assist review on PR #1744 (two MEDIUM):

- cmd detection used program.eq_ignore_ascii_case("cmd"), which fails
  for a full path (C:\Windows\System32\cmd.exe) or a .exe suffix, so the
  raw_arg quoting fix would not apply. Use Path::file_stem() instead
  (fully-qualified std::path::Path -> no unused import off-Windows).
- Strengthen the Windows block of issue_1691_quoted_commit_message_round_trips
  to assert argv content equals spec.args, not just arg count, so the
  raw_arg payload (quotes preserved, no extra escaping) is actually
  verified. sandbox/mod.rs already asserts content -- left untouched.

Windows paths are cfg-gated (compile-checked on macOS, executed on
Windows CI). macOS build + clippy clean.

Refs #1691.
Plain Home and End now navigate within the current line instead of
jumping to the absolute start/end of the entire input.  Ctrl+A and
Ctrl+E remain as absolute start/end shortcuts.

- Add move_cursor_line_start() / move_cursor_line_end() to App
- Wire Home -> move_cursor_line_start(), End -> move_cursor_line_end()
- On single-line input the new methods behave identically to the
  absolute versions (no behaviour change)
- End on a newline character skips to the end of the next line
- 14 tests covering multiline, singleline, and edge cases
Per gemini-code-assist review on #1749: End on a newline character
should stay at the end of the current line (idempotent), not skip
to the next line.  Removes the non-standard skip-past-newline logic
and updates the associated tests.
Copilot AI review requested due to automatic review settings May 20, 2026 00:01
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the project to version 0.8.40 and introduces several fixes and enhancements across the TUI and core engine. Key changes include deferred clipboard initialization to prevent hangs on headless Linux/WSL2 systems, improved Windows shell argument quoting, and the ability to restore the last submitted prompt after a cancellation. The update also ensures DeepSeek reasoning replay works through OpenAI-compatible endpoints, handles thinking-only turns by surfacing a status message, and implements a fallback mechanism for compaction when the context window is exceeded. Additionally, Home/End keys are now line-local in multiline composer drafts, and repeated shell wait rows in the sidebar are collapsed for better visibility. I have no feedback to provide as there were no review comments to assess.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prepares the v0.8.40 “Stability & Polish” release by bumping workspace/package versions and rolling up a set of TUI stability fixes across clipboard startup behavior, Windows shell/logging quirks, provider model passthrough, streaming/cancel UX, compaction fallback, and sub-agent transcript retrieval.

Changes:

  • Bump workspace/crate/npm wrapper versions to 0.8.40 and update changelogs/compare links.
  • Improve cross-platform TUI robustness: deferred clipboard init, Windows cmd /C quoting, alt-screen logging isolation, cancel/prompt-restore stream suppression, and tasks sidebar wait-row collapsing.
  • Provider/client correctness fixes: custom model passthrough for non-DeepSeek providers and reasoning-content replay consistency for DeepSeek reasoning models via OpenAI-compatible endpoints; plus compaction retry on context-window overflow and sub-agent full transcript handle exposure.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
npm/deepseek-tui/package.json Bump npm wrapper + binary version to 0.8.40.
crates/tui/src/tui/ui/tests.rs Add regression tests for cancel suppression, prompt restore tracking, Home/End behavior.
crates/tui/src/tui/ui.rs Add local-cancel stream suppression, prompt restore on Ctrl+C, line-local Home/End, refactors for cancel paths.
crates/tui/src/tui/sidebar.rs Collapse repeated “wait shell job” rows for the same task in Tasks sidebar.
crates/tui/src/tui/clipboard.rs Defer clipboard connection and bound init time to avoid headless/WSL2 startup hangs.
crates/tui/src/tui/app.rs Track local-cancel suppression state + last submitted prompt; add line-local cursor movement + restore helper.
crates/tui/src/tools/subagent/tests.rs Test preference for full transcript handle in terminal projections.
crates/tui/src/tools/subagent/mod.rs Store and expose full sub-agent transcripts via handle_read-compatible handles.
crates/tui/src/tools/shell/tests.rs Add regression test for quoted-arg round-tripping on Windows shell invocation.
crates/tui/src/tools/shell.rs Implement Windows cmd /C raw-arg path to preserve quoting.
crates/tui/src/sandbox/mod.rs Add regression test ensuring shell command strings are passed unsplit.
crates/tui/src/logging.rs Stop treating RUST_LOG as a verbose CLI-output gate; clarify behavior in docs.
crates/tui/src/core/engine/turn_loop.rs Improve cancel responsiveness and surface “thinking-only” turns at the correct terminal point.
crates/tui/src/config.rs Preserve explicit custom models for non-DeepSeek providers; route DEEPSEEK_MODEL into provider-scoped slot where appropriate; add regression test.
crates/tui/src/compaction.rs Retry compaction summaries with bounded formatted input on context-window overflow; add detection + tests.
crates/tui/src/client/chat.rs Make reasoning replay and streaming classification model-aware for OpenAI-compatible DeepSeek endpoints; add tests.
crates/tui/src/client.rs Update tests to reflect model-aware reasoning behavior on generic openai provider.
crates/tui/CHANGELOG.md Add v0.8.40 release notes and update compare links.
crates/tui/Cargo.toml Bump internal dependency versions to 0.8.40.
crates/tools/Cargo.toml Bump deepseek-protocol dependency version to 0.8.40.
crates/hooks/Cargo.toml Bump deepseek-protocol dependency version to 0.8.40.
crates/execpolicy/Cargo.toml Bump deepseek-protocol dependency version to 0.8.40.
crates/core/Cargo.toml Bump internal workspace dependency versions to 0.8.40.
crates/config/Cargo.toml Bump deepseek-secrets dependency version to 0.8.40.
crates/cli/Cargo.toml Bump internal workspace dependency versions to 0.8.40.
crates/app-server/Cargo.toml Bump internal workspace dependency versions to 0.8.40.
crates/agent/Cargo.toml Bump deepseek-config dependency version to 0.8.40.
CHANGELOG.md Add v0.8.40 release notes and update compare links.
Cargo.toml Bump workspace package version to 0.8.40.
Cargo.lock Update lockfile package versions to 0.8.40.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1653 to +1659
/// `provider_accepts_reasoning_content(provider) || requires_reasoning_content(model)`
/// short-circuits to `requires_reasoning_content(model)` once the model gate
/// already holds, so the effective rule is purely model-driven — kept explicit
/// here to mirror the predicate above.
fn is_reasoning_model_for_stream(provider: ApiProvider, model: &str) -> bool {
requires_reasoning_content(model)
&& (provider_accepts_reasoning_content(provider) || requires_reasoning_content(model))
Comment on lines +180 to +189
/// Issue #1691: on Windows the shell command is invoked as
/// `cmd /C "chcp 65001 >NUL & <command>"`. Rust's `Command::arg` applies
/// MSVCRT (`CommandLineToArgvW`) escaping, turning the embedded `"` in a
/// quoted argument (e.g. `git commit -m "feat: complete sub-pages"`) into
/// `\"`. `cmd.exe` does NOT use MSVCRT parsing — it treats `\` literally and
/// `"` as a bare quote toggle — so the escaped payload is mis-tokenized and
/// `git` receives `feat:`, `complete`, `sub-pages"` as separate pathspecs
/// (the reported `pathspec 'sub-pages"' did not match` symptom). Passing the
/// `cmd /C` payload through `CommandExt::raw_arg` suppresses std's escaping so
/// the string reaches `cmd.exe` verbatim, exactly as a terminal would.
This was referenced May 21, 2026
@Hmbown Hmbown merged commit faf5e07 into main May 21, 2026
13 checks passed
@Hmbown Hmbown mentioned this pull request May 24, 2026
@Hmbown Hmbown deleted the work/v0.8.40-stability branch May 24, 2026 01:08
Comment thread CHANGELOG.md
@@ -5,6 +5,173 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J

Comment thread CHANGELOG.md
@@ -5,6 +5,173 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ja

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants