11use std:: path:: PathBuf ;
22
3+ use serde_json:: Value ;
34use tokio:: sync:: Mutex ;
45
56use crate :: codex:: config as codex_config;
6- use crate :: storage:: write_settings;
7- use crate :: types:: AppSettings ;
7+ use crate :: storage:: { read_settings , write_settings} ;
8+ use crate :: types:: { AppSettings , SettingsSyncMode } ;
89
910fn normalize_personality ( value : & str ) -> Option < & ' static str > {
1011 match value. trim ( ) {
@@ -16,25 +17,27 @@ fn normalize_personality(value: &str) -> Option<&'static str> {
1617
1718pub ( crate ) async fn get_app_settings_core ( app_settings : & Mutex < AppSettings > ) -> AppSettings {
1819 let mut settings = app_settings. lock ( ) . await . clone ( ) ;
19- if let Ok ( Some ( collaboration_modes_enabled) ) = codex_config:: read_collaboration_modes_enabled ( )
20- {
21- settings. collaboration_modes_enabled = collaboration_modes_enabled;
22- }
23- if let Ok ( Some ( steer_enabled) ) = codex_config:: read_steer_enabled ( ) {
24- settings. steer_enabled = steer_enabled;
25- }
26- if let Ok ( Some ( unified_exec_enabled) ) = codex_config:: read_unified_exec_enabled ( ) {
27- settings. unified_exec_enabled = unified_exec_enabled;
28- }
29- if let Ok ( Some ( apps_enabled) ) = codex_config:: read_apps_enabled ( ) {
30- settings. experimental_apps_enabled = apps_enabled;
31- }
32- if let Ok ( personality) = codex_config:: read_personality ( ) {
33- settings. personality = personality
34- . as_deref ( )
35- . and_then ( normalize_personality)
36- . unwrap_or ( "friendly" )
37- . to_string ( ) ;
20+ if matches ! ( settings. sync_mode, SettingsSyncMode :: Bidirectional ) {
21+ if let Ok ( Some ( collaboration_modes_enabled) ) = codex_config:: read_collaboration_modes_enabled ( )
22+ {
23+ settings. collaboration_modes_enabled = collaboration_modes_enabled;
24+ }
25+ if let Ok ( Some ( steer_enabled) ) = codex_config:: read_steer_enabled ( ) {
26+ settings. steer_enabled = steer_enabled;
27+ }
28+ if let Ok ( Some ( unified_exec_enabled) ) = codex_config:: read_unified_exec_enabled ( ) {
29+ settings. unified_exec_enabled = unified_exec_enabled;
30+ }
31+ if let Ok ( Some ( apps_enabled) ) = codex_config:: read_apps_enabled ( ) {
32+ settings. experimental_apps_enabled = apps_enabled;
33+ }
34+ if let Ok ( personality) = codex_config:: read_personality ( ) {
35+ settings. personality = personality
36+ . as_deref ( )
37+ . and_then ( normalize_personality)
38+ . unwrap_or ( "friendly" )
39+ . to_string ( ) ;
40+ }
3841 }
3942 settings
4043}
@@ -44,15 +47,86 @@ pub(crate) async fn update_app_settings_core(
4447 app_settings : & Mutex < AppSettings > ,
4548 settings_path : & PathBuf ,
4649) -> Result < AppSettings , String > {
47- let _ = codex_config:: write_collaboration_modes_enabled ( settings. collaboration_modes_enabled ) ;
48- let _ = codex_config:: write_steer_enabled ( settings. steer_enabled ) ;
49- let _ = codex_config:: write_unified_exec_enabled ( settings. unified_exec_enabled ) ;
50- let _ = codex_config:: write_apps_enabled ( settings. experimental_apps_enabled ) ;
51- let _ = codex_config:: write_personality ( settings. personality . as_str ( ) ) ;
52- write_settings ( settings_path, & settings) ?;
50+ let previous = app_settings. lock ( ) . await . clone ( ) ;
51+ let mut next = settings;
52+
53+ if matches ! ( next. sync_mode, SettingsSyncMode :: Bidirectional ) {
54+ if let Ok ( disk_settings) = read_settings ( settings_path) {
55+ next = merge_bidirectional_settings ( previous. clone ( ) , next, disk_settings) ?;
56+ }
57+ reconcile_managed_config_fields ( & previous, & mut next) ;
58+ }
59+
60+ let _ = codex_config:: write_collaboration_modes_enabled ( next. collaboration_modes_enabled ) ;
61+ let _ = codex_config:: write_steer_enabled ( next. steer_enabled ) ;
62+ let _ = codex_config:: write_unified_exec_enabled ( next. unified_exec_enabled ) ;
63+ let _ = codex_config:: write_apps_enabled ( next. experimental_apps_enabled ) ;
64+ let _ = codex_config:: write_personality ( next. personality . as_str ( ) ) ;
65+ write_settings ( settings_path, & next) ?;
5366 let mut current = app_settings. lock ( ) . await ;
54- * current = settings. clone ( ) ;
55- Ok ( settings)
67+ * current = next. clone ( ) ;
68+ Ok ( next)
69+ }
70+
71+ fn merge_bidirectional_settings (
72+ previous : AppSettings ,
73+ incoming : AppSettings ,
74+ disk : AppSettings ,
75+ ) -> Result < AppSettings , String > {
76+ let previous_value = serde_json:: to_value ( previous) . map_err ( |e| e. to_string ( ) ) ?;
77+ let incoming_value = serde_json:: to_value ( incoming. clone ( ) ) . map_err ( |e| e. to_string ( ) ) ?;
78+ let disk_value = serde_json:: to_value ( disk) . map_err ( |e| e. to_string ( ) ) ?;
79+
80+ let mut merged = incoming_value. clone ( ) ;
81+ let ( Value :: Object ( previous_map) , Value :: Object ( incoming_map) , Value :: Object ( disk_map) , Value :: Object ( merged_map) ) =
82+ ( & previous_value, & incoming_value, & disk_value, & mut merged)
83+ else {
84+ return Ok ( incoming) ;
85+ } ;
86+
87+ for ( key, incoming_field) in incoming_map {
88+ if let Some ( previous_field) = previous_map. get ( key) {
89+ if incoming_field == previous_field {
90+ if let Some ( disk_field) = disk_map. get ( key) {
91+ merged_map. insert ( key. clone ( ) , disk_field. clone ( ) ) ;
92+ }
93+ }
94+ }
95+ }
96+
97+ serde_json:: from_value ( merged) . map_err ( |e| e. to_string ( ) )
98+ }
99+
100+ fn reconcile_managed_config_fields ( previous : & AppSettings , next : & mut AppSettings ) {
101+ if next. collaboration_modes_enabled == previous. collaboration_modes_enabled {
102+ if let Ok ( Some ( value) ) = codex_config:: read_collaboration_modes_enabled ( ) {
103+ next. collaboration_modes_enabled = value;
104+ }
105+ }
106+ if next. steer_enabled == previous. steer_enabled {
107+ if let Ok ( Some ( value) ) = codex_config:: read_steer_enabled ( ) {
108+ next. steer_enabled = value;
109+ }
110+ }
111+ if next. unified_exec_enabled == previous. unified_exec_enabled {
112+ if let Ok ( Some ( value) ) = codex_config:: read_unified_exec_enabled ( ) {
113+ next. unified_exec_enabled = value;
114+ }
115+ }
116+ if next. experimental_apps_enabled == previous. experimental_apps_enabled {
117+ if let Ok ( Some ( value) ) = codex_config:: read_apps_enabled ( ) {
118+ next. experimental_apps_enabled = value;
119+ }
120+ }
121+ if next. personality == previous. personality {
122+ if let Ok ( personality) = codex_config:: read_personality ( ) {
123+ next. personality = personality
124+ . as_deref ( )
125+ . and_then ( normalize_personality)
126+ . unwrap_or ( "friendly" )
127+ . to_string ( ) ;
128+ }
129+ }
56130}
57131
58132pub ( crate ) fn get_codex_config_path_core ( ) -> Result < String , String > {
0 commit comments