diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_hook_background_and_failure_snapshot.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_hook_background_and_failure_snapshot.snap index a856a10101..aa44d32b8b 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_hook_background_and_failure_snapshot.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_hook_background_and_failure_snapshot.snap @@ -9,5 +9,8 @@ history: visible history: +• PostToolUse hook (completed) + warning: background hook warning + • PostToolUse hook (failed) error: hook exited with code 7 diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index f1e54c385e..9332618eb5 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -2381,14 +2381,38 @@ async fn hidden_hooks_skip_background_rendering_but_keep_failures_visible() { let quiet_snapshot = hook_live_and_history_snapshot(&chat, "hidden background hook", ""); assert!(drain_insert_history(&mut rx).is_empty()); - let mut hidden_failed = hook_completed_run( + let mut hidden_warning = hook_completed_run( "post-tool-use:1:/tmp/hooks.json", codex_app_server_protocol::HookEventName::PostToolUse, + codex_app_server_protocol::HookRunStatus::Completed, + vec![ + codex_app_server_protocol::HookOutputEntry { + kind: codex_app_server_protocol::HookOutputEntryKind::Warning, + text: "background hook warning".to_string(), + }, + codex_app_server_protocol::HookOutputEntry { + kind: codex_app_server_protocol::HookOutputEntryKind::Context, + text: "verbose hidden context".to_string(), + }, + ], + ); + hidden_warning.visibility_hint = codex_app_server_protocol::HookVisibilityHint::Hidden; + handle_hook_completed(&mut chat, hidden_warning); + + let mut hidden_failed = hook_completed_run( + "post-tool-use:2:/tmp/hooks.json", + codex_app_server_protocol::HookEventName::PostToolUse, codex_app_server_protocol::HookRunStatus::Failed, - vec![codex_app_server_protocol::HookOutputEntry { - kind: codex_app_server_protocol::HookOutputEntryKind::Error, - text: "hook exited with code 7".to_string(), - }], + vec![ + codex_app_server_protocol::HookOutputEntry { + kind: codex_app_server_protocol::HookOutputEntryKind::Context, + text: "verbose hidden context".to_string(), + }, + codex_app_server_protocol::HookOutputEntry { + kind: codex_app_server_protocol::HookOutputEntryKind::Error, + text: "hook exited with code 7".to_string(), + }, + ], ); hidden_failed.visibility_hint = codex_app_server_protocol::HookVisibilityHint::Hidden; handle_hook_completed(&mut chat, hidden_failed); diff --git a/codex-rs/tui/src/history_cell/hook_cell.rs b/codex-rs/tui/src/history_cell/hook_cell.rs index 1b27953068..ddd8bb442f 100644 --- a/codex-rs/tui/src/history_cell/hook_cell.rs +++ b/codex-rs/tui/src/history_cell/hook_cell.rs @@ -231,12 +231,16 @@ impl HookCell { status_message, status, entries, + visibility_hint, .. } = run; let existing = &mut self.runs[index]; existing.event_name = event_name; existing.status_message = status_message; - existing.state = HookRunState::completed(status, entries); + existing.state = HookRunState::completed( + status, + visible_hook_output_entries(visibility_hint, entries), + ); true } @@ -253,13 +257,17 @@ impl HookCell { status_message, status, entries, + visibility_hint, .. } = run; self.runs.push(HookRunCell { id, event_name, status_message, - state: HookRunState::completed(status, entries), + state: HookRunState::completed( + status, + visible_hook_output_entries(visibility_hint, entries), + ), }); } @@ -693,11 +701,28 @@ pub(crate) fn hook_run_is_hidden(run: &HookRunSummary) -> bool { HookRunStatus::Completed => run .entries .iter() - .all(|entry| entry.kind == HookOutputEntryKind::Context), + .all(|entry| !hook_output_entry_is_visible(run.visibility_hint, entry.kind)), HookRunStatus::Blocked | HookRunStatus::Failed | HookRunStatus::Stopped => false, } } +fn visible_hook_output_entries( + visibility_hint: HookVisibilityHint, + entries: Vec, +) -> Vec { + entries + .into_iter() + .filter(|entry| hook_output_entry_is_visible(visibility_hint, entry.kind)) + .collect() +} + +fn hook_output_entry_is_visible( + visibility_hint: HookVisibilityHint, + kind: HookOutputEntryKind, +) -> bool { + visibility_hint != HookVisibilityHint::Hidden || kind != HookOutputEntryKind::Context +} + fn hook_completed_bullet(status: HookRunStatus, entries: &[HookOutputEntry]) -> Span<'static> { match status { HookRunStatus::Completed => {