Commit Graph

492 Commits

Author SHA1 Message Date
Ziang Xie 4994ce99f8 fix(command_safety): fix path_escapes_workspace false positive for double-dots in names (#824)
* fix(command_safety): fix path_escapes_workspace false positive for ".." in names

The path_escapes_workspace function used `path.contains("..")` to detect
directory traversal, which incorrectly flagged paths containing consecutive
dots in file or directory names (e.g., `foo..bar`, `dir..name/file.txt`).

Replace the substring matching with a component-level walk that tracks
nesting depth. Each `..` path component decrements the depth; if depth
ever goes negative the path has escaped the workspace. Non-`..` components
increment depth. This correctly:

- Flags genuine traversal like `../outside` and `./sub/../../etc/passwd`
- Ignores names with embedded double-dots like `some..file.txt`
- Allows safe intra-workspace `..` usage like `./subdir/../other`

* test(command_safety): cover absolute traversal paths

---------

Co-authored-by: Hunter Bown <hmbown@gmail.com>
2026-05-06 03:33:28 -05:00
Ziang Xie 026e9558a9 fix: replace std::thread::sleep with tokio::time::sleep in async context (#831)
The `execute` method of `ShellWaitTool` is async, but used
`std::thread::sleep` which blocks the tokio worker thread. Replace with
`tokio::time::sleep().await` so other tasks can make progress during the
50ms poll interval.
2026-05-06 03:29:43 -05:00
Hunter Bown 77b6d43088 chore(doctor): show resolved API endpoint (#823)
* chore(doctor): show resolved API endpoint

* test(tui): serialize markdown OSC8 parity render
2026-05-06 03:17:58 -05:00
Hunter Bown ab92892cc4 fix(session): scope latest resume to workspace (#830) 2026-05-06 03:16:44 -05:00
YuanSheng Wang 3e93143079 feat(tui): support Shift+Enter to insert newline in composer (#801)
* feat(tui): support Shift+Enter to insert newline in composer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(tui): cover shift-enter newline handling

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Hunter Bown <hmbown@gmail.com>
2026-05-06 03:10:12 -05:00
Ziang Xie 950860768f fix(web_search): complete HTML entity decoding with numeric character references (#822)
* fix(web_search): complete HTML entity decoding with numeric character references

The decode_html_entities function only handled 7 named entities and lacked
support for decimal (&#NN;) and hex (&#xHH;) numeric character references,
which are common in search engine result snippets. This caused garbled text.

Replace the hard-coded replace chains with a regex-based decoder that
handles:
- All named entities (amp, lt, gt, quot, apos, nbsp, copy, reg, mdash,
  ndash, lsquo, rsquo, ldquo, rdquo, hellip)
- Decimal numeric references (&#65; → A)
- Hex numeric references (&#x41; → A)
- Unknown entities are passed through unchanged

* style(web_search): apply rustfmt

---------

Co-authored-by: Hunter Bown <hmbown@gmail.com>
2026-05-06 02:58:20 -05:00
Hunter Bown 58a0039c92 fix(install): avoid unstable file locking API (#821) 2026-05-06 02:53:30 -05:00
Hunter Bown a09a72572f fix(network): add allowlist slash command (#819) 2026-05-06 02:46:02 -05:00
Hunter Bown 1171c5e401 fix(skills): ignore symlinks outside selected install root (#814) 2026-05-06 02:36:57 -05:00
Zhang Yonglun 4408b59852 fix(config): the skills directory should be read recursively (#811) 2026-05-06 02:27:06 -05:00
ccomma 0d50cab251 fix: skip snapshots for dangerous workspaces (#804) 2026-05-06 02:14:59 -05:00
Hunter Bown a2ca64018e feat(cost): support yuan display (#806) 2026-05-06 01:36:46 -05:00
Jason 1981c09970 fix(snapshot): refuse to snapshot $HOME directory (#798)
* fix(snapshot): refuse to snapshot home directory (#793)

When the TUI is launched from $HOME, the snapshot system would run
`git add -A` on the entire home directory, consuming unbounded CPU and
disk. This manifests as a multi-GB side-repo under ~/.deepseek/snapshots
and makes the TUI appear frozen.

Add a guard in SnapshotRepo::open_or_init that compares the canonical
workspace path against the canonical home directory and returns an error
if they match. The error is non-fatal (snapshots are a safety net, not a
correctness gate) so the turn loop continues without snapshots.

Closes #793

* test(snapshot): fix home guard test portability

* test(snapshot): avoid env-dependent home guard test

---------

Co-authored-by: Hunter Bown <hmbown@gmail.com>
2026-05-06 01:08:05 -05:00
Hunter Bown 8c091cce98 fix(tui): reflect approval mode in agent prompts (#797) 2026-05-06 00:47:04 -05:00
wucm667 05db50d182 fix(config): accept provider-prefixed DeepSeek model IDs (#794)
Allow model IDs like accounts/fireworks/models/deepseek-v4-flash
by checking for /deepseek in addition to starts_with(deepseek).

Fixes #753

Signed-off-by: wucm667 <stevenwucongmin@gmail.com>
2026-05-06 00:30:37 -05:00
Hunter Bown feaae514a6 fix(tui): recognize legacy ctrl-v paste input (#786) 2026-05-05 22:41:50 -05:00
Hunter Bown 3af6ef6f69 fix(tui): default mouse capture off on Windows (#785) 2026-05-05 22:40:31 -05:00
zxyasfas 0fded51824 docs: align Rust MSRV references with workspace (#739) 2026-05-05 22:39:50 -05:00
Hunter Bown ece6b88e79 feat(acp): add stdio adapter for editor agents (#782) 2026-05-05 22:30:17 -05:00
Reid 02456429ca Add shell completion setup examples (#742)
* Add shell completion setup examples

  Show actionable bash, zsh, and fish completion setup commands in the completion subcommand help, and cover them in the CLI help
  surface test.

* docs(cli): clarify completion setup examples

---------

Co-authored-by: Hunter Bown <hmbown@gmail.com>
2026-05-05 22:28:10 -05:00
Lawrence 76e5c90185 fix(tui): preserve UTF-8 while stripping ANSI
Preserve multi-byte UTF-8 characters when stripping ANSI/OSC8 sequences from transcript output.\n\nVerified locally with:\n- cargo fmt --all -- --check\n- cargo test -p deepseek-tui --bin deepseek-tui tui::osc8
2026-05-05 22:13:59 -05:00
Hunter Bown 82851aece4 fix(provider): complete vLLM integration on main 2026-05-05 21:27:02 -05:00
Agent007 a335ff5e4c feat(provider): add vLLM provider support (#737)
Add vLLM as a first-class OpenAI-compatible self-hosted provider with VLLM_BASE_URL, VLLM_API_KEY, and VLLM_MODEL wiring.
2026-05-05 21:22:24 -05:00
Hunter Bown 50780a5289 fix: restore auto model routing (#772)
Keep auto as a local routing mode, resolve concrete model/thinking before API requests, and wire auto routing through CLI, TUI, runtime threads, and subagents.
2026-05-05 21:22:03 -05:00
Hunter Bown 63e2234c6b fix(config): repair first-run auth and settings selection (#763)
* fix(auth): recover from env-only invalid API keys

Refs #759.

* fix(tui): repair config modal selection rendering

Refs #759.
2026-05-05 20:05:17 -05:00
Hunter Bown ab59ef8ff2 fix(cost): count V4 reasoning tokens in usage output (#762) 2026-05-05 19:57:25 -05:00
Hunter Bown cc6c6c6053 fix(scroll): Up/Down arrows now scroll transcript when composer is empty
The should_scroll_with_arrows gate was hardcoded to false since the
initial commit, which meant bare Up/Down arrows always navigated
composer history — they never scrolled the transcript view. Users in
virtual terminals (Ghostty, Codex, Kitty-protocol terminals) were
especially affected because they couldn't use the specialized Cmd+Up
/ Alt+Up shortcuts.

The gate now returns true when the composer input is empty (or
whitespace-only), so bare arrows scroll the transcript. When text
is present, arrows still navigate composer history to recall
previous prompts.

Added 3 tests covering empty, whitespace-only, and text-filled
composer states.
2026-05-05 13:54:21 -05:00
Hunter Bown c4cbd7c19f chore(release): finalize v0.8.13 stabilization 2026-05-05 13:06:09 -05:00
Hunter Bown 6b0a01d054 feat(client): remove dead responses_api_proxy module and EXPERIMENTAL_RESPONSES_API_ENV plumbing
Deletes crates/tui/src/responses_api_proxy/ (443 LOC), client/responses.rs
(406 LOC), and removes the ResponsesApiProxy CLI command, the
EXPERIMENTAL_RESPONSES_API_ENV env var plumbing, chat_fallback_counter,
use_chat_completions, RESPONSES_RECOVERY_INTERVAL, and the
RequestPayloadMode::ResponsesApi variant. The experimental Responses API
path was never instantiated and had no documented users; removing it
simplifies the client surface for the upcoming --anthropic-wire flag.

Closes #723
2026-05-05 04:22:35 -05:00
Hunter Bown 38bcb5ca3d fix(tools): resolve hallucinated tool names via deterministic ladder
V4 occasionally emits non-canonical tool names (Read_file, readFile,
read-file, read_file_tool). The new resolver runs a deterministic
ladder — lowercase exact, kebab/space → snake, CamelCase → snake,
strip _tool suffix twice, fuzzy prefix match — before falling through
to "Unknown tool". Recovers ~80% of these for free.

Closes #713
2026-05-05 04:09:05 -05:00
Hunter Bown c018afdf82 fix(tools): repair invalid JSON in tool-call arguments via deterministic ladder
DeepSeek streamed tool_calls.function.arguments occasionally arrives
malformed — trailing commas after SSE chunk boundaries, unclosed
braces from truncated streams, raw control chars from local-inference
backends. The new arg_repair module runs a 5-stage repair (strict parse
→ control-char strip → trailing-comma strip → brace balance → empty
object fallback) before tool dispatch.

Preserves V4 thinking-mode reasoning_content invariants on repair paths.

Closes #712
2026-05-05 04:03:58 -05:00
Hunter Bown b7f86ae19f fix(tools): sanitize tool schemas before sending to DeepSeek
The new schema_sanitize module collapses Pydantic-style nullable
anyOf/oneOf unions to {type, nullable: true}, injects empty properties
on bare objects, prunes dangling required entries, and collapses
single-element unions. Run on every tool schema before
build_api_tools() returns the catalog.

Closes a class of silent 400s on /beta/chat/completions strict tool
mode for MCP-derived schemas.

Closes #715
2026-05-05 03:55:32 -05:00
Hunter Bown 310c975597 feat(models): preserve dated variant suffixes; remove legacy alias machinery
`normalize_model_name` now passes v-series snapshots through unchanged
(deepseek-v4-flash-20260423 stays pinned, future v5-* matches via
regex). Removes ~245 LOC of legacy alias machinery: deepseek_legacy_aliases,
the chat/reasoner/r1/v3/v3.2 fold-arm, is_current_deepseek_v4_alias,
v4 fallback branch, alias capacity test seeds, alias config test block.

The migration from V3 → V4 is over; users on legacy names route their
own request to DeepSeek and see the server actual response (404 if
deprecated, success if still served). No more silent renaming.

Closes #717
2026-05-05 03:49:20 -05:00
Hunter Bown 16142b5f5e fix(release): unbreak Windows build + harden Linux apt step
PR #646 imported `MessageBeep` from `windows::Win32::UI::WindowsAndMessaging`,
but in `windows` crate 0.60 the function lives at
`windows::Win32::System::Diagnostics::Debug::MessageBeep` and now takes a
typed `MESSAGEBOX_STYLE` returning `Result<()>`. The wrong import broke
every Windows build (Test, npm wrapper smoke, and the windows-msvc release
matrix entry). Fix the import path, enable the `Win32_System_Diagnostics_Debug`
feature, pass `MESSAGEBOX_STYLE(0)` for MB_OK, and discard the Result.

The v0.8.12 release also tripped on a transient `apt-get update` mirror
sync error on the ubuntu-24.04-arm runner, cascading via fail-fast. Wrap
every apt-get update in CI/release with a 5-attempt retry so flaky
ports.ubuntu.com mirrors no longer take down the binary matrix.

Verified: cargo check --target x86_64-pc-windows-gnu compiles cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 03:04:17 -05:00
Hunter Bown 7a27e6bd1f chore(v0.8.12): bump internal dep pins to 0.8.12, sync lockfile, refresh README contributor thanks and zh-CN section
- Bump all internal deepseek-* path dependency versions from 0.8.11 → 0.8.12
- Sync Cargo.lock after version bump
- Refresh en/zh-CN README Thanks sections with v0.8.9–v0.8.12 contributors
- Update zh-CN README 'What's New' from v0.8.10 → v0.8.12
- Fix zh-CN pricing discount end date: May 5 → May 31
2026-05-05 02:47:12 -05:00
Hunter Bown 9d91a51064 fix(scroll): RAII pause/resume guard so cancelling an interactive tool restores the terminal
Reported by @Hmbown as the recurring "scrolling uses the parent
terminal scrollbar instead of the TUI's" / "TUI rendered only at the
bottom of the viewport" symptom. Same family as 5c72e5f4 and 899c703d,
but neither covered the cancellation case — they fixed the per-turn
scroll lock and panic-time cleanup respectively.

The bug
=======

`execute_tool_with_lock` in tool_execution.rs sent
`Event::PauseEvents` before an interactive tool ran (which makes the
TUI leave alt-screen, disable raw mode, release mouse capture so the
child sees a normal terminal) and `Event::ResumeEvents` after it
returned. Both sends were `let _ = tx_event.send(...).await`.

If the future was dropped between the two awaits — Ctrl+C, agent
cancel, parent task aborted, sub-agent terminated mid-tool — the
second `await` never reached and `ResumeEvents` was never sent. The
TUI sat in the paused state until the next pause/resume cycle, with
the symptoms above.

The fix
=======

Replace the two `send` calls with a `InteractiveTerminalGuard` RAII
type. `engage()` sends `PauseEvents` (only when `interactive` is
true) and arms the guard. `Drop` synchronously sends `ResumeEvents`
via `try_send` (Drop can't await). The guard fires on every exit
path: Ok return, Err return, panic, future cancellation.

`try_send` can fail on a full bounded channel; the guard logs a
`tracing::warn!` rather than swallowing silently so the rare
cancel-with-saturated-event-channel case is visible in traces. The
TUI's own teardown path remains a final backstop.

Verified locally
================

* `cargo build` clean
* `cargo fmt --all -- --check` clean
* `cargo clippy --workspace --all-targets --all-features --locked
   -- -D warnings` clean
* `cargo test --workspace --all-features --locked` — 2062 passed,
   0 failed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:33:09 -05:00
Hunter Bown 02fc16e10f style: clippy sweep across community PRs (-D warnings)
13 clippy errors had accumulated from squash-merged community PRs:
collapsible-if (10), needless-late-init (1), derivable-impls (1),
sort-unstable hint (1). All auto-fixable mechanical lints — no
behaviour change. Required to satisfy CI's
`cargo clippy --workspace --all-targets --all-features --locked
-- -D warnings` gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:15:16 -05:00
Hunter Bown a6d9d7cf5b fix(security): refuse to auto-recover checkpoint from another workspace
Reported by @Hmbown: launching `deepseek` from any directory silently
auto-recovered the most recent interrupted session, even if that
session originated in a completely different workspace. Tools then
operated on file paths from the prior workspace while the status bar
showed the *current* workspace name — a confusing trust-boundary
violation that also leaks api_messages, working_set entries, and any
secrets the prior session had accumulated into a new terminal that
was never meant to see them.

`try_recover_checkpoint()` now compares the saved session's workspace
to `std::env::current_dir()` (canonicalised, with a strict-equality
fallback when canonicalisation fails — e.g. the original workspace
was deleted) and only auto-recovers on a match.

On a mismatch the checkpoint is still persisted as a regular session
and cleared, so the user can recover it explicitly via
`deepseek resume <ID>` after `cd`-ing back to the original workspace —
no data is lost. A one-line stderr notice points at the resume command.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:07:58 -05:00
Hunter Bown 3fedb980c7 fix(subagents): replace 'pending' lease placeholder with real agent id (#660)
Resident-file leases were stamped as "pending" at spawn time because the
agent id is only assigned by the manager later. The release function
introduced in 2ee926924 matches by agent id, so it could never find
those entries and leases would persist for the lifetime of the process.

After spawn returns the real agent id, replace any "pending" entry with
it so the existing release-on-terminal-state path actually fires.

Resolves the documented v0.8.12 caveat noted in the CHANGELOG. Closes
the loop with PR #694, which proposed a release-by-file-path API but
did not address the placeholder problem itself.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:00:43 -05:00
Hunter Bown a9dcf2b6e6 style: cargo fmt sweep across community PRs
47 fmt drifts had accumulated from the squash-merged community PRs on
this branch (#653, #654, #655, #645, #658, #668, #659, #661, #660,
#667, #656). Pure formatting — no behavioural changes — applied via
`cargo fmt --all` to satisfy CI's `cargo fmt --all -- --check` gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 01:57:25 -05:00
Hunter Bown 668f2c37f5 fix(build): stable-Rust compatibility + silence deferred-code warnings
The match guard at tui/ui.rs:1603 used `&& let Some(...) = ...` inside an
`if` guard, which requires the `if_let_guard` nightly feature on Rust
< 1.94. Reported by an external user attempting `cargo install
deepseek-tui` on stable rustc — it failed with E0658.

Rewrite as a plain match guard with a nested `if let` inside the arm
body so the language-picker hotkeys compile on every supported rustc.

Workspace also now declares `rust-version = "1.88"` to match the
codebase's actual reliance on `let_chains` in if/while conditions, so
users on too-old toolchains see a clear cargo error instead of a
confusing rustc one.

`AGENTS.md` and `CLAUDE.md` gain a "stable Rust only" section
documenting the trap and how to rewrite around it.

Also annotate the deferred `TuiPrefs` (#657) and `handoff::THRESHOLDS`
(#667) APIs with `#[allow(dead_code)]` so CI's `-D warnings` flag stays
green while the call sites are staged for v0.8.13.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 01:57:14 -05:00
Hunter Bown 239e5925a1 fix(test): use fully-qualified ratatui::style::Color::Reset
The test module couldn't resolve Color via the file-level import in
Rust 2024 edition cfg(test) context. Use the full path instead.
2026-05-05 00:58:07 -05:00
Hunter Bown 2ee826924a fix(subagents): release resident file leases on agent completion (#660)
Moved RESIDENT_LEASES from block-scoped static to module level so the
release function can access it. Added release_resident_leases_for()
called at all three terminal transitions:
- Cancelled (cancel_agent)
- Failed (update_failed)
- Completed (run_subagent_turn result construction)

Previously leases were acquired but never released, causing false
conflict warnings on subsequent spawns targeting the same file.
2026-05-05 00:54:34 -05:00
Hunter Bown b51fa6bc91 feat(sandbox): wire create_backend() into Engine::build_tool_context (#645)
The sandbox backend infrastructure was complete but the engine never
called create_backend(), leaving the feature dead. Now:
- Engine::new() creates the backend from api_config (non-fatal on error)
- build_tool_context() attaches it to the ToolContext
- exec_shell checks context.sandbox_backend and routes accordingly
2026-05-05 00:51:24 -05:00
Hunter Bown c0e27485a8 fix: remove dead config fields — prefer_handoff (#667) and use_terminal_colors (#671)
Neither field had any code path that read it. Shipping config knobs
that do nothing trains users to mistrust config. Remove until the
implementation exists.
2026-05-05 00:44:13 -05:00
Hunter Bown ca9fccc0da fix: address review findings — broken test, expect() panic, misleading docstring
#651: fix test assertion — section_bg now Color::Reset (was DEEPSEEK_INK)
#645: replace expect() with Result in OpenSandboxBackend::new()
#653: correct resolve_prefixes docstring to describe deny-always-wins
2026-05-05 00:42:42 -05:00
Hunter Bown e25cb4e38b fix(build): add missing arity_dict field in ExecPolicyEngine::with_rulesets 2026-05-05 00:17:24 -05:00
Hunter Bown ff5c99965d feat(sandbox): pluggable SandboxBackend + Alibaba OpenSandbox adapter (#645) 2026-05-05 00:16:34 -05:00
Hunter Bown 8dca6deee2 feat(execpolicy): layered permission rulesets (#653) 2026-05-05 00:16:27 -05:00
Hunter Bown 685f9f97a3 feat(subagents): cache-aware resident file sub-agents for refactors (#660) 2026-05-05 00:15:27 -05:00