Commit Graph

1038 Commits

Author SHA1 Message Date
Hunter Bown c02e554f4b docs(changelog): add [0.8.28] entry for the four cherry-picks + test stabilization
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:34:24 -05:00
Hunter Bown c2d13c21bf test: stabilize parallel test execution + bump task_manager timeouts
Fixes a cluster of intermittent failures observed on macOS under
parallel test load. Root causes were tests mutating shared global
state (HOME, USERPROFILE, DEEPSEEK_CONFIG_PATH env vars and
~/.deepseek/ filesystem) without holding the process-wide test
lock, plus a few outdated-by-PR assertions and a tight 3s timeout
on Windows CI.

Changes:

* Three EnvGuard / HomeGuard types (commands/config.rs,
  commands/network.rs, tools/recall_archive.rs) now acquire
  crate::test_support::lock_test_env() and hold the MutexGuard
  for their full lifetime, replacing local mutexes that
  serialized only within a module. Call sites that previously
  acquired lock_test_env() explicitly with `let _lock = ...`
  before constructing the guard drop that redundant acquisition;
  std::sync::Mutex is not reentrant and double-locking on the
  same thread would deadlock.
* settings.rs::config_path_test_guard() now returns the global
  test_env lock instead of an isolated module-local mutex.
* model_picker.rs create_test_app() now returns (App, MutexGuard)
  so picker tests hold the same lock — eliminates env-var races
  with config-mutating tests in adjacent modules.
* task_manager.rs: 4 tests using wait_for_terminal_state bump
  3s -> 10s to give Windows CI file-I/O headroom (we saw one
  intermittent timeout on the v0.8.27 PR Windows job).
* config.rs: 2 api-key tests now set DEEPSEEK_SECRET_BACKEND=local
  so they exercise file-backed storage in CI rather than fail on
  Keychain access.
* history.rs: removes streaming_thinking_live_collapses_unless_verbose
  which asserted the OLD behavior PR #1390 (#861 RC4) intentionally
  changed. The new contract is covered by the three tests PR #1390
  added.
* .claude/HANDOFF_v0.8.28_user_issues.md: notes #1394 / PR #1393
  as a deferred prompt-reliability enhancement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:33:57 -05:00
