@@ -11,9 +11,9 @@ use crate::agentic::events::{
1111 AgenticEvent , EventPriority , EventQueue , EventRouter , EventSubscriber ,
1212} ;
1313use crate :: agentic:: execution:: { ExecutionContext , ExecutionEngine } ;
14+ use crate :: agentic:: image_analysis:: ImageContextData ;
1415use crate :: agentic:: session:: SessionManager ;
1516use crate :: agentic:: tools:: pipeline:: { SubagentParentInfo , ToolPipeline } ;
16- use crate :: agentic:: image_analysis:: ImageContextData ;
1717use crate :: util:: errors:: { BitFunError , BitFunResult } ;
1818use log:: { debug, error, info, warn} ;
1919use std:: sync:: Arc ;
@@ -147,8 +147,7 @@ impl ConversationCoordinator {
147147 workspace_path : Option < String > ,
148148 ) -> BitFunResult < Session > {
149149 let effective_workspace_path = workspace_path. or_else ( || {
150- crate :: infrastructure:: get_workspace_path ( )
151- . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
150+ crate :: infrastructure:: get_workspace_path ( ) . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
152151 } ) ;
153152
154153 // Persist the workspace binding inside the session config so execution can
@@ -246,17 +245,16 @@ impl ConversationCoordinator {
246245 terminal_session_id : existing
247246 . as_ref ( )
248247 . and_then ( |m| m. terminal_session_id . clone ( ) ) ,
249- snapshot_session_id : session
250- . snapshot_session_id
251- . clone ( )
252- . or_else ( || existing. as_ref ( ) . and_then ( |m| m. snapshot_session_id . clone ( ) ) ) ,
248+ snapshot_session_id : session. snapshot_session_id . clone ( ) . or_else ( || {
249+ existing
250+ . as_ref ( )
251+ . and_then ( |m| m. snapshot_session_id . clone ( ) )
252+ } ) ,
253253 tags : existing
254254 . as_ref ( )
255255 . map ( |m| m. tags . clone ( ) )
256256 . unwrap_or_default ( ) ,
257- custom_metadata : existing
258- . as_ref ( )
259- . and_then ( |m| m. custom_metadata . clone ( ) ) ,
257+ custom_metadata : existing. as_ref ( ) . and_then ( |m| m. custom_metadata . clone ( ) ) ,
260258 todos : existing. as_ref ( ) . and_then ( |m| m. todos . clone ( ) ) ,
261259 workspace_path : Some ( workspace_path) ,
262260 } ;
@@ -390,8 +388,15 @@ impl ConversationCoordinator {
390388 agent_type : String ,
391389 trigger_source : DialogTriggerSource ,
392390 ) -> BitFunResult < ( ) > {
393- self . start_dialog_turn_internal ( session_id, user_input, None , turn_id, agent_type, trigger_source)
394- . await
391+ self . start_dialog_turn_internal (
392+ session_id,
393+ user_input,
394+ None ,
395+ turn_id,
396+ agent_type,
397+ trigger_source,
398+ )
399+ . await
395400 }
396401
397402 pub async fn start_dialog_turn_with_image_contexts (
@@ -560,12 +565,40 @@ impl ConversationCoordinator {
560565 }
561566 } ;
562567
563- let effective_agent_type = if session. agent_type . is_empty ( ) {
564- agent_type
565- } else {
568+ let requested_agent_type = agent_type. trim ( ) . to_string ( ) ;
569+
570+ let effective_agent_type = if !requested_agent_type. is_empty ( ) {
571+ requested_agent_type. clone ( )
572+ } else if !session. agent_type . is_empty ( ) {
566573 session. agent_type . clone ( )
574+ } else {
575+ "agentic" . to_string ( )
567576 } ;
568577
578+ debug ! (
579+ "Resolved dialog turn agent type: session_id={}, turn_id={}, requested_agent_type={}, session_agent_type={}, effective_agent_type={}, trigger_source={:?}" ,
580+ session_id,
581+ turn_id. as_deref( ) . unwrap_or( "" ) ,
582+ if requested_agent_type. is_empty( ) {
583+ "<empty>"
584+ } else {
585+ requested_agent_type. as_str( )
586+ } ,
587+ if session. agent_type. is_empty( ) {
588+ "<empty>"
589+ } else {
590+ session. agent_type. as_str( )
591+ } ,
592+ effective_agent_type,
593+ trigger_source
594+ ) ;
595+
596+ if session. agent_type != effective_agent_type {
597+ self . session_manager
598+ . update_session_agent_type ( & session_id, & effective_agent_type)
599+ . await ?;
600+ }
601+
569602 debug ! (
570603 "Checking session state: session_id={}, state={:?}" ,
571604 session_id, session. state
@@ -709,8 +742,14 @@ impl ConversationCoordinator {
709742 // vision model to pre-analyze them, then enhance the user message with text descriptions.
710743 // This is the single authoritative code path for all image handling (desktop, remote, bot).
711744 // If no vision model is configured, the request is rejected with a user-friendly message.
712- let ( user_input, image_contexts) =
713- self . pre_analyze_images_if_needed ( user_input, image_contexts, & session_id, user_message_metadata. clone ( ) ) . await ?;
745+ let ( user_input, image_contexts) = self
746+ . pre_analyze_images_if_needed (
747+ user_input,
748+ image_contexts,
749+ & session_id,
750+ user_message_metadata. clone ( ) ,
751+ )
752+ . await ?;
714753
715754 let wrapped_user_input = self
716755 . wrap_user_input ( & effective_agent_type, user_input)
@@ -906,7 +945,10 @@ impl ConversationCoordinator {
906945 let is_cancellation = matches ! ( & e, BitFunError :: Cancelled ( _) ) ;
907946
908947 if is_cancellation {
909- info ! ( "Dialog turn cancelled: session={}, turn={}" , session_id_clone, turn_id_clone) ;
948+ info ! (
949+ "Dialog turn cancelled: session={}, turn={}" ,
950+ session_id_clone, turn_id_clone
951+ ) ;
910952
911953 // The execution engine only emits DialogTurnCancelled when
912954 // cancellation is detected between rounds. If cancellation
@@ -968,11 +1010,7 @@ impl ConversationCoordinator {
9681010 . await ;
9691011
9701012 let _ = session_manager
971- . fail_dialog_turn (
972- & session_id_clone,
973- & turn_id_clone,
974- e. to_string ( ) ,
975- )
1013+ . fail_dialog_turn ( & session_id_clone, & turn_id_clone, e. to_string ( ) )
9761014 . await ;
9771015
9781016 let _ = session_manager
0 commit comments