@@ -3538,7 +3538,7 @@ pub struct SessionNetworkProxyRuntime {
35383538 pub socks_addr : String ,
35393539}
35403540
3541- #[ derive( Debug , Clone , Deserialize , Serialize , JsonSchema , TS ) ]
3541+ #[ derive( Debug , Clone , Serialize , JsonSchema , TS ) ]
35423542pub struct SessionConfiguredEvent {
35433543 pub session_id : ThreadId ,
35443544 #[ serde( skip_serializing_if = "Option::is_none" ) ]
@@ -3566,16 +3566,8 @@ pub struct SessionConfiguredEvent {
35663566 #[ serde( default ) ]
35673567 pub approvals_reviewer : ApprovalsReviewer ,
35683568
3569- /// Legacy sandbox projection for commands executed in the system.
3570- ///
3571- /// Consumers should prefer `permission_profile` when it is present. This
3572- /// field remains available as a compatibility fallback for older emitters
3573- /// and sessions that only expose legacy sandbox state.
3574- pub sandbox_policy : SandboxPolicy ,
3575-
35763569 /// Canonical effective permissions for commands executed in the session.
3577- #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
3578- pub permission_profile : Option < PermissionProfile > ,
3570+ pub permission_profile : PermissionProfile ,
35793571
35803572 /// Working directory that should be treated as the *root* of the
35813573 /// session.
@@ -3606,6 +3598,67 @@ pub struct SessionConfiguredEvent {
36063598 pub rollout_path : Option < PathBuf > ,
36073599}
36083600
3601+ impl < ' de > Deserialize < ' de > for SessionConfiguredEvent {
3602+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
3603+ where
3604+ D : serde:: Deserializer < ' de > ,
3605+ {
3606+ #[ derive( Deserialize ) ]
3607+ struct Wire {
3608+ session_id : ThreadId ,
3609+ forked_from_id : Option < ThreadId > ,
3610+ #[ serde( default ) ]
3611+ thread_name : Option < String > ,
3612+ model : String ,
3613+ model_provider_id : String ,
3614+ service_tier : Option < ServiceTier > ,
3615+ approval_policy : AskForApproval ,
3616+ #[ serde( default ) ]
3617+ approvals_reviewer : ApprovalsReviewer ,
3618+ sandbox_policy : Option < SandboxPolicy > ,
3619+ permission_profile : Option < PermissionProfile > ,
3620+ cwd : AbsolutePathBuf ,
3621+ reasoning_effort : Option < ReasoningEffortConfig > ,
3622+ history_log_id : u64 ,
3623+ history_entry_count : usize ,
3624+ initial_messages : Option < Vec < EventMsg > > ,
3625+ network_proxy : Option < SessionNetworkProxyRuntime > ,
3626+ rollout_path : Option < PathBuf > ,
3627+ }
3628+
3629+ let wire = Wire :: deserialize ( deserializer) ?;
3630+ let permission_profile = match ( wire. permission_profile , wire. sandbox_policy ) {
3631+ ( Some ( permission_profile) , _) => permission_profile,
3632+ ( None , Some ( sandbox_policy) ) => PermissionProfile :: from_legacy_sandbox_policy_for_cwd (
3633+ & sandbox_policy,
3634+ wire. cwd . as_path ( ) ,
3635+ ) ,
3636+ ( None , None ) => {
3637+ return Err ( serde:: de:: Error :: missing_field ( "permission_profile" ) ) ;
3638+ }
3639+ } ;
3640+
3641+ Ok ( Self {
3642+ session_id : wire. session_id ,
3643+ forked_from_id : wire. forked_from_id ,
3644+ thread_name : wire. thread_name ,
3645+ model : wire. model ,
3646+ model_provider_id : wire. model_provider_id ,
3647+ service_tier : wire. service_tier ,
3648+ approval_policy : wire. approval_policy ,
3649+ approvals_reviewer : wire. approvals_reviewer ,
3650+ permission_profile,
3651+ cwd : wire. cwd ,
3652+ reasoning_effort : wire. reasoning_effort ,
3653+ history_log_id : wire. history_log_id ,
3654+ history_entry_count : wire. history_entry_count ,
3655+ initial_messages : wire. initial_messages ,
3656+ network_proxy : wire. network_proxy ,
3657+ rollout_path : wire. rollout_path ,
3658+ } )
3659+ }
3660+ }
3661+
36093662#[ derive( Debug , Clone , Deserialize , Serialize , JsonSchema , TS ) ]
36103663pub struct ThreadNameUpdatedEvent {
36113664 pub thread_id : ThreadId ,
@@ -5085,6 +5138,7 @@ mod tests {
50855138 fn serialize_event ( ) -> Result < ( ) > {
50865139 let conversation_id = ThreadId :: from_string ( "67e55044-10b1-426f-9247-bb680e5fe0c8" ) ?;
50875140 let rollout_file = NamedTempFile :: new ( ) ?;
5141+ let permission_profile = PermissionProfile :: read_only ( ) ;
50885142 let event = Event {
50895143 id : "1234" . to_string ( ) ,
50905144 msg : EventMsg :: SessionConfigured ( SessionConfiguredEvent {
@@ -5096,8 +5150,7 @@ mod tests {
50965150 service_tier : None ,
50975151 approval_policy : AskForApproval :: Never ,
50985152 approvals_reviewer : ApprovalsReviewer :: User ,
5099- sandbox_policy : SandboxPolicy :: new_read_only_policy ( ) ,
5100- permission_profile : None ,
5153+ permission_profile : permission_profile. clone ( ) ,
51015154 cwd : test_path_buf ( "/home/user/project" ) . abs ( ) ,
51025155 reasoning_effort : Some ( ReasoningEffortConfig :: default ( ) ) ,
51035156 history_log_id : 0 ,
@@ -5117,9 +5170,7 @@ mod tests {
51175170 "model_provider_id" : "openai" ,
51185171 "approval_policy" : "never" ,
51195172 "approvals_reviewer" : "user" ,
5120- "sandbox_policy" : {
5121- "type" : "read-only"
5122- } ,
5173+ "permission_profile" : permission_profile,
51235174 "cwd" : test_path_buf( "/home/user/project" ) ,
51245175 "reasoning_effort" : "medium" ,
51255176 "history_log_id" : 0 ,
@@ -5131,6 +5182,28 @@ mod tests {
51315182 Ok ( ( ) )
51325183 }
51335184
5185+ #[ test]
5186+ fn deserialize_legacy_session_configured_event_uses_sandbox_policy ( ) -> Result < ( ) > {
5187+ let cwd = test_path_buf ( "/home/user/project" ) ;
5188+ let value = json ! ( {
5189+ "session_id" : "67e55044-10b1-426f-9247-bb680e5fe0c8" ,
5190+ "model" : "codex-mini-latest" ,
5191+ "model_provider_id" : "openai" ,
5192+ "approval_policy" : "never" ,
5193+ "approvals_reviewer" : "user" ,
5194+ "sandbox_policy" : {
5195+ "type" : "read-only"
5196+ } ,
5197+ "cwd" : cwd,
5198+ "history_log_id" : 0 ,
5199+ "history_entry_count" : 0 ,
5200+ } ) ;
5201+
5202+ let event: SessionConfiguredEvent = serde_json:: from_value ( value) ?;
5203+ assert_eq ! ( event. permission_profile, PermissionProfile :: read_only( ) ) ;
5204+ Ok ( ( ) )
5205+ }
5206+
51345207 #[ test]
51355208 fn vec_u8_as_base64_serialization_and_deserialization ( ) -> Result < ( ) > {
51365209 let event = ExecCommandOutputDeltaEvent {
0 commit comments