Harvested from PR #2118 by @Hmbown.
Includes Kimi/Moonshot OAuth, v0.8.45 release prep, the Codex/ChatGPT OAuth removal, open-source-first model defaults, and the safe green PR batch merged into main before the release branch refresh.
* feat(tui): add command palette voice input
* feat(rlm): expose active session objects
* fix(tui): do not restore slash commands as retry drafts
* fix(config): expose voice input settings rows
* fix: sync ActiveTurnState.auto_approve when remember is set
When a user checks 'Remember for this tool' and approves a tool call,
remember_thread_auto_approve() only persisted thread.auto_approve to disk
but did not update the in-memory ActiveTurnState for the current turn.
This meant subsequent tool calls within the same turn would still require
manual approval, making the remember checkbox appear non-functional.
Now remember_thread_auto_approve() also sets
ActiveTurnState.auto_approve = true, so active_turn_flags() returns
the correct value and the approval_decision() logic auto-approves
remaining tool calls in the current turn.
(cherry picked from commit 2ccf048c8984d61e3341a4304d0796a1f965d3e7)
* test(runtime): cover remembered auto approve on active turn
---------
Co-authored-by: Ben Gao <bengao168@msn.com>
- Separate model auto-routing from Plan/Agent/YOLO TUI modes across all
READMEs and docs/MODES.md
- Introduce Fin as the fast thinking-off deepseek-v4-flash seam for
routing, summaries, RLM child calls, and coordination work
- Document /goal as current session tracking (not a TUI mode), with note
that a future Goal work surface should stay distinct from --model auto
- Extend deprecation shim timeline from 'one release cycle' to
through v0.8.x in REBRAND.md and npm READMEs
- Fix selection_to_text test to expect inline thinking preview
(short completed thinking now renders inline without Ctrl+O
affordance)
Sweep brand mentions of `DeepSeek TUI` / `deepseek-tui` / bare
`deepseek` (the dispatcher binary) across all user-facing docs to
the new `codewhale` brand. The DeepSeek **provider** integration is
left untouched throughout: env vars (`DEEPSEEK_*`), model IDs
(`deepseek-v4-pro`, `deepseek-v4-flash`, `deepseek-chat`,
`deepseek-reasoner`), the `api.deepseek.com` host, the
`~/.deepseek/` config dir, and the `--provider deepseek` argument
value all keep the legacy spelling.
Anti-scope items deliberately left as the legacy `deepseek-tui`:
- Homebrew tap and formula (`Hmbown/homebrew-deepseek-tui`,
`brew install deepseek-tui`, `scoop install deepseek-tui`). The
tap rename ships separately.
- Docker image (`ghcr.io/hmbown/deepseek-tui`). Image-tag rename
ships separately.
- CNB mirror namespace (`cnb.cool/deepseek-tui.com/DeepSeek-TUI`).
Third-party hosted path.
- Security contact email (`security@deepseek-tui.com`).
- GitHub repo URL (`Hmbown/DeepSeek-TUI`).
New artifact:
- `docs/REBRAND.md` documents what changed, what didn't, the
deprecation window, and migration commands for npm / Cargo /
Homebrew / manual installs.
CHANGELOG entries:
- Root `CHANGELOG.md` and `crates/tui/CHANGELOG.md` both gain a
new `[Unreleased]` section describing the rename and the one-
release deprecation window. Historical entries are untouched.
Issue templates:
- `.github/ISSUE_TEMPLATE/bug_report.md` and `feature_request.md`
refer to "codewhale" / `codewhale --version` instead of the old
brand name in their environment fields.
The rebrand sweep was driven by a perl script with bulk patterns
(`deepseek-tui` -> `codewhale-tui`, `DeepSeek TUI` -> `codewhale`,
bare `deepseek` -> `codewhale` with provider/model/host/env-var/
config-path negative lookbehind/lookahead) followed by targeted
reverts for the anti-scope items above. Output was visually
reviewed file-by-file before committing.
Verified:
- `cargo check --workspace --all-targets --locked` — pass.
- `cargo test --workspace --all-features --locked` — pass (no
test source touched here; suite stayed green to confirm no
doc-from-string assertions broke).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AtlasCloud (https://atlascloud.ai) hosts the V4 family on its own
DeepSeek-compatible endpoint at `https://api.atlascloud.ai/v1`, and
several contributors had been running it through the
OpenAI-compatible passthrough with manual `base_url` / model
overrides. Selecting `provider = "atlascloud"` in
`~/.deepseek/config.toml` (or via `DEEPSEEK_PROVIDER=atlascloud`)
now wires up:
- documented `DEFAULT_ATLASCLOUD_BASE_URL` /
`DEFAULT_ATLASCLOUD_MODEL` defaults so a fresh install needs
only the api_key
- a `[providers.atlascloud]` config block with the same fields
every other named provider exposes (api_key / base_url / model
/ http_headers)
- `ATLASCLOUD_API_KEY` env var path, including the secrets test
cleanup loop so per-test env hygiene continues to work
- the provider-picker / `/provider` slash command entries so the
provider is reachable from the runtime UI, not just config
- the env-driven `*_BASE_URL` override branch so users who pin a
proxy can still flip it without editing config.toml
Trust-boundary pins held: AtlasCloud is opt-in (default remains
DeepSeek), no API keys are hardcoded, the api_key resolution flows
through the same `secrets` crate path every other provider uses,
and the provider-config base_url stays settable per environment.
Resolved 3-way merge conflicts in `crates/secrets/src/lib.rs` (env
cleanup loop) and `crates/tui/src/config.rs` (per-provider
base_url match arm + `provider_passes_model_through` predicate)
so the contributor's AtlasCloud branch coexists with the v0.8.x
provider expansion already on `main`. Added the missing match arm
in `validate_provider_base_url` so the non-exhaustive-pattern
check passes after the new variant lands.
Harvested from PR #1436 by @lucaszhu-hue
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend /note beyond append-only usage with list, show, edit, remove,
clear, path, and explicit add subcommands.
Keep existing /note <text> behavior compatible, preserve the existing
--- separated file format, and number notes only at display time so the
stored notes stay clean.
Update command help, localization, docs, and tests.
Replace the separate /agent, /plan, and /yolo commands with a single
/mode command that can either open a picker or switch directly by name
or number.
This keeps mode switching in one command surface and avoids duplicating
similar commands for each mode.
* fix(config): keep DeepSeek beta endpoint for legacy cn alias
* fix(ci): filter download-artifact to deepseek* pattern
Prevents the release aggregation job from picking up non-binary
artifacts (e.g. Docker .dockerbuild cache layers) that cause the
checksum manifest to include spurious entries and the Release to
carry files it shouldn't.
* fix(tui): enable focus events to restore IME after app-switch
On macOS, switching away (Cmd+Tab) and back suspends the IME compositor.
Without focus-event handling, the TUI never signals readiness to the
terminal, so CJK input methods (Pinyin, Zhuyin, etc.) stop working.
- EnableFocusChange on startup so the terminal reports FocusGained/FocusLost
- Re-push KeyboardEnhancementFlags on FocusGained (some terminals reset
the enhanced keyboard mode on focus-loss)
- DisableFocusChange on shutdown for clean terminal handoff
* chore: cargo fmt
* docs: add DataWhale and DeepSeek to acknowledgments
* docs: fix DeepSeek name etymology in acknowledgments
* fix(tui): recapture viewport on focus restore
* docs: thank DeepSeek and DataWhale bilingually
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
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.
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.
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>
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>
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>
Post-merge review feedback on #583 surfaced four small accuracy gaps:
1. The narrative docs in `docs/CONFIGURATION.md` and the inline comment
in `config.example.toml` said the notification fires "when a turn
takes longer than a threshold" — but the call site in
`tui/ui.rs:928` is gated on `TurnOutcomeStatus::Completed`. Failed
and cancelled turns are silent on purpose. Spell that out so users
don't expect alerts on long failures.
2. The `notify_done` rustdoc still summarised `Auto` as "Osc9 for known
terminals, Bel otherwise" — internally inconsistent with the new
Windows-aware fallback documented one screen earlier on the
`Method::Auto` enum and on `resolve_method`. Update the public
rustdoc to point at the canonical resolution table on
`resolve_method` and call out the `Off`-on-Windows branch.
3. The `## Key Reference` list in `docs/CONFIGURATION.md` had no entries
for `[notifications].method`, `[notifications].threshold_secs`, or
`[notifications].include_summary`. Other features with a dedicated
subsection (e.g. `[memory].enabled`) are listed there too, so readers
scanning the canonical key list could not discover the notification
knobs. Added the three keys with cross-references to the
Notifications subsection.
4. The Windows-only test only covered the unknown-`TERM_PROGRAM` →
`Off` fallback. The positive path (known OSC-9 terminal still
resolves to `Osc9`) was only tested via `iTerm.app`, which is a
macOS-only program — Windows CI would still pass if the `WezTerm`
arm of the match disappeared. Added
`auto_detect_picks_osc9_for_wezterm_on_windows` so the
WezTerm-on-Windows compatibility guarantee is exercised on the
Windows runner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On Windows, the audio stack maps BEL (`\x07`) to the
`SystemAsterisk` / `MB_OK` chime — the same sound applications use
for error popups. So with the previous `Method::Auto` fallback to
`Bel`, every successful turn-completion notification ended up
sounding identical to a software error.
Reported by a community user who described it as "the popup-error
sound from a CAD program I used to use" (#583).
resolve_method() now returns `Off` instead of `Bel` on Windows for
unknown TERM_PROGRAM values. Known OSC-9-capable terminals
(`iTerm.app`, `Ghostty`, `WezTerm`) still resolve to `Osc9` on
every platform, so users running WezTerm on Windows keep getting
real notifications. macOS and Linux behaviour is unchanged.
Windows users who actively want an audible cue can opt back in by
setting `[notifications].method = "bel"` in `~/.deepseek/config.toml`.
Also:
- Documents `[notifications]` in `docs/CONFIGURATION.md` with an
explicit Windows note (the schema was previously undocumented).
- Updates the inline comment in `config.example.toml` so users
reading the seed config see the platform-specific behaviour.
- Splits the existing `auto_detect_picks_bel_for_unknown` test
into a Unix variant (`#[cfg(not(target_os = "windows"))]`) and
adds a new Windows-gated test that asserts the `Off` fallback,
so CI's Windows runner exercises the platform-specific path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- add /memory help and clearer invalid-subcommand guidance
- register /memory in shared slash-command help
- align memory docs with current behavior and config
- add focused tests for help and discovery
Resolves the post-#514/#517/#518 conflicts:
- CHANGELOG.md: kept both polish-stack and Linux ARM64 entries under
[Unreleased]; reordered so the ARM64/install-message Changed/Docs
sections precede the Releases footer.
- config.example.toml: kept both the `instructions = [...]` example
and the `[memory]` opt-in stanza in sequence.
- crates/tui/src/config.rs: kept both `instructions_paths()` (#454)
and `memory_enabled()` (#489) on the Config impl.
- crates/tui/src/prompts.rs: extended
`system_prompt_for_mode_with_context_and_skills` to take BOTH
`instructions: Option<&[PathBuf]>` and `user_memory_block:
Option<&str>`. Section 2.5a renders instructions; 2.5b renders the
memory block — both above the skills block so KV prefix caching
still wins.
- crates/tui/src/core/engine.rs: thread both args through the two
call sites.
- crates/tui/src/prompts.rs: update the `system_prompt_for_mode_with_context`
forwarder and the test caller to pass `None` for the new arg.
- .gitignore: ignore `.claude/*.local.md` and `*.local.json` so
local ralph / Claude-Code notes can't leak into commits.
Folds in two valid suggestions from the gemini-code-assist review on #519:
- `client.rs`: collapse the duplicated `LlmError → label` match and the
`human_retry_reason` body into a single
`retry_reason_label_and_human(err) -> (&'static str, String)` helper.
- `widgets/footer.rs::retry_banner_spans`: merge the two separate
`match &props.retry` blocks into one that returns both `(label, color)`.
Behavior is unchanged; refactor is a pure DRY win.
Corporate users behind TLS-inspecting proxies (Zscaler, Netskope,
Palo Alto, in-house mitmproxy fleets) need to add the proxy's
intermediate CA to the trusted-roots set so the deepseek client
doesn't fail with `unable to get local issuer certificate`.
The reqwest builder already trusts the platform's system store
via native-tls. This adds opt-in support for the conventional
`SSL_CERT_FILE` env var so users can point at their own bundle:
* New `add_extra_root_certs(builder, path)` helper reads the
file, tries `Certificate::from_pem_bundle` (covers single-cert
files too), falls back to `from_der` for binary cert files.
* Wired into `build_http_client` when `SSL_CERT_FILE` is set
and non-empty. Failures log a warning via the existing
`logging::warn` channel and return the builder unchanged —
the existing system trust still applies, so a malformed env
var degrades gracefully instead of bricking the launch.
* Each successful load logs `info` with the cert count so
operators can confirm their bundle was picked up.
Documented in `docs/CONFIGURATION.md`'s environment-variables
list alongside the existing TLS-related notes.
No new dependency — reqwest's `native-tls` feature already
exposes `Certificate::from_pem_bundle` / `from_der`.
Catches up `docs/CONFIGURATION.md` with the v0.8.8 polish stack so
operators have one source of truth for the new surfaces:
* `NO_ANIMATIONS` env override (#450) joins the existing
environment-variable list, with a cross-reference to
`docs/ACCESSIBILITY.md`.
* New `### Instruction sources` section documents the
`instructions = [...]` config field (#454): expansion rules,
100 KiB per-file cap with `[…elided]` marker, missing-file
warning behavior, and the project-wholesale-replaces-user
override semantics.
* New `### /hooks listing` section documents the read-only
slash command (#460 MVP) so users know how to introspect
configured lifecycle hooks without `cat`-ing config.toml.
* New `### Composer stash` section documents Ctrl+S +
`/stash list|pop|clear` (#440) including the 200-entry cap
and multiline preservation.
Pure documentation; no code changes. Existing prompt-stability
and config-loading tests are unaffected.
The memory MVP shipped in PR #518 added three surfaces (\`# \` quick-add,
\`/memory\` slash command, \`remember\` model tool) plus the opt-in
toggle, but the only user-facing reference today is the one-line
mention of \`memory_path\` in CONFIGURATION.md and the \`#489\` cross-
reference in SUBAGENTS.md. This commit adds a dedicated user-facing
doc covering the whole feature.
Coverage:
- Why opt-in by default
- How to enable (env var + config.toml)
- What the system prompt block looks like
- Three ways to add to memory:
1. \`# foo\` composer prefix (#492)
2. \`/memory\` slash command (#491) — show / path / clear / edit
3. \`remember\` tool (#489) — model-callable, auto-approved
- File format — timestamped Markdown bullets, hand-editable
- What stays out of memory — secrets / transient state / long
instructions / conversation snippets
- Privacy and scope — per-user, never uploaded, provider-agnostic
- Configuration reference — settings table with defaults and overrides
Cross-link added in CONFIGURATION.md so the existing \`memory_path\`
mention now points at the full feature doc.
No Rust code changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The project-config overlay (`<workspace>/.deepseek/config.toml` merged
on top of the user's global `~/.deepseek/config.toml`) was already
wired but only carried four string fields: model, api_key, base_url,
reasoning_effort. The use cases users actually file under #485 — "this
repo wants a different sandbox / approval policy / MCP server set / hard
sub-agent cap" — weren't covered.
### What ships
Adds the following keys to the project overlay, all merged with
identical "non-empty wins" semantics for strings:
- `provider` — pick a different backend per repo (e.g. `nvidia-nim` for
an enterprise repo, `deepseek-cn` for a CN-team repo).
- `approval_policy` — `never` / `on-request` / `untrusted` for repos
with strict policies.
- `sandbox_mode` — `read-only` / `workspace-write` / `danger-full-access`.
- `mcp_config_path` — per-repo MCP server set without touching the
user's global file.
- `notes_path` — keep notes in-repo for projects where the notes tool
is part of the dev workflow.
Plus two non-string fields:
- `max_subagents` (positive integer; clamped to `1..=MAX_SUBAGENTS=20`).
- `allow_shell` (bool).
### What stays user-global
`skills_dir`, `hooks`, `[capacity]`, `[retry]`, `[memory]`, etc. — those
are user-shaped settings, not repo-shaped. If a future use case
demands per-project values for any of them, a follow-up PR can extend
the overlay rather than letting the boundary blur.
### Tests
- 8 new tests in `project_config_tests` covering: provider+model,
approval+sandbox, max_subagents+allow_shell, max_subagents
clamping, negative-max_subagents rejection, missing config file
pass-through, malformed TOML pass-through, and empty-string
no-op.
### Docs
- New "Per-project overlay (#485)" section in `docs/CONFIGURATION.md`
with a table of supported keys and the rationale for which fields
stay user-global.
### Verification
cargo fmt --all -- --check ✓
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings ✓
cargo test --workspace --all-features --locked ✓ 1828 + supporting (was 1820)
Closes#485
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>