@@ -70,6 +70,9 @@ use crate::client_common::Prompt;
7070use crate :: client_common:: ResponseEvent ;
7171use crate :: compact:: collect_user_messages;
7272use crate :: config:: Config ;
73+ use crate :: config:: Constrained ;
74+ use crate :: config:: ConstraintError ;
75+ use crate :: config:: ConstraintResult ;
7376use crate :: config:: types:: ShellEnvironmentPolicy ;
7477use crate :: context_manager:: ContextManager ;
7578use crate :: environment_context:: EnvironmentContext ;
@@ -87,6 +90,7 @@ use crate::protocol::ApplyPatchApprovalRequestEvent;
8790use crate :: protocol:: AskForApproval ;
8891use crate :: protocol:: BackgroundEventEvent ;
8992use crate :: protocol:: DeprecationNoticeEvent ;
93+ use crate :: protocol:: ErrorEvent ;
9094use crate :: protocol:: Event ;
9195use crate :: protocol:: EventMsg ;
9296use crate :: protocol:: ExecApprovalRequestEvent ;
@@ -191,7 +195,7 @@ impl Codex {
191195 user_instructions,
192196 base_instructions : config. base_instructions . clone ( ) ,
193197 compact_prompt : config. compact_prompt . clone ( ) ,
194- approval_policy : config. approval_policy ,
198+ approval_policy : config. approval_policy . clone ( ) ,
195199 sandbox_policy : config. sandbox_policy . clone ( ) ,
196200 cwd : config. cwd . clone ( ) ,
197201 original_config_do_not_use : Arc :: clone ( & config) ,
@@ -339,7 +343,7 @@ pub(crate) struct SessionConfiguration {
339343 compact_prompt : Option < String > ,
340344
341345 /// When to escalate for approval for execution
342- approval_policy : AskForApproval ,
346+ approval_policy : Constrained < AskForApproval > ,
343347 /// How to sandbox commands executed in the system
344348 sandbox_policy : SandboxPolicy ,
345349
@@ -362,7 +366,7 @@ pub(crate) struct SessionConfiguration {
362366}
363367
364368impl SessionConfiguration {
365- pub ( crate ) fn apply ( & self , updates : & SessionSettingsUpdate ) -> Self {
369+ pub ( crate ) fn apply ( & self , updates : & SessionSettingsUpdate ) -> ConstraintResult < Self > {
366370 let mut next_configuration = self . clone ( ) ;
367371 if let Some ( model) = updates. model . clone ( ) {
368372 next_configuration. model = model;
@@ -374,15 +378,15 @@ impl SessionConfiguration {
374378 next_configuration. model_reasoning_summary = summary;
375379 }
376380 if let Some ( approval_policy) = updates. approval_policy {
377- next_configuration. approval_policy = approval_policy;
381+ next_configuration. approval_policy . set ( approval_policy) ? ;
378382 }
379383 if let Some ( sandbox_policy) = updates. sandbox_policy . clone ( ) {
380384 next_configuration. sandbox_policy = sandbox_policy;
381385 }
382386 if let Some ( cwd) = updates. cwd . clone ( ) {
383387 next_configuration. cwd = cwd;
384388 }
385- next_configuration
389+ Ok ( next_configuration)
386390 }
387391}
388392
@@ -450,7 +454,7 @@ impl Session {
450454 base_instructions : session_configuration. base_instructions . clone ( ) ,
451455 compact_prompt : session_configuration. compact_prompt . clone ( ) ,
452456 user_instructions : session_configuration. user_instructions . clone ( ) ,
453- approval_policy : session_configuration. approval_policy ,
457+ approval_policy : session_configuration. approval_policy . value ( ) ,
454458 sandbox_policy : session_configuration. sandbox_policy . clone ( ) ,
455459 shell_environment_policy : per_turn_config. shell_environment_policy . clone ( ) ,
456460 tools_config,
@@ -566,7 +570,7 @@ impl Session {
566570 config. model_reasoning_summary ,
567571 config. model_context_window ,
568572 config. model_auto_compact_token_limit ,
569- config. approval_policy ,
573+ config. approval_policy . value ( ) ,
570574 config. sandbox_policy . clone ( ) ,
571575 config. mcp_servers . keys ( ) . map ( String :: as_str) . collect ( ) ,
572576 config. active_profile . clone ( ) ,
@@ -609,7 +613,7 @@ impl Session {
609613 session_id : conversation_id,
610614 model : session_configuration. model . clone ( ) ,
611615 model_provider_id : config. model_provider_id . clone ( ) ,
612- approval_policy : session_configuration. approval_policy ,
616+ approval_policy : session_configuration. approval_policy . value ( ) ,
613617 sandbox_policy : session_configuration. sandbox_policy . clone ( ) ,
614618 cwd : session_configuration. cwd . clone ( ) ,
615619 reasoning_effort : session_configuration. model_reasoning_effort ,
@@ -688,7 +692,7 @@ impl Session {
688692 }
689693
690694 async fn record_initial_history ( & self , conversation_history : InitialHistory ) {
691- let turn_context = self . new_turn ( SessionSettingsUpdate :: default ( ) ) . await ;
695+ let turn_context = self . new_default_turn ( ) . await ;
692696 match conversation_history {
693697 InitialHistory :: New => {
694698 // Build and record initial items (user instructions + environment context)
@@ -747,29 +751,71 @@ impl Session {
747751 }
748752 }
749753
750- pub ( crate ) async fn update_settings ( & self , updates : SessionSettingsUpdate ) {
754+ pub ( crate ) async fn update_settings (
755+ & self ,
756+ updates : SessionSettingsUpdate ,
757+ ) -> ConstraintResult < ( ) > {
751758 let mut state = self . state . lock ( ) . await ;
752759
753- state. session_configuration = state. session_configuration . apply ( & updates) ;
754- }
755-
756- pub ( crate ) async fn new_turn ( & self , updates : SessionSettingsUpdate ) -> Arc < TurnContext > {
757- let sub_id = self . next_internal_sub_id ( ) ;
758- self . new_turn_with_sub_id ( sub_id, updates) . await
760+ match state. session_configuration . apply ( & updates) {
761+ Ok ( updated) => {
762+ state. session_configuration = updated;
763+ Ok ( ( ) )
764+ }
765+ Err ( err) => {
766+ let wrapped = ConstraintError {
767+ message : format ! ( "Could not update config: {err}" ) ,
768+ } ;
769+ warn ! ( %wrapped, "rejected session settings update" ) ;
770+ Err ( wrapped)
771+ }
772+ }
759773 }
760774
761775 pub ( crate ) async fn new_turn_with_sub_id (
762776 & self ,
763777 sub_id : String ,
764778 updates : SessionSettingsUpdate ,
765- ) -> Arc < TurnContext > {
779+ ) -> ConstraintResult < Arc < TurnContext > > {
766780 let session_configuration = {
767781 let mut state = self . state . lock ( ) . await ;
768- let session_configuration = state. session_configuration . clone ( ) . apply ( & updates) ;
769- state. session_configuration = session_configuration. clone ( ) ;
770- session_configuration
782+ match state. session_configuration . clone ( ) . apply ( & updates) {
783+ Ok ( next) => {
784+ state. session_configuration = next. clone ( ) ;
785+ next
786+ }
787+ Err ( err) => {
788+ let wrapped = ConstraintError {
789+ message : format ! ( "Could not update config: {err}" ) ,
790+ } ;
791+ self . send_event_raw ( Event {
792+ id : sub_id. clone ( ) ,
793+ msg : EventMsg :: Error ( ErrorEvent {
794+ message : wrapped. to_string ( ) ,
795+ codex_error_info : Some ( CodexErrorInfo :: BadRequest ) ,
796+ } ) ,
797+ } )
798+ . await ;
799+ return Err ( wrapped) ;
800+ }
801+ }
771802 } ;
772803
804+ Ok ( self
805+ . new_turn_from_configuration (
806+ sub_id,
807+ session_configuration,
808+ updates. final_output_json_schema ,
809+ )
810+ . await )
811+ }
812+
813+ async fn new_turn_from_configuration (
814+ & self ,
815+ sub_id : String ,
816+ session_configuration : SessionConfiguration ,
817+ final_output_json_schema : Option < Option < Value > > ,
818+ ) -> Arc < TurnContext > {
773819 let per_turn_config = Self :: build_per_turn_config ( & session_configuration) ;
774820 let model_family = self
775821 . services
@@ -786,12 +832,26 @@ impl Session {
786832 self . conversation_id ,
787833 sub_id,
788834 ) ;
789- if let Some ( final_schema) = updates . final_output_json_schema {
835+ if let Some ( final_schema) = final_output_json_schema {
790836 turn_context. final_output_json_schema = final_schema;
791837 }
792838 Arc :: new ( turn_context)
793839 }
794840
841+ pub ( crate ) async fn new_default_turn ( & self ) -> Arc < TurnContext > {
842+ self . new_default_turn_with_sub_id ( self . next_internal_sub_id ( ) )
843+ . await
844+ }
845+
846+ pub ( crate ) async fn new_default_turn_with_sub_id ( & self , sub_id : String ) -> Arc < TurnContext > {
847+ let session_configuration = {
848+ let state = self . state . lock ( ) . await ;
849+ state. session_configuration . clone ( )
850+ } ;
851+ self . new_turn_from_configuration ( sub_id, session_configuration, None )
852+ . await
853+ }
854+
795855 fn build_environment_update_item (
796856 & self ,
797857 previous : Option < & Arc < TurnContext > > ,
@@ -1462,8 +1522,7 @@ impl Session {
14621522
14631523async fn submission_loop ( sess : Arc < Session > , config : Arc < Config > , rx_sub : Receiver < Submission > ) {
14641524 // Seed with context in case there is an OverrideTurnContext first.
1465- let mut previous_context: Option < Arc < TurnContext > > =
1466- Some ( sess. new_turn ( SessionSettingsUpdate :: default ( ) ) . await ) ;
1525+ let mut previous_context: Option < Arc < TurnContext > > = Some ( sess. new_default_turn ( ) . await ) ;
14671526
14681527 if config. features . enabled ( Feature :: RemoteModels )
14691528 && let Err ( err) = sess
@@ -1492,6 +1551,7 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
14921551 } => {
14931552 handlers:: override_turn_context (
14941553 & sess,
1554+ sub. id . clone ( ) ,
14951555 SessionSettingsUpdate {
14961556 cwd,
14971557 approval_policy,
@@ -1602,8 +1662,21 @@ mod handlers {
16021662 sess. interrupt_task ( ) . await ;
16031663 }
16041664
1605- pub async fn override_turn_context ( sess : & Session , updates : SessionSettingsUpdate ) {
1606- sess. update_settings ( updates) . await ;
1665+ pub async fn override_turn_context (
1666+ sess : & Session ,
1667+ sub_id : String ,
1668+ updates : SessionSettingsUpdate ,
1669+ ) {
1670+ if let Err ( err) = sess. update_settings ( updates) . await {
1671+ sess. send_event_raw ( Event {
1672+ id : sub_id,
1673+ msg : EventMsg :: Error ( ErrorEvent {
1674+ message : err. to_string ( ) ,
1675+ codex_error_info : Some ( CodexErrorInfo :: BadRequest ) ,
1676+ } ) ,
1677+ } )
1678+ . await ;
1679+ }
16071680 }
16081681
16091682 pub async fn user_input_or_turn (
@@ -1638,7 +1711,9 @@ mod handlers {
16381711 _ => unreachable ! ( ) ,
16391712 } ;
16401713
1641- let current_context = sess. new_turn_with_sub_id ( sub_id, updates) . await ;
1714+ let Ok ( current_context) = sess. new_turn_with_sub_id ( sub_id, updates) . await else {
1715+ return ;
1716+ } ;
16421717 current_context
16431718 . client
16441719 . get_otel_event_manager ( )
@@ -1665,9 +1740,7 @@ mod handlers {
16651740 command : String ,
16661741 previous_context : & mut Option < Arc < TurnContext > > ,
16671742 ) {
1668- let turn_context = sess
1669- . new_turn_with_sub_id ( sub_id, SessionSettingsUpdate :: default ( ) )
1670- . await ;
1743+ let turn_context = sess. new_default_turn_with_sub_id ( sub_id) . await ;
16711744 sess. spawn_task (
16721745 Arc :: clone ( & turn_context) ,
16731746 Vec :: new ( ) ,
@@ -1822,17 +1895,13 @@ mod handlers {
18221895 }
18231896
18241897 pub async fn undo ( sess : & Arc < Session > , sub_id : String ) {
1825- let turn_context = sess
1826- . new_turn_with_sub_id ( sub_id, SessionSettingsUpdate :: default ( ) )
1827- . await ;
1898+ let turn_context = sess. new_default_turn_with_sub_id ( sub_id) . await ;
18281899 sess. spawn_task ( turn_context, Vec :: new ( ) , UndoTask :: new ( ) )
18291900 . await ;
18301901 }
18311902
18321903 pub async fn compact ( sess : & Arc < Session > , sub_id : String ) {
1833- let turn_context = sess
1834- . new_turn_with_sub_id ( sub_id, SessionSettingsUpdate :: default ( ) )
1835- . await ;
1904+ let turn_context = sess. new_default_turn_with_sub_id ( sub_id) . await ;
18361905
18371906 sess. spawn_task (
18381907 Arc :: clone ( & turn_context) ,
@@ -1886,9 +1955,7 @@ mod handlers {
18861955 sub_id : String ,
18871956 review_request : ReviewRequest ,
18881957 ) {
1889- let turn_context = sess
1890- . new_turn_with_sub_id ( sub_id. clone ( ) , SessionSettingsUpdate :: default ( ) )
1891- . await ;
1958+ let turn_context = sess. new_default_turn_with_sub_id ( sub_id. clone ( ) ) . await ;
18921959 match resolve_review_request ( review_request, config. cwd . as_path ( ) ) {
18931960 Ok ( resolved) => {
18941961 spawn_review_thread (
@@ -2595,7 +2662,7 @@ mod tests {
25952662 user_instructions : config. user_instructions . clone ( ) ,
25962663 base_instructions : config. base_instructions . clone ( ) ,
25972664 compact_prompt : config. compact_prompt . clone ( ) ,
2598- approval_policy : config. approval_policy ,
2665+ approval_policy : config. approval_policy . clone ( ) ,
25992666 sandbox_policy : config. sandbox_policy . clone ( ) ,
26002667 cwd : config. cwd . clone ( ) ,
26012668 original_config_do_not_use : Arc :: clone ( & config) ,
@@ -2666,7 +2733,7 @@ mod tests {
26662733 user_instructions : config. user_instructions . clone ( ) ,
26672734 base_instructions : config. base_instructions . clone ( ) ,
26682735 compact_prompt : config. compact_prompt . clone ( ) ,
2669- approval_policy : config. approval_policy ,
2736+ approval_policy : config. approval_policy . clone ( ) ,
26702737 sandbox_policy : config. sandbox_policy . clone ( ) ,
26712738 cwd : config. cwd . clone ( ) ,
26722739 original_config_do_not_use : Arc :: clone ( & config) ,
@@ -2867,7 +2934,7 @@ mod tests {
28672934 user_instructions : config. user_instructions . clone ( ) ,
28682935 base_instructions : config. base_instructions . clone ( ) ,
28692936 compact_prompt : config. compact_prompt . clone ( ) ,
2870- approval_policy : config. approval_policy ,
2937+ approval_policy : config. approval_policy . clone ( ) ,
28712938 sandbox_policy : config. sandbox_policy . clone ( ) ,
28722939 cwd : config. cwd . clone ( ) ,
28732940 original_config_do_not_use : Arc :: clone ( & config) ,
@@ -2949,7 +3016,7 @@ mod tests {
29493016 user_instructions : config. user_instructions . clone ( ) ,
29503017 base_instructions : config. base_instructions . clone ( ) ,
29513018 compact_prompt : config. compact_prompt . clone ( ) ,
2952- approval_policy : config. approval_policy ,
3019+ approval_policy : config. approval_policy . clone ( ) ,
29533020 sandbox_policy : config. sandbox_policy . clone ( ) ,
29543021 cwd : config. cwd . clone ( ) ,
29553022 original_config_do_not_use : Arc :: clone ( & config) ,
0 commit comments