docs(notifications): only completed turns notify; add Key Reference + WezTerm-on-Windows test
Post-merge review feedback on #583 surfaced four small accuracy gaps: 1. The narrative docs in `docs/CONFIGURATION.md` and the inline comment in `config.example.toml` said the notification fires "when a turn takes longer than a threshold" — but the call site in `tui/ui.rs:928` is gated on `TurnOutcomeStatus::Completed`. Failed and cancelled turns are silent on purpose. Spell that out so users don't expect alerts on long failures. 2. The `notify_done` rustdoc still summarised `Auto` as "Osc9 for known terminals, Bel otherwise" — internally inconsistent with the new Windows-aware fallback documented one screen earlier on the `Method::Auto` enum and on `resolve_method`. Update the public rustdoc to point at the canonical resolution table on `resolve_method` and call out the `Off`-on-Windows branch. 3. The `## Key Reference` list in `docs/CONFIGURATION.md` had no entries for `[notifications].method`, `[notifications].threshold_secs`, or `[notifications].include_summary`. Other features with a dedicated subsection (e.g. `[memory].enabled`) are listed there too, so readers scanning the canonical key list could not discover the notification knobs. Added the three keys with cross-references to the Notifications subsection. 4. The Windows-only test only covered the unknown-`TERM_PROGRAM` → `Off` fallback. The positive path (known OSC-9 terminal still resolves to `Osc9`) was only tested via `iTerm.app`, which is a macOS-only program — Windows CI would still pass if the `WezTerm` arm of the match disappeared. Added `auto_detect_picks_osc9_for_wezterm_on_windows` so the WezTerm-on-Windows compatibility guarantee is exercised on the Windows runner. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
-2
@@ -281,8 +281,10 @@ default_text_model = "deepseek-ai/deepseek-v4-pro"
|
||||
# ─────────────────────────────────────────────────────────────────────────────────
|
||||
# Desktop Notifications (OSC 9 / BEL on long agent-turn completion)
|
||||
# ─────────────────────────────────────────────────────────────────────────────────
|
||||
# Emits an escape sequence to the terminal when a turn finishes and took longer
|
||||
# than `threshold_secs`. Useful when you tab away from the TUI and want an alert.
|
||||
# Emits an escape sequence to the terminal when a turn **completes successfully**
|
||||
# and took longer than `threshold_secs`. Failed or cancelled turns are
|
||||
# intentionally silent. Useful when you tab away from the TUI and want an alert
|
||||
# for "your task is ready".
|
||||
#
|
||||
# method = "auto" # auto | osc9 | bel | off
|
||||
# auto: OSC 9 for iTerm.app / Ghostty / WezTerm.
|
||||
|
||||
@@ -122,9 +122,13 @@ pub fn notify_done_to<W: Write>(
|
||||
|
||||
/// Emit a turn-complete notification to **stdout** if `elapsed >= threshold`.
|
||||
///
|
||||
/// With `method = Auto`, selects `Osc9` for known capable terminals and `Bel`
|
||||
/// otherwise. Pass `in_tmux = true` (i.e. `$TMUX` is non-empty at runtime)
|
||||
/// to wrap OSC 9 in a DCS passthrough.
|
||||
/// With `method = Auto`, selects `Osc9` for known capable terminals
|
||||
/// (`iTerm.app`, `Ghostty`, `WezTerm`); the unknown-terminal fallback is
|
||||
/// platform-aware — `Bel` on macOS / Linux, `Off` on Windows (where BEL
|
||||
/// maps to the `SystemAsterisk` / `MB_OK` error chime, #583). See
|
||||
/// [`resolve_method`] for the canonical resolution table. Pass
|
||||
/// `in_tmux = true` (i.e. `$TMUX` is non-empty at runtime) to wrap OSC 9
|
||||
/// in a DCS passthrough.
|
||||
pub fn notify_done(
|
||||
method: Method,
|
||||
in_tmux: bool,
|
||||
@@ -310,9 +314,7 @@ mod tests {
|
||||
|
||||
/// #583: on Windows, an unknown TERM_PROGRAM resolves to `Off`
|
||||
/// (not `Bel`) so the post-turn notification doesn't ring the
|
||||
/// `SystemAsterisk` / `MB_OK` chime. Known OSC-9 terminals like
|
||||
/// WezTerm still resolve to `Osc9` — see the iTerm test, which
|
||||
/// also exercises the OSC-9 branch on Windows.
|
||||
/// `SystemAsterisk` / `MB_OK` chime.
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn auto_detect_picks_off_for_unknown_on_windows() {
|
||||
@@ -331,6 +333,31 @@ mod tests {
|
||||
assert_eq!(resolved, Method::Off);
|
||||
}
|
||||
|
||||
/// #583: known OSC-9 terminals must still resolve to `Osc9` on
|
||||
/// Windows — the off-fallback only applies to unrecognised
|
||||
/// `TERM_PROGRAM`. The cross-platform iTerm test above is a thin
|
||||
/// proxy because iTerm itself only runs on macOS; if the WezTerm
|
||||
/// arm of the match silently disappeared, that test would still
|
||||
/// pass on the Windows runner and we'd lose the WezTerm-on-Windows
|
||||
/// compatibility guarantee. Pin it directly.
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn auto_detect_picks_osc9_for_wezterm_on_windows() {
|
||||
let _lock = env_lock();
|
||||
let prev = std::env::var_os("TERM_PROGRAM");
|
||||
// SAFETY: test-only; serialised by env_lock().
|
||||
unsafe { std::env::set_var("TERM_PROGRAM", "WezTerm") };
|
||||
let resolved = resolve_method();
|
||||
// SAFETY: test-only; serialised by env_lock().
|
||||
unsafe {
|
||||
match prev {
|
||||
Some(v) => std::env::set_var("TERM_PROGRAM", v),
|
||||
None => std::env::remove_var("TERM_PROGRAM"),
|
||||
}
|
||||
}
|
||||
assert_eq!(resolved, Method::Osc9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn humanize_duration_seconds_and_minutes() {
|
||||
assert_eq!(humanize_duration(Duration::from_secs(0)), "0s");
|
||||
|
||||
+15
-1
@@ -368,6 +368,20 @@ If you are upgrading from older releases:
|
||||
- `[capacity].deepseek_v4_pro_prior` (float, default `3.5`)
|
||||
- `[capacity].deepseek_v4_flash_prior` (float, default `4.2`)
|
||||
- `[capacity].fallback_default_prior` (float, default `3.8`)
|
||||
- `[notifications].method` (string, optional): `auto`, `osc9`, `bel`, or
|
||||
`off`. Defaults to `auto`. The TUI fires this on completed (successful)
|
||||
turns whose elapsed time meets `threshold_secs`; failed and cancelled
|
||||
turns are silent. `auto` resolves to `osc9` for `iTerm.app`, `Ghostty`,
|
||||
and `WezTerm` (detected via `$TERM_PROGRAM`). Otherwise the fallback is
|
||||
`bel` on macOS / Linux and `off` on Windows (where BEL maps to the
|
||||
system error chime — see the [Notifications](#notifications) section
|
||||
for the full rationale, #583).
|
||||
- `[notifications].threshold_secs` (int, optional): defaults to `30`.
|
||||
Only completed turns whose elapsed time meets or exceeds this fire a
|
||||
notification.
|
||||
- `[notifications].include_summary` (bool, optional): defaults to
|
||||
`false`. When `true`, the notification body includes the elapsed
|
||||
duration and the turn's USD cost.
|
||||
- `tui.alternate_screen` (string, optional): `auto`, `always`, or `never`. `auto` disables the alternate screen in Zellij; `--no-alt-screen` forces inline mode. Set `never` or run with `--no-alt-screen` when you want real terminal scrollback.
|
||||
- `tui.mouse_capture` (bool, optional, default `true` when the alternate screen is active): enable internal mouse scrolling, transcript selection, and right-click context actions. TUI-owned drag selection copies only user/assistant transcript text. Set this to `false` or run with `--no-mouse-capture` for raw terminal selection.
|
||||
- `tui.terminal_probe_timeout_ms` (int, optional, default `500`): startup terminal-mode probe timeout in milliseconds. Values are clamped to `100..=5000`; timeout emits a warning and aborts startup instead of hanging indefinitely.
|
||||
@@ -402,7 +416,7 @@ Notes:
|
||||
|
||||
### Notifications
|
||||
|
||||
The TUI can emit a desktop notification (OSC 9 escape or plain BEL) when a turn takes longer than a threshold, so you can tab away while a long task runs. Configuration lives under `[notifications]`:
|
||||
The TUI can emit a desktop notification (OSC 9 escape or plain BEL) when a turn **completes successfully** and took longer than a threshold, so you can tab away while a long task runs. Failed or cancelled turns are intentionally silent — the notification is a "your task is ready" cue, not a generic ping. Configuration lives under `[notifications]`:
|
||||
|
||||
```toml
|
||||
[notifications]
|
||||
|
||||
Reference in New Issue
Block a user