@@ -127,7 +127,11 @@ use tool_manager::{
127127 ToolManager ,
128128 ToolManagerBuilder ,
129129} ;
130- use tools:: delegate:: status_all_agents;
130+ use tools:: delegate:: {
131+ AgentExecution ,
132+ save_agent_execution,
133+ status_all_agents,
134+ } ;
131135use tools:: gh_issue:: GhIssueContext ;
132136use tools:: {
133137 NATIVE_TOOLS ,
@@ -465,12 +469,71 @@ fn trust_all_text() -> String {
465469 ui_text:: trust_all_warning ( )
466470}
467471
472+ fn format_rich_notification ( executions : & [ AgentExecution ] ) -> String {
473+ let count = executions. len ( ) ;
474+ let header = if count == 1 {
475+ "1 Background Task Completed" . to_string ( )
476+ } else {
477+ format ! ( "{} Background Tasks Completed" , count)
478+ } ;
479+
480+ // Plain text notification - will be colored by highlight_prompt
481+ let mut notification = format ! ( "{}\n \n " , header) ;
482+
483+ for ( i, execution) in executions. iter ( ) . enumerate ( ) {
484+ let status_icon = match execution. status {
485+ tools:: delegate:: AgentStatus :: Completed => "✓ SUCCESS" ,
486+ tools:: delegate:: AgentStatus :: Failed => "✗ FAILED" ,
487+ tools:: delegate:: AgentStatus :: Running => "⏳ RUNNING" , // shouldn't happen but just in case
488+ } ;
489+
490+ let time_ago = if let Some ( completed_at) = execution. completed_at {
491+ let duration = chrono:: Utc :: now ( ) . signed_duration_since ( completed_at) ;
492+ if duration. num_minutes ( ) < 1 {
493+ "Completed just now" . to_string ( )
494+ } else if duration. num_minutes ( ) < 60 {
495+ format ! ( "Completed {} min ago" , duration. num_minutes( ) )
496+ } else if duration. num_hours ( ) < 24 {
497+ format ! ( "Completed {} hr ago" , duration. num_hours( ) )
498+ } else {
499+ format ! ( "Completed {} days ago" , duration. num_days( ) )
500+ }
501+ } else {
502+ "unknown" . to_string ( )
503+ } ;
504+
505+ // Shorten CWD path - replace home directory with ~
506+ let shortened_cwd = if let Ok ( home) = std:: env:: var ( "HOME" ) {
507+ execution. cwd . replace ( & home, "~" )
508+ } else {
509+ execution. cwd . clone ( )
510+ } ;
511+
512+ let summary = execution. summary . as_deref ( ) . unwrap_or ( "No summary available" ) ;
513+
514+ notification. push_str ( & format ! (
515+ "[{}] {} · {} · {} · {}\n \n Task: {}\n \n {}\n \n " ,
516+ i + 1 ,
517+ execution. agent,
518+ shortened_cwd,
519+ status_icon,
520+ time_ago,
521+ execution. task,
522+ summary
523+ ) ) ;
524+ }
525+
526+ // Add footer with instructions
527+ notification. push_str ( "To read the full details of any task, ask the delegate tool.\n " ) ;
528+
529+ notification
530+ }
531+
468532const TOOL_BULLET : & str = " ● " ;
469533const CONTINUATION_LINE : & str = " ⋮ " ;
470534const PURPOSE_ARROW : & str = " ↳ " ;
471535const SUCCESS_TICK : & str = " ✓ " ;
472536const ERROR_EXCLAMATION : & str = " ❗ " ;
473- const DELEGATE_NOTIFIER : & str = "[BACKGROUND TASK READY]" ;
474537
475538/// Enum used to denote the origin of a tool use event
476539enum ToolUseStatus {
@@ -611,6 +674,8 @@ pub struct ChatSession {
611674 ctrlc_rx : broadcast:: Receiver < ( ) > ,
612675 wrap : Option < WrapMode > ,
613676 prompt_ack_rx : std:: sync:: mpsc:: Receiver < ( ) > ,
677+ /// Additional context to be added to the next user message (e.g., delegate task summaries)
678+ pending_additional_context : Option < String > ,
614679}
615680
616681impl ChatSession {
@@ -747,6 +812,7 @@ impl ChatSession {
747812 ctrlc_rx,
748813 wrap,
749814 prompt_ack_rx,
815+ pending_additional_context : None ,
750816 } )
751817 }
752818
@@ -2219,7 +2285,11 @@ impl ChatSession {
22192285 } ;
22202286 self . conversation . abandon_tool_use ( & self . tool_uses , user_input) ;
22212287 } else {
2222- self . conversation . set_next_user_message ( user_input) . await ;
2288+ // Add additional context if available (e.g., delegate summaries)
2289+ let context = self . pending_additional_context . take ( ) . unwrap_or_default ( ) ;
2290+ self . conversation
2291+ . set_next_user_message_with_context ( user_input, context)
2292+ . await ;
22232293 }
22242294
22252295 self . reset_user_turn ( ) ;
@@ -3486,8 +3556,24 @@ impl ChatSession {
34863556 let mut generated_prompt =
34873557 prompt:: generate_prompt ( profile. as_deref ( ) , all_trusted, tangent_mode, usage_percentage) ;
34883558
3489- if ExperimentManager :: is_enabled ( os, ExperimentName :: Delegate ) && status_all_agents ( os) . await . is_ok ( ) {
3490- generated_prompt = format ! ( "{DELEGATE_NOTIFIER}\n {generated_prompt}" ) ;
3559+ if ExperimentManager :: is_enabled ( os, ExperimentName :: Delegate ) {
3560+ if let Ok ( mut executions) = status_all_agents ( os) . await {
3561+ if !executions. is_empty ( ) {
3562+ let rich_notification = format_rich_notification ( & executions) ;
3563+ generated_prompt = format ! ( "{}\n {}" , rich_notification, generated_prompt) ;
3564+
3565+ // Use the notification text as context for the model (it's already plain text)
3566+ self . pending_additional_context = Some ( rich_notification. clone ( ) ) ;
3567+
3568+ // Mark all shown tasks as user_notified
3569+ for execution in & mut executions {
3570+ execution. user_notified = true ;
3571+ if let Err ( e) = save_agent_execution ( os, execution) . await {
3572+ eprintln ! ( "Failed to mark agent execution as notified: {}" , e) ;
3573+ }
3574+ }
3575+ }
3576+ }
34913577 }
34923578
34933579 generated_prompt
0 commit comments