A small cleanup pass to catch brand mentions that the R5 sweep missed
because they hid in:
- HTTP User-Agent format strings (`Mozilla/5.0 (compatible; deepseek-tui/`
in `client.rs` and `fetch_url.rs`).
- Multi-line error messages whose phrase boundary straddled a line break
("…restart\n deepseek-tui." in `js_execution.rs`,
`tool_catalog.rs`, `repl/runtime.rs`).
- Doc comments mentioning `deepseek-tui` as a binary (`config/src/lib.rs`,
`core/capacity.rs`, `tui/streaming/chunking.rs`, `features.rs`).
- Skill descriptions shipped in `crates/tui/assets/skills/*/SKILL.md`.
- Test fixtures with placeholder paths / git emails
(`tui/external_editor.rs`, `snapshot/repo.rs`).
- `task_manager.rs`'s `cargo test -p deepseek-tui --lib` example.
- `scripts/tencent-lighthouse/doctor.sh` info-line prefix.
The remaining `deepseek-tui` mentions in the codebase are intentional
(the legacy `[[bin]]` entry in `crates/tui/Cargo.toml`, the legacy
`npm/deepseek-tui/` deprecation shim package, the CNB mirror namespace,
the security email, the legacy bin's shim source file, and historical
CHANGELOG entries) and were preserved per the rebrand anti-scope.
Local gates green: `cargo check --workspace --all-targets --locked`,
`cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets
--all-features --locked -- -D warnings`, `cargo test --workspace
--all-features --locked` (3226+ pass, 0 fail).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename the 14 workspace member crates from `deepseek-*` (and
`deepseek-tui-*`) to `codewhale-*`. Internal-only — binary names
(`deepseek` and `deepseek-tui`) are intentionally untouched in this
phase; they move in the next phase along with the deprecation shims.
Affects:
- 14 `[package] name = "..."` declarations.
- All inter-crate `[dependencies]` entries that referenced the old
package names.
- All `use deepseek_*::...` statements rewritten to `use codewhale_*`.
- Cargo.lock regenerated.
CI workflows and release scripts that pass `-p deepseek-*` still
reference the old names; those move with the binary rename phase so
that pair lands together.
Local gates green: `cargo check --workspace --all-targets --locked`,
`cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets
--all-features --locked -- -D warnings`, `cargo test --workspace
--all-features --locked` (3226+ pass, 0 fail).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Map legacy DeepSeek CN provider names back to the canonical Deepseek provider in both manual parsing and TOML deserialization.
Co-authored-by: qiyan233 <qiyan233@users.noreply.github.com>
Workspace, all 9 path-pinned crate deps, and the npm wrapper's
package.json all advance from 0.8.31 → 0.8.32. `scripts/release/
check-versions.sh` passes (workspace ↔ npm ↔ Cargo.lock all in
sync).
Auto-tag only fires on push-to-main, so this bump on `work/v0.8.32`
doesn't accidentally cut a release; it just makes the
in-development binary identify itself correctly. When this branch
merges to main, the existing release pipeline takes over from
here.
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>
- workspace.package.version: 0.8.29 → 0.8.30
- per-crate path-dependency version pins: 0.8.29 → 0.8.30
- npm/deepseek-tui: version + deepseekBinaryVersion → 0.8.30
- Cargo.lock refreshed via `cargo update --workspace --offline`
- CHANGELOG: `[Unreleased]` → `[0.8.30] - 2026-05-11` with the full
release-theme paragraph and the new "Changed" section for the
Alt+<key> unification
Verified with `./scripts/release/check-versions.sh`:
Version state OK: workspace=0.8.30, npm=0.8.30, lockfile in sync.
The water-spout strip in the footer used to be hard-gated by `!low_motion`,
which meant the typewriter-streaming option silently killed the spout
animation — even with `fancy_animations = true` the strip stayed plain
whitespace. Users testing the typewriter pacing in v0.8.29 reported "where
did the whale go," which is on us: we'd collapsed two concerns
(streaming pacing vs footer animation) onto one flag.
This commit makes the two flags orthogonal:
- `low_motion` governs streaming pacing only (typewriter = one char per
commit tick vs upstream cadence = drain everything queued).
- `fancy_animations` governs whether the spout-strip is rendered at all.
It also wires in a new idea that fell out naturally once the two were
decoupled: instead of driving the wave animation off wall-clock
milliseconds, drive it off a per-turn character-commit counter
(`StreamingState::stream_commit_frame`). The wave then visually moves at
the same cadence as the text:
- Typewriter mode → wave drips at one frame per character.
- Upstream mode → wave surges when V4-pro bursts a warm-cache turn.
- Tool calls and planning pauses → no chars arrive, wave freezes. The
textual `working...` pulse still ticks on wall-clock, so a heartbeat
is always visible.
- New turn (`StreamingState::reset`) → counter zeroes so each turn
opens with a fresh wave shape.
`stream_commit_frame` is a `u64` advanced inside `commit_text` and
`finalize_block_text` by the character count of each committed slice,
so multi-byte UTF-8 (e.g., CJK) advances the wave by one glyph per
character rather than three frames per character — matching the
visual weight of each glyph.
Regression-guarded by five new tests in `crates/tui/src/tui/streaming/mod.rs`:
- `stream_commit_frame_advances_by_character_count_on_commit`
- `stream_commit_frame_counts_unicode_chars_not_bytes`
- `stream_commit_frame_advances_on_finalize`
- `stream_commit_frame_resets_on_reset`
- `stream_commit_frame_freezes_when_no_text_arrives`
Also folds in `cargo fmt` cleanup for two files where prior commits on
this branch landed without re-formatting (`crates/tui/src/tui/ui.rs`
around the new Esc-arm wrapper introduced for the `gg` double-tap, and
the new `fireworks_custom_base_url_preserves_provider_model` test in
`crates/config/src/lib.rs`). No behavior change from those edits.
Settings doc comments in `crates/tui/src/settings.rs` updated to spell
out the new orthogonal semantics so the next maintainer doesn't have
to reverse-engineer it from `render_footer`.
CHANGELOG entry added under a new `[Unreleased]` section.
Previously only OpenRouter was whitelisted via provider_preserves_custom_base_url_model,
causing six other providers (Sglang, Novita, Fireworks, Vllm, Ollama, NvidiaNim) to still
rewrite user-configured model names when a custom base URL was set. Users routing through
their own gateway would get 400s because the TUI sent provider-prefixed model names
(e.g. accounts/fireworks/models/deepseek-v4-pro) that the gateway didn't recognise.
The fix removes the provider-specific guard: when base_url_is_custom_for_provider()
returns true (i.e. the user set a non-default endpoint), the model name is preserved
as-is for every provider, not just OpenRouter.
Affected:
- crates/config/src/lib.rs: ProviderKind::Openrouter guard removed
- crates/tui/src/config.rs: ApiProvider::Openrouter guard removed
- Test: fireworks_custom_base_url_preserves_provider_model added
- Test: nvidia_nim_reads_facade_provider_table updated for new behaviour
Addresses the #857 class bug (B1 in the v0.8.30 audit).
Workspace + per-crate path-dep version pins, npm wrapper, and
deepseekBinaryVersion all advance 0.8.28 -> 0.8.29.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workspace + per-crate path-dep version pins, npm wrapper, and
`deepseekBinaryVersion` all advance from 0.8.27 → 0.8.28. Lockfile
refreshed via `cargo update --workspace --offline`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CLI dispatcher accepted --yolo but only passed it to Exec(TuiPassthroughArgs),
not to the plain Run(RunArgs) path used for interactive sessions.
Fix: pass DEEPSEEK_YOLO=true env var to the TUI binary. The TUI already
reads this env var (matching DEEPSEEK_SANDBOX_MODE pattern) and sets
allow_shell + start_in_agent_mode + yolo.
Also adds yolo field to CliRuntimeOverrides and ResolvedRuntimeOptions
so the flag propagates through the full resolve chain.
Two responsibly-disclosed security fixes:
- GHSA-88gh-2526-gfrr (@JafarAkhondali)
- GHSA-72w5-pf8h-xfp4 (@47Cid)
Plus version bump, CHANGELOG, regression tests for both.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bump workspace version 0.8.22 → 0.8.23 across Cargo.toml, every per-crate
path-dependency pin, npm/deepseek-tui/package.json (both `version` and
`deepseekBinaryVersion`), and Cargo.lock.
- Add a 0.8.23 CHANGELOG entry covering the security hardening stack
(sanitized child env, plan-mode tool surface, sub-agent approvals,
symlink walks, runtime API auth, shell safety classification, MCP
config path traversal), the macOS Keychain prompt fix, the #1244 MCP
spawn error visibility + env passthrough work, the compact-thinking UX
change, and a Known issues callout for mid-run MCP stderr.
- Backfill missing CHANGELOG entries for v0.8.21 (community-heavy
release, contributors credited) and v0.8.22 (fetch_url redirect
validation). The gap was unintentional, so contributor work is being
reflected in-repo now.
- Add docs/RELEASE_CHECKLIST.md so future releases gate on the
CHANGELOG/version/preflight steps explicitly.
* 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
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>
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:
- 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.
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>
* fix: save config with restrictive permissions and improve secret redaction
- Config files containing API keys were written with default permissions
(typically 0644), making them world-readable on multi-user systems. Use
OpenOptions with mode 0o600 on Unix to restrict access to the file owner.
- `redact_secret` threshold raised from 8 to 16 characters — previously a
9-character secret would leak 8 of its 9 characters (4 prefix + 4
suffix). Now secrets up to 16 chars are fully masked with "********".
* fix(config): keep secret saves warning-free on windows
---------
Co-authored-by: Hunter Bown <hmbown@gmail.com>