Deduplicate official DeepSeek model completions and normalize known prefixed aliases to the bare model IDs expected by official DeepSeek providers, while preserving provider-specific IDs for compatible backends.\n\nFixes #1594.\n\nCo-authored-by: reidliu41 <reid201711@gmail.com>
Make the translation client optional so missing or invalid API configuration does not crash startup before onboarding can render.\n\nCo-authored-by: Crvena <kuragectl@gmail.com>
Capture and replay Mcp-Session-Id for Streamable HTTP transports, and apply configured custom headers to the GET preflight.\n\nCloses #1629.\n\nCo-authored-by: Zhiping <2716057626@qq.com>
Write the Kitty keyboard protocol probe (ESC[>0u) on Windows instead of enabling disambiguation flags that crossterm does not decode there. Fixes#1599.
Make the sidebar expiry test avoid subtracting from a fresh Instant on Windows runners, falling back to a short sleep only when an older Instant cannot be represented.
Hard-break oversized streaming tokens so CJK runs, URLs, and other no-whitespace content stay within the target width. Includes regression coverage for long CJK text and first-token overflow.
Add the Feishu/Lark long-connection bridge, Tencent Lighthouse runbooks, CNB mirror guidance, CNB tag release pipeline, and China-friendly update fallback documentation for the v0.8.37 line.
Summary:
- restore auto sidebar focus when Ctrl+Alt+0 is pressed from hidden state
- preserve existing hide behavior from visible sidebar states
- add regression coverage
Validation: CI green before merge.
Summary:
- include generic tool input in approval-cache fingerprints
- keep exact repeat denials stable
- prevent one denied generic tool call from blocking future distinct calls
Validation: CI green before merge.
Summary:
- concise live shell/tool labels
- collapsed pending CI polling rows
- hardened stale task-panel timing test
Validation: CI green before merge.
Horace Liu (@liuhq) contributed Nix package support and install
documentation in the v0.8.34 cycle but was inadvertently omitted from
that release's changelog and the README contributor list. This commit
adds them to both.
The workspace ships two CHANGELOG files — the repo-root one and the
crate-local `crates/tui/CHANGELOG.md`. The `prompts::tests::
changelog_entry_exists_for_current_package_version` gate scans the
nearest CHANGELOG to the manifest, so the crate-local copy needs the
same `## [<version>]` section before tagging.
Copy the [0.8.34] section over from the root CHANGELOG, including the
edit_file fuzzy-punctuation bullet added later in the session. No new
content; the two files now agree.
When `edit_file` is called with `fuzz: true` and exact match fails,
the existing fallback strips leading whitespace and retries. That
catches indentation differences, but not the much more common
copy-paste failure mode where the search string came from a browser
or chat client that silently substituted Unicode punctuation:
* U+201C / U+201D (`"` / `"`) ↔ ASCII `"`
* U+2018 / U+2019 (`'` / `'`) ↔ ASCII `'`
* U+2013 / U+2014 (en/em dash `–` / `—`) ↔ ASCII `-`
* U+00A0 (non-breaking space) ↔ ASCII space
Add a second fallback after the indentation pass: when that yields
no matches, retry once more with both the file contents and the
search string punctuation-normalized to ASCII. A byte-map sized to
the normalized output recovers the original byte range, so the
replacement still goes back into the file untouched (the replacement
text is taken verbatim from the caller).
The fuzz note appended to the success message now distinguishes the
two cases:
Replaced 1 occurrence in foo.txt (fuzzy indentation match)
Replaced 1 occurrence in foo.rs (fuzzy punctuation match — typographic quotes/dashes normalized)
Adds two unit tests: one that recovers a smart-quote substitution,
one that handles em-dash + NBSP together.
Inspired by pi-agent's `edit-diff.ts` Unicode normalization step.
Two more cohesive seams move out of ui.rs:
* `tui::format_helpers` (new) — the three pure builders for the
cache-warmup status message, the prefix-stability footer chip, and
the `/models` listing. Renamed `format_cache_warmup_result` →
`cache_warmup_result`, `format_prefix_stability_chip` →
`prefix_stability_chip`, `format_available_models_message` →
`available_models_message` (the `format_` prefix was redundant once
the module name is `format_helpers`). Adds two unit tests.
* `tui::key_shortcuts` (new) — the 10 cross-platform key-event
predicates and labels: `is_copy_shortcut`,
`is_file_tree_toggle_shortcut`, `tool_details_shortcut_label`,
`activity_shortcut_label`, `alt_nav_modifiers`,
`is_macos_option_v_legacy_key` (+ the test-only platform variant),
`is_paste_shortcut`, `is_text_input_key`, `is_ctrl_h_backspace`.
These were the cross-platform glue around `crossterm`'s `KeyEvent`
that normalises Ctrl-vs-Cmd, the macOS Option-Latin escape, and
legacy Ctrl+H-as-backspace behavior.
ui.rs is now **8916 lines** — under 9000 for the first time, down
from the 10,025-line starting point (a ~11% reduction across the
session). All 956 tui:: unit tests still pass.
The `/files` picker ranks workspace files by three signals harvested
from the session: which files git reports as modified, which the user
@-mentioned in the composer, and which recent tool calls touched. The
scoring code — `open_file_picker` plus 9 helpers (build_relevance,
modified_workspace_paths, parse_git_status_path,
mark_tool_detail_paths / from_value / from_text, workspace_file_candidate,
clean_path_token, workspace_path_to_picker_string) — was ~218 lines
of self-contained logic mid-ui.rs.
Moved to `tui/file_picker_relevance.rs`. Same behavior; the picker
view file (`tui/file_picker.rs`) keeps the rendering layer, and the
new module owns the per-session ranking that fed it.
ui.rs is now 9073 lines (down ~950 from the pre-refactor 10025).
The Key Features list was missing several capabilities that meaningfully
distinguish DeepSeek TUI from other terminal coding agents:
* Prefix-cache stability tracking (the footer chip from #1473).
* OS-level sandboxing — Seatbelt on macOS, Landlock on Linux, Windows
Job Objects. The README previously said "approval gates" without
noting that shell execution actually runs through OS isolation.
* Bundled starter skill set (11 skills) so `/skills` is useful on
first launch, before any community skill is installed.
* Terminal-native notifications across OSC 9 / 99 / 777 plus the
desktop fallback.
* Theme picker covering Catppuccin / Tokyo Night / Dracula / Gruvbox.
* CNY cost display when the session locale is `zh-Hans`.
No promotional language added; entries describe shipped behavior that
already existed but went unnamed in the elevator pitch.
`crates/tui/src/modules/` was never registered with `mod modules;` in
the binary and nothing in the workspace called `run_deepseek_chat` /
`run_official_chat`. The file is a 23 KB text-REPL workflow from a
much earlier iteration of the project — superseded by the actual
TUI runtime in `crates/tui/src/tui/` long ago, but never deleted.
Removing it cuts ~580 lines of unreachable code and 44 `println!`
calls from the workspace audit, with zero behavior change (the
compiler confirms nothing imports from this path).
`crates/tui/src/core/engine.rs` is the agent-loop core (1983 lines after
this commit, down from 2077). One of the cleanest seams in there is the
public `EngineHandle` method surface: 9 short async/sync methods that
just push operations down the engine's mpsc mailbox — `send`, `cancel`
+ `cancel_with_reason` + `is_cancelled`, `approve_tool_call` /
`deny_tool_call` / `retry_tool_with_policy`, `submit_user_input` /
`cancel_user_input`, and `steer`.
Move that impl block to a new `engine/handle.rs` submodule. The struct
itself stays in `engine.rs` because two construction sites
(`Engine::new` and the test-only `mock_engine_handle`) need access to
its private channels; child modules see parent private items, so the
impl in `handle.rs` works without widening any field visibility.
No behavior change. All 100 `core::engine::*` tests still pass.
The tool dispatch path (`Engine::execute_tool_with_lock`) had no
observability around the actual call: we knew when a tool was
*requested* (via the existing engine-loop logs) and when it *audited*
(via `DEEPSEEK_TOOL_AUDIT_LOG` JSONL), but not how long any individual
tool took, what its outcome was, or how the request was routed (MCP
vs in-process registry).
Add two tracing events around every tool call:
* `tool.exec.start` — tool name, dispatch kind (`mcp` / `registry` /
`missing`), interactive flag, supports-parallel flag, input byte
size. `tracing::debug!` so the noise is opt-in via
`RUST_LOG=engine.tool_execution=debug`.
* `tool.exec.end` — same tool/dispatch fields plus `duration_ms`,
`success` (or `error_kind` mapped from `ToolError` variants), and
output byte size. Successful calls log at `debug`; failures log at
`warn` so they surface under the default filter.
Targets are flat strings (`engine.tool_execution`) following the
existing convention already used by `InteractiveTerminalGuard`. CLAUDE.md
documents `RUST_LOG=deepseek_cli::core::engine=debug` as the canonical
filter for agent-loop events; this is the per-tool-call layer beneath
that.
The notification dispatch primitives (`Method`, `notify_done`,
`humanize_duration`, OSC 9 / OSC 99 / OSC 777 formatters) already live
in `tui/notifications.rs`. The five composition helpers that decided
*when* and *what* to notify still lived in ui.rs:
* `notification_settings` → `notifications::settings`
* `completed_turn_notification_message` → `notifications::completed_turn_message`
* `subagent_completion_notification_message` → `notifications::subagent_completion_message`
* `latest_assistant_notification_text` → `notifications::latest_assistant_text`
* `notification_text_summary` → `notifications::text_summary`
Move them so the whole notification stack lives in one file. The lone
streaming sanitizer they share, `sanitize_stream_chunk`, stays in ui.rs
(it has three other call sites in the streaming render path) but is
now `pub(super)` so notifications.rs can reuse it via `super::ui::`.
ui.rs drops to ~9290 lines (down ~735 from the pre-refactor baseline
of 10025). All 954 tui:: tests still pass.
Three more cohesive function clusters move out of the 10k-line ui.rs:
* `tui::vim_mode` (new) — `handle_vim_normal_key`, the composer's
Normal-mode dispatch (h/j/k/l, w/b, x/dd, i/a/o/v/G, plus the
pending-`d` operator state).
* `tui::workspace_context` (new) — the composer-header git context
badge: `refresh_if_needed`, `collect`, `branch`, `change_summary`,
`run_git`, the `ChangeSummary` struct, and the `REFRESH_SECS` TTL
constant. Replaces `refresh_workspace_context_if_needed` /
`collect_workspace_context` / `workspace_git_branch` /
`workspace_git_change_summary` / `run_git_query` / `WorkspaceChangeSummary`
/ `WORKSPACE_CONTEXT_REFRESH_SECS` in ui.rs.
* `tui::streaming_thinking` (new) — the 10-function lifecycle that
manages the live "Thinking" entry inside `active_cell`: ensuring an
entry exists, appending chunks, animating the translation
placeholder, replacing it with finalized text, starting / finalizing
a block, and stashing the reasoning buffer onto `app.last_reasoning`
so it survives compaction.
Plus the unused `ActiveCell` import in ui.rs that became dead after
the streaming-thinking move.
ui.rs is now ~9434 lines (down from ~10025). All 954 tui::* tests
still pass; no runtime behavior change.
ui.rs has grown past 10k lines; the first two extraction targets are
self-contained function clusters that don't share state with the rest
of the file:
* `tui::auto_router` (new module) gathers the 7 functions that decide
whether to run the auto-route flash model and what context to send
it: `should_resolve_auto_model_selection`,
`resolve_auto_model_selection`, `normalize_auto_routed_effort`,
`recent_auto_router_context`, and three private helpers
(`content_blocks_text`, `append_router_text`,
`truncate_for_auto_router`). Adds unit tests for the pure pieces.
* `tui::onboarding` (existing module) grows three state-machine
transitions and one API-key validator: `validate_api_key_for_onboarding`,
`advance_onboarding_from_welcome`,
`advance_onboarding_after_language`,
`sync_api_key_validation_status`, plus the `ApiKeyValidation` enum.
These belong next to the existing onboarding screen renderers.
Adds unit tests for the validator.
Net effect: ui.rs is ~160 lines shorter and two cohesive concerns are
now reviewable without scrolling through unrelated UI plumbing. No
behavior change. All 954 tui:: tests still pass.
The /skills menu renders one history cell containing every discoverable
skill with full description. With the v0.8.34 bundled-skill set (11
first-party skills), a typical 40-row terminal viewport no longer fits
the message, and the top of the list scrolls off — including any
workspace-level user skill at .agents/skills/, which the registry
returns first thanks to precedence ordering.
The qa_pty `skills_menu_shows_local_and_global_skills` test exercises
exactly this case: a workspace skill plus a sealed-home global skill
plus the 11 bundled skills come to 13 entries, the viewport cuts off
above /global-alpha, and the workspace skill is invisible. Discovery
was healthy — confirmed by the new unit test
`discover_finds_both_workspace_and_global_skills`.
Render the menu in two sections so user-created skills stay prominent:
- `Your skills (N):` lists every user-installed skill with its full
description.
- `Built-in skills (M):` either lists every bundled skill with its
description (when there are no user skills to surface) or compacts
them into a comma-separated name list with a "run /skills <name>
for details" hint (when user skills are present).
Filtered views (`/skills <prefix>`) keep the old flat list — the user
has already narrowed the catalog and section headers would be noise.
Expose `skills::is_bundled_skill_name` so the renderer can partition
without a side-channel marker. Replace one unit test that asserted
the old between-entry blank-line spacing with one that asserts the
section structure.