Zhiping 5ccfe91bff fix: cache usage unknown shown as 0, and add truthful reporting prompt (#false-claim)
Two independent fixes:

1. **Prompt truthful reporting** (base.md): add explicit rules for honest
   outcome reporting — if a tool fails/returns-empty say so; if cache
   usage is unobserved treat it as unknown/null, not 0.

2. **Cache usage u64 → Option<u64>** (session.rs): when the API does
   not report cache hit/miss tokens, the cumulative SessionUsage
   defaulted to 0. Models interpreted this as "no cache hits" rather
   than "unknown". Changing to Option<u64> ensures absent cache data
   serializes as null in the model context.

Tests added for all three cases: starts None, stays None when API
omits cache, accumulates correctly when API reports cache.
2026-05-10 16:43:26 -05:00
LinQ ca5204e311 fix(ui): stream thinking content during collapsed view (#861 RC4, #1324)
Closes the visibility gap reported in #1324 ("Thinking 思考内容不能流式
输出,只能等到完全输出后通过 ctrl+O 查看完整思考内容") and root cause 4
of #861.

Today `render_thinking` blanks the body whenever `collapsed && streaming`:

```rust
let body_text = if collapsed && streaming {
    String::new()
} else if collapsed { … } else { … };
```

That left the user staring at a "thinking..." placeholder for the
entire reasoning phase — V4-Pro thinking can run for tens of seconds,
so the live transcript looked frozen even though tokens were flowing.

Fix:

1. During `collapsed && streaming` we now render the raw content
   instead of blanking. `extract_reasoning_summary` is meaningless
   while the block is mid-flight (no completed reasoning to summarise),
   so the streaming branch returns the body verbatim.

2. The `> THINKING_SUMMARY_LINE_LIMIT` truncation now drops *head*
   lines while streaming, keeping the visible window tracking the live
   cursor at the bottom — which is what users expect when watching a
   model think.

3. The existing "thinking collapsed; press Ctrl+O for full text"
   affordance was gated on `!streaming`; it now renders during
   streaming as well, with a slightly different label ("thinking
   continues; …") so the user knows there's more content above and
   how to reach it.

Three new tests cover the new contract: streaming-collapsed shows
live content, the head is dropped not the tail, and the live
affordance fires when truncated.

Refs #861 (RC4), closes #1324
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:43:16 -05:00
LinQ 741f36d2be fix(ui): drain active thinking entry on MessageComplete (#861 RC3)
Closes the order-of-events race in #861 root cause 3: when the engine
bursts events, the dispatch loop can pull `MessageComplete` off the
channel ahead of `ThinkingComplete`. Today's `MessageComplete` reads
`app.last_reasoning.take()` to attach the reasoning block to the
assistant message in `api_messages`. If `ThinkingComplete` has not
fired yet, `last_reasoning` is `None` and the thinking content is
dropped — DeepSeek V4 then returns HTTP 400 on the next turn because
it requires `reasoning_content` replay for assistant messages that
carry tool calls.

Adds a defensive head-of-handler drain in `MessageComplete`: when
`streaming_thinking_active_entry.is_some()`, finalize the active
thinking entry and stash the reasoning buffer into `last_reasoning`
before the existing body runs. The drain is a no-op in the normal
case where `ThinkingComplete` arrived first (the entry has already
been cleared), so this branch is order-independent.

Adds `message_complete_drain_preserves_thinking_when_thinking_complete_lost`
which exercises the head-of-handler invariant directly: with a
thinking entry still active and `last_reasoning` empty, the drain
must move the buffer into `last_reasoning` before downstream reads.

Refs #861 (RC3). RC1 and RC2 are already addressed by the existing
`finalize_current_streaming_thinking` plumbing in
`apply_engine_error_to_app` and `start_streaming_thinking_block`;
RC4 (streaming-time truncation affordance) is left out of this PR
to keep the scope on the data-loss path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:43:16 -05:00
Zhiping d4a4892ee3 fix: deny of one tool call permanently blocks all future same-tool calls (#1377)
When a user denies a tool call (e.g. edit_file), the tool_name was
inserted into approval_session_denied alongside the per-call
approval_key. Every subsequent invocation of the same tool type was
then auto-denied for the rest of the session without prompting.

Fix: only store the approval_key (per-call unique). This still
prevents the model's retry loop from re-prompting the exact same
command (#360), but allows the user to approve a fresh invocation of
the same tool type.
2026-05-10 16:43:16 -05:00
Hunter Bown 9ada15fc70 docs(handoff): roll user-issues doc forward to v0.8.28
Replaces the v0.8.27 handoff with a fresh v0.8.28 doc focused on
what's actually outstanding after the v0.8.27 ship:

- P1: CNB.cool mirror automation (token had no push perm during
  v0.8.27 release), Ctrl+Enter as newline config flag (#1372),
  Windows task_manager test timeout bump, general test flakiness
  audit.
- P2: comment-pinged issues awaiting reporter (#1112 snapshot
  growth, #1357 input/runtime overlap, #1281 Cmux notifications).
- P3: deferred items (#1338 Windows panic, #1062 capacity recovery,
  #1067 musl build, #1364 hooks v2, #1343 desktop GUI).

The v0.8.27 doc had ~25 items inline; the v0.8.28 doc only carries
what's still outstanding (everything else landed in the v0.8.27
cycle — see PR #1375). Starts smaller so the next agent can ship
a focused release rather than wade through completed work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:26:00 -05:00
Hunter Bown 87f253811f docs(readme): bump What's New to v0.8.27 (en + zh-CN)
Replaces the v0.8.25 / v0.8.26 What's New sections with the v0.8.27
headline summary across both READMEs. Covers cross-terminal flicker
fix, long-text wrap, pager copy-out, context-sensitive Ctrl+C, MCP
auto-reload, notify tool, onboarding localization, paste UX rebuild,
/skills filter + diagnostic hints, and the 17 community PRs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:23:46 -05:00
Hunter Bown aaccaee6fd Merge pull request #1375 from Hmbown/work/v0.8.27
chore(release): prepare v0.8.27
2026-05-10 12:13:52 -05:00
Hunter Bown 8956b698c0 fix(mcp): validate path inside mcp_config_mtime (CodeQL defensive)
CodeQL's rust/path-injection scan flagged `mcp_config_mtime(path)`
because the helper takes `&Path` and calls `fs::metadata(path)`.
Both call sites already validate via `validate_mcp_config_path` —
`from_config_path` runs the check before constructing the pool, and
`reload_if_config_changed` only sees paths that came from a
`from_config_path`-validated `config_source` field — so the alert
is a false positive about cross-function data flow.

The clean fix is to tie the validation to the call site rather
than rely on cross-function reasoning: `mcp_config_mtime` now
short-circuits to `None` for paths that fail the same allow-list
check `load_config` and `save_config` already use. The lazy-reload
caller already treats `None` as "skip the check this turn", so a
rejected path simply degrades gracefully rather than producing an
error path. Cost is one regex check per call on a path we're about
to stat anyway.

This also makes the helper safe-by-construction for any future
caller that forgets to validate, which matches the pattern of the
adjacent `load_config` / `save_config` helpers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 12:01:19 -05:00
Hunter Bown c87196835c feat(skills): /skills <prefix> filters the local list (#1318)
On top of v0.8.26's inter-row spacing for /skills (#1328 from
@reidliu41), the list now also accepts an optional name-prefix
argument so users with crowded skill folders can narrow the view
without scrolling.

  /skills           → full list (unchanged)
  /skills git       → only skills whose name starts with "git"
  /skills GIT       → same (case-insensitive)
  /skills nope      → "No skills match prefix `nope` (out of 12 …)"
  /skills --remote  → unchanged
  /skills sync      → unchanged
  /skills --bogus   → "Usage: …" error (rejected so future flags
                      don't silently turn into no-match prefixes)

The match-count header reflects both the matched count and the
registry total, so the user can see at a glance how aggressive
the filter is. Empty match sets explicitly say so and point back
at unfiltered `/skills`. Skill names that start with `-` aren't
allowed by the loader, so reserving the dash prefix for flags is
safe.

Plus the matching usage / description updates in the command
metadata + all four shipping locales (en / ja / zh-Hans / pt-BR)
so /help shows the new argument.

Closes #1318. Thanks @simuusang for the report.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:51:57 -05:00
Hunter Bown 3abd4dd988 feat(tui): localize onboarding screens (api key, trust, tips, picker)
When a user picked 简体中文 / 日本語 / Português (Brasil) on the
step-2 language picker, every subsequent onboarding screen used to
stay in English. The set_locale_from_onboarding path already
re-resolved `app.ui_locale`, but the hardcoded `Line::from(Span::
styled("Connect your DeepSeek API key", …))` strings in
`onboarding/api_key.rs`, `trust_directory.rs`, `language.rs`, and
the `tips_lines()` block in `onboarding/mod.rs` never consulted
the locale.

This commit:

- Adds 25 `MessageId` entries (`OnboardLanguageTitle`,
  `OnboardApiKey*`, `OnboardTrust*`, `OnboardTips*`, …) covering
  the title / body / hint / footer strings for each screen.
- Translates each into all four shipping locales (en / ja /
  zh-Hans / pt-BR), with the same care the existing translation
  surfaces use (no machine translation; idiomatic phrasing for
  each locale).
- Threads the active locale through `language::lines`,
  `api_key::lines`, `trust_directory::lines`, and `tips_lines`
  via `app.tr(MessageId::…)`.
- Adds `api_key_screen_renders_in_selected_locale` regression
  test pinning that the rendered lines actually contain the
  translated strings for zh-Hans / ja / en.

Particularly noticeable for users on CJK input methods: picking
their language at step 2 now means the remaining setup runs in
that language rather than forcing IME juggling for English text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:43:04 -05:00
Hunter Bown f267d4b874 fix(tui): paste-burst detects short CJK first line as paste (#1302)
Cherry-picked from @reidliu41's PR #1342. Pasting `请联网搜索:\n…`
(short non-ASCII first line + newline) used to fail the
`decide_begin_buffer` heuristic — `grabbed.chars().any(is_whitespace)`
is false on a 6-codepoint Chinese run, and `chars().count() >= 16`
is false at 6 chars — so the trailing pasted newline fell through
as a real Enter and submitted the first line on its own.

The heuristic now also treats `!grabbed.is_ascii()` as paste-like,
which captures the CJK case without false-firing on ASCII typing
(plain ASCII typists still need either whitespace or 16+ chars to
look like a paste).

Includes the regression test from PR #1342, slightly reworded.

Closes #1302. Thanks @reidliu41.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:30:57 -05:00
Hunter Bown 8bf2557692 feat(tui): consolidate large pastes at paste-time + auto-disable burst
Two paste-UX improvements that address recurring complaints:

**1. Visible-before-submit consolidation.** v0.7.x added a 16 000-char
safety cap that folded oversized inputs into `.deepseek/pastes/paste-
…md` and swapped them for an `@`-mention so the model could read the
full content via the normal mention-resolution path. The cap was
checked inside `submit_input` only — meaning a user who pasted 50k
chars and pressed Enter saw the file get created AND the message
sent in the same frame, with no chance to review the @-mention
beforehand. People reasonably read this as "the TUI auto-sent an
@-mention I didn't authorise." Consolidation now also runs at the
end of `insert_paste_text`, so the @-mention shows up in the
composer (along with a "consolidated — sent as @mention" toast) the
moment the paste lands. The submit-time path stays as a safety net
for any other code path that fills the buffer above the cap, so the
cap is still enforced exactly once.

**2. Auto-disable paste-burst on verified bracketed paste.** The
paste-burst heuristic (rapid-keystroke detection for terminals
without bracketed paste) used to run unconditionally. On modern
terminals (Ghostty / iTerm2 / WezTerm / Windows Terminal) bracketed
paste is reliable, and paste-burst running alongside it created
false positives — fast typing, IME commits, autocomplete bursts
could all be mis-classified as a paste. The new
`App::bracketed_paste_seen` flag flips to `true` the first time a
real `Event::Paste` arrives; from that moment, `handle_paste_burst_
key` short-circuits. Terminals that never deliver bracketed paste
(the original target audience) are unaffected — the heuristic
still fires for them.

Both changes have new unit tests:
- `paste_consolidates_oversized_text_into_paste_file_visibly`
- `paste_under_threshold_does_not_consolidate`
- `paste_burst_short_circuits_after_bracketed_paste_observed`

Existing `submit_input_consolidates_oversized_input_into_paste_file`
still passes — it bypasses `insert_paste_text` and exercises the
safety net.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:29:03 -05:00
Hunter Bown 524c513c03 chore: workspace clippy + missing-field fix-up for preflight
Two small workspace-clippy gaps that snuck through the per-crate
sweeps in this branch:

* `crates/cli/src/lib.rs` — the OpenAI-provider passthrough test was
  building a `ResolvedRuntimeOptions` literal directly and missed the
  `yolo: Option<bool>` field that landed earlier on this branch in
  665801bb8 (`fix(cli): forward --yolo to TUI binary`). Set to `None`
  to match the test's non-yolo intent.
* `crates/tui/src/mcp.rs` — the new `reload_if_config_changed` swap
  test was using `iter().any(|n| *n == "new")`, which is rust-1.94
  clippy's `manual_contains` lint. Switched to `names.contains(&"new")`.

`cargo clippy --workspace --all-targets --all-features --locked --
-D warnings` is now green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:15:09 -05:00
Hunter Bown 675a3c3d6f docs(changelog): reframe v0.8.27 opener to reflect bug-sweep scope
The 17-community-PR opener was accurate for the first half of the
cycle but undersells the user-issue work that landed afterwards
(flicker, wrap, pager copy-out, Ctrl+C, MCP auto-reload, notify
tool). Updated headline so the changelog matches the shipping
release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:11:29 -05:00
Hunter Bown 499937be50 feat(tools): add model-callable notify tool (#1322)
Adds a new always-loaded tool spec `notify` that lets the model
trigger a single desktop notification when a long-running task
completes or genuinely needs the user's attention. Implementation
delegates to the existing `tui::notifications` infrastructure, so
the user's `[notifications].method` config drives delivery:

- iTerm2 / Ghostty / WezTerm → OSC 9 (banner + sound)
- macOS / Linux fallback → BEL
- Windows → off by default; opt-in to BEL + MessageBeep
- `method = "off"` → silent no-op (the tool still succeeds)

Title and body are character-bounded (80 / 200 chars) and
trim-checked, so a runaway model can't paint a paragraph into the
terminal title bar or slice through a multi-byte sequence and emit
invalid UTF-8. tmux passthrough is detected via `$TMUX` and OSC 9
is double-escaped so the outer terminal still receives it.

The tool description steers the model away from chatter — only fire
on real completion / attention beats, not as a "still alive" ping.
Always-loaded (added to `should_default_defer_tool`'s allowlist) so
the model sees it without a ToolSearch round-trip; auto-approval
since the only side effect is a single terminal escape write.

Closes #1322.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:09:50 -05:00
Hunter Bown 0ee70178ef feat(mcp): lazy auto-reload of pool on mcp.json content change (#1267 pt2)
v0.8.26 surfaced the spawn-error tail (`Stdio transport closed` →
the underlying EACCES/sandbox-deny line). v0.8.27 closes the second
half of the report: users no longer need to manually `/mcp reload`
after editing `~/.deepseek/mcp.json`.

`McpPool` gained three fields: a `config_source` path (set when the
pool is built via `from_config_path`), a 64-bit content hash of the
active config, and the most recently observed mtime of the source
file. `reload_if_config_changed` does a cheap `stat` first; on
mtime-equal it returns immediately. Only when the mtime has moved
does the pool re-read the file, hash it, and compare to the stored
hash — content-unchanged reloads (e.g. `touch` on a networked FS)
are skipped. On a real content change the connections map is
cleared so the next `get_or_connect` reattaches under the new
config (sandbox flags, env, args, server set).

`get_or_connect` now invokes `reload_if_config_changed` at entry
and swallows its errors (a transient stat/parse failure can't take
down the existing pool). Pools built via `McpPool::new` (tests,
ad-hoc snapshots) are unaffected — they have no source path and
short-circuit out.

No file watcher, no long-lived task, no signature changes for the
existing callers.

Closes the part-2 follow-up on #1267.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:04:21 -05:00
Hunter Bown 21f9e9d38d feat(tui): context-sensitive Ctrl+C — copy / cancel / arm-exit (#1337, #1367)
Plain Ctrl+C used to mean "cancel turn or arm exit" unconditionally,
which fought the OS-wide copy convention on Windows: every time a
user pressed Ctrl+C to copy a model response, they instead armed the
exit prompt and lost their place. Ctrl+Shift+C and Cmd+C copied
correctly but weren't discoverable.

The handler is now a four-stage decision, factored into a
`CtrlCDisposition` helper with a unit-tested priority table:

  1. CopySelection — transcript selection active → copy + clear it
     (matches Windows / cross-platform Ctrl+C convention; #1337).
  2. CancelTurn — turn in flight → cancel (unchanged).
  3. ConfirmExit — quit-armed within the 2s window → exit.
  4. ArmExit — idle, no selection → arm the "press Ctrl+C again"
     prompt for 2s (unchanged).

A turn-in-flight beats a quit-arm even when both are true, so a
Ctrl+C that lands while the user is mid-turn but had recently
half-armed the exit prompt always cancels the turn rather than
exiting. Pinned by `ctrl_c_disposition_loading_beats_armed_quit`.

Cmd+C (macOS) and Ctrl+Shift+C continue to copy via `is_copy_shortcut`
unchanged; only plain Ctrl+C now branches on selection state.

For #1367, on TurnStarted the status-message slot now surfaces
"Press Esc or Ctrl+C to cancel" if it's empty. Real transient
messages still take precedence; the hint clears on the next status
update. Closes the discoverability gap for users who didn't know
how to interrupt a long-running task.

Closes #1337, #1367.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:57:57 -05:00
Hunter Bown be54a046d0 fix(markdown): hard-break overlong words to stop right-edge overflow
Both `render_line_with_links` (paragraphs, list items) and the
standalone `wrap_text` (code blocks) were word-based wrappers: when
a single word's display width exceeded the available column budget
they placed the word alone on a line and let it overflow the right
edge of the transcript silently. Long URLs, file paths, commit
hashes, JWTs, and any no-whitespace CJK run all hit this in #1344
and #1351 reports.

The fix mirrors the v0.8.25 table-cell fix (`wrap_cell_text`):
extract the per-character width-aware splitter as a free helper
`push_word_breaking_chars`, and call it from `wrap_text`,
`wrap_cell_text`, and the new char-break branch in
`render_line_with_links`. Each rendered line is now guaranteed to
fit in the requested width; full content is preserved across the
wrapped segments.

Snapshot-style regression tests pin the invariant at widths 40, 60,
80, and 120 — covering 200-char `a`-runs, long URL fixtures,
mixed-short+overlong-word fixtures, and the existing table-cell
property. A regression guard also confirms short words still break
on whitespace (no mid-word breaks for ordinary prose).

Closes #1344 (output-side overflow). Partial fix for #1351 (the
table-cell concern was already fixed in v0.8.25; the long-prompt
input-area concern is a separate visible-window issue, not a wrap
bug — the composer already uses a grapheme-based wrapper).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:51:49 -05:00
Hunter Bown aea6bb5f46 feat(tui): pager copy-out via c / y key (#1354)
The pager intercepts mouse capture, so terminal-native selection is
disabled inside it. Until now there was no in-app way to copy the
content the user came specifically to see — high-frustration UX gap
for the Alt+V (tool details), Ctrl+O (thinking), shell-job, task,
MCP-manager, and selection pagers.

Both `c` (clipboard convention) and `y` (vim-yank convention) now
emit a `ViewEvent::CopyToClipboard` carrying the full pager body.
The host dispatcher in `ui.rs` writes through `app.clipboard` and
toasts a status confirmation ("Pager content copied" /
"Copy failed"). Empty-body pagers report the empty state instead of
silently no-op'ing.

Footer hint updated to surface the new keys:
  j/k scroll  Space page  Ctrl+D/U half  g/G top/bottom  / search  c copy  q/Esc close

Mouse selection inside the pager remains intercepted (the alternative
— releasing capture inside the modal — would break vim-style
navigation), so this is the supported copy path.

Closes #1354.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:45:37 -05:00
Hunter Bown 328e3bd191 fix(skills): surface common-cause hints on /skills --remote / sync errors
The user-facing error path already formatted the underlying anyhow
chain with `{err:#}`, but reqwest chains alone read as opaque
fragments ("error sending request: tcp connect: connection refused"
etc.) for users without low-level network experience.

`format_registry_error` now inspects the formatted chain for common
failure signatures and appends a one-line hint:

- DNS lookup / `getaddrinfo` failures
- connection refused / reset / aborted
- TLS handshake / certificate / SSL
- HTTP 404 / 401 / 403 / 429
- request timed out

Each hint points at the most likely cause (network reachability,
trust store, registry URL, rate limit) and a concrete next step.
The original chain is still rendered verbatim above the hint, so
power users keep their detail and casual users get a starting
point.

Closes #1329 (the diagnostic side; the actual root cause is now
diagnosable from the surfaced chain + hint).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:40:57 -05:00
Hunter Bown abf3fa66f6 fix(tui): drop destructive 2J/3J from viewport reset to fix flicker
The forced-repaint sequence written before each TurnComplete /
focus-gain / resize used to be:

    \x1b[r\x1b[?6l\x1b[H\x1b[2J\x1b[3J

which combined with the immediately-following ratatui
`terminal.clear()` produced a double-clear. Terminals that don't
optimize successive clears against the alt-screen buffer (Ghostty,
VSCode integrated terminal, Win10 conhost) rendered the second
clear as a visible blank-then-repaint flicker on every redraw
trigger.

The lighter sequence `\x1b[r\x1b[?6l\x1b[H` resets DECSTBM and DECOM
and homes the cursor (still solving the original viewport-drift fix
that 0.8.22 added) but leaves the pixel-clear to ratatui's diff
renderer. The alt-screen buffer's double-buffering absorbs that
single clear without flicker on every terminal we tested. Terminals
that were already flicker-free (macOS Terminal.app, iTerm2,
alacritty) remain so.

Closes #1119, #1260, #1295, #1352, #1356, #1363, #1366.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:26:16 -05:00
Hunter Bown fe0673d683 test: ignore unwired #1270 tests; mark for v0.8.28 follow-up 2026-05-10 09:00:31 -05:00
Hunter Bown b8f01f1e49 fix: resolve clippy warnings from cherry-picked PRs 2026-05-10 08:55:53 -05:00
Hunter Bown 4c3b24a9a7 chore: update Cargo.lock for v0.8.27 2026-05-10 08:41:45 -05:00
Hunter Bown 6c25a18b42 chore(release): bump to v0.8.27, add CHANGELOG 2026-05-10 08:41:04 -05:00
imkingjh999 cb084d1564 style: fix fmt and clippy warnings 2026-05-10 08:37:25 -05:00
imkingjh999 5065092a11 fix: let OPENAI_MODEL env override config.toml provider model
Previously OPENAI_MODEL only set default_text_model which was lower
priority than the provider config model. Now it directly overrides
the openai provider's model field.
2026-05-10 08:37:25 -05:00
imkingjh999 a80c318098 fix: only use default_model fallback for DeepSeek providers
Other providers (openai, ollama) should get their model from
config.toml / env vars (e.g. OPENAI_MODEL), not from the global
default_model setting which is DeepSeek-centric.
2026-05-10 08:37:25 -05:00
imkingjh999 9fae156182 fix: address PR review feedback
- Use CARGO_PKG_VERSION for User-Agent instead of hardcoded version
- Restore default_model fallback for backward compatibility
2026-05-10 08:37:25 -05:00
imkingjh999 b024a92ec4 feat: persist provider/model per-provider in settings
- Save provider choice to settings.default_provider on switch
- Save model per-provider to settings.provider_models
- On startup, load provider-specific model instead of global default
- Hide DeepSeek models from picker on pass-through providers
- Show friendly message for /models on unsupported providers
2026-05-10 08:36:52 -05:00
lbcheng 0ab0d77b98 feat(tui): add card-rail (╭ │ ╰) boxing to tool output cells
Wraps ExecCell and GenericToolCell rendered output with card-rail
glyphs for visual structure, similar to Claude Code's card-style
tool rendering.

- wrap_card_rail(): adds ╭/│/╰ glyphs to rendered lines
- Applied to ExecCell::render and GenericToolCell::lines_with_mode
- Pre-computed caches (output_summary, is_diff) kept from previous
  commit for per-frame performance
- Live mode output remains visible (head+tail+omitted), not collapsed
- Card-rail glyphs reused from existing tool_card.rs CardRail enum

Test plan: cargo test -p deepseek-tui (2380 passed, 0 failed)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 08:34:55 -05:00
lbcheng 3cbd336bd5 perf(tui): pre-compute tool output summaries and collapse live view
Pre-compute render caches to avoid re-parsing every frame:
- Add output_summary: Option<String> to ExecCell and GenericToolCell
- Add is_diff: bool to GenericToolCell (cached after first detection)
- Populate caches once in handle_tool_call_complete / orphan path

Live mode rendering simplified to one-line summary + expand affordance:
- GenericToolCell and ExecCell now show a single muted summary line
  with "Enter to expand tool output" affordance in Live mode
- Transcript mode still emits full output
- render_tool_output_summary_line truncates to fit terminal width
- Make output_looks_like_diff pub(crate) for pre-computation access

Test plan:
- cargo test -p deepseek-tui (2379 passed)
- config_ui::build_document_reflects_app_state is a pre-existing failure

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 08:34:55 -05:00
lbcheng 9f2b8a5f14 fix(tui): move tolerance to module-level const, add total helpers
Promote COST_EQ_TOLERANCE from a function-local const to a module-level
constant in sidebar.rs.

Add SessionCostSnapshot::total_usd() and total_cny() helpers that
encapsulate session+subagent cost summation, used during session restore.

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

# Conflicts:
#	crates/tui/src/session_manager.rs
#	crates/tui/src/tui/sidebar.rs
#	crates/tui/src/tui/ui.rs
2026-05-10 08:32:25 -05:00
Hunter Bown a617e3af18 fix: add missing closing brace in project_context tests 2026-05-10 08:29:03 -05:00
samhandsome 0866fd6edc fix(tui): honor deepseekignore in mention fallback 2026-05-10 08:28:41 -05:00
manaskarra cdcb907780 feat(project-context): load global AGENTS fallback 2026-05-10 08:24:21 -05:00
lbcheng fddda04fca fix(tui): add composer_arrows_scroll config option for trackpad terminals
Instead of unconditionally changing Up/Down behavior, gate the
empty-composer-scroll path behind a new `tui.composer_arrows_scroll`
config option (default false).  Users whose terminals map trackpad
gestures to arrow keys can opt in via:

    [tui]
    composer_arrows_scroll = true

When enabled, empty-composer Up/Down scroll the transcript; otherwise
plain arrows always navigate input history (preserving #1117 default).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 08:20:44 -05:00
fuleinist 665801bb8e fix(cli): forward --yolo to TUI binary via DEEPSEEK_YOLO env
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.
2026-05-10 08:19:08 -05:00
whtis c227a805bd docs(readme): sync zh-CN with run pr dispatcher path 2026-05-10 08:19:07 -05:00
THINKER_ONLY 0fedee00cc feat(tui): add session artifact metadata 2026-05-10 08:16:38 -05:00
reidliu41 0ccd4e731c Add feedback command for GitHub links
Add a /feedback command for opening project feedback links.

  The command shows a picker when run without arguments and supports direct
  bug, feature, and security targets. Bug and feature options open the matching
  GitHub issue templates, while security opens the repository security policy.
2026-05-10 08:16:13 -05:00
reidliu41 24ec0839e1 Add runtime status command
Add a dedicated /status command that reports the current runtime session state.

  The new report shows provider, model, workspace, mode, permissions, session,
  context usage, token telemetry, cache telemetry, cost, transcript counts, and
  rate-limit availability. /statusline remains available for footer configuration.
2026-05-10 08:16:07 -05:00
reidliu41 4f8eff0c69 refactor: unify mode switching under /mode
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.
2026-05-10 08:15:19 -05:00
THINKER_ONLY 250953ad35 feat(tools/agent_spawn): teach parent that subagent results are self-reports 2026-05-10 08:15:19 -05:00
dst1213 682e915857 fix: retry quota errors returned as HTTP 400 2026-05-10 08:15:19 -05:00
Zhiping 39fd5379fd docs(tui): clarify Windows mouse capture behavior 2026-05-10 08:15:19 -05:00
Zhang Yonglun fd5a0aaec5 fix(tests): cover hook event dispatch paths 2026-05-10 08:15:19 -05:00
Zhang Yonglun 0428f08625 fix(tests): cover approval decision branches 2026-05-10 08:15:19 -05:00