mirror of
https://github.com/openai/codex.git
synced 2026-11-07 16:35:32 +00:00
Centralize thread environment selection state (#38423)
## What changed - Make `ThreadEnvironments` the source of truth for live environment selections and use it when building configuration snapshots, permission profiles, per-turn configuration, and MCP refresh inputs. - Keep settings previews side-effect free while applying accepted environment updates consistently to subsequent turns. - Move `EnvironmentConfig` into `codex-protocol` so environment ownership no longer depends on a core-only type. ## Testing - Cover environment previews and snapshots, accepted and rejected steer updates, and active-turn environment stability. GitOrigin-RevId: 7bcf8fb5df1215f720e4e9c419541a51a871aa5e
This commit is contained in:
1 parent
588e18aae5
commit
781445f7c6
16 files changed
+428
-327
No files matched your search
@@ -1,7 +1,6 @@
|
|||||||
use crate::agent::AgentStatus;
|
use crate::agent::AgentStatus;
|
||||||
use crate::config::ConstraintResult;
|
use crate::config::ConstraintResult;
|
||||||
use crate::elicitation::ElicitationRegistration;
|
use crate::elicitation::ElicitationRegistration;
|
||||||
use crate::environment_config::EnvironmentConfig;
|
|
||||||
use crate::session::SessionIo;
|
use crate::session::SessionIo;
|
||||||
use crate::session::SessionSettingsUpdate;
|
use crate::session::SessionSettingsUpdate;
|
||||||
use crate::session::session::Session;
|
use crate::session::session::Session;
|
||||||
@@ -27,6 +26,7 @@ use codex_protocol::models::PermissionProfile;
|
|||||||
use codex_protocol::models::ResponseItem;
|
use codex_protocol::models::ResponseItem;
|
||||||
use codex_protocol::openai_models::ReasoningEffort;
|
use codex_protocol::openai_models::ReasoningEffort;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfig;
|
||||||
use codex_protocol::protocol::Event;
|
use codex_protocol::protocol::Event;
|
||||||
use codex_protocol::protocol::MultiAgentVersion;
|
use codex_protocol::protocol::MultiAgentVersion;
|
||||||
use codex_protocol::protocol::Op;
|
use codex_protocol::protocol::Op;
|
||||||
@@ -669,7 +669,7 @@ impl CodexThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn environment_selections(&self) -> Vec<TurnEnvironmentSelection> {
|
pub async fn environment_selections(&self) -> Vec<TurnEnvironmentSelection> {
|
||||||
self.session.thread_environment_selections().await
|
self.session.services.turn_environments.selections()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Installs resolved environment configuration and capability roots on this thread.
|
/// Installs resolved environment configuration and capability roots on this thread.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use codex_protocol::capabilities::CapabilityRootLocation;
|
|||||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||||
use codex_protocol::error::CodexErr;
|
use codex_protocol::error::CodexErr;
|
||||||
use codex_protocol::error::Result as CodexResult;
|
use codex_protocol::error::Result as CodexResult;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfig;
|
||||||
use codex_protocol::protocol::EnvironmentConnectionEvent;
|
use codex_protocol::protocol::EnvironmentConnectionEvent;
|
||||||
use codex_protocol::protocol::Event;
|
use codex_protocol::protocol::Event;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
@@ -27,7 +28,6 @@ use futures::future::BoxFuture;
|
|||||||
use futures::future::Shared;
|
use futures::future::Shared;
|
||||||
use tokio_util::task::AbortOnDropHandle;
|
use tokio_util::task::AbortOnDropHandle;
|
||||||
|
|
||||||
use crate::environment_config::EnvironmentConfig;
|
|
||||||
use crate::session::turn_context::ShellSnapshotTask;
|
use crate::session::turn_context::ShellSnapshotTask;
|
||||||
use crate::session::turn_context::TurnEnvironment;
|
use crate::session::turn_context::TurnEnvironment;
|
||||||
use crate::session::turn_context::TurnEnvironmentConfig;
|
use crate::session::turn_context::TurnEnvironmentConfig;
|
||||||
@@ -251,6 +251,38 @@ impl ThreadEnvironments {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn selections(&self) -> Vec<TurnEnvironmentSelection> {
|
||||||
|
self.environments
|
||||||
|
.load()
|
||||||
|
.iter()
|
||||||
|
.map(|environment| environment.selection.clone())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primary_workspace_roots(&self) -> Vec<AbsolutePathBuf> {
|
||||||
|
self.environments
|
||||||
|
.load()
|
||||||
|
.first()
|
||||||
|
.map_or_else(Vec::new, |environment| {
|
||||||
|
Self::primary_workspace_roots_for(std::slice::from_ref(&environment.selection))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primary_workspace_roots_for(
|
||||||
|
selections: &[TurnEnvironmentSelection],
|
||||||
|
) -> Vec<AbsolutePathBuf> {
|
||||||
|
selections
|
||||||
|
.first()
|
||||||
|
.map(|selection| {
|
||||||
|
selection
|
||||||
|
.workspace_roots
|
||||||
|
.iter()
|
||||||
|
.filter_map(|workspace_root| workspace_root.to_abs_path().ok())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn update_environment_configs(&self, config: &TurnEnvironmentConfig) {
|
pub(crate) fn update_environment_configs(&self, config: &TurnEnvironmentConfig) {
|
||||||
let environments = self
|
let environments = self
|
||||||
.environments
|
.environments
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ mod compact_remote;
|
|||||||
mod compact_remote_history;
|
mod compact_remote_history;
|
||||||
mod compact_remote_v2;
|
mod compact_remote_v2;
|
||||||
mod compact_token_budget;
|
mod compact_token_budget;
|
||||||
|
pub use codex_protocol::protocol::EnvironmentConfig;
|
||||||
pub use codex_thread::BackgroundTerminalInfo;
|
pub use codex_thread::BackgroundTerminalInfo;
|
||||||
pub use codex_thread::CodexThread;
|
pub use codex_thread::CodexThread;
|
||||||
pub use codex_thread::CodexThreadSettingsOverrides;
|
pub use codex_thread::CodexThreadSettingsOverrides;
|
||||||
pub use codex_thread::ThreadConfigSnapshot;
|
pub use codex_thread::ThreadConfigSnapshot;
|
||||||
pub use environment_config::EnvironmentConfig;
|
|
||||||
pub use session::turn_context::TurnContext;
|
pub use session::turn_context::TurnContext;
|
||||||
mod agent;
|
mod agent;
|
||||||
mod agent_communication;
|
mod agent_communication;
|
||||||
@@ -48,7 +48,6 @@ pub mod context;
|
|||||||
mod context_manager;
|
mod context_manager;
|
||||||
mod current_time;
|
mod current_time;
|
||||||
mod elicitation;
|
mod elicitation;
|
||||||
mod environment_config;
|
|
||||||
mod environment_selection;
|
mod environment_selection;
|
||||||
pub mod exec;
|
pub mod exec;
|
||||||
pub mod exec_env;
|
pub mod exec_env;
|
||||||
|
|||||||
@@ -5,12 +5,23 @@ use codex_exec_server::SelectedCapabilityRootsStatus;
|
|||||||
use codex_protocol::capabilities::CapabilityRootLocation;
|
use codex_protocol::capabilities::CapabilityRootLocation;
|
||||||
use codex_protocol::error::CodexErr;
|
use codex_protocol::error::CodexErr;
|
||||||
use codex_protocol::error::Result as CodexResult;
|
use codex_protocol::error::Result as CodexResult;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfig;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
|
|
||||||
use crate::environment_config::EnvironmentConfig;
|
use crate::config::ConstraintResult;
|
||||||
use crate::session::session::Session;
|
use crate::session::session::Session;
|
||||||
|
use crate::session::session::SessionConfiguration;
|
||||||
|
use crate::session::session::SessionSettingsUpdate;
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
|
pub(super) fn apply_session_settings(
|
||||||
|
&self,
|
||||||
|
current: &SessionConfiguration,
|
||||||
|
updates: &SessionSettingsUpdate,
|
||||||
|
) -> ConstraintResult<SessionConfiguration> {
|
||||||
|
current.apply(updates, &self.services.turn_environments.selections())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn environment_ready(
|
pub(crate) async fn environment_ready(
|
||||||
&self,
|
&self,
|
||||||
selection: &TurnEnvironmentSelection,
|
selection: &TurnEnvironmentSelection,
|
||||||
|
|||||||
@@ -24,6 +24,21 @@ pub(super) struct McpDesiredState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
|
pub(super) fn mcp_inputs_differ(
|
||||||
|
&self,
|
||||||
|
current: &SessionConfiguration,
|
||||||
|
next: &SessionConfiguration,
|
||||||
|
updates: &SessionSettingsUpdate,
|
||||||
|
) -> bool {
|
||||||
|
current.cwd() != next.cwd()
|
||||||
|
|| current.approval_policy.value() != next.approval_policy.value()
|
||||||
|
|| current.approvals_reviewer != next.approvals_reviewer
|
||||||
|
|| current.permission_profile() != next.permission_profile()
|
||||||
|
|| updates.environments.as_ref().is_some_and(|environments| {
|
||||||
|
environments.environments != self.services.turn_environments.selections()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Waits on this session's refreshed server before tool execution is admitted.
|
/// Waits on this session's refreshed server before tool execution is admitted.
|
||||||
pub(crate) async fn wait_for_mcp_server(self: &Arc<Self>, server: &str) {
|
pub(crate) async fn wait_for_mcp_server(self: &Arc<Self>, server: &str) {
|
||||||
self.refresh_mcp_if_dirty().await;
|
self.refresh_mcp_if_dirty().await;
|
||||||
@@ -60,7 +75,7 @@ impl Session {
|
|||||||
.primary()
|
.primary()
|
||||||
.and_then(|environment| environment.cwd().to_abs_path().ok())
|
.and_then(|environment| environment.cwd().to_abs_path().ok())
|
||||||
.unwrap_or_else(|| session_configuration.cwd().clone());
|
.unwrap_or_else(|| session_configuration.cwd().clone());
|
||||||
let config = Self::build_per_turn_config(&session_configuration, cwd);
|
let config = self.build_per_turn_config(&session_configuration, cwd);
|
||||||
let local_process_cwd = environments
|
let local_process_cwd = environments
|
||||||
.local_environment_cwd()
|
.local_environment_cwd()
|
||||||
.unwrap_or_else(|| session_configuration.cwd().clone())
|
.unwrap_or_else(|| session_configuration.cwd().clone())
|
||||||
@@ -88,7 +103,7 @@ impl Session {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let cwd = AbsolutePathBuf::from_absolute_path(mcp_runtime_cwd)
|
let cwd = AbsolutePathBuf::from_absolute_path(mcp_runtime_cwd)
|
||||||
.unwrap_or_else(|_| session_configuration.cwd().clone());
|
.unwrap_or_else(|_| session_configuration.cwd().clone());
|
||||||
let config = Self::build_per_turn_config(session_configuration, cwd);
|
let config = self.build_per_turn_config(session_configuration, cwd);
|
||||||
let local_process_cwd = resolved_environments
|
let local_process_cwd = resolved_environments
|
||||||
.local_environment_cwd()
|
.local_environment_cwd()
|
||||||
.unwrap_or_else(|| session_configuration.cwd().clone())
|
.unwrap_or_else(|| session_configuration.cwd().clone())
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ use codex_protocol::protocol::TurnAbortReason;
|
|||||||
use codex_protocol::protocol::TurnContextItem;
|
use codex_protocol::protocol::TurnContextItem;
|
||||||
use codex_protocol::protocol::TurnContextNetworkItem;
|
use codex_protocol::protocol::TurnContextNetworkItem;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelections;
|
|
||||||
use codex_protocol::protocol::W3cTraceContext;
|
use codex_protocol::protocol::W3cTraceContext;
|
||||||
use codex_protocol::protocol::WorldStateItem;
|
use codex_protocol::protocol::WorldStateItem;
|
||||||
use codex_protocol::request_permissions::PermissionGrantScope;
|
use codex_protocol::request_permissions::PermissionGrantScope;
|
||||||
@@ -697,10 +696,7 @@ impl Session {
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: session_permission_profile_state_from_config(&config)?,
|
permission_profile_state: session_permission_profile_state_from_config(&config)?,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
config.cwd.clone(),
|
|
||||||
environment_selections,
|
|
||||||
),
|
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -718,7 +714,7 @@ impl Session {
|
|||||||
user_shell_override,
|
user_shell_override,
|
||||||
};
|
};
|
||||||
session_configuration
|
session_configuration
|
||||||
.validate_auto_review_requirement()
|
.validate_auto_review_requirement(&environment_selections)
|
||||||
.map_err(|err| CodexErr::InvalidRequest(err.to_string()))?;
|
.map_err(|err| CodexErr::InvalidRequest(err.to_string()))?;
|
||||||
|
|
||||||
// Generate a unique ID for the lifetime of this session.
|
// Generate a unique ID for the lifetime of this session.
|
||||||
@@ -727,6 +723,7 @@ impl Session {
|
|||||||
|
|
||||||
let session = Box::pin(Session::new(
|
let session = Box::pin(Session::new(
|
||||||
session_configuration,
|
session_configuration,
|
||||||
|
&environment_selections,
|
||||||
config.clone(),
|
config.clone(),
|
||||||
user_instructions,
|
user_instructions,
|
||||||
installation_id,
|
installation_id,
|
||||||
@@ -1570,7 +1567,8 @@ impl Session {
|
|||||||
let notify_config_contributors = !self.services.extensions.config_contributors().is_empty();
|
let notify_config_contributors = !self.services.extensions.config_contributors().is_empty();
|
||||||
let (previous_config, new_config, permission_profile_changed, mcp_inputs_changed) = {
|
let (previous_config, new_config, permission_profile_changed, mcp_inputs_changed) = {
|
||||||
let mut state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
let updated = match state.session_configuration.apply(&updates) {
|
let updated = match self.apply_session_settings(&state.session_configuration, &updates)
|
||||||
|
{
|
||||||
Ok(updated) => updated,
|
Ok(updated) => updated,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("rejected session settings update: {err}");
|
warn!("rejected session settings update: {err}");
|
||||||
@@ -1579,25 +1577,26 @@ impl Session {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let previous_config = notify_config_contributors
|
let previous_config = notify_config_contributors
|
||||||
.then(|| Self::build_effective_session_config(&state.session_configuration));
|
.then(|| self.build_effective_session_config(&state.session_configuration));
|
||||||
let new_config =
|
|
||||||
notify_config_contributors.then(|| Self::build_effective_session_config(&updated));
|
|
||||||
let previous_permission_profile = state.session_configuration.permission_profile();
|
let previous_permission_profile = state.session_configuration.permission_profile();
|
||||||
let updated_permission_profile = updated.permission_profile();
|
let updated_permission_profile = updated.permission_profile();
|
||||||
let permission_profile_changed =
|
let permission_profile_changed =
|
||||||
previous_permission_profile != updated_permission_profile;
|
previous_permission_profile != updated_permission_profile;
|
||||||
let mcp_inputs_changed = state.session_configuration.mcp_inputs_differ(&updated);
|
let mcp_inputs_changed =
|
||||||
|
self.mcp_inputs_differ(&state.session_configuration, &updated, &updates);
|
||||||
let environment_config = updated.turn_environment_config();
|
let environment_config = updated.turn_environment_config();
|
||||||
if updates.environments.is_some() {
|
if let Some(environments) = &updates.environments {
|
||||||
self.services
|
self.services
|
||||||
.turn_environments
|
.turn_environments
|
||||||
.update_selections(updated.environment_selections(), &environment_config);
|
.update_selections(&environments.environments, &environment_config);
|
||||||
} else if state.session_configuration.turn_environment_config() != environment_config {
|
} else if state.session_configuration.turn_environment_config() != environment_config {
|
||||||
self.services
|
self.services
|
||||||
.turn_environments
|
.turn_environments
|
||||||
.update_environment_configs(&environment_config);
|
.update_environment_configs(&environment_config);
|
||||||
}
|
}
|
||||||
state.session_configuration = updated;
|
state.session_configuration = updated;
|
||||||
|
let new_config = notify_config_contributors
|
||||||
|
.then(|| self.build_effective_session_config(&state.session_configuration));
|
||||||
if mcp_inputs_changed {
|
if mcp_inputs_changed {
|
||||||
self.mark_mcp_runtime_dirty();
|
self.mark_mcp_runtime_dirty();
|
||||||
}
|
}
|
||||||
@@ -1624,15 +1623,19 @@ impl Session {
|
|||||||
updates: &SessionSettingsUpdate,
|
updates: &SessionSettingsUpdate,
|
||||||
) -> ConstraintResult<ThreadConfigSnapshot> {
|
) -> ConstraintResult<ThreadConfigSnapshot> {
|
||||||
let state = self.state.lock().await;
|
let state = self.state.lock().await;
|
||||||
state
|
let configuration = self.apply_session_settings(&state.session_configuration, updates)?;
|
||||||
.session_configuration
|
let environments = updates.environments.as_ref().map_or_else(
|
||||||
.apply(updates)
|
|| self.services.turn_environments.selections(),
|
||||||
.map(|configuration| configuration.thread_config_snapshot())
|
|environments| environments.environments.clone(),
|
||||||
|
);
|
||||||
|
Ok(configuration.thread_config_snapshot(environments))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn thread_config_snapshot(&self) -> ThreadConfigSnapshot {
|
pub(crate) async fn thread_config_snapshot(&self) -> ThreadConfigSnapshot {
|
||||||
let state = self.state.lock().await;
|
let state = self.state.lock().await;
|
||||||
state.session_configuration.thread_config_snapshot()
|
state
|
||||||
|
.session_configuration
|
||||||
|
.thread_config_snapshot(self.services.turn_environments.selections())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn set_app_server_client_info(
|
pub(crate) async fn set_app_server_client_info(
|
||||||
@@ -1662,14 +1665,6 @@ impl Session {
|
|||||||
.map_or_else(Vec::new, |instructions| instructions.sources().collect())
|
.map_or_else(Vec::new, |instructions| instructions.sources().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn thread_environment_selections(&self) -> Vec<TurnEnvironmentSelection> {
|
|
||||||
let state = self.state.lock().await;
|
|
||||||
state
|
|
||||||
.session_configuration
|
|
||||||
.environment_selections()
|
|
||||||
.to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn set_session_startup_prewarm(
|
pub(crate) async fn set_session_startup_prewarm(
|
||||||
&self,
|
&self,
|
||||||
startup_prewarm: SessionStartupPrewarmHandle,
|
startup_prewarm: SessionStartupPrewarmHandle,
|
||||||
@@ -1708,7 +1703,7 @@ impl Session {
|
|||||||
let (previous_config, new_config, config) = {
|
let (previous_config, new_config, config) = {
|
||||||
let mut state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
let previous_config = notify_config_contributors
|
let previous_config = notify_config_contributors
|
||||||
.then(|| Self::build_effective_session_config(&state.session_configuration));
|
.then(|| self.build_effective_session_config(&state.session_configuration));
|
||||||
let mut config = (*state.session_configuration.original_config_do_not_use).clone();
|
let mut config = (*state.session_configuration.original_config_do_not_use).clone();
|
||||||
config.config_layer_stack = config
|
config.config_layer_stack = config
|
||||||
.config_layer_stack
|
.config_layer_stack
|
||||||
@@ -1727,7 +1722,7 @@ impl Session {
|
|||||||
state.session_configuration.original_config_do_not_use = Arc::clone(&config);
|
state.session_configuration.original_config_do_not_use = Arc::clone(&config);
|
||||||
self.mark_mcp_runtime_dirty();
|
self.mark_mcp_runtime_dirty();
|
||||||
let new_config = notify_config_contributors
|
let new_config = notify_config_contributors
|
||||||
.then(|| Self::build_effective_session_config(&state.session_configuration));
|
.then(|| self.build_effective_session_config(&state.session_configuration));
|
||||||
(previous_config, new_config, config)
|
(previous_config, new_config, config)
|
||||||
};
|
};
|
||||||
self.emit_config_changed_contributors(previous_config.as_ref(), new_config.as_ref());
|
self.emit_config_changed_contributors(previous_config.as_ref(), new_config.as_ref());
|
||||||
|
|||||||
@@ -92,9 +92,8 @@ pub(crate) struct SessionConfiguration {
|
|||||||
pub(super) permission_profile_state: PermissionProfileState,
|
pub(super) permission_profile_state: PermissionProfileState,
|
||||||
pub(super) windows_sandbox_level: WindowsSandboxLevel,
|
pub(super) windows_sandbox_level: WindowsSandboxLevel,
|
||||||
|
|
||||||
/// Sticky thread-level environment selections plus the legacy cwd used
|
/// Legacy thread cwd used when a turn does not select an environment.
|
||||||
/// when a turn does not select an environment.
|
pub(super) legacy_fallback_cwd: AbsolutePathBuf,
|
||||||
pub(super) environments: TurnEnvironmentSelections,
|
|
||||||
/// Directory containing all Codex state for this session.
|
/// Directory containing all Codex state for this session.
|
||||||
pub(super) codex_home: AbsolutePathBuf,
|
pub(super) codex_home: AbsolutePathBuf,
|
||||||
/// Optional user-facing name for the thread, updated during the session.
|
/// Optional user-facing name for the thread, updated during the session.
|
||||||
@@ -126,25 +125,7 @@ pub(crate) struct SessionConfiguration {
|
|||||||
|
|
||||||
impl SessionConfiguration {
|
impl SessionConfiguration {
|
||||||
pub(super) fn cwd(&self) -> &AbsolutePathBuf {
|
pub(super) fn cwd(&self) -> &AbsolutePathBuf {
|
||||||
&self.environments.legacy_fallback_cwd
|
&self.legacy_fallback_cwd
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn environment_selections(&self) -> &[TurnEnvironmentSelection] {
|
|
||||||
&self.environments.environments
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn primary_workspace_roots(&self) -> Vec<AbsolutePathBuf> {
|
|
||||||
self.environments
|
|
||||||
.environments
|
|
||||||
.first()
|
|
||||||
.map(|environment| {
|
|
||||||
environment
|
|
||||||
.workspace_roots
|
|
||||||
.iter()
|
|
||||||
.filter_map(|workspace_root| workspace_root.to_abs_path().ok())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn codex_home(&self) -> &AbsolutePathBuf {
|
pub(crate) fn codex_home(&self) -> &AbsolutePathBuf {
|
||||||
@@ -170,16 +151,13 @@ impl SessionConfiguration {
|
|||||||
self.permission_profile_state.permission_profile().clone()
|
self.permission_profile_state.permission_profile().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn mcp_inputs_differ(&self, next: &Self) -> bool {
|
fn materialized_permission_profile(
|
||||||
self.environments != next.environments
|
&self,
|
||||||
|| self.approval_policy.value() != next.approval_policy.value()
|
environments: &[TurnEnvironmentSelection],
|
||||||
|| self.approvals_reviewer != next.approvals_reviewer
|
) -> PermissionProfile {
|
||||||
|| self.permission_profile() != next.permission_profile()
|
let workspace_roots = ThreadEnvironments::primary_workspace_roots_for(environments);
|
||||||
}
|
|
||||||
|
|
||||||
fn materialized_permission_profile(&self) -> PermissionProfile {
|
|
||||||
self.permission_profile()
|
self.permission_profile()
|
||||||
.materialize_project_roots_with_workspace_roots(&self.primary_workspace_roots())
|
.materialize_project_roots_with_workspace_roots(&workspace_roots)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {
|
pub(super) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {
|
||||||
@@ -206,16 +184,22 @@ impl SessionConfiguration {
|
|||||||
.set_legacy_permission_profile(permission_profile)
|
.set_legacy_permission_profile(permission_profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn sandbox_policy(&self) -> SandboxPolicy {
|
pub(super) fn sandbox_policy(
|
||||||
let permission_profile = self.materialized_permission_profile();
|
&self,
|
||||||
|
environments: &[TurnEnvironmentSelection],
|
||||||
|
) -> SandboxPolicy {
|
||||||
|
let permission_profile = self.materialized_permission_profile(environments);
|
||||||
codex_sandboxing::compatibility_sandbox_policy_for_permission_profile(
|
codex_sandboxing::compatibility_sandbox_policy_for_permission_profile(
|
||||||
&permission_profile,
|
&permission_profile,
|
||||||
self.cwd(),
|
self.cwd(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
|
pub(super) fn file_system_sandbox_policy(
|
||||||
self.materialized_permission_profile()
|
&self,
|
||||||
|
environments: &[TurnEnvironmentSelection],
|
||||||
|
) -> FileSystemSandboxPolicy {
|
||||||
|
self.materialized_permission_profile(environments)
|
||||||
.file_system_sandbox_policy()
|
.file_system_sandbox_policy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,17 +209,25 @@ impl SessionConfiguration {
|
|||||||
.network_sandbox_policy()
|
.network_sandbox_policy()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn thread_config_snapshot(&self) -> ThreadConfigSnapshot {
|
pub(super) fn thread_config_snapshot(
|
||||||
|
&self,
|
||||||
|
environment_selections: Vec<TurnEnvironmentSelection>,
|
||||||
|
) -> ThreadConfigSnapshot {
|
||||||
|
let workspace_roots =
|
||||||
|
ThreadEnvironments::primary_workspace_roots_for(&environment_selections);
|
||||||
ThreadConfigSnapshot {
|
ThreadConfigSnapshot {
|
||||||
model: self.collaboration_mode.model().to_string(),
|
model: self.collaboration_mode.model().to_string(),
|
||||||
model_provider_id: self.original_config_do_not_use.model_provider_id.clone(),
|
model_provider_id: self.original_config_do_not_use.model_provider_id.clone(),
|
||||||
service_tier: self.service_tier.clone(),
|
service_tier: self.service_tier.clone(),
|
||||||
approval_policy: self.approval_policy.value(),
|
approval_policy: self.approval_policy.value(),
|
||||||
approvals_reviewer: self.approvals_reviewer,
|
approvals_reviewer: self.approvals_reviewer,
|
||||||
permission_profile: self.materialized_permission_profile(),
|
permission_profile: self.materialized_permission_profile(&environment_selections),
|
||||||
active_permission_profile: self.active_permission_profile(),
|
active_permission_profile: self.active_permission_profile(),
|
||||||
environments: self.environments.clone(),
|
environments: TurnEnvironmentSelections::new(
|
||||||
workspace_roots: self.primary_workspace_roots(),
|
self.legacy_fallback_cwd.clone(),
|
||||||
|
environment_selections,
|
||||||
|
),
|
||||||
|
workspace_roots,
|
||||||
profile_workspace_roots: self.profile_workspace_roots().to_vec(),
|
profile_workspace_roots: self.profile_workspace_roots().to_vec(),
|
||||||
ephemeral: self.original_config_do_not_use.ephemeral,
|
ephemeral: self.original_config_do_not_use.ephemeral,
|
||||||
reasoning_effort: self.collaboration_mode.reasoning_effort(),
|
reasoning_effort: self.collaboration_mode.reasoning_effort(),
|
||||||
@@ -251,7 +243,10 @@ impl SessionConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn validate_auto_review_requirement(&self) -> ConstraintResult<()> {
|
pub(super) fn validate_auto_review_requirement(
|
||||||
|
&self,
|
||||||
|
environments: &[TurnEnvironmentSelection],
|
||||||
|
) -> ConstraintResult<()> {
|
||||||
if self.trusted_guardian_reviewer {
|
if self.trusted_guardian_reviewer {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -268,7 +263,7 @@ impl SessionConfiguration {
|
|||||||
if self.approval_policy.value() == AskForApproval::OnRequest
|
if self.approval_policy.value() == AskForApproval::OnRequest
|
||||||
&& self.approvals_reviewer == ApprovalsReviewer::AutoReview
|
&& self.approvals_reviewer == ApprovalsReviewer::AutoReview
|
||||||
&& !self
|
&& !self
|
||||||
.file_system_sandbox_policy()
|
.file_system_sandbox_policy(environments)
|
||||||
.has_full_disk_write_access()
|
.has_full_disk_write_access()
|
||||||
&& self
|
&& self
|
||||||
.original_config_do_not_use
|
.original_config_do_not_use
|
||||||
@@ -283,10 +278,15 @@ impl SessionConfiguration {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply(&self, updates: &SessionSettingsUpdate) -> ConstraintResult<Self> {
|
pub(super) fn apply(
|
||||||
|
&self,
|
||||||
|
updates: &SessionSettingsUpdate,
|
||||||
|
current_environments: &[TurnEnvironmentSelection],
|
||||||
|
) -> ConstraintResult<Self> {
|
||||||
let mut next_configuration = self.clone();
|
let mut next_configuration = self.clone();
|
||||||
let current_sandbox_policy = self.sandbox_policy();
|
let current_sandbox_policy = self.sandbox_policy(current_environments);
|
||||||
let current_file_system_sandbox_policy = self.file_system_sandbox_policy();
|
let current_file_system_sandbox_policy =
|
||||||
|
self.file_system_sandbox_policy(current_environments);
|
||||||
let current_network_sandbox_policy = self.network_sandbox_policy();
|
let current_network_sandbox_policy = self.network_sandbox_policy();
|
||||||
let legacy_file_system_projection =
|
let legacy_file_system_projection =
|
||||||
FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries(
|
FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries(
|
||||||
@@ -371,12 +371,10 @@ impl SessionConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let current_cwd = self.cwd().clone();
|
let current_cwd = self.cwd().clone();
|
||||||
let next_environments = updates
|
if let Some(environments) = &updates.environments {
|
||||||
.environments
|
next_configuration.legacy_fallback_cwd = environments.legacy_fallback_cwd.clone();
|
||||||
.clone()
|
}
|
||||||
.unwrap_or_else(|| self.environments.clone());
|
let cwd_changed = next_configuration.legacy_fallback_cwd != current_cwd;
|
||||||
let cwd_changed = next_environments.legacy_fallback_cwd != current_cwd;
|
|
||||||
next_configuration.environments = next_environments;
|
|
||||||
|
|
||||||
if let Some(permission_profile) = updates.permission_profile.clone() {
|
if let Some(permission_profile) = updates.permission_profile.clone() {
|
||||||
let active_permission_profile =
|
let active_permission_profile =
|
||||||
@@ -466,7 +464,13 @@ impl SessionConfiguration {
|
|||||||
if let Some(app_server_client_version) = updates.app_server_client_version.clone() {
|
if let Some(app_server_client_version) = updates.app_server_client_version.clone() {
|
||||||
next_configuration.app_server_client_version = Some(app_server_client_version);
|
next_configuration.app_server_client_version = Some(app_server_client_version);
|
||||||
}
|
}
|
||||||
next_configuration.validate_auto_review_requirement()?;
|
let next_environments = updates
|
||||||
|
.environments
|
||||||
|
.as_ref()
|
||||||
|
.map_or(current_environments, |environments| {
|
||||||
|
environments.environments.as_slice()
|
||||||
|
});
|
||||||
|
next_configuration.validate_auto_review_requirement(next_environments)?;
|
||||||
Ok(next_configuration)
|
Ok(next_configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,6 +576,7 @@ impl Session {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) async fn new(
|
pub(crate) async fn new(
|
||||||
mut session_configuration: SessionConfiguration,
|
mut session_configuration: SessionConfiguration,
|
||||||
|
environment_selections: &[TurnEnvironmentSelection],
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
user_instructions: Option<codex_extension_api::UserInstructions>,
|
user_instructions: Option<codex_extension_api::UserInstructions>,
|
||||||
installation_id: String,
|
installation_id: String,
|
||||||
@@ -838,8 +843,7 @@ impl Session {
|
|||||||
let thread_extension_data_for_mcp = &thread_extension_data;
|
let thread_extension_data_for_mcp = &thread_extension_data;
|
||||||
let mcp_originator = session_configuration.originator.clone();
|
let mcp_originator = session_configuration.originator.clone();
|
||||||
let mcp_session_source = session_configuration.session_source.clone();
|
let mcp_session_source = session_configuration.session_source.clone();
|
||||||
let mcp_runtime_cwd = session_configuration
|
let mcp_runtime_cwd = environment_selections
|
||||||
.environment_selections()
|
|
||||||
.first()
|
.first()
|
||||||
.and_then(|environment| environment.cwd.to_abs_path().ok())
|
.and_then(|environment| environment.cwd.to_abs_path().ok())
|
||||||
.map(|cwd| cwd.to_path_buf())
|
.map(|cwd| cwd.to_path_buf())
|
||||||
@@ -899,7 +903,10 @@ impl Session {
|
|||||||
model: session_configuration.collaboration_mode.model().to_string(),
|
model: session_configuration.collaboration_mode.model().to_string(),
|
||||||
provider_name: config.model_provider_id.clone(),
|
provider_name: config.model_provider_id.clone(),
|
||||||
approval_policy: session_configuration.approval_policy.value().to_string(),
|
approval_policy: session_configuration.approval_policy.value().to_string(),
|
||||||
sandbox_policy: format!("{:?}", session_configuration.sandbox_policy()),
|
sandbox_policy: format!(
|
||||||
|
"{:?}",
|
||||||
|
session_configuration.sandbox_policy(environment_selections)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let rollout_thread_trace = if matches!(
|
let rollout_thread_trace = if matches!(
|
||||||
session_configuration.session_source,
|
session_configuration.session_source,
|
||||||
@@ -1062,7 +1069,7 @@ impl Session {
|
|||||||
config.features.enabled(Feature::DeferredExecutor),
|
config.features.enabled(Feature::DeferredExecutor),
|
||||||
));
|
));
|
||||||
turn_environments.update_selections(
|
turn_environments.update_selections(
|
||||||
session_configuration.environment_selections(),
|
environment_selections,
|
||||||
&session_configuration.turn_environment_config(),
|
&session_configuration.turn_environment_config(),
|
||||||
);
|
);
|
||||||
let resolved_environments = turn_environments.snapshot().await;
|
let resolved_environments = turn_environments.snapshot().await;
|
||||||
@@ -1194,7 +1201,7 @@ impl Session {
|
|||||||
config: config.as_ref(),
|
config: config.as_ref(),
|
||||||
session_source: &session_configuration.session_source,
|
session_source: &session_configuration.session_source,
|
||||||
persistent_thread_state_available: state_db_ctx.is_some(),
|
persistent_thread_state_available: state_db_ctx.is_some(),
|
||||||
environments: session_configuration.environment_selections(),
|
environments: environment_selections,
|
||||||
mcp_resource_client: Some(Arc::clone(&mcp_resource_client)),
|
mcp_resource_client: Some(Arc::clone(&mcp_resource_client)),
|
||||||
extension_metrics: Some(Arc::clone(&extension_metrics)),
|
extension_metrics: Some(Arc::clone(&extension_metrics)),
|
||||||
session_store: &session_extension_data,
|
session_store: &session_extension_data,
|
||||||
@@ -1338,7 +1345,8 @@ impl Session {
|
|||||||
service_tier: session_configuration.service_tier.clone(),
|
service_tier: session_configuration.service_tier.clone(),
|
||||||
approval_policy: session_configuration.approval_policy.value(),
|
approval_policy: session_configuration.approval_policy.value(),
|
||||||
approvals_reviewer: session_configuration.approvals_reviewer,
|
approvals_reviewer: session_configuration.approvals_reviewer,
|
||||||
permission_profile: session_configuration.materialized_permission_profile(),
|
permission_profile: session_configuration
|
||||||
|
.materialized_permission_profile(environment_selections),
|
||||||
active_permission_profile: session_configuration.active_permission_profile(),
|
active_permission_profile: session_configuration.active_permission_profile(),
|
||||||
cwd: session_configuration.cwd().clone(),
|
cwd: session_configuration.cwd().clone(),
|
||||||
reasoning_effort: session_configuration.collaboration_mode.reasoning_effort(),
|
reasoning_effort: session_configuration.collaboration_mode.reasoning_effort(),
|
||||||
|
|||||||
+203
-209
@@ -4181,7 +4181,7 @@ async fn set_rate_limits_retains_previous_credits() {
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -4290,7 +4290,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -4777,10 +4777,13 @@ async fn session_settings_null_service_tier_update_uses_default_service_tier() {
|
|||||||
let session_configuration = make_session_configuration_for_tests().await;
|
let session_configuration = make_session_configuration_for_tests().await;
|
||||||
|
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
service_tier: Some(None),
|
&SessionSettingsUpdate {
|
||||||
..Default::default()
|
service_tier: Some(None),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("null service tier update should apply");
|
.expect("null service tier update should apply");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -4794,10 +4797,13 @@ async fn session_settings_legacy_fast_service_tier_update_uses_priority_request_
|
|||||||
let session_configuration = make_session_configuration_for_tests().await;
|
let session_configuration = make_session_configuration_for_tests().await;
|
||||||
|
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
service_tier: Some(Some("fast".to_string())),
|
&SessionSettingsUpdate {
|
||||||
..Default::default()
|
service_tier: Some(Some("fast".to_string())),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("legacy fast service tier update should apply");
|
.expect("legacy fast service tier update should apply");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -4838,7 +4844,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -4895,7 +4901,7 @@ async fn emit_subagent_session_started_includes_fork_lineage_and_originator() {
|
|||||||
SessionId::from(child_thread_id),
|
SessionId::from(child_thread_id),
|
||||||
child_thread_id,
|
child_thread_id,
|
||||||
Some(parent_thread_id),
|
Some(parent_thread_id),
|
||||||
session_configuration.thread_config_snapshot(),
|
session_configuration.thread_config_snapshot(Vec::new()),
|
||||||
SubAgentSource::ThreadSpawn {
|
SubAgentSource::ThreadSpawn {
|
||||||
parent_thread_id,
|
parent_thread_id,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
@@ -4942,6 +4948,7 @@ async fn emit_subagent_session_started_includes_fork_lineage_and_originator() {
|
|||||||
|
|
||||||
async fn resolved_environments_for_configuration(
|
async fn resolved_environments_for_configuration(
|
||||||
session_configuration: &SessionConfiguration,
|
session_configuration: &SessionConfiguration,
|
||||||
|
environment_selections: &[TurnEnvironmentSelection],
|
||||||
) -> (Arc<EnvironmentManager>, TurnEnvironmentSnapshot) {
|
) -> (Arc<EnvironmentManager>, TurnEnvironmentSnapshot) {
|
||||||
let environment_manager = Arc::new(EnvironmentManager::default_for_tests());
|
let environment_manager = Arc::new(EnvironmentManager::default_for_tests());
|
||||||
let turn_environments = ThreadEnvironments::new(
|
let turn_environments = ThreadEnvironments::new(
|
||||||
@@ -4953,7 +4960,7 @@ async fn resolved_environments_for_configuration(
|
|||||||
/*non_blocking_snapshots*/ false,
|
/*non_blocking_snapshots*/ false,
|
||||||
);
|
);
|
||||||
turn_environments.update_selections(
|
turn_environments.update_selections(
|
||||||
session_configuration.environment_selections(),
|
environment_selections,
|
||||||
&session_configuration.turn_environment_config(),
|
&session_configuration.turn_environment_config(),
|
||||||
);
|
);
|
||||||
(environment_manager, turn_environments.snapshot().await)
|
(environment_manager, turn_environments.snapshot().await)
|
||||||
@@ -4970,8 +4977,7 @@ async fn session_configuration_apply_preserves_profile_file_system_policy_on_cwd
|
|||||||
let project_root = project_root.abs();
|
let project_root = project_root.abs();
|
||||||
let docs_dir = docs_dir.abs();
|
let docs_dir = docs_dir.abs();
|
||||||
|
|
||||||
session_configuration.environments =
|
session_configuration.legacy_fallback_cwd = original_cwd.abs();
|
||||||
TurnEnvironmentSelections::new(original_cwd.abs(), Vec::new());
|
|
||||||
let sandbox_policy = SandboxPolicy::WorkspaceWrite {
|
let sandbox_policy = SandboxPolicy::WorkspaceWrite {
|
||||||
writable_roots: Vec::new(),
|
writable_roots: Vec::new(),
|
||||||
network_access: false,
|
network_access: false,
|
||||||
@@ -5002,20 +5008,21 @@ async fn session_configuration_apply_preserves_profile_file_system_policy_on_cwd
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.expect("set permission profile");
|
.expect("set permission profile");
|
||||||
let expected_file_system_sandbox_policy = file_system_sandbox_policy
|
let expected_file_system_sandbox_policy =
|
||||||
.materialize_project_roots_with_workspace_roots(
|
file_system_sandbox_policy.materialize_project_roots_with_workspace_roots(&[]);
|
||||||
&session_configuration.primary_workspace_roots(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
environments: Some(TurnEnvironmentSelections::new(project_root, Vec::new())),
|
&SessionSettingsUpdate {
|
||||||
..Default::default()
|
environments: Some(TurnEnvironmentSelections::new(project_root, Vec::new())),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("cwd-only update should succeed");
|
.expect("cwd-only update should succeed");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated.file_system_sandbox_policy(),
|
updated.file_system_sandbox_policy(&[]),
|
||||||
expected_file_system_sandbox_policy
|
expected_file_system_sandbox_policy
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5024,8 +5031,7 @@ async fn session_configuration_apply_preserves_profile_file_system_policy_on_cwd
|
|||||||
async fn session_configuration_apply_permission_profile_preserves_existing_deny_read_entries() {
|
async fn session_configuration_apply_permission_profile_preserves_existing_deny_read_entries() {
|
||||||
let mut session_configuration = make_session_configuration_for_tests().await;
|
let mut session_configuration = make_session_configuration_for_tests().await;
|
||||||
let cwd = tempfile::tempdir().expect("create temp dir");
|
let cwd = tempfile::tempdir().expect("create temp dir");
|
||||||
session_configuration.environments =
|
session_configuration.legacy_fallback_cwd = cwd.path().abs();
|
||||||
TurnEnvironmentSelections::new(cwd.path().abs(), Vec::new());
|
|
||||||
|
|
||||||
let workspace_policy = SandboxPolicy::new_workspace_write_policy();
|
let workspace_policy = SandboxPolicy::new_workspace_write_policy();
|
||||||
let deny_entry = FileSystemSandboxEntry {
|
let deny_entry = FileSystemSandboxEntry {
|
||||||
@@ -5061,20 +5067,21 @@ async fn session_configuration_apply_permission_profile_preserves_existing_deny_
|
|||||||
NetworkSandboxPolicy::Restricted,
|
NetworkSandboxPolicy::Restricted,
|
||||||
);
|
);
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
permission_profile: Some(permission_profile),
|
&SessionSettingsUpdate {
|
||||||
..Default::default()
|
permission_profile: Some(permission_profile),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("permission profile update should succeed");
|
.expect("permission profile update should succeed");
|
||||||
|
|
||||||
let mut expected_file_system_policy = requested_file_system_policy
|
let mut expected_file_system_policy =
|
||||||
.materialize_project_roots_with_workspace_roots(
|
requested_file_system_policy.materialize_project_roots_with_workspace_roots(&[]);
|
||||||
&session_configuration.primary_workspace_roots(),
|
|
||||||
);
|
|
||||||
expected_file_system_policy.glob_scan_max_depth = Some(2);
|
expected_file_system_policy.glob_scan_max_depth = Some(2);
|
||||||
expected_file_system_policy.entries.push(deny_entry);
|
expected_file_system_policy.entries.push(deny_entry);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated.file_system_sandbox_policy(),
|
updated.file_system_sandbox_policy(&[]),
|
||||||
expected_file_system_policy
|
expected_file_system_policy
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5083,8 +5090,7 @@ async fn session_configuration_apply_permission_profile_preserves_existing_deny_
|
|||||||
async fn session_configuration_apply_permission_profile_accepts_direct_write_roots() {
|
async fn session_configuration_apply_permission_profile_accepts_direct_write_roots() {
|
||||||
let mut session_configuration = make_session_configuration_for_tests().await;
|
let mut session_configuration = make_session_configuration_for_tests().await;
|
||||||
let cwd = tempfile::tempdir().expect("create cwd");
|
let cwd = tempfile::tempdir().expect("create cwd");
|
||||||
session_configuration.environments =
|
session_configuration.legacy_fallback_cwd = cwd.path().abs();
|
||||||
TurnEnvironmentSelections::new(cwd.path().abs(), Vec::new());
|
|
||||||
let external_write_dir = tempfile::tempdir().expect("create external write root");
|
let external_write_dir = tempfile::tempdir().expect("create external write root");
|
||||||
let external_write_path = AbsolutePathBuf::from_absolute_path(
|
let external_write_path = AbsolutePathBuf::from_absolute_path(
|
||||||
codex_utils_absolute_path::canonicalize_preserving_symlinks(external_write_dir.path())
|
codex_utils_absolute_path::canonicalize_preserving_symlinks(external_write_dir.path())
|
||||||
@@ -5105,19 +5111,22 @@ async fn session_configuration_apply_permission_profile_accepts_direct_write_roo
|
|||||||
);
|
);
|
||||||
|
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
permission_profile: Some(permission_profile.clone()),
|
&SessionSettingsUpdate {
|
||||||
..Default::default()
|
permission_profile: Some(permission_profile.clone()),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("permission profile update should accept direct runtime permissions");
|
.expect("permission profile update should accept direct runtime permissions");
|
||||||
|
|
||||||
assert_eq!(updated.permission_profile(), permission_profile);
|
assert_eq!(updated.permission_profile(), permission_profile);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated.file_system_sandbox_policy(),
|
updated.file_system_sandbox_policy(&[]),
|
||||||
file_system_sandbox_policy
|
file_system_sandbox_policy
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated.sandbox_policy(),
|
updated.sandbox_policy(&[]),
|
||||||
SandboxPolicy::WorkspaceWrite {
|
SandboxPolicy::WorkspaceWrite {
|
||||||
writable_roots: vec![external_write_path],
|
writable_roots: vec![external_write_path],
|
||||||
network_access: false,
|
network_access: false,
|
||||||
@@ -5217,11 +5226,14 @@ async fn active_profile_update_rebuilds_network_proxy_config() -> std::io::Resul
|
|||||||
session_configuration.original_config_do_not_use = Arc::clone(&locked_config);
|
session_configuration.original_config_do_not_use = Arc::clone(&locked_config);
|
||||||
|
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
permission_profile: Some(selected_config.permissions.permission_profile().clone()),
|
&SessionSettingsUpdate {
|
||||||
active_permission_profile: selected_config.permissions.active_permission_profile(),
|
permission_profile: Some(selected_config.permissions.permission_profile().clone()),
|
||||||
..Default::default()
|
active_permission_profile: selected_config.permissions.active_permission_profile(),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("active profile update should apply");
|
.expect("active profile update should apply");
|
||||||
|
|
||||||
let network = updated
|
let network = updated
|
||||||
@@ -5335,8 +5347,7 @@ async fn session_configuration_apply_preserves_absolute_cwd_write_root_on_cwd_up
|
|||||||
let original_cwd = original_cwd.abs();
|
let original_cwd = original_cwd.abs();
|
||||||
let next_cwd = next_cwd.abs();
|
let next_cwd = next_cwd.abs();
|
||||||
|
|
||||||
session_configuration.environments =
|
session_configuration.legacy_fallback_cwd = original_cwd.clone();
|
||||||
TurnEnvironmentSelections::new(original_cwd.clone(), Vec::new());
|
|
||||||
let file_system_sandbox_policy = FileSystemSandboxPolicy::restricted(vec![
|
let file_system_sandbox_policy = FileSystemSandboxPolicy::restricted(vec![
|
||||||
FileSystemSandboxEntry {
|
FileSystemSandboxEntry {
|
||||||
path: FileSystemPath::Special {
|
path: FileSystemPath::Special {
|
||||||
@@ -5364,25 +5375,28 @@ async fn session_configuration_apply_preserves_absolute_cwd_write_root_on_cwd_up
|
|||||||
.expect("set permission profile");
|
.expect("set permission profile");
|
||||||
|
|
||||||
let updated = session_configuration
|
let updated = session_configuration
|
||||||
.apply(&SessionSettingsUpdate {
|
.apply(
|
||||||
environments: Some(TurnEnvironmentSelections::new(next_cwd.clone(), Vec::new())),
|
&SessionSettingsUpdate {
|
||||||
..Default::default()
|
environments: Some(TurnEnvironmentSelections::new(next_cwd.clone(), Vec::new())),
|
||||||
})
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.expect("cwd-only update should succeed");
|
.expect("cwd-only update should succeed");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated.file_system_sandbox_policy(),
|
updated.file_system_sandbox_policy(&[]),
|
||||||
file_system_sandbox_policy
|
file_system_sandbox_policy
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
updated
|
updated
|
||||||
.file_system_sandbox_policy()
|
.file_system_sandbox_policy(&[])
|
||||||
.can_write_path_with_cwd(original_cwd.as_path(), updated.cwd().as_path()),
|
.can_write_path_with_cwd(original_cwd.as_path(), updated.cwd().as_path()),
|
||||||
"absolute grant to the old cwd must remain writable"
|
"absolute grant to the old cwd must remain writable"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!updated
|
!updated
|
||||||
.file_system_sandbox_policy()
|
.file_system_sandbox_policy(&[])
|
||||||
.can_write_path_with_cwd(next_cwd.as_path(), updated.cwd().as_path()),
|
.can_write_path_with_cwd(next_cwd.as_path(), updated.cwd().as_path()),
|
||||||
"cwd-only update must not reinterpret an absolute old-cwd grant as :workspace_roots"
|
"cwd-only update must not reinterpret an absolute old-cwd grant as :workspace_roots"
|
||||||
);
|
);
|
||||||
@@ -5393,13 +5407,7 @@ async fn session_update_settings_does_not_rewrite_sticky_environment_cwds() {
|
|||||||
let (session, turn_context) = make_session_and_context().await;
|
let (session, turn_context) = make_session_and_context().await;
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let updated_cwd = turn_context.cwd.join("project");
|
let updated_cwd = turn_context.cwd.join("project");
|
||||||
let current_environments = {
|
let current_environments = session.services.turn_environments.selections();
|
||||||
let state = session.state.lock().await;
|
|
||||||
state
|
|
||||||
.session_configuration
|
|
||||||
.environment_selections()
|
|
||||||
.to_vec()
|
|
||||||
};
|
|
||||||
let expected_environments = current_environments.clone();
|
let expected_environments = current_environments.clone();
|
||||||
std::fs::create_dir_all(updated_cwd.as_path()).expect("create project dir");
|
std::fs::create_dir_all(updated_cwd.as_path()).expect("create project dir");
|
||||||
|
|
||||||
@@ -5414,16 +5422,11 @@ async fn session_update_settings_does_not_rewrite_sticky_environment_cwds() {
|
|||||||
.await
|
.await
|
||||||
.expect("cwd update should succeed");
|
.expect("cwd update should succeed");
|
||||||
|
|
||||||
let (session_cwd, stored_environments) = {
|
let session_cwd = {
|
||||||
let state = session.state.lock().await;
|
let state = session.state.lock().await;
|
||||||
(
|
state.session_configuration.cwd().clone()
|
||||||
state.session_configuration.cwd().clone(),
|
|
||||||
state
|
|
||||||
.session_configuration
|
|
||||||
.environment_selections()
|
|
||||||
.to_vec(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
let stored_environments = session.services.turn_environments.selections();
|
||||||
let config = session.get_config().await;
|
let config = session.get_config().await;
|
||||||
let next_turn = session.new_default_turn().await;
|
let next_turn = session.new_default_turn().await;
|
||||||
|
|
||||||
@@ -5496,11 +5499,13 @@ async fn permission_profile_updates_apply_to_next_turn_environment() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn relative_cwd_update_without_environments_resolves_under_session_cwd() {
|
async fn relative_cwd_update_without_environments_resolves_under_session_cwd() {
|
||||||
let (session, _turn_context) = make_session_and_context().await;
|
let (session, _turn_context) = make_session_and_context().await;
|
||||||
let original_cwd = {
|
let original_cwd = session
|
||||||
let mut state = session.state.lock().await;
|
.state
|
||||||
state.session_configuration.environments.environments = Vec::new();
|
.lock()
|
||||||
state.session_configuration.cwd().clone()
|
.await
|
||||||
};
|
.session_configuration
|
||||||
|
.cwd()
|
||||||
|
.clone();
|
||||||
let updated_cwd = original_cwd.join("project");
|
let updated_cwd = original_cwd.join("project");
|
||||||
std::fs::create_dir_all(updated_cwd.as_path()).expect("create project dir");
|
std::fs::create_dir_all(updated_cwd.as_path()).expect("create project dir");
|
||||||
|
|
||||||
@@ -5517,23 +5522,17 @@ async fn relative_cwd_update_without_environments_resolves_under_session_cwd() {
|
|||||||
|
|
||||||
let state = session.state.lock().await;
|
let state = session.state.lock().await;
|
||||||
assert_eq!(state.session_configuration.cwd(), &updated_cwd);
|
assert_eq!(state.session_configuration.cwd(), &updated_cwd);
|
||||||
assert!(
|
assert!(session.services.turn_environments.selections().is_empty());
|
||||||
state
|
|
||||||
.session_configuration
|
|
||||||
.environment_selections()
|
|
||||||
.is_empty()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn environment_settings_preserve_explicit_primary_cwd() {
|
async fn environment_settings_preserve_explicit_primary_cwd() {
|
||||||
let (session, _turn_context) = make_session_and_context().await;
|
let (session, _turn_context) = make_session_and_context().await;
|
||||||
let (original_cwd, environment_cwd, environments) = {
|
let (original_cwd, environment_cwd, environments) = {
|
||||||
let mut state = session.state.lock().await;
|
let state = session.state.lock().await;
|
||||||
let original_cwd = state.session_configuration.cwd().clone();
|
let original_cwd = state.session_configuration.cwd().clone();
|
||||||
let environment_cwd = original_cwd.join("environment");
|
let environment_cwd = original_cwd.join("environment");
|
||||||
let environments = vec![local(environment_cwd.clone())];
|
let environments = vec![local(environment_cwd.clone())];
|
||||||
state.session_configuration.environments.environments = environments.clone();
|
|
||||||
(original_cwd, environment_cwd, environments)
|
(original_cwd, environment_cwd, environments)
|
||||||
};
|
};
|
||||||
let updated_cwd = original_cwd.join("project");
|
let updated_cwd = original_cwd.join("project");
|
||||||
@@ -5553,7 +5552,7 @@ async fn environment_settings_preserve_explicit_primary_cwd() {
|
|||||||
let state = session.state.lock().await;
|
let state = session.state.lock().await;
|
||||||
assert_eq!(state.session_configuration.cwd(), &updated_cwd);
|
assert_eq!(state.session_configuration.cwd(), &updated_cwd);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.session_configuration.environment_selections()[0].cwd,
|
session.services.turn_environments.selections()[0].cwd,
|
||||||
PathUri::from_abs_path(&environment_cwd)
|
PathUri::from_abs_path(&environment_cwd)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5634,7 +5633,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -5663,6 +5662,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
|
|||||||
let environment_manager = Arc::new(EnvironmentManager::default_for_tests());
|
let environment_manager = Arc::new(EnvironmentManager::default_for_tests());
|
||||||
let result = Session::new(
|
let result = Session::new(
|
||||||
session_configuration,
|
session_configuration,
|
||||||
|
/*environment_selections*/ &[],
|
||||||
Arc::clone(&config),
|
Arc::clone(&config),
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
"11111111-1111-4111-8111-111111111111".to_string(),
|
"11111111-1111-4111-8111-111111111111".to_string(),
|
||||||
@@ -5775,7 +5775,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -5792,12 +5792,6 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
dynamic_tools: Vec::new(),
|
dynamic_tools: Vec::new(),
|
||||||
user_shell_override: None,
|
user_shell_override: None,
|
||||||
};
|
};
|
||||||
let per_turn_config =
|
|
||||||
Session::build_per_turn_config(&session_configuration, session_configuration.cwd().clone());
|
|
||||||
let model_info = construct_model_info_offline_for_tests(
|
|
||||||
session_configuration.collaboration_mode.model(),
|
|
||||||
&per_turn_config.to_models_manager_config(),
|
|
||||||
);
|
|
||||||
let session_telemetry = session_telemetry(
|
let session_telemetry = session_telemetry(
|
||||||
thread_id,
|
thread_id,
|
||||||
config.as_ref(),
|
config.as_ref(),
|
||||||
@@ -5807,7 +5801,8 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
|
|
||||||
let state = SessionState::new(session_configuration.clone());
|
let state = SessionState::new(session_configuration.clone());
|
||||||
let (environment_manager, resolved_environments) =
|
let (environment_manager, resolved_environments) =
|
||||||
resolved_environments_for_configuration(&session_configuration).await;
|
resolved_environments_for_configuration(&session_configuration, &default_environments)
|
||||||
|
.await;
|
||||||
let resolved_turn_environments = resolved_environments.clone();
|
let resolved_turn_environments = resolved_environments.clone();
|
||||||
let turn_environments = Arc::new(ThreadEnvironments::new(
|
let turn_environments = Arc::new(ThreadEnvironments::new(
|
||||||
environment_manager,
|
environment_manager,
|
||||||
@@ -5925,43 +5920,6 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
turn_environments: Arc::clone(&turn_environments),
|
turn_environments: Arc::clone(&turn_environments),
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins_input = per_turn_config.plugins_config_input();
|
|
||||||
let plugin_outcome = services
|
|
||||||
.plugins_manager
|
|
||||||
.plugins_for_config(&plugins_input)
|
|
||||||
.await;
|
|
||||||
let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots();
|
|
||||||
let plugin_skill_snapshots = services
|
|
||||||
.plugins_manager
|
|
||||||
.plugin_skill_snapshots_for_config(&plugins_input);
|
|
||||||
let skills_input =
|
|
||||||
crate::skills_load_input_from_config(&per_turn_config, effective_skill_roots)
|
|
||||||
.with_plugin_skill_snapshots(plugin_skill_snapshots);
|
|
||||||
let skill_fs = environment.get_filesystem();
|
|
||||||
let skills_snapshot = services
|
|
||||||
.skills_service
|
|
||||||
.snapshot_for_config(&skills_input, Some(Arc::clone(&skill_fs)))
|
|
||||||
.await;
|
|
||||||
let turn_context = Session::make_turn_context(
|
|
||||||
thread_id,
|
|
||||||
SessionId::from(thread_id),
|
|
||||||
Some(Arc::clone(&auth_manager)),
|
|
||||||
&session_telemetry,
|
|
||||||
session_configuration.provider.clone(),
|
|
||||||
&session_configuration,
|
|
||||||
config.multi_agent_version_from_features(),
|
|
||||||
services.user_shell.as_ref(),
|
|
||||||
services.shell_zsh_path.as_ref(),
|
|
||||||
services.main_execve_wrapper_exe.as_ref(),
|
|
||||||
per_turn_config,
|
|
||||||
model_info,
|
|
||||||
&models_manager,
|
|
||||||
/*network*/ None,
|
|
||||||
resolved_turn_environments,
|
|
||||||
session_configuration.cwd().clone(),
|
|
||||||
"turn_id".to_string(),
|
|
||||||
skills_snapshot,
|
|
||||||
);
|
|
||||||
let session = Session {
|
let session = Session {
|
||||||
thread_id,
|
thread_id,
|
||||||
installation_id: "11111111-1111-4111-8111-111111111111".to_string(),
|
installation_id: "11111111-1111-4111-8111-111111111111".to_string(),
|
||||||
@@ -5989,7 +5947,48 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
fork_persistence: ForkPersistence::Copied,
|
fork_persistence: ForkPersistence::Copied,
|
||||||
next_internal_sub_id: AtomicU64::new(0),
|
next_internal_sub_id: AtomicU64::new(0),
|
||||||
};
|
};
|
||||||
|
let per_turn_config =
|
||||||
|
session.build_per_turn_config(&session_configuration, session_configuration.cwd().clone());
|
||||||
|
let plugins_input = per_turn_config.plugins_config_input();
|
||||||
|
let plugin_outcome = session
|
||||||
|
.services
|
||||||
|
.plugins_manager
|
||||||
|
.plugins_for_config(&plugins_input)
|
||||||
|
.await;
|
||||||
|
let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots();
|
||||||
|
let plugin_skill_snapshots = session
|
||||||
|
.services
|
||||||
|
.plugins_manager
|
||||||
|
.plugin_skill_snapshots_for_config(&plugins_input);
|
||||||
|
let skills_input =
|
||||||
|
crate::skills_load_input_from_config(&per_turn_config, effective_skill_roots)
|
||||||
|
.with_plugin_skill_snapshots(plugin_skill_snapshots);
|
||||||
|
let skill_fs = environment.get_filesystem();
|
||||||
|
let skills_snapshot = session
|
||||||
|
.services
|
||||||
|
.skills_service
|
||||||
|
.snapshot_for_config(&skills_input, Some(Arc::clone(&skill_fs)))
|
||||||
|
.await;
|
||||||
|
let turn_context = Session::make_turn_context(
|
||||||
|
thread_id,
|
||||||
|
SessionId::from(thread_id),
|
||||||
|
Some(Arc::clone(&auth_manager)),
|
||||||
|
&session_telemetry,
|
||||||
|
session_configuration.provider.clone(),
|
||||||
|
&session_configuration,
|
||||||
|
config.multi_agent_version_from_features(),
|
||||||
|
session.services.user_shell.as_ref(),
|
||||||
|
session.services.shell_zsh_path.as_ref(),
|
||||||
|
session.services.main_execve_wrapper_exe.as_ref(),
|
||||||
|
per_turn_config,
|
||||||
|
model_info,
|
||||||
|
&models_manager,
|
||||||
|
/*network*/ None,
|
||||||
|
resolved_turn_environments,
|
||||||
|
session_configuration.cwd().clone(),
|
||||||
|
"turn_id".to_string(),
|
||||||
|
skills_snapshot,
|
||||||
|
);
|
||||||
session.mark_mcp_runtime_dirty();
|
session.mark_mcp_runtime_dirty();
|
||||||
(session, turn_context)
|
(session, turn_context)
|
||||||
}
|
}
|
||||||
@@ -6054,7 +6053,7 @@ async fn make_session_with_config_and_rx(
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -6084,6 +6083,7 @@ async fn make_session_with_config_and_rx(
|
|||||||
|
|
||||||
let session = Session::new(
|
let session = Session::new(
|
||||||
session_configuration,
|
session_configuration,
|
||||||
|
&default_environments,
|
||||||
Arc::clone(&config),
|
Arc::clone(&config),
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
"11111111-1111-4111-8111-111111111111".to_string(),
|
"11111111-1111-4111-8111-111111111111".to_string(),
|
||||||
@@ -6168,7 +6168,7 @@ async fn make_session_with_history_source_and_agent_control_and_rx(
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -6198,6 +6198,7 @@ async fn make_session_with_history_source_and_agent_control_and_rx(
|
|||||||
|
|
||||||
let session = Session::new(
|
let session = Session::new(
|
||||||
session_configuration,
|
session_configuration,
|
||||||
|
&default_environments,
|
||||||
Arc::clone(&config),
|
Arc::clone(&config),
|
||||||
/*user_instructions*/ None,
|
/*user_instructions*/ None,
|
||||||
"11111111-1111-4111-8111-111111111111".to_string(),
|
"11111111-1111-4111-8111-111111111111".to_string(),
|
||||||
@@ -7142,23 +7143,20 @@ async fn turn_environments_set_primary_environment() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn default_turn_does_not_overlay_legacy_fallback_cwd_onto_stored_thread_environments() {
|
async fn default_turn_does_not_overlay_legacy_fallback_cwd_onto_stored_thread_environments() {
|
||||||
let (session, initial_turn, _rx) = make_session_and_context_with_rx().await;
|
let (session, _initial_turn, _rx) = make_session_and_context_with_rx().await;
|
||||||
let session_cwd = session.get_config().await.cwd.clone();
|
let session_cwd = session.get_config().await.cwd.clone();
|
||||||
let selected_cwd =
|
let selected_cwd =
|
||||||
AbsolutePathBuf::try_from(session_cwd.as_path().join("selected")).expect("absolute path");
|
AbsolutePathBuf::try_from(session_cwd.as_path().join("selected")).expect("absolute path");
|
||||||
session.services.turn_environments.update_selections(
|
session
|
||||||
&[local(selected_cwd.clone())],
|
.update_settings(SessionSettingsUpdate {
|
||||||
&initial_turn
|
environments: Some(TurnEnvironmentSelections::new(
|
||||||
.environments
|
session_cwd.clone(),
|
||||||
.primary()
|
vec![local(selected_cwd.clone())],
|
||||||
.expect("initial turn environment")
|
)),
|
||||||
.config,
|
..Default::default()
|
||||||
);
|
})
|
||||||
{
|
.await
|
||||||
let mut state = session.state.lock().await;
|
.expect("environment selection update should succeed");
|
||||||
state.session_configuration.environments.environments = vec![local(selected_cwd.clone())];
|
|
||||||
}
|
|
||||||
|
|
||||||
let turn_context = session.new_default_turn().await;
|
let turn_context = session.new_default_turn().await;
|
||||||
|
|
||||||
let turn_environments = &turn_context.environments;
|
let turn_environments = &turn_context.environments;
|
||||||
@@ -7182,22 +7180,19 @@ async fn default_turn_does_not_overlay_legacy_fallback_cwd_onto_stored_thread_en
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn default_turn_honors_empty_stored_thread_environments() {
|
async fn default_turn_honors_empty_stored_thread_environments() {
|
||||||
let (session, initial_turn, _rx) = make_session_and_context_with_rx().await;
|
let (session, _initial_turn, _rx) = make_session_and_context_with_rx().await;
|
||||||
let session_cwd = session.get_config().await.cwd.clone();
|
let session_cwd = session.get_config().await.cwd.clone();
|
||||||
|
|
||||||
session.services.turn_environments.update_selections(
|
session
|
||||||
&[],
|
.update_settings(SessionSettingsUpdate {
|
||||||
&initial_turn
|
environments: Some(TurnEnvironmentSelections::new(
|
||||||
.environments
|
session_cwd.clone(),
|
||||||
.primary()
|
Vec::new(),
|
||||||
.expect("initial turn environment")
|
)),
|
||||||
.config,
|
..Default::default()
|
||||||
);
|
})
|
||||||
{
|
.await
|
||||||
let mut state = session.state.lock().await;
|
.expect("environment selection update should succeed");
|
||||||
state.session_configuration.environments.environments = Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let turn_context = session.new_default_turn().await;
|
let turn_context = session.new_default_turn().await;
|
||||||
|
|
||||||
assert!(turn_context.environments.primary().is_none());
|
assert!(turn_context.environments.primary().is_none());
|
||||||
@@ -7960,7 +7955,7 @@ where
|
|||||||
approvals_reviewer: config.approvals_reviewer,
|
approvals_reviewer: config.approvals_reviewer,
|
||||||
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
permission_profile_state: config.permissions.permission_profile_state().clone(),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
|
legacy_fallback_cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
thread_name: None,
|
thread_name: None,
|
||||||
original_config_do_not_use: Arc::clone(&config),
|
original_config_do_not_use: Arc::clone(&config),
|
||||||
@@ -7977,12 +7972,6 @@ where
|
|||||||
dynamic_tools,
|
dynamic_tools,
|
||||||
user_shell_override: None,
|
user_shell_override: None,
|
||||||
};
|
};
|
||||||
let per_turn_config =
|
|
||||||
Session::build_per_turn_config(&session_configuration, session_configuration.cwd().clone());
|
|
||||||
let model_info = construct_model_info_offline_for_tests(
|
|
||||||
session_configuration.collaboration_mode.model(),
|
|
||||||
&per_turn_config.to_models_manager_config(),
|
|
||||||
);
|
|
||||||
let session_telemetry = session_telemetry(
|
let session_telemetry = session_telemetry(
|
||||||
thread_id,
|
thread_id,
|
||||||
config.as_ref(),
|
config.as_ref(),
|
||||||
@@ -7992,7 +7981,8 @@ where
|
|||||||
|
|
||||||
let state = SessionState::new(session_configuration.clone());
|
let state = SessionState::new(session_configuration.clone());
|
||||||
let (environment_manager, resolved_turn_environments) =
|
let (environment_manager, resolved_turn_environments) =
|
||||||
resolved_environments_for_configuration(&session_configuration).await;
|
resolved_environments_for_configuration(&session_configuration, &default_environments)
|
||||||
|
.await;
|
||||||
let turn_environments = Arc::new(ThreadEnvironments::new(
|
let turn_environments = Arc::new(ThreadEnvironments::new(
|
||||||
environment_manager,
|
environment_manager,
|
||||||
default_user_shell(),
|
default_user_shell(),
|
||||||
@@ -8109,43 +8099,6 @@ where
|
|||||||
turn_environments: Arc::clone(&turn_environments),
|
turn_environments: Arc::clone(&turn_environments),
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins_input = per_turn_config.plugins_config_input();
|
|
||||||
let plugin_outcome = services
|
|
||||||
.plugins_manager
|
|
||||||
.plugins_for_config(&plugins_input)
|
|
||||||
.await;
|
|
||||||
let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots();
|
|
||||||
let plugin_skill_snapshots = services
|
|
||||||
.plugins_manager
|
|
||||||
.plugin_skill_snapshots_for_config(&plugins_input);
|
|
||||||
let skills_input =
|
|
||||||
crate::skills_load_input_from_config(&per_turn_config, effective_skill_roots)
|
|
||||||
.with_plugin_skill_snapshots(plugin_skill_snapshots);
|
|
||||||
let skill_fs = environment.get_filesystem();
|
|
||||||
let skills_snapshot = services
|
|
||||||
.skills_service
|
|
||||||
.snapshot_for_config(&skills_input, Some(Arc::clone(&skill_fs)))
|
|
||||||
.await;
|
|
||||||
let turn_context = Arc::new(Session::make_turn_context(
|
|
||||||
thread_id,
|
|
||||||
SessionId::from(thread_id),
|
|
||||||
Some(Arc::clone(&auth_manager)),
|
|
||||||
&session_telemetry,
|
|
||||||
session_configuration.provider.clone(),
|
|
||||||
&session_configuration,
|
|
||||||
config.multi_agent_version_from_features(),
|
|
||||||
services.user_shell.as_ref(),
|
|
||||||
services.shell_zsh_path.as_ref(),
|
|
||||||
services.main_execve_wrapper_exe.as_ref(),
|
|
||||||
per_turn_config,
|
|
||||||
model_info,
|
|
||||||
&models_manager,
|
|
||||||
/*network*/ None,
|
|
||||||
resolved_turn_environments,
|
|
||||||
session_configuration.cwd().clone(),
|
|
||||||
"turn_id".to_string(),
|
|
||||||
skills_snapshot,
|
|
||||||
));
|
|
||||||
let session = Arc::new(Session {
|
let session = Arc::new(Session {
|
||||||
thread_id,
|
thread_id,
|
||||||
installation_id: "11111111-1111-4111-8111-111111111111".to_string(),
|
installation_id: "11111111-1111-4111-8111-111111111111".to_string(),
|
||||||
@@ -8173,7 +8126,48 @@ where
|
|||||||
fork_persistence: ForkPersistence::Copied,
|
fork_persistence: ForkPersistence::Copied,
|
||||||
next_internal_sub_id: AtomicU64::new(0),
|
next_internal_sub_id: AtomicU64::new(0),
|
||||||
});
|
});
|
||||||
|
let per_turn_config =
|
||||||
|
session.build_per_turn_config(&session_configuration, session_configuration.cwd().clone());
|
||||||
|
let plugins_input = per_turn_config.plugins_config_input();
|
||||||
|
let plugin_outcome = session
|
||||||
|
.services
|
||||||
|
.plugins_manager
|
||||||
|
.plugins_for_config(&plugins_input)
|
||||||
|
.await;
|
||||||
|
let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots();
|
||||||
|
let plugin_skill_snapshots = session
|
||||||
|
.services
|
||||||
|
.plugins_manager
|
||||||
|
.plugin_skill_snapshots_for_config(&plugins_input);
|
||||||
|
let skills_input =
|
||||||
|
crate::skills_load_input_from_config(&per_turn_config, effective_skill_roots)
|
||||||
|
.with_plugin_skill_snapshots(plugin_skill_snapshots);
|
||||||
|
let skill_fs = environment.get_filesystem();
|
||||||
|
let skills_snapshot = session
|
||||||
|
.services
|
||||||
|
.skills_service
|
||||||
|
.snapshot_for_config(&skills_input, Some(Arc::clone(&skill_fs)))
|
||||||
|
.await;
|
||||||
|
let turn_context = Arc::new(Session::make_turn_context(
|
||||||
|
thread_id,
|
||||||
|
SessionId::from(thread_id),
|
||||||
|
Some(Arc::clone(&auth_manager)),
|
||||||
|
&session_telemetry,
|
||||||
|
session_configuration.provider.clone(),
|
||||||
|
&session_configuration,
|
||||||
|
config.multi_agent_version_from_features(),
|
||||||
|
session.services.user_shell.as_ref(),
|
||||||
|
session.services.shell_zsh_path.as_ref(),
|
||||||
|
session.services.main_execve_wrapper_exe.as_ref(),
|
||||||
|
per_turn_config,
|
||||||
|
model_info,
|
||||||
|
&models_manager,
|
||||||
|
/*network*/ None,
|
||||||
|
resolved_turn_environments,
|
||||||
|
session_configuration.cwd().clone(),
|
||||||
|
"turn_id".to_string(),
|
||||||
|
skills_snapshot,
|
||||||
|
));
|
||||||
session.mark_mcp_runtime_dirty();
|
session.mark_mcp_runtime_dirty();
|
||||||
(session, turn_context, rx_event)
|
(session, turn_context, rx_event)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,10 +108,7 @@ pub(super) async fn emit_applied(session: &Session, submission_id: String) {
|
|||||||
/// Builds the effective thread-settings event used by live updates and
|
/// Builds the effective thread-settings event used by live updates and
|
||||||
/// synthesized fork history.
|
/// synthesized fork history.
|
||||||
pub(super) async fn applied_event(session: &Session) -> EventMsg {
|
pub(super) async fn applied_event(session: &Session) -> EventMsg {
|
||||||
let snapshot = {
|
let snapshot = session.thread_config_snapshot().await;
|
||||||
let state = session.state.lock().await;
|
|
||||||
state.session_configuration.thread_config_snapshot()
|
|
||||||
};
|
|
||||||
EventMsg::ThreadSettingsApplied(ThreadSettingsAppliedEvent {
|
EventMsg::ThreadSettingsApplied(ThreadSettingsAppliedEvent {
|
||||||
thread_settings: snapshot.into_thread_settings_snapshot(),
|
thread_settings: snapshot.into_thread_settings_snapshot(),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -944,10 +944,7 @@ async fn track_turn_resolved_config_analytics(
|
|||||||
turn_context: &TurnContext,
|
turn_context: &TurnContext,
|
||||||
input: &[TurnInput],
|
input: &[TurnInput],
|
||||||
) {
|
) {
|
||||||
let thread_config = {
|
let thread_config = sess.thread_config_snapshot().await;
|
||||||
let state = sess.state.lock().await;
|
|
||||||
state.session_configuration.thread_config_snapshot()
|
|
||||||
};
|
|
||||||
let is_first_turn = {
|
let is_first_turn = {
|
||||||
let mut state = sess.state.lock().await;
|
let mut state = sess.state.lock().await;
|
||||||
state.take_next_turn_is_first()
|
state.take_next_turn_is_first()
|
||||||
|
|||||||
@@ -508,6 +508,7 @@ fn local_time_context() -> (String, String) {
|
|||||||
impl Session {
|
impl Session {
|
||||||
/// Don't expand the number of mutated arguments on config. We are in the process of getting rid of it.
|
/// Don't expand the number of mutated arguments on config. We are in the process of getting rid of it.
|
||||||
pub(crate) fn build_per_turn_config(
|
pub(crate) fn build_per_turn_config(
|
||||||
|
&self,
|
||||||
session_configuration: &SessionConfiguration,
|
session_configuration: &SessionConfiguration,
|
||||||
cwd: AbsolutePathBuf,
|
cwd: AbsolutePathBuf,
|
||||||
) -> Config {
|
) -> Config {
|
||||||
@@ -516,7 +517,7 @@ impl Session {
|
|||||||
let mut per_turn_config = (*config).clone();
|
let mut per_turn_config = (*config).clone();
|
||||||
per_turn_config.cwd = cwd;
|
per_turn_config.cwd = cwd;
|
||||||
per_turn_config.permissions.approval_policy = session_configuration.approval_policy.clone();
|
per_turn_config.permissions.approval_policy = session_configuration.approval_policy.clone();
|
||||||
let workspace_roots = session_configuration.primary_workspace_roots();
|
let workspace_roots = self.services.turn_environments.primary_workspace_roots();
|
||||||
per_turn_config.workspace_roots = workspace_roots.clone();
|
per_turn_config.workspace_roots = workspace_roots.clone();
|
||||||
per_turn_config
|
per_turn_config
|
||||||
.permissions
|
.permissions
|
||||||
@@ -552,10 +553,11 @@ impl Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_effective_session_config(
|
pub(crate) fn build_effective_session_config(
|
||||||
|
&self,
|
||||||
session_configuration: &SessionConfiguration,
|
session_configuration: &SessionConfiguration,
|
||||||
) -> Config {
|
) -> Config {
|
||||||
let mut config =
|
let mut config =
|
||||||
Self::build_per_turn_config(session_configuration, session_configuration.cwd().clone());
|
self.build_per_turn_config(session_configuration, session_configuration.cwd().clone());
|
||||||
config.model = Some(session_configuration.collaboration_mode.model().to_string());
|
config.model = Some(session_configuration.collaboration_mode.model().to_string());
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
@@ -686,24 +688,22 @@ impl Session {
|
|||||||
let notify_config_contributors = !self.services.extensions.config_contributors().is_empty();
|
let notify_config_contributors = !self.services.extensions.config_contributors().is_empty();
|
||||||
let update_result: CodexResult<_> = {
|
let update_result: CodexResult<_> = {
|
||||||
let mut state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
match state.session_configuration.clone().apply(&updates) {
|
match self.apply_session_settings(&state.session_configuration, &updates) {
|
||||||
Ok(next) => {
|
Ok(next) => {
|
||||||
let mcp_inputs_changed = state.session_configuration.mcp_inputs_differ(&next);
|
let mcp_inputs_changed =
|
||||||
|
self.mcp_inputs_differ(&state.session_configuration, &next, &updates);
|
||||||
let previous_permission_profile =
|
let previous_permission_profile =
|
||||||
state.session_configuration.permission_profile();
|
state.session_configuration.permission_profile();
|
||||||
let next_permission_profile = next.permission_profile();
|
let next_permission_profile = next.permission_profile();
|
||||||
let permission_profile_changed =
|
let permission_profile_changed =
|
||||||
previous_permission_profile != next_permission_profile;
|
previous_permission_profile != next_permission_profile;
|
||||||
let previous_config = notify_config_contributors.then(|| {
|
let previous_config = notify_config_contributors
|
||||||
Self::build_effective_session_config(&state.session_configuration)
|
.then(|| self.build_effective_session_config(&state.session_configuration));
|
||||||
});
|
|
||||||
let new_config = notify_config_contributors
|
|
||||||
.then(|| Self::build_effective_session_config(&next));
|
|
||||||
let environment_config = next.turn_environment_config();
|
let environment_config = next.turn_environment_config();
|
||||||
if updates.environments.is_some() {
|
if let Some(environments) = &updates.environments {
|
||||||
self.services
|
self.services
|
||||||
.turn_environments
|
.turn_environments
|
||||||
.update_selections(next.environment_selections(), &environment_config);
|
.update_selections(&environments.environments, &environment_config);
|
||||||
} else if state.session_configuration.turn_environment_config()
|
} else if state.session_configuration.turn_environment_config()
|
||||||
!= environment_config
|
!= environment_config
|
||||||
{
|
{
|
||||||
@@ -715,6 +715,8 @@ impl Session {
|
|||||||
self.mark_mcp_runtime_dirty();
|
self.mark_mcp_runtime_dirty();
|
||||||
}
|
}
|
||||||
state.session_configuration = next.clone();
|
state.session_configuration = next.clone();
|
||||||
|
let new_config = notify_config_contributors
|
||||||
|
.then(|| self.build_effective_session_config(&state.session_configuration));
|
||||||
Ok((
|
Ok((
|
||||||
next,
|
next,
|
||||||
mcp_inputs_changed,
|
mcp_inputs_changed,
|
||||||
@@ -814,7 +816,7 @@ impl Session {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|turn_environment| turn_environment.cwd().to_abs_path().ok())
|
.and_then(|turn_environment| turn_environment.cwd().to_abs_path().ok())
|
||||||
.unwrap_or_else(|| session_configuration.cwd().clone());
|
.unwrap_or_else(|| session_configuration.cwd().clone());
|
||||||
let per_turn_config = Self::build_per_turn_config(&session_configuration, cwd.clone());
|
let per_turn_config = self.build_per_turn_config(&session_configuration, cwd.clone());
|
||||||
let model_info = self
|
let model_info = self
|
||||||
.services
|
.services
|
||||||
.models_manager
|
.models_manager
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use base64::Engine;
|
|||||||
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
||||||
use codex_api::AuthProvider;
|
use codex_api::AuthProvider;
|
||||||
use codex_config::types::ApprovalsReviewer;
|
use codex_config::types::ApprovalsReviewer;
|
||||||
|
use codex_core::CodexThreadSettingsOverrides;
|
||||||
use codex_core::EnvironmentConfig;
|
use codex_core::EnvironmentConfig;
|
||||||
use codex_core::StartThreadOptions;
|
use codex_core::StartThreadOptions;
|
||||||
use codex_core::TurnInputRequest;
|
use codex_core::TurnInputRequest;
|
||||||
@@ -537,8 +538,11 @@ async fn settings_update_does_not_retarget_active_turn_environment() -> Result<(
|
|||||||
});
|
});
|
||||||
let test = builder.build(&server).await?;
|
let test = builder.build(&server).await?;
|
||||||
let initial_cwd = test.config.cwd.clone();
|
let initial_cwd = test.config.cwd.clone();
|
||||||
|
let initial_environments = test.codex.environment_selections().await;
|
||||||
let next_workspace = TempDir::new()?;
|
let next_workspace = TempDir::new()?;
|
||||||
let next_cwd = next_workspace.path().abs();
|
let next_cwd = next_workspace.path().abs();
|
||||||
|
let next_environments =
|
||||||
|
TurnEnvironmentSelections::new(next_cwd.clone(), vec![local(next_cwd.clone())]);
|
||||||
|
|
||||||
test.codex
|
test.codex
|
||||||
.start_or_steer_turn(TurnInputRequest::user_input(vec![UserInput::Text {
|
.start_or_steer_turn(TurnInputRequest::user_input(vec![UserInput::Text {
|
||||||
@@ -552,17 +556,43 @@ async fn settings_update_does_not_retarget_active_turn_environment() -> Result<(
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let preview = test
|
||||||
|
.codex
|
||||||
|
.preview_thread_settings_overrides(CodexThreadSettingsOverrides {
|
||||||
|
environments: Some(next_environments.clone()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
assert_eq!(
|
||||||
|
preview.environment_selections(),
|
||||||
|
&next_environments.environments
|
||||||
|
);
|
||||||
|
assert_eq!(preview.cwd(), &next_cwd);
|
||||||
|
assert_eq!(preview.workspace_roots, vec![next_cwd.clone()]);
|
||||||
|
assert_eq!(
|
||||||
|
test.codex.environment_selections().await,
|
||||||
|
initial_environments
|
||||||
|
);
|
||||||
|
|
||||||
submit_thread_settings(
|
submit_thread_settings(
|
||||||
&test.codex,
|
&test.codex,
|
||||||
ThreadSettingsOverrides {
|
ThreadSettingsOverrides {
|
||||||
environments: Some(TurnEnvironmentSelections::new(
|
environments: Some(next_environments.clone()),
|
||||||
next_cwd.clone(),
|
|
||||||
vec![local(next_cwd.clone())],
|
|
||||||
)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
assert_eq!(
|
||||||
|
test.codex.environment_selections().await,
|
||||||
|
next_environments.environments
|
||||||
|
);
|
||||||
|
let snapshot = test.codex.config_snapshot().await;
|
||||||
|
assert_eq!(
|
||||||
|
snapshot.environment_selections(),
|
||||||
|
next_environments.environments
|
||||||
|
);
|
||||||
|
assert_eq!(snapshot.cwd(), &next_cwd);
|
||||||
|
assert_eq!(snapshot.workspace_roots, vec![next_cwd.clone()]);
|
||||||
test.codex
|
test.codex
|
||||||
.submit(Op::UserInputAnswer {
|
.submit(Op::UserInputAnswer {
|
||||||
id: request.turn_id,
|
id: request.turn_id,
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ use codex_protocol::config_types::Settings;
|
|||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||||
|
use codex_protocol::protocol::TurnEnvironmentSelections;
|
||||||
use codex_protocol::user_input::UserInput;
|
use codex_protocol::user_input::UserInput;
|
||||||
use core_test_support::responses;
|
use core_test_support::responses;
|
||||||
use core_test_support::responses::ev_completed;
|
use core_test_support::responses::ev_completed;
|
||||||
use core_test_support::responses::ev_response_created;
|
use core_test_support::responses::ev_response_created;
|
||||||
use core_test_support::streaming_sse::StreamingSseChunk;
|
use core_test_support::streaming_sse::StreamingSseChunk;
|
||||||
use core_test_support::streaming_sse::start_streaming_sse_server;
|
use core_test_support::streaming_sse::start_streaming_sse_server;
|
||||||
|
use core_test_support::test_codex::local;
|
||||||
use core_test_support::test_codex::test_codex;
|
use core_test_support::test_codex::test_codex;
|
||||||
use core_test_support::wait_for_event;
|
use core_test_support::wait_for_event;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@@ -292,11 +294,15 @@ async fn turn_input_submission_applies_thread_settings_only_after_accepted_input
|
|||||||
.await
|
.await
|
||||||
.expect("started turn should reach its first model request");
|
.expect("started turn should reach its first model request");
|
||||||
|
|
||||||
|
let steered_cwd = test.config.cwd.join("steered-environment");
|
||||||
|
let steered_environments =
|
||||||
|
TurnEnvironmentSelections::new(steered_cwd.clone(), vec![local(steered_cwd)]);
|
||||||
let steered = codex
|
let steered = codex
|
||||||
.start_or_steer_turn(
|
.start_or_steer_turn(
|
||||||
user_message_request("steer active turn").with_thread_settings(
|
user_message_request("steer active turn").with_thread_settings(
|
||||||
ThreadSettingsOverrides {
|
ThreadSettingsOverrides {
|
||||||
approval_policy: Some(AskForApproval::Never),
|
approval_policy: Some(AskForApproval::Never),
|
||||||
|
environments: Some(steered_environments.clone()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -308,16 +314,25 @@ async fn turn_input_submission_applies_thread_settings_only_after_accepted_input
|
|||||||
codex.config_snapshot().await.approval_policy,
|
codex.config_snapshot().await.approval_policy,
|
||||||
AskForApproval::Never
|
AskForApproval::Never
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
codex.environment_selections().await,
|
||||||
|
steered_environments.environments
|
||||||
|
);
|
||||||
|
|
||||||
release_response
|
release_response
|
||||||
.send(())
|
.send(())
|
||||||
.expect("response gate should remain open");
|
.expect("response gate should remain open");
|
||||||
wait_for_event(codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
|
wait_for_event(codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
|
||||||
|
|
||||||
|
let rejected_cwd = test.config.cwd.join("rejected-environment");
|
||||||
let rejected = codex
|
let rejected = codex
|
||||||
.steer_turn(
|
.steer_turn(
|
||||||
user_message_request("no active turn").with_thread_settings(ThreadSettingsOverrides {
|
user_message_request("no active turn").with_thread_settings(ThreadSettingsOverrides {
|
||||||
approval_policy: Some(AskForApproval::OnRequest),
|
approval_policy: Some(AskForApproval::OnRequest),
|
||||||
|
environments: Some(TurnEnvironmentSelections::new(
|
||||||
|
rejected_cwd.clone(),
|
||||||
|
vec![local(rejected_cwd)],
|
||||||
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
"missing-turn".to_string(),
|
"missing-turn".to_string(),
|
||||||
@@ -334,6 +349,10 @@ async fn turn_input_submission_applies_thread_settings_only_after_accepted_input
|
|||||||
codex.config_snapshot().await.approval_policy,
|
codex.config_snapshot().await.approval_policy,
|
||||||
AskForApproval::Never
|
AskForApproval::Never
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
codex.environment_selections().await,
|
||||||
|
steered_environments.environments
|
||||||
|
);
|
||||||
server.shutdown().await;
|
server.shutdown().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
use crate::capabilities::SelectedCapabilityRoot;
|
||||||
|
|
||||||
/// Resolved configuration supplied by the owner of a thread/environment attachment.
|
/// Resolved configuration supplied by the owner of a thread/environment attachment.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@@ -16,6 +16,7 @@ pub mod approvals;
|
|||||||
pub mod capabilities;
|
pub mod capabilities;
|
||||||
pub mod config_types;
|
pub mod config_types;
|
||||||
pub mod dynamic_tools;
|
pub mod dynamic_tools;
|
||||||
|
mod environment;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod exec_output;
|
pub mod exec_output;
|
||||||
pub mod items;
|
pub mod items;
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ pub use crate::approvals::NetworkApprovalContext;
|
|||||||
pub use crate::approvals::NetworkApprovalProtocol;
|
pub use crate::approvals::NetworkApprovalProtocol;
|
||||||
pub use crate::approvals::NetworkPolicyAmendment;
|
pub use crate::approvals::NetworkPolicyAmendment;
|
||||||
pub use crate::approvals::NetworkPolicyRuleAction;
|
pub use crate::approvals::NetworkPolicyRuleAction;
|
||||||
|
pub use crate::environment::EnvironmentConfig;
|
||||||
pub use crate::legacy_events::HasLegacyEvent;
|
pub use crate::legacy_events::HasLegacyEvent;
|
||||||
pub use crate::permissions::FileSystemAccessMode;
|
pub use crate::permissions::FileSystemAccessMode;
|
||||||
pub use crate::permissions::FileSystemPath;
|
pub use crate::permissions::FileSystemPath;
|
||||||
|
|||||||
Reference in new issue
Block a user