Commit Graph

1436 Commits

Author SHA1 Message Date
Hunter Bown 96b903aa39 fix(tui): filter terminal control sequence fragments from composer
Prevent leaked OSC/Kitty terminal control fragments from landing in composer input while preserving legitimate overlapping user text.
2026-05-23 13:18:00 -05:00
Hunter Bown 6356a810c5 fix(tui): launch feedback urls asynchronously
Keep feedback URL launching off the UI path so feedback actions do not stall interactive input.
2026-05-23 13:17:51 -05:00
Hunter Bown 1777891137 docs(readme): mark V4 Pro rates as permanent
Update README pricing language to describe V4 Pro rates as permanent rather than temporary.
2026-05-23 13:08:02 -05:00
Hunter Bown ee47d64597 fix(tui): quiet animations under tmux
Force low-motion terminal behavior under tmux/screen and keep the fallback footer label stable so passive animations do not register as activity.
2026-05-23 13:06:57 -05:00
Hunter Bown 4143409208 fix(tui): detach clipboard fallback waits
Detach clipboard fallback helpers from the Enter hot path so terminal paste/clipboard probing cannot stall submission.
2026-05-23 13:06:46 -05:00
Hunter Bown c675fe64d2 fix(tui): offload offline queue persistence
Move offline queue persistence off the UI hot path so Enter handling does not wait on synchronous disk work.
2026-05-23 13:06:32 -05:00
Hunter Bown 7a87aebec7 fix(tui): move composer history persistence off UI thread
Move composer history persistence off the Enter hot path so submitting messages does not block the TUI event loop.
2026-05-23 13:05:59 -05:00
Hunter Bown d9fb21c0a0 fix(tui): avoid blocking on bare separator mentions
Skip bare separator and dot tokens during @-mention completion so Windows and WSL2 workspaces do not trigger an eager filesystem walk on the UI thread.
2026-05-23 13:05:48 -05:00
Hunter Bown c09f5bf039 fix(subagent): keep agent_eval recoverable after child termination
Keep agent_eval recoverable after a child session terminates by preserving the available transcript result instead of losing the final output.
2026-05-23 13:05:36 -05:00
Hunter Bown b7e31a734b test: isolate tool-result spillover tests
Isolate SHA spillover tests with the existing guard and a temporary spillover root so Windows parallel test runs cannot race shared spillover state.
2026-05-23 12:50:02 -05:00
Hunter Bown 1ca495f5a3 style: cargo fmt 2026-05-21 16:22:23 +08:00
Zhongyue Lin a5501e608a fix(client): keep mutation-tool result confirmations off wire dedup (#1695)
Co-Authored-By: LeoLin990405 <126708262+LeoLin990405@users.noreply.github.com>
2026-05-21 03:21:42 -05:00
Hunter Bown faf5e07adb feat(tui): add Fin tool-agent and screenshot OCR 2026-05-21 10:47:14 +08:00
Hunter Bown 3c95eadc22 test(tui): satisfy model picker lint 2026-05-21 09:51:18 +08:00
Hunter Bown 396b0e822b fix(tui): persist model picker effort 2026-05-21 09:45:03 +08:00
Hunter Bown 26c66079ba fix(tui): persist selected model mode 2026-05-21 09:15:52 +08:00
Hunter Bown 67fb788110 fix(tui): harvest final v0.8.40 polish 2026-05-21 08:45:04 +08:00
Hunter Bown b639740b73 chore: bump CHANGELOG date to 2026-05-21 for v0.8.40 release 2026-05-21 06:52:48 +08:00
Hunter Bown 66b4a5fd59 fix(tui): rank exact slash aliases first 2026-05-21 01:00:18 +08:00
Hunter Bown 9f0ff4be93 fix(subagents): allow explicit write-role edits 2026-05-21 00:54:27 +08:00
Hunter Bown ad122fd6f8 feat(subagents): make step API timeout configurable 2026-05-21 00:45:04 +08:00
Hunter Bown 2c642ec375 feat(session): fork conversations inside the TUI 2026-05-21 00:24:52 +08:00
Hunter Bown d065241c93 fix(tui): clear all displayed cost state 2026-05-21 00:13:02 +08:00
Hunter Bown 6821f294e2 fix(rlm): decode stdout lossily 2026-05-21 00:12:59 +08:00
Hunter Bown ae1bb9dd95 fix(tui): avoid grayscale luma overflow 2026-05-21 00:12:55 +08:00
Hunter Bown 57958dd444 fix(feishu): reattach active turns on restart 2026-05-21 00:03:28 +08:00
Hunter Bown f8aa5b95e0 docs(subagents): clarify detached session lifecycle 2026-05-21 00:03:17 +08:00
Hunter Bown 0e222ed0bd fix(review): parse encoded JSON output 2026-05-21 00:03:02 +08:00
Hunter Bown eed4c2dfa6 fix(eval): preserve quoted shell payloads 2026-05-21 00:02:52 +08:00
Hunter Bown a595edd56d fix(logging): rotate TUI logs per process 2026-05-21 00:02:41 +08:00
Hunter Bown e63a4ba4a9 fix(tui): preserve drafts around mouse reports 2026-05-21 00:02:11 +08:00
Hunter Bown 8597afc076 feat(provider): add Wanjie Ark support 2026-05-21 00:02:02 +08:00
Hunter Bown 938d681edb ci(cnb): preflight stability release branches 2026-05-20 23:57:47 +08:00
Hunter Bown d106773178 test(shell): stabilize Windows background helper 2026-05-20 23:57:38 +08:00
Hunter Bown 912da38cca chore(release): prepare v0.8.40 2026-05-18 23:29:20 +08:00
Nightt 4144fc6a2b test(tui): cover steer prompt restore state 2026-05-18 23:07:22 +08:00
Nightt 710505db76 fix(tui): stop rendering after cancel restore 2026-05-18 23:07:22 +08:00
Nightt 4a03f83997 fix(tui): restore cancelled prompt on ctrl-c 2026-05-18 23:07:22 +08:00
Paulo Aboim Pinto ba8b4b7adf fix: remove skip-past-newline from move_cursor_line_end
Per gemini-code-assist review on #1749: End on a newline character
should stay at the end of the current line (idempotent), not skip
to the next line.  Removes the non-standard skip-past-newline logic
and updates the associated tests.
2026-05-18 23:07:13 +08:00
Paulo Aboim Pinto f77a07e207 feat: Home/End jump to line start/end in multiline composer
Plain Home and End now navigate within the current line instead of
jumping to the absolute start/end of the entire input.  Ctrl+A and
Ctrl+E remain as absolute start/end shortcuts.

- Add move_cursor_line_start() / move_cursor_line_end() to App
- Wire Home -> move_cursor_line_start(), End -> move_cursor_line_end()
- On single-line input the new methods behave identically to the
  absolute versions (no behaviour change)
- End on a newline character skips to the end of the next line
- 14 tests covering multiline, singleline, and edge cases
2026-05-18 23:07:13 +08:00
Zhongyue Lin 0ce41505bc fix(shell): robust cmd detection + stronger test assertion (review #1744)
Address gemini-code-assist review on PR #1744 (two MEDIUM):

- cmd detection used program.eq_ignore_ascii_case("cmd"), which fails
  for a full path (C:\Windows\System32\cmd.exe) or a .exe suffix, so the
  raw_arg quoting fix would not apply. Use Path::file_stem() instead
  (fully-qualified std::path::Path -> no unused import off-Windows).
- Strengthen the Windows block of issue_1691_quoted_commit_message_round_trips
  to assert argv content equals spec.args, not just arg count, so the
  raw_arg payload (quotes preserved, no extra escaping) is actually
  verified. sandbox/mod.rs already asserts content -- left untouched.

Windows paths are cfg-gated (compile-checked on macOS, executed on
Windows CI). macOS build + clippy clean.

Refs #1691.
2026-05-18 23:06:52 +08:00
Zhongyue Lin b3223cd48d fix(shell): preserve quoted args on Windows cmd /C invocation (#1691)
git commit -m "feat: complete sub-pages" failed on Windows with
'pathspec sub-pages" did not match' because the quoted -m message was
split on spaces. Root cause is not a tokenizer bug: CommandSpec::shell()
builds 'cmd /C "chcp 65001 >/dev/null & <command>"' and std::process::Command
applies MSVCRT escaping (" -> \"); cmd.exe does not use MSVCRT parsing,
so the quoting is destroyed and git receives feat:/complete/sub-pages"
as separate pathspecs. The Unix sh -c path was already correct.

Add a cfg-split push_shell_args() replacing cmd.args() at the 3 std
spawn sites in shell.rs. On Windows, for the cmd /C <payload> shape
only, pass /C and payload via CommandExt::raw_arg so the string reaches
cmd.exe verbatim (as a terminal does); other programs keep normal
escaping. Non-Windows is a faithful pass-through (byte-for-byte
unchanged). portable_pty path intentionally untouched (out of scope).

The Unix path is provably unchanged (tested); the Windows raw_arg
runtime correctness is only verifiable on a Windows runner -- flagged in
the PR for Windows CI verification per the #1736 Windows policy.

Refs #1691, #1736.
2026-05-18 23:06:52 +08:00
Zhongyue Lin 351898d05f fix(tui): emit thinking-only notice only at true turn end (review #1742)
Address gemini-code-assist review on PR #1742 (MEDIUM): the status was
emitted at the assistant-persist site, but the same turn can still
CONTINUE for pending steers or sub-agent completions -- the user would
see a spurious 'turn ended without output' notice immediately before the
turn resumed.

Capture thinking_only_no_sendable at the persist site (no emission
there) and decide at the end of the tool_uses.is_empty() path, just
before the terminal break -- reachable only when there were no pending
steers, no sub-agent completions, and we were not holding for running
children. Extend should_emit_thinking_only_status with steers_pending
and holding_for_subagents (false if either), recomputed live at the
decision point as defense-in-depth. Unit test updated with the two new
no-emit cases.

Refs #1727.
2026-05-18 23:06:52 +08:00
Zhongyue Lin 4ab7c53442 fix(tui): surface thinking-only turns instead of silently ending (#1727)
When a model streams a turn with only a reasoning block (empty content,
no tool_calls -- e.g. gpt-oss via ollama's harmony->OpenAI shim mapping
to reasoning_content), has_sendable_assistant_content is false: the
if-only persist branch was skipped, NO event was emitted, and the turn
fell through to break. The UI spinner hung with no reply and no error.

Add an else-if at the same persist site that, only on a clean end
(tool_uses empty, turn_error.is_none(), not cancelled), warns and emits
an Event::status notice telling the user the turn ended and they can
retry. No assistant message is persisted (prior behavior preserved); no
retry/re-prompt/placeholder policy is added (out of scope). Guard
ordering extracted into a pure should_emit_thinking_only_status() helper
with a unit test, matching the existing should_hold_turn_for_subagents
style. Maintainer audit #1736 confirms #1727 is not shipped in v0.8.39
and release-blocking.

Refs #1727, #1736.
2026-05-18 23:06:52 +08:00
Zhongyue Lin 323f43df60 fix(client): align stream reasoning classification with replay (review #1743)
Address gemini-code-assist review on PR #1743:

- HIGH: should_replay_reasoning_content_for_provider was made model-aware
  in the previous commit, but handle_chat_completion_stream still computed
  is_reasoning_model = requires_reasoning_content(model) &&
  provider_accepts_reasoning_content(provider). On the openai provider +
  a DeepSeek model that was false during SSE parsing, so reasoning tokens
  were stored as content (not reasoning_content) and the next request
  still 400'd -- the fix was incomplete. Extract is_reasoning_model_for_stream()
  and route the stream call site through it; add an equivalence test
  locking it to the replay predicate so the two paths can't drift.
- MEDIUM: rename generic_openai_provider_drops_deepseek_reasoning_content
  -> generic_openai_provider_drops_reasoning_content_for_non_deepseek_models
  (now uses gpt-4o; old name was misleading).

Non-DeepSeek models on any provider are unaffected (#1542 not regressed).

Refs #1739, #1694, #1542.
2026-05-18 23:06:52 +08:00
Zhongyue Lin 1a5ee2f67d fix(client): replay reasoning_content for DeepSeek models on openai provider (#1739)
should_replay_reasoning_content_for_provider() returned false whenever
provider_accepts_reasoning_content(provider) was false (true for
ApiProvider::Openai) without checking the model. This single gate feeds
both build_for_provider (include_reasoning) and
sanitize_thinking_mode_messages, so a DeepSeek reasoning model on the
generic openai provider (DeepSeek-compatible endpoint) had all
reasoning_content stripped -> the DeepSeek thinking-mode API 400s
('reasoning_content in the thinking mode must be passed back'). This is
the over-aggressive half of ac01b225 (fix #1542).

Gate the early return on the model too:
!provider_accepts_reasoning_content(provider) && !requires_reasoning_content(model).
Known DeepSeek reasoning models replay regardless of provider; genuine
non-DeepSeek models on openai still strip (effort=off still wins). #1542
not regressed (provider_accepts_reasoning_content untouched).

Two pre-existing client.rs tests asserted the buggy case (deepseek-v4-pro
on Openai -> dropped); retargeted to gpt-4o to preserve their #1542
intent without encoding the bug. New positive/negative coverage in
chat.rs.

Refs #1739, #1694, #1542, #1736.
2026-05-18 23:06:52 +08:00
Zhongyue Lin 3adc05d627 style(config): mark unreachable DeepSeek match arms (review #1740)
Address gemini-code-assist review on PR #1740 (MEDIUM, clarity): the
non-DeepSeek else-branch match listed ApiProvider::Deepseek /
DeepseekCN arms that are logically unreachable (handled by the if
branch above). Collapse to a single
'Deepseek | DeepseekCN => unreachable!(...)' arm so the intent is
explicit for future maintainers. No behavior change.

Refs #1714.
2026-05-18 23:06:52 +08:00
Zhongyue Lin b156d7da33 fix(config): honor explicit custom model for non-DeepSeek providers (#1714)
The CLI --model handoff only exports DEEPSEEK_MODEL, which apply_env_overrides
funneled into the DeepSeek-only root default_text_model slot. default_model()
then treated it as a normalizable weak default and fell back to a
DeepSeek/provider default for an unrecognized custom model on a non-DeepSeek
provider, so e.g. --provider openai --model MiniMax-M2.7 silently sent a
DeepSeek model to the endpoint.

Route DEEPSEEK_MODEL into the active provider's model slot for non-DeepSeek
providers (mirroring the existing OPENAI_MODEL branch), and return an explicit
non-DeepSeek-alias model verbatim from default_model(). DeepSeek/DeepSeekCN
behavior is unchanged. Adds a regression test next to
openai_provider_accepts_custom_model_and_base_url.

Refs #1714, #1739, #1736.
2026-05-18 23:06:52 +08:00
Paulo Aboim Pinto 1874359ac5 fix: stop RUST_LOG from leaking tracing messages into TUI alt-screen on Windows
Only DEEPSEEK_LOG_LEVEL should gate verbose CLI output. RUST_LOG controls
the tracing subscriber independently (file logging). On Windows stderr is
not redirected to the log file, so coupling the two causes tracing log
messages to leak into the TUI alt-screen, corrupting the display.

Closes #1774
2026-05-18 23:06:52 +08:00
jayzhu 5c452328ab fix: defer clipboard init to avoid blocking TUI startup on X11-less hosts
On Linux, `arboard::Clipboard::new()` opens a blocking connect() to the
X11 Unix socket. When no X server is running (headless, WSL2 without
WSLg), the call hangs indefinitely. Because raw mode and the alternate
screen are already active at that point, Ctrl+C no longer generates
SIGINT and the event loop hasn't started yet — leaving the user with a
blank screen and no way to exit.

Move clipboard initialization from `ClipboardHandler::new()` (called
synchronously during App construction) to a lazy `ensure_clipboard()`
that runs on first read/write with a 500 ms timeout. If the X11
connection doesn't respond in time, the handler stays in fallback mode
and `write_text` falls through to the existing OSC 52 / pbcopy /
PowerShell paths.
2026-05-18 23:06:52 +08:00