diff --git a/cortex-tui/src/app.rs b/cortex-tui/src/app.rs index 5a630b15..f07f7006 100644 --- a/cortex-tui/src/app.rs +++ b/cortex-tui/src/app.rs @@ -272,6 +272,9 @@ pub struct SubagentTaskDisplay { pub started_at: Instant, /// Current todo items from the subagent (if any). pub todos: Vec, + /// Error message if the subagent failed. + /// Stored separately so it can be displayed even when the task is removed. + pub error_message: Option, } impl SubagentTaskDisplay { @@ -294,6 +297,7 @@ impl SubagentTaskDisplay { output_preview: String::new(), started_at: Instant::now(), todos: Vec::new(), + error_message: None, } } diff --git a/cortex-tui/src/runner/event_loop.rs b/cortex-tui/src/runner/event_loop.rs index 14f21a7a..9f8d4218 100644 --- a/cortex-tui/src/runner/event_loop.rs +++ b/cortex-tui/src/runner/event_loop.rs @@ -3419,12 +3419,23 @@ impl EventLoop { format!("Error: {}", error), ); - // If this is a Task tool, remove the subagent from display + // If this is a Task tool, update subagent status to Failed with error message + // Keep the subagent visible so users can see what error occurred if name == "Task" || name == "task" { let session_id = format!("subagent_{}", id); - self.app_state.remove_subagent(&session_id); - // Stop delegation mode if no more active subagents - if !self.app_state.has_active_subagents() { + let error_clone = error.clone(); + self.app_state.update_subagent(&session_id, |task| { + task.status = SubagentDisplayStatus::Failed; + task.error_message = Some(error_clone); + task.current_activity = "Failed".to_string(); + }); + // Stop delegation mode if no more active (non-failed) subagents + let has_running_subagents = self + .app_state + .active_subagents + .iter() + .any(|t| !t.status.is_terminal()); + if !has_running_subagents { self.app_state.streaming.stop_delegation(); } } diff --git a/cortex-tui/src/views/minimal_session.rs b/cortex-tui/src/views/minimal_session.rs index 791aff92..09389c7a 100644 --- a/cortex-tui/src/views/minimal_session.rs +++ b/cortex-tui/src/views/minimal_session.rs @@ -413,8 +413,35 @@ impl<'a> MinimalSessionView<'a> { ), ])); - // Display todos if any - use ⎿ prefix for first, space for rest - if !task.todos.is_empty() { + // Display error message if task failed + if task.status == SubagentDisplayStatus::Failed { + if let Some(ref error_msg) = task.error_message { + lines.push(Line::from(vec![ + Span::styled(" ⎿ ", Style::default().fg(self.colors.text_muted)), + Span::styled("Error: ", Style::default().fg(self.colors.error)), + ])); + // Display error message, truncate if too long + for (i, err_line) in error_msg.lines().take(5).enumerate() { + let truncated = if err_line.len() > 70 { + format!("{}...", &err_line.chars().take(67).collect::()) + } else { + err_line.to_string() + }; + let prefix = if i == 0 { " " } else { " " }; + lines.push(Line::from(vec![ + Span::styled(prefix, Style::default().fg(self.colors.text_muted)), + Span::styled(truncated, Style::default().fg(self.colors.error)), + ])); + } + } else { + // Fallback: no error message provided + lines.push(Line::from(vec![ + Span::styled(" ⎿ ", Style::default().fg(self.colors.text_muted)), + Span::styled("Task failed", Style::default().fg(self.colors.error)), + ])); + } + } else if !task.todos.is_empty() { + // Display todos if any - use ⎿ prefix for first, space for rest for (i, todo) in task.todos.iter().enumerate() { let (status_text, status_color) = match todo.status { SubagentTodoStatus::Completed => ("[completed]", self.colors.success),