mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Separate thread settings from environment configuration (#39597)
## Why Environment-owned permission profiles and workspace roots must remain effective without being persisted or restored as thread-owned settings. ## What changed - Keep thread settings snapshots and restore data separate from the effective configuration supplied by the primary environment. - Use resolved environment permissions, active profiles, workspace roots, and network policy when building turn context and session events. - Preserve environment selections when agents are evicted, resumed, or spawned, and validate owner configuration when it becomes ready. - Start memory initialization only after the primary environment is configured. ## Testing - Extend remote-environment and multi-agent tests to cover settings persistence, owner permission profiles, workspace roots, spawning, eviction, and resume. GitOrigin-RevId: d82f126981cd0190233020bbdd61d96c70558868
This commit is contained in:
1 parent
8f4a48a6ad
commit
d75c85f651
35 files changed
+564
-308
No files matched your search
@@ -137,10 +137,6 @@ impl SessionConfiguration {
|
||||
&self.codex_home
|
||||
}
|
||||
|
||||
pub(super) fn permission_profile_state(&self) -> &PermissionProfileState {
|
||||
&self.permission_profile_state
|
||||
}
|
||||
|
||||
pub(super) fn inferred_environment_config(&self) -> EnvironmentConfig {
|
||||
EnvironmentConfig {
|
||||
allow_login_shell: self.allow_login_shell,
|
||||
@@ -170,10 +166,6 @@ impl SessionConfiguration {
|
||||
self.permission_profile_state.active_permission_profile()
|
||||
}
|
||||
|
||||
pub(super) fn profile_workspace_roots(&self) -> &[AbsolutePathBuf] {
|
||||
self.permission_profile_state.profile_workspace_roots()
|
||||
}
|
||||
|
||||
pub(super) fn apply_permission_profile_to_permissions(
|
||||
&self,
|
||||
permissions: &mut crate::config::Permissions,
|
||||
@@ -221,20 +213,26 @@ impl SessionConfiguration {
|
||||
) -> ThreadConfigSnapshot {
|
||||
let workspace_roots =
|
||||
ThreadEnvironments::primary_workspace_roots_for(&environment_selections);
|
||||
let permission_profile = ThreadEnvironments::primary_config_for(&environment_selections)
|
||||
.map(|config| config.permission_profile.clone())
|
||||
.unwrap_or_else(|| self.permission_profile_state.snapshot());
|
||||
ThreadConfigSnapshot {
|
||||
model: self.collaboration_mode.model().to_string(),
|
||||
model_provider_id: self.original_config_do_not_use.model_provider_id.clone(),
|
||||
service_tier: self.service_tier.clone(),
|
||||
approval_policy: self.approval_policy.value(),
|
||||
approvals_reviewer: self.approvals_reviewer,
|
||||
permission_profile: self.materialized_permission_profile(&environment_selections),
|
||||
active_permission_profile: self.active_permission_profile(),
|
||||
permission_profile: permission_profile
|
||||
.permission_profile()
|
||||
.clone()
|
||||
.materialize_project_roots_with_workspace_roots(&workspace_roots),
|
||||
active_permission_profile: permission_profile.active_permission_profile(),
|
||||
environments: TurnEnvironmentSelections::new(
|
||||
self.legacy_fallback_cwd.clone(),
|
||||
environment_selections,
|
||||
),
|
||||
workspace_roots,
|
||||
profile_workspace_roots: self.profile_workspace_roots().to_vec(),
|
||||
profile_workspace_roots: permission_profile.profile_workspace_roots().to_vec(),
|
||||
ephemeral: self.original_config_do_not_use.ephemeral,
|
||||
reasoning_effort: self.collaboration_mode.reasoning_effort(),
|
||||
reasoning_summary: self.model_reasoning_summary,
|
||||
@@ -249,6 +247,54 @@ impl SessionConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/// Captures thread-owned settings for persistence and resume.
|
||||
pub(super) fn thread_settings_snapshot(
|
||||
&self,
|
||||
environment_selections: &[TurnEnvironmentSelection],
|
||||
) -> ThreadSettingsSnapshot {
|
||||
ThreadSettingsSnapshot {
|
||||
model: self.collaboration_mode.model().to_string(),
|
||||
model_provider_id: self.original_config_do_not_use.model_provider_id.clone(),
|
||||
service_tier: self.service_tier.clone(),
|
||||
approval_policy: self.approval_policy.value(),
|
||||
approvals_reviewer: self.approvals_reviewer,
|
||||
permission_profile: self.materialized_permission_profile(environment_selections),
|
||||
active_permission_profile: self.active_permission_profile(),
|
||||
cwd: self.legacy_fallback_cwd.clone(),
|
||||
reasoning_effort: self.collaboration_mode.reasoning_effort(),
|
||||
reasoning_summary: self.model_reasoning_summary,
|
||||
personality: self.personality,
|
||||
collaboration_mode: self.collaboration_mode.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Captures thread-owned settings and their separately owned environments.
|
||||
pub(super) fn restorable_thread_settings(
|
||||
&self,
|
||||
environment_selections: Vec<TurnEnvironmentSelection>,
|
||||
) -> CodexThreadSettingsOverrides {
|
||||
CodexThreadSettingsOverrides {
|
||||
environments: Some(TurnEnvironmentSelections::new(
|
||||
self.legacy_fallback_cwd.clone(),
|
||||
environment_selections,
|
||||
)),
|
||||
profile_workspace_roots: Some(
|
||||
self.permission_profile_state
|
||||
.profile_workspace_roots()
|
||||
.to_vec(),
|
||||
),
|
||||
approval_policy: Some(self.approval_policy.value()),
|
||||
approvals_reviewer: Some(self.approvals_reviewer),
|
||||
permission_profile: Some(self.permission_profile()),
|
||||
active_permission_profile: self.active_permission_profile(),
|
||||
summary: self.model_reasoning_summary,
|
||||
service_tier: Some(self.service_tier.clone()),
|
||||
collaboration_mode: Some(self.collaboration_mode.clone()),
|
||||
personality: self.personality,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn validate(
|
||||
&self,
|
||||
environments: &[TurnEnvironmentSelection],
|
||||
@@ -258,7 +304,7 @@ impl SessionConfiguration {
|
||||
ConstraintError::InvalidValue {
|
||||
field_name: "environments",
|
||||
candidate: "environment configuration".to_string(),
|
||||
allowed: format!("valid selected capability roots ({error})"),
|
||||
allowed: format!("valid environment configuration ({error})"),
|
||||
requirement_source: codex_config::RequirementSource::Unknown,
|
||||
}
|
||||
})
|
||||
@@ -281,9 +327,16 @@ impl SessionConfiguration {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let permission_profile = ThreadEnvironments::primary_config_for(environments)
|
||||
.map(|config| config.permission_profile.permission_profile())
|
||||
.unwrap_or_else(|| self.permission_profile_state.permission_profile())
|
||||
.clone()
|
||||
.materialize_project_roots_with_workspace_roots(
|
||||
&ThreadEnvironments::primary_workspace_roots_for(environments),
|
||||
);
|
||||
if self.approvals_reviewer == ApprovalsReviewer::AutoReview
|
||||
&& !self
|
||||
.file_system_sandbox_policy(environments)
|
||||
&& !permission_profile
|
||||
.file_system_sandbox_policy()
|
||||
.has_full_disk_write_access()
|
||||
&& self
|
||||
.original_config_do_not_use
|
||||
@@ -1392,33 +1445,32 @@ impl Session {
|
||||
// Dispatch the SessionConfiguredEvent first and then report any errors.
|
||||
// If resuming, include converted initial messages in the payload so UIs can render them immediately.
|
||||
let initial_messages = initial_history.get_event_msgs();
|
||||
let thread_config =
|
||||
session_configuration.thread_config_snapshot(turn_environments.selections());
|
||||
let events = std::iter::once(Event {
|
||||
id: INITIAL_SUBMIT_ID.to_owned(),
|
||||
msg: EventMsg::SessionConfigured(SessionConfiguredEvent {
|
||||
session_id,
|
||||
thread_id,
|
||||
forked_from_id,
|
||||
parent_thread_id,
|
||||
thread_source: session_configuration.thread_source.clone(),
|
||||
cwd: thread_config.cwd().clone(),
|
||||
forked_from_id: thread_config.forked_from_thread_id,
|
||||
parent_thread_id: thread_config.parent_thread_id,
|
||||
thread_source: thread_config.thread_source,
|
||||
thread_name: session_configuration.thread_name.clone(),
|
||||
model: session_configuration.collaboration_mode.model().to_string(),
|
||||
model_provider_id: config.model_provider_id.clone(),
|
||||
service_tier: session_configuration.service_tier.clone(),
|
||||
approval_policy: session_configuration.approval_policy.value(),
|
||||
approvals_reviewer: session_configuration.approvals_reviewer,
|
||||
permission_profile: session_configuration
|
||||
.materialized_permission_profile(environment_selections),
|
||||
active_permission_profile: session_configuration.active_permission_profile(),
|
||||
cwd: session_configuration.cwd().clone(),
|
||||
reasoning_effort: session_configuration.collaboration_mode.reasoning_effort(),
|
||||
initial_messages,
|
||||
model: thread_config.model,
|
||||
model_provider_id: thread_config.model_provider_id,
|
||||
service_tier: thread_config.service_tier,
|
||||
approval_policy: thread_config.approval_policy,
|
||||
approvals_reviewer: thread_config.approvals_reviewer,
|
||||
network_proxy: session_network_proxy.filter(|_| {
|
||||
Self::managed_network_proxy_active_for_permission_profile(
|
||||
session_configuration
|
||||
.permission_profile_state()
|
||||
.permission_profile(),
|
||||
&thread_config.permission_profile,
|
||||
)
|
||||
}),
|
||||
permission_profile: thread_config.permission_profile,
|
||||
active_permission_profile: thread_config.active_permission_profile,
|
||||
reasoning_effort: thread_config.reasoning_effort,
|
||||
initial_messages,
|
||||
rollout_path,
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user