@@ -2505,6 +2505,229 @@ async fn command_ack_does_not_persist_assistant_preference_in_core_service() {
25052505 assert ! ( refreshed. extra. get( "current_model_id" ) . is_none( ) ) ;
25062506}
25072507
2508+ #[ tokio:: test]
2509+ async fn set_config_option_persists_runtime_model_into_assistant_preference_when_observed ( ) {
2510+ let task_mgr = Arc :: new ( MockTaskManager :: new ( ) ) ;
2511+ let ( svc, _broadcaster, repo, definition_repo, overlay_repo, preference_repo) =
2512+ make_service_with_mock_task_manager_and_assistant_support ( task_mgr. clone ( ) ) . await ;
2513+
2514+ upsert_test_assistant_definition (
2515+ & definition_repo,
2516+ "asstdef_acp_auto" ,
2517+ "assistant-acp-auto" ,
2518+ "codex" ,
2519+ "auto" ,
2520+ "auto" ,
2521+ )
2522+ . await ;
2523+ overlay_repo
2524+ . upsert ( & UpsertAssistantOverlayParams {
2525+ definition_id : "asstdef_acp_auto" ,
2526+ enabled : true ,
2527+ sort_order : 0 ,
2528+ agent_backend_override : None ,
2529+ last_used_at : None ,
2530+ } )
2531+ . await
2532+ . unwrap ( ) ;
2533+ preference_repo
2534+ . upsert ( & UpsertAssistantPreferenceParams {
2535+ definition_id : "asstdef_acp_auto" ,
2536+ last_model_id : Some ( "legacy-acp-model" ) ,
2537+ last_permission_value : Some ( "legacy-mode" ) ,
2538+ last_skill_ids : "[]" ,
2539+ last_disabled_builtin_skill_ids : "[]" ,
2540+ last_mcp_ids : "[]" ,
2541+ } )
2542+ . await
2543+ . unwrap ( ) ;
2544+
2545+ let conv = create_assistant_backed_conversation ( & svc, "user_1" , "acp" , "codex" , "assistant-acp-auto" ) . await ;
2546+
2547+ let agent = Arc :: new ( MockAgent :: new ( & conv. id ) ) ;
2548+ task_mgr. insert_agent ( & conv. id , AgentInstance :: Mock ( agent) ) ;
2549+
2550+ let result = svc
2551+ . set_config_option (
2552+ & conv. id ,
2553+ "model" ,
2554+ SetConfigOptionRequest {
2555+ value : "gpt-5.5" . to_owned ( ) ,
2556+ } ,
2557+ )
2558+ . await
2559+ . unwrap ( ) ;
2560+ assert_eq ! ( result. confirmation, ConfigOptionConfirmation :: Observed ) ;
2561+
2562+ let pref_after_model = preference_repo. get ( "asstdef_acp_auto" ) . await . unwrap ( ) . unwrap ( ) ;
2563+ assert_eq ! ( pref_after_model. last_model_id. as_deref( ) , Some ( "gpt-5.5" ) ) ;
2564+ assert_eq ! ( pref_after_model. last_permission_value. as_deref( ) , Some ( "legacy-mode" ) ) ;
2565+ let snapshot_after_model = repo. get_assistant_snapshot ( & conv. id ) . await . unwrap ( ) . unwrap ( ) ;
2566+ assert_eq ! ( snapshot_after_model. resolved_model_id. as_deref( ) , Some ( "gpt-5.5" ) ) ;
2567+
2568+ svc. set_config_option (
2569+ & conv. id ,
2570+ "mode" ,
2571+ SetConfigOptionRequest {
2572+ value : "plan" . to_owned ( ) ,
2573+ } ,
2574+ )
2575+ . await
2576+ . unwrap ( ) ;
2577+ let pref_after_mode = preference_repo. get ( "asstdef_acp_auto" ) . await . unwrap ( ) . unwrap ( ) ;
2578+ assert_eq ! ( pref_after_mode. last_model_id. as_deref( ) , Some ( "gpt-5.5" ) ) ;
2579+ assert_eq ! ( pref_after_mode. last_permission_value. as_deref( ) , Some ( "plan" ) ) ;
2580+ let snapshot_after_mode = repo. get_assistant_snapshot ( & conv. id ) . await . unwrap ( ) . unwrap ( ) ;
2581+ assert_eq ! ( snapshot_after_mode. resolved_permission_value. as_deref( ) , Some ( "plan" ) ) ;
2582+
2583+ // Unrelated option ids must not touch preferences.
2584+ svc. set_config_option (
2585+ & conv. id ,
2586+ "thought_level" ,
2587+ SetConfigOptionRequest {
2588+ value : "high" . to_owned ( ) ,
2589+ } ,
2590+ )
2591+ . await
2592+ . unwrap ( ) ;
2593+ let pref_after_thought = preference_repo. get ( "asstdef_acp_auto" ) . await . unwrap ( ) . unwrap ( ) ;
2594+ assert_eq ! ( pref_after_thought. last_model_id. as_deref( ) , Some ( "gpt-5.5" ) ) ;
2595+ assert_eq ! ( pref_after_thought. last_permission_value. as_deref( ) , Some ( "plan" ) ) ;
2596+ }
2597+
2598+ #[ tokio:: test]
2599+ async fn set_config_option_skips_preference_write_back_when_default_mode_is_fixed ( ) {
2600+ let task_mgr = Arc :: new ( MockTaskManager :: new ( ) ) ;
2601+ let ( svc, _broadcaster, repo, definition_repo, overlay_repo, preference_repo) =
2602+ make_service_with_mock_task_manager_and_assistant_support ( task_mgr. clone ( ) ) . await ;
2603+
2604+ upsert_test_assistant_definition (
2605+ & definition_repo,
2606+ "asstdef_acp_fixed" ,
2607+ "assistant-acp-fixed" ,
2608+ "codex" ,
2609+ "fixed" ,
2610+ "fixed" ,
2611+ )
2612+ . await ;
2613+ overlay_repo
2614+ . upsert ( & UpsertAssistantOverlayParams {
2615+ definition_id : "asstdef_acp_fixed" ,
2616+ enabled : true ,
2617+ sort_order : 0 ,
2618+ agent_backend_override : None ,
2619+ last_used_at : None ,
2620+ } )
2621+ . await
2622+ . unwrap ( ) ;
2623+ preference_repo
2624+ . upsert ( & UpsertAssistantPreferenceParams {
2625+ definition_id : "asstdef_acp_fixed" ,
2626+ last_model_id : Some ( "legacy-fixed-model" ) ,
2627+ last_permission_value : Some ( "legacy-fixed-mode" ) ,
2628+ last_skill_ids : "[]" ,
2629+ last_disabled_builtin_skill_ids : "[]" ,
2630+ last_mcp_ids : "[]" ,
2631+ } )
2632+ . await
2633+ . unwrap ( ) ;
2634+
2635+ let conv = create_assistant_backed_conversation ( & svc, "user_1" , "acp" , "codex" , "assistant-acp-fixed" ) . await ;
2636+ let agent = Arc :: new ( MockAgent :: new ( & conv. id ) ) ;
2637+ task_mgr. insert_agent ( & conv. id , AgentInstance :: Mock ( agent) ) ;
2638+
2639+ svc. set_config_option (
2640+ & conv. id ,
2641+ "model" ,
2642+ SetConfigOptionRequest {
2643+ value : "transient-model" . to_owned ( ) ,
2644+ } ,
2645+ )
2646+ . await
2647+ . unwrap ( ) ;
2648+ svc. set_config_option (
2649+ & conv. id ,
2650+ "mode" ,
2651+ SetConfigOptionRequest {
2652+ value : "transient-mode" . to_owned ( ) ,
2653+ } ,
2654+ )
2655+ . await
2656+ . unwrap ( ) ;
2657+
2658+ let pref = preference_repo. get ( "asstdef_acp_fixed" ) . await . unwrap ( ) . unwrap ( ) ;
2659+ assert_eq ! ( pref. last_model_id. as_deref( ) , Some ( "legacy-fixed-model" ) ) ;
2660+ assert_eq ! ( pref. last_permission_value. as_deref( ) , Some ( "legacy-fixed-mode" ) ) ;
2661+ // The snapshot still tracks the runtime override so the active session reflects it,
2662+ // even though the persisted assistant preference must not change for fixed defaults.
2663+ let snapshot = repo. get_assistant_snapshot ( & conv. id ) . await . unwrap ( ) . unwrap ( ) ;
2664+ assert_eq ! ( snapshot. resolved_model_id. as_deref( ) , Some ( "transient-model" ) ) ;
2665+ assert_eq ! ( snapshot. resolved_permission_value. as_deref( ) , Some ( "transient-mode" ) ) ;
2666+ }
2667+
2668+ #[ tokio:: test]
2669+ async fn set_config_option_command_ack_does_not_persist_assistant_preference ( ) {
2670+ let task_mgr = Arc :: new ( MockTaskManager :: new ( ) ) ;
2671+ let ( svc, _broadcaster, repo, definition_repo, overlay_repo, preference_repo) =
2672+ make_service_with_mock_task_manager_and_assistant_support ( task_mgr. clone ( ) ) . await ;
2673+
2674+ upsert_test_assistant_definition (
2675+ & definition_repo,
2676+ "asstdef_acp_ack" ,
2677+ "assistant-acp-ack" ,
2678+ "codex" ,
2679+ "auto" ,
2680+ "auto" ,
2681+ )
2682+ . await ;
2683+ overlay_repo
2684+ . upsert ( & UpsertAssistantOverlayParams {
2685+ definition_id : "asstdef_acp_ack" ,
2686+ enabled : true ,
2687+ sort_order : 0 ,
2688+ agent_backend_override : None ,
2689+ last_used_at : None ,
2690+ } )
2691+ . await
2692+ . unwrap ( ) ;
2693+ preference_repo
2694+ . upsert ( & UpsertAssistantPreferenceParams {
2695+ definition_id : "asstdef_acp_ack" ,
2696+ last_model_id : Some ( "legacy-ack-model" ) ,
2697+ last_permission_value : Some ( "legacy-ack-mode" ) ,
2698+ last_skill_ids : "[]" ,
2699+ last_disabled_builtin_skill_ids : "[]" ,
2700+ last_mcp_ids : "[]" ,
2701+ } )
2702+ . await
2703+ . unwrap ( ) ;
2704+
2705+ let conv = create_assistant_backed_conversation ( & svc, "user_1" , "acp" , "codex" , "assistant-acp-ack" ) . await ;
2706+ let agent = Arc :: new (
2707+ MockAgent :: new ( & conv. id ) . with_set_config_option_response ( SetConfigOptionResponse {
2708+ confirmation : ConfigOptionConfirmation :: CommandAck ,
2709+ config_options : None ,
2710+ } ) ,
2711+ ) ;
2712+ task_mgr. insert_agent ( & conv. id , AgentInstance :: Mock ( agent) ) ;
2713+
2714+ svc. set_config_option (
2715+ & conv. id ,
2716+ "model" ,
2717+ SetConfigOptionRequest {
2718+ value : "ack-only-model" . to_owned ( ) ,
2719+ } ,
2720+ )
2721+ . await
2722+ . unwrap ( ) ;
2723+
2724+ let pref = preference_repo. get ( "asstdef_acp_ack" ) . await . unwrap ( ) . unwrap ( ) ;
2725+ assert_eq ! ( pref. last_model_id. as_deref( ) , Some ( "legacy-ack-model" ) ) ;
2726+ assert_eq ! ( pref. last_permission_value. as_deref( ) , Some ( "legacy-ack-mode" ) ) ;
2727+ let snapshot = repo. get_assistant_snapshot ( & conv. id ) . await . unwrap ( ) . unwrap ( ) ;
2728+ assert_eq ! ( snapshot. resolved_model_id. as_deref( ) , Some ( "legacy-ack-model" ) ) ;
2729+ }
2730+
25082731#[ tokio:: test]
25092732async fn update_aionrs_model_updates_assistant_preference_only_when_snapshot_model_mode_is_auto ( ) {
25102733 let task_mgr = Arc :: new ( MockTaskManager :: new ( ) ) ;
0 commit comments