@@ -3541,7 +3541,7 @@ pub struct SessionNetworkProxyRuntime {
35413541 pub socks_addr : String ,
35423542}
35433543
3544- #[ derive( Debug , Clone , Deserialize , Serialize , JsonSchema , TS ) ]
3544+ #[ derive( Debug , Clone , Serialize , JsonSchema , TS ) ]
35453545pub struct SessionConfiguredEvent {
35463546 pub session_id : ThreadId ,
35473547 #[ serde( skip_serializing_if = "Option::is_none" ) ]
@@ -3569,16 +3569,8 @@ pub struct SessionConfiguredEvent {
35693569 #[ serde( default ) ]
35703570 pub approvals_reviewer : ApprovalsReviewer ,
35713571
3572- /// Legacy sandbox projection for commands executed in the system.
3573- ///
3574- /// Consumers should prefer `permission_profile` when it is present. This
3575- /// field remains available as a compatibility fallback for older emitters
3576- /// and sessions that only expose legacy sandbox state.
3577- pub sandbox_policy : SandboxPolicy ,
3578-
35793572 /// Canonical effective permissions for commands executed in the session.
3580- #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
3581- pub permission_profile : Option < PermissionProfile > ,
3573+ pub permission_profile : PermissionProfile ,
35823574
35833575 /// Working directory that should be treated as the *root* of the
35843576 /// session.
@@ -3609,6 +3601,67 @@ pub struct SessionConfiguredEvent {
36093601 pub rollout_path : Option < PathBuf > ,
36103602}
36113603
3604+ impl < ' de > Deserialize < ' de > for SessionConfiguredEvent {
3605+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
3606+ where
3607+ D : serde:: Deserializer < ' de > ,
3608+ {
3609+ #[ derive( Deserialize ) ]
3610+ struct Wire {
3611+ session_id : ThreadId ,
3612+ forked_from_id : Option < ThreadId > ,
3613+ #[ serde( default ) ]
3614+ thread_name : Option < String > ,
3615+ model : String ,
3616+ model_provider_id : String ,
3617+ service_tier : Option < ServiceTier > ,
3618+ approval_policy : AskForApproval ,
3619+ #[ serde( default ) ]
3620+ approvals_reviewer : ApprovalsReviewer ,
3621+ sandbox_policy : Option < SandboxPolicy > ,
3622+ permission_profile : Option < PermissionProfile > ,
3623+ cwd : AbsolutePathBuf ,
3624+ reasoning_effort : Option < ReasoningEffortConfig > ,
3625+ history_log_id : u64 ,
3626+ history_entry_count : usize ,
3627+ initial_messages : Option < Vec < EventMsg > > ,
3628+ network_proxy : Option < SessionNetworkProxyRuntime > ,
3629+ rollout_path : Option < PathBuf > ,
3630+ }
3631+
3632+ let wire = Wire :: deserialize ( deserializer) ?;
3633+ let permission_profile = match ( wire. permission_profile , wire. sandbox_policy ) {
3634+ ( Some ( permission_profile) , _) => permission_profile,
3635+ ( None , Some ( sandbox_policy) ) => PermissionProfile :: from_legacy_sandbox_policy_for_cwd (
3636+ & sandbox_policy,
3637+ wire. cwd . as_path ( ) ,
3638+ ) ,
3639+ ( None , None ) => {
3640+ return Err ( serde:: de:: Error :: missing_field ( "permission_profile" ) ) ;
3641+ }
3642+ } ;
3643+
3644+ Ok ( Self {
3645+ session_id : wire. session_id ,
3646+ forked_from_id : wire. forked_from_id ,
3647+ thread_name : wire. thread_name ,
3648+ model : wire. model ,
3649+ model_provider_id : wire. model_provider_id ,
3650+ service_tier : wire. service_tier ,
3651+ approval_policy : wire. approval_policy ,
3652+ approvals_reviewer : wire. approvals_reviewer ,
3653+ permission_profile,
3654+ cwd : wire. cwd ,
3655+ reasoning_effort : wire. reasoning_effort ,
3656+ history_log_id : wire. history_log_id ,
3657+ history_entry_count : wire. history_entry_count ,
3658+ initial_messages : wire. initial_messages ,
3659+ network_proxy : wire. network_proxy ,
3660+ rollout_path : wire. rollout_path ,
3661+ } )
3662+ }
3663+ }
3664+
36123665#[ derive( Debug , Clone , Deserialize , Serialize , JsonSchema , TS ) ]
36133666pub struct ThreadNameUpdatedEvent {
36143667 pub thread_id : ThreadId ,
@@ -5088,6 +5141,7 @@ mod tests {
50885141 fn serialize_event ( ) -> Result < ( ) > {
50895142 let conversation_id = ThreadId :: from_string ( "67e55044-10b1-426f-9247-bb680e5fe0c8" ) ?;
50905143 let rollout_file = NamedTempFile :: new ( ) ?;
5144+ let permission_profile = PermissionProfile :: read_only ( ) ;
50915145 let event = Event {
50925146 id : "1234" . to_string ( ) ,
50935147 msg : EventMsg :: SessionConfigured ( SessionConfiguredEvent {
@@ -5099,8 +5153,7 @@ mod tests {
50995153 service_tier : None ,
51005154 approval_policy : AskForApproval :: Never ,
51015155 approvals_reviewer : ApprovalsReviewer :: User ,
5102- sandbox_policy : SandboxPolicy :: new_read_only_policy ( ) ,
5103- permission_profile : None ,
5156+ permission_profile : permission_profile. clone ( ) ,
51045157 cwd : test_path_buf ( "/home/user/project" ) . abs ( ) ,
51055158 reasoning_effort : Some ( ReasoningEffortConfig :: default ( ) ) ,
51065159 history_log_id : 0 ,
@@ -5120,9 +5173,7 @@ mod tests {
51205173 "model_provider_id" : "openai" ,
51215174 "approval_policy" : "never" ,
51225175 "approvals_reviewer" : "user" ,
5123- "sandbox_policy" : {
5124- "type" : "read-only"
5125- } ,
5176+ "permission_profile" : permission_profile,
51265177 "cwd" : test_path_buf( "/home/user/project" ) ,
51275178 "reasoning_effort" : "medium" ,
51285179 "history_log_id" : 0 ,
@@ -5134,6 +5185,28 @@ mod tests {
51345185 Ok ( ( ) )
51355186 }
51365187
5188+ #[ test]
5189+ fn deserialize_legacy_session_configured_event_uses_sandbox_policy ( ) -> Result < ( ) > {
5190+ let cwd = test_path_buf ( "/home/user/project" ) ;
5191+ let value = json ! ( {
5192+ "session_id" : "67e55044-10b1-426f-9247-bb680e5fe0c8" ,
5193+ "model" : "codex-mini-latest" ,
5194+ "model_provider_id" : "openai" ,
5195+ "approval_policy" : "never" ,
5196+ "approvals_reviewer" : "user" ,
5197+ "sandbox_policy" : {
5198+ "type" : "read-only"
5199+ } ,
5200+ "cwd" : cwd,
5201+ "history_log_id" : 0 ,
5202+ "history_entry_count" : 0 ,
5203+ } ) ;
5204+
5205+ let event: SessionConfiguredEvent = serde_json:: from_value ( value) ?;
5206+ assert_eq ! ( event. permission_profile, PermissionProfile :: read_only( ) ) ;
5207+ Ok ( ( ) )
5208+ }
5209+
51375210 #[ test]
51385211 fn vec_u8_as_base64_serialization_and_deserialization ( ) -> Result < ( ) > {
51395212 let event = ExecCommandOutputDeltaEvent {
0 commit comments