Commit Graph

685 Commits

Author SHA1 Message Date
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 f738f0175a docs(keybindings): fix Ctrl-S (stash not history search), add Alt-R, audit to v0.8.13
- Fix Ctrl-S: stash current draft, not reverse history search
- Add Alt-R: search prompt history
- Note bare Up/Down arrows now scroll transcript when composer is empty (v0.8.13)
- Remove phantom Alt+Up from audit notes
- Note tui.toml wiring still deferred
- Remove dated 'v0.8.11 follow-up' reference in header
2026-05-05 18:03:45 -05:00
Hunter Bown 401cf34827 docs(readme): fix single-binary claim, phantom Alt+Up, Rust version, add Homebrew + contributors
- Fix 'single binary' to 'self-contained binaries' (two binaries ship)
- Remove phantom Alt+Up shortcut (does not exist in key dispatch)
- Fix Rust version 1.85+ to 1.88+ in source install
- Add Homebrew install path to both READMEs
- Add v0.8.13 contributors: @dfwqdyl-ui (#729), @Oliver-ZPLiu (#738)
- Add LOCALIZATION.md to docs table in both READMEs
- Add missing usage commands to zh-CN (--version, resume, fork)
- Align zh-CN lede with English (prefix-cache aware, thinking-mode)
2026-05-05 18:02:33 -05:00
Hunter Bown b07fed8c9c docs(changelog): add scroll-fix entry for v0.8.13 2026-05-05 17:18:38 -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 2d809225be Merge feat/v0.8.12 into main
Release v0.8.12: 20 community PRs, internal dep pin bump, README refresh.
2026-05-05 02:47:23 -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 1db5415887 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:14 -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 3faae20041 docs(changelog): document v0.8.12 cross-workspace session bleed fix
Captures the security/privacy fix that was hotfixed to main earlier
(commit f779c7de6) so the v0.8.12 CHANGELOG reflects it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:15:36 -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 6d0c708307 Merge branch 'main' into feat/v0.8.12 2026-05-05 02:08:25 -05:00
Hunter Bown f779c7de6e 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:08:02 -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 546ef939bd docs+ci(v0.8.12): Resume by UUID, triage workflows, CHANGELOG refresh
- README Usage block now documents `deepseek resume <SESSION_ID>` and
  `deepseek fork <SESSION_ID>`. Both commands have existed since v0.7
  but were undiscoverable; #682 reported "no way to resume."
- New GitHub Actions for issue triage (#688):
    * triage.yml      — keyword-driven auto-labeller (bug / feat / docs /
                        question, area:* by file-path mention, os:*,
                        lang:zh on CJK titles). Only adds labels that
                        already exist on the repo so it can't create
                        noise unilaterally.
    * stale.yml       — 14 d stale → 7 d close on `needs-info` issues
                        only; PRs untouched; respects pinned/keep-open/
                        bug/security exemptions.
    * spam-lockdown.yml — auto-closes promotional issues from accounts
                          <30 days old. Pure github-script (no
                          third-party action) so the matching rules
                          stay readable.
- CHANGELOG (v0.8.12) updated: README install rewrite (#672), Scoop
  (#696), pricing extension (#692), Resume docs surface, and the
  cargo-install-on-stable fix from the previous commit. Lease
  "pending" caveat removed since it's now actually fixed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:03:16 -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
woyxiang 48d529fa93 docs: add Scoop installation instructions for Windows 2026-05-05 01:59:18 -05:00
wangfeng 327a9af1be docs: update pricing/discount info — promotion period extended (closes #691) 2026-05-05 01:59:07 -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 da266e2105 Merge branch 'main' into feat/v0.8.12 2026-05-05 01:50:50 -05:00
Hunter Bown 3d289e5fdd docs: clarify Node.js is not a runtime dependency (closes #672)
Replace the prerequisite block that told readers to install Node before
anything else (which contradicted the lede claim that the binary needs
no Node runtime) with a multi-path install section. npm, cargo, and
direct download are presented side-by-side; the npm path is documented
as a thin installer that downloads the prebuilt binary, not as a
runtime dependency.

Same change applied to README.zh-CN.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 01:50:36 -05:00
Hunter Bown 2f499858dd docs(release): add v0.8.12 CHANGELOG + update README What's New 2026-05-05 01:02:27 -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 8785e6865e chore(release): bump version to 0.8.12 2026-05-05 00:18:14 -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
Hunter Bown 2cdf4a2560 feat(engine): context-limit handoff replaces routine compaction (#667) 2026-05-05 00:15:15 -05:00
Hunter Bown 937f5f33f3 feat(runtime): route large tool outputs through workshop to protect parent context (#658) 2026-05-05 00:14:16 -05:00
Hunter Bown b31bc4104b feat(config): separate tui.toml for theme and keybinds (#657) 2026-05-05 00:12:29 -05:00
Hunter Bown 1e968a3ec4 feat(tui): vim modal editing in composer (#659) 2026-05-05 00:12:28 -05:00
Hunter Bown caf1ac2a89 feat(skills): remote registry sync with /skills sync command (#654) 2026-05-05 00:12:17 -05:00
Hunter Bown 2928d20e09 feat(tools): FIM edit tool for V4 /beta endpoint (#668) 2026-05-05 00:12:17 -05:00
Hunter Bown 84ff982086 feat(lsp): auto-attach diagnostics to edit results (#656) 2026-05-05 00:12:09 -05:00
Hunter Bown fb2e61e123 feat(execpolicy): bash arity dictionary for command-prefix allow rules (#655) 2026-05-05 00:12:09 -05:00