@@ -83,6 +83,7 @@ use codex_app_server_protocol::TurnSteerParams;
8383use codex_app_server_protocol:: TurnSteerResponse ;
8484use codex_otel:: TelemetryAuthMode ;
8585use codex_protocol:: ThreadId ;
86+ use codex_protocol:: models:: PermissionProfile ;
8687use codex_protocol:: models:: ResponseItem ;
8788use codex_protocol:: openai_models:: ModelAvailabilityNux ;
8889use codex_protocol:: openai_models:: ModelPreset ;
@@ -146,6 +147,7 @@ pub(crate) struct ThreadSessionState {
146147 pub ( crate ) approval_policy : AskForApproval ,
147148 pub ( crate ) approvals_reviewer : codex_protocol:: config_types:: ApprovalsReviewer ,
148149 pub ( crate ) sandbox_policy : SandboxPolicy ,
150+ pub ( crate ) permission_profile : PermissionProfile ,
149151 pub ( crate ) cwd : AbsolutePathBuf ,
150152 pub ( crate ) instruction_source_paths : Vec < AbsolutePathBuf > ,
151153 pub ( crate ) reasoning_effort : Option < codex_protocol:: openai_models:: ReasoningEffort > ,
@@ -525,6 +527,14 @@ impl AppServerSession {
525527 output_schema : Option < serde_json:: Value > ,
526528 ) -> Result < TurnStartResponse > {
527529 let request_id = self . next_request_id ( ) ;
530+ let ( sandbox_policy, permission_profile) = match sandbox_policy {
531+ policy @ SandboxPolicy :: ExternalSandbox { .. } => ( Some ( policy. into ( ) ) , None ) ,
532+ policy => {
533+ let permission_profile =
534+ PermissionProfile :: from_legacy_sandbox_policy ( & policy, & cwd) ;
535+ ( None , Some ( permission_profile. into ( ) ) )
536+ }
537+ } ;
528538 self . client
529539 . request_typed ( ClientRequest :: TurnStart {
530540 request_id,
@@ -535,8 +545,8 @@ impl AppServerSession {
535545 cwd : Some ( cwd) ,
536546 approval_policy : Some ( approval_policy. into ( ) ) ,
537547 approvals_reviewer : Some ( approvals_reviewer. into ( ) ) ,
538- sandbox_policy : Some ( sandbox_policy . into ( ) ) ,
539- permission_profile : None ,
548+ sandbox_policy,
549+ permission_profile,
540550 model : Some ( model) ,
541551 service_tier,
542552 effort,
@@ -1004,19 +1014,38 @@ fn sandbox_mode_from_policy(
10041014 }
10051015}
10061016
1017+ fn permission_profile_override_from_config (
1018+ config : & Config ,
1019+ ) -> Option < codex_app_server_protocol:: PermissionProfile > {
1020+ if matches ! (
1021+ config. permissions. sandbox_policy. get( ) ,
1022+ SandboxPolicy :: ExternalSandbox { .. }
1023+ ) {
1024+ None
1025+ } else {
1026+ Some ( config. permissions . permission_profile ( ) . into ( ) )
1027+ }
1028+ }
1029+
10071030fn thread_start_params_from_config (
10081031 config : & Config ,
10091032 thread_params_mode : ThreadParamsMode ,
10101033 remote_cwd_override : Option < & std:: path:: Path > ,
10111034 session_start_source : Option < ThreadStartSource > ,
10121035) -> ThreadStartParams {
1036+ let permission_profile = permission_profile_override_from_config ( config) ;
1037+ let sandbox = permission_profile
1038+ . is_none ( )
1039+ . then ( || sandbox_mode_from_policy ( config. permissions . sandbox_policy . get ( ) . clone ( ) ) )
1040+ . flatten ( ) ;
10131041 ThreadStartParams {
10141042 model : config. model . clone ( ) ,
10151043 model_provider : thread_params_mode. model_provider_from_config ( config) ,
10161044 cwd : thread_cwd_from_config ( config, thread_params_mode, remote_cwd_override) ,
10171045 approval_policy : Some ( config. permissions . approval_policy . value ( ) . into ( ) ) ,
10181046 approvals_reviewer : approvals_reviewer_override_from_config ( config) ,
1019- sandbox : sandbox_mode_from_policy ( config. permissions . sandbox_policy . get ( ) . clone ( ) ) ,
1047+ sandbox,
1048+ permission_profile,
10201049 config : config_request_overrides_from_config ( config) ,
10211050 ephemeral : Some ( config. ephemeral ) ,
10221051 session_start_source,
@@ -1031,14 +1060,20 @@ fn thread_resume_params_from_config(
10311060 thread_params_mode : ThreadParamsMode ,
10321061 remote_cwd_override : Option < & std:: path:: Path > ,
10331062) -> ThreadResumeParams {
1063+ let permission_profile = permission_profile_override_from_config ( & config) ;
1064+ let sandbox = permission_profile
1065+ . is_none ( )
1066+ . then ( || sandbox_mode_from_policy ( config. permissions . sandbox_policy . get ( ) . clone ( ) ) )
1067+ . flatten ( ) ;
10341068 ThreadResumeParams {
10351069 thread_id : thread_id. to_string ( ) ,
10361070 model : config. model . clone ( ) ,
10371071 model_provider : thread_params_mode. model_provider_from_config ( & config) ,
10381072 cwd : thread_cwd_from_config ( & config, thread_params_mode, remote_cwd_override) ,
10391073 approval_policy : Some ( config. permissions . approval_policy . value ( ) . into ( ) ) ,
10401074 approvals_reviewer : approvals_reviewer_override_from_config ( & config) ,
1041- sandbox : sandbox_mode_from_policy ( config. permissions . sandbox_policy . get ( ) . clone ( ) ) ,
1075+ sandbox,
1076+ permission_profile,
10421077 config : config_request_overrides_from_config ( & config) ,
10431078 persist_extended_history : true ,
10441079 ..ThreadResumeParams :: default ( )
@@ -1051,14 +1086,20 @@ fn thread_fork_params_from_config(
10511086 thread_params_mode : ThreadParamsMode ,
10521087 remote_cwd_override : Option < & std:: path:: Path > ,
10531088) -> ThreadForkParams {
1089+ let permission_profile = permission_profile_override_from_config ( & config) ;
1090+ let sandbox = permission_profile
1091+ . is_none ( )
1092+ . then ( || sandbox_mode_from_policy ( config. permissions . sandbox_policy . get ( ) . clone ( ) ) )
1093+ . flatten ( ) ;
10541094 ThreadForkParams {
10551095 thread_id : thread_id. to_string ( ) ,
10561096 model : config. model . clone ( ) ,
10571097 model_provider : thread_params_mode. model_provider_from_config ( & config) ,
10581098 cwd : thread_cwd_from_config ( & config, thread_params_mode, remote_cwd_override) ,
10591099 approval_policy : Some ( config. permissions . approval_policy . value ( ) . into ( ) ) ,
10601100 approvals_reviewer : approvals_reviewer_override_from_config ( & config) ,
1061- sandbox : sandbox_mode_from_policy ( config. permissions . sandbox_policy . get ( ) . clone ( ) ) ,
1101+ sandbox,
1102+ permission_profile,
10621103 config : config_request_overrides_from_config ( & config) ,
10631104 base_instructions : config. base_instructions . clone ( ) ,
10641105 developer_instructions : config. developer_instructions . clone ( ) ,
@@ -1135,6 +1176,7 @@ async fn thread_session_state_from_thread_start_response(
11351176 response. approval_policy . to_core ( ) ,
11361177 response. approvals_reviewer . to_core ( ) ,
11371178 response. sandbox . to_core ( ) ,
1179+ response. permission_profile . clone ( ) . into ( ) ,
11381180 response. cwd . clone ( ) ,
11391181 response. instruction_sources . clone ( ) ,
11401182 response. reasoning_effort ,
@@ -1158,6 +1200,7 @@ async fn thread_session_state_from_thread_resume_response(
11581200 response. approval_policy . to_core ( ) ,
11591201 response. approvals_reviewer . to_core ( ) ,
11601202 response. sandbox . to_core ( ) ,
1203+ response. permission_profile . clone ( ) . into ( ) ,
11611204 response. cwd . clone ( ) ,
11621205 response. instruction_sources . clone ( ) ,
11631206 response. reasoning_effort ,
@@ -1181,6 +1224,7 @@ async fn thread_session_state_from_thread_fork_response(
11811224 response. approval_policy . to_core ( ) ,
11821225 response. approvals_reviewer . to_core ( ) ,
11831226 response. sandbox . to_core ( ) ,
1227+ response. permission_profile . clone ( ) . into ( ) ,
11841228 response. cwd . clone ( ) ,
11851229 response. instruction_sources . clone ( ) ,
11861230 response. reasoning_effort ,
@@ -1223,6 +1267,7 @@ async fn thread_session_state_from_thread_response(
12231267 approval_policy : AskForApproval ,
12241268 approvals_reviewer : codex_protocol:: config_types:: ApprovalsReviewer ,
12251269 sandbox_policy : SandboxPolicy ,
1270+ permission_profile : PermissionProfile ,
12261271 cwd : AbsolutePathBuf ,
12271272 instruction_source_paths : Vec < AbsolutePathBuf > ,
12281273 reasoning_effort : Option < codex_protocol:: openai_models:: ReasoningEffort > ,
@@ -1249,6 +1294,7 @@ async fn thread_session_state_from_thread_response(
12491294 approval_policy,
12501295 approvals_reviewer,
12511296 sandbox_policy,
1297+ permission_profile,
12521298 cwd,
12531299 instruction_source_paths,
12541300 reasoning_effort,
@@ -1341,6 +1387,11 @@ mod tests {
13411387 ) ;
13421388
13431389 assert_eq ! ( params. cwd, Some ( config. cwd. to_string_lossy( ) . to_string( ) ) ) ;
1390+ assert_eq ! ( params. sandbox, None ) ;
1391+ assert_eq ! (
1392+ params. permission_profile,
1393+ Some ( config. permissions. permission_profile( ) . into( ) )
1394+ ) ;
13441395 assert_eq ! ( params. model_provider, Some ( config. model_provider_id) ) ;
13451396 }
13461397
@@ -1521,6 +1572,10 @@ mod tests {
15211572 started. session. instruction_source_paths,
15221573 response. instruction_sources
15231574 ) ;
1575+ assert_eq ! (
1576+ started. session. permission_profile,
1577+ response. permission_profile. clone( ) . into( )
1578+ ) ;
15241579 assert_eq ! ( started. turns. len( ) , 1 ) ;
15251580 assert_eq ! ( started. turns[ 0 ] , response. thread. turns[ 0 ] ) ;
15261581 }
@@ -1549,6 +1604,10 @@ mod tests {
15491604 AskForApproval :: Never ,
15501605 codex_protocol:: config_types:: ApprovalsReviewer :: User ,
15511606 SandboxPolicy :: new_read_only_policy ( ) ,
1607+ PermissionProfile :: from_legacy_sandbox_policy (
1608+ & SandboxPolicy :: new_read_only_policy ( ) ,
1609+ std:: path:: Path :: new ( "/tmp/project" ) ,
1610+ ) ,
15521611 test_path_buf ( "/tmp/project" ) . abs ( ) ,
15531612 Vec :: new ( ) ,
15541613 /*reasoning_effort*/ None ,
@@ -1579,6 +1638,10 @@ mod tests {
15791638 AskForApproval :: Never ,
15801639 codex_protocol:: config_types:: ApprovalsReviewer :: User ,
15811640 SandboxPolicy :: new_read_only_policy ( ) ,
1641+ PermissionProfile :: from_legacy_sandbox_policy (
1642+ & SandboxPolicy :: new_read_only_policy ( ) ,
1643+ std:: path:: Path :: new ( "/tmp/project" ) ,
1644+ ) ,
15821645 test_path_buf ( "/tmp/project" ) . abs ( ) ,
15831646 Vec :: new ( ) ,
15841647 /*reasoning_effort*/ None ,
0 commit comments