From c28071a8ce72bb2016190565b8046797b901add7 Mon Sep 17 00:00:00 2001 From: Friel Date: Fri, 3 Apr 2026 23:09:22 +0000 Subject: [PATCH] Handle non-root watchdog close events in TUI --- codex-rs/tui/src/app.rs | 78 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index cf99ea30c3..9e7bcafc5c 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -2237,9 +2237,18 @@ impl App { _ => {} } } else { - let updates = self.subagents.on_agent_event(thread_id, &event.msg); - for cell in updates { - self.emit_or_queue_subagent_history(cell); + match &event.msg { + EventMsg::CollabCloseEnd(ev) => { + if let Some(cell) = self.subagents.on_close_end(ev) { + self.emit_or_queue_subagent_history(cell); + } + } + _ => { + let updates = self.subagents.on_agent_event(thread_id, &event.msg); + for cell in updates { + self.emit_or_queue_subagent_history(cell); + } + } } } @@ -7913,6 +7922,69 @@ mod tests { assert!(screen.contains("watchdog-agent")); } + #[tokio::test] + async fn process_subagent_side_effects_handles_non_root_watchdog_close_end() { + let (mut app, mut app_event_rx, _op_rx) = make_test_app_with_channels().await; + let root_thread_id = ThreadId::new(); + let watchdog_thread_id = ThreadId::new(); + let helper_thread_id = ThreadId::new(); + + app.primary_thread_id = Some(root_thread_id); + app.active_thread_id = Some(root_thread_id); + + app.process_subagent_side_effects( + root_thread_id, + &Event { + id: "spawn-watchdog".to_string(), + msg: EventMsg::CollabAgentSpawnEnd(CollabAgentSpawnEndEvent { + call_id: "spawn-watchdog".to_string(), + sender_thread_id: root_thread_id, + new_thread_id: Some(watchdog_thread_id), + new_agent_nickname: Some("Hume".to_string()), + new_agent_role: Some("watchdog".to_string()), + prompt: "watchdog prompt".to_string(), + model: "gpt-test".to_string(), + reasoning_effort: ReasoningEffortConfig::Low, + spawn_mode: CollabAgentSpawnMode::Watchdog, + status: AgentStatus::PendingInit, + }), + }, + ); + while app_event_rx.try_recv().is_ok() {} + + app.process_subagent_side_effects( + helper_thread_id, + &Event { + id: "close-watchdog".to_string(), + msg: EventMsg::CollabCloseEnd(CollabCloseEndEvent { + call_id: "close-watchdog".to_string(), + sender_thread_id: helper_thread_id, + receiver_thread_id: watchdog_thread_id, + receiver_agent_nickname: Some("Hume".to_string()), + receiver_agent_role: Some("watchdog".to_string()), + status: AgentStatus::Shutdown, + }), + }, + ); + + let mut saw_shutdown_update = false; + let mut saw_clear_panel = false; + while let Ok(event) = app_event_rx.try_recv() { + match event { + AppEvent::InsertHistoryCell(cell) => { + let transcript = lines_to_single_string(&cell.transcript_lines(/*width*/ 80)); + saw_shutdown_update |= transcript.contains("shutdown"); + } + AppEvent::ClearSubagentPanel => saw_clear_panel = true, + _ => {} + } + } + + assert!(saw_shutdown_update); + assert!(saw_clear_panel); + assert!(app.subagents.panel_cell().is_none()); + } + #[tokio::test] async fn replay_thread_snapshot_restores_collaboration_mode_for_draft_submit() { let (mut app, _app_event_rx, _op_rx) = make_test_app_with_channels().await;