Pair thread environment settings (#26687)

## Why

Thread cwd and environment selections are a single logical setting in
core: updating one without the other can silently desynchronize the
next-turn execution context. This change makes that relationship
explicit in the internal thread settings flow while preserving the
existing app-server public API shape.

## What changed

- Moved the cwd/environment pair through internal
`ThreadSettingsOverrides.environment_settings` instead of a top-level
internal `cwd` field.
- Kept `thread/settings/update` public params unchanged, with app-server
translating top-level `cwd` into the paired internal settings shape.
- Moved `Op::UserInput` environment overrides into thread settings so
user turns and settings updates use the same core path.
- Updated core, app-server, MCP, memories, sample, and test callsites to
construct the paired settings shape.

## Verification

- `git diff --check`
- Local test run starting after PR creation.
This commit is contained in:
pakrym-oai authored and GitHub committed 2026-06-08 13:55:15 -07:00
1 parent f9a680b907
commit f3c1283411
90 files changed
+521 -820

No files matched your search

+13 -38
View File
@@ -441,7 +441,7 @@ impl Session {
session_configuration: &SessionConfiguration,
) -> 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.permissions.approval_policy = session_configuration.approval_policy.clone();
config.workspace_roots = session_configuration.workspace_roots.clone();
@@ -587,19 +587,9 @@ impl Session {
let mut state = self.state.lock().await;
match state.session_configuration.clone().apply(&updates) {
Ok(next) => {
let mut effective_environments = updates
.environments
.clone()
.unwrap_or_else(|| next.environments.clone());
if updates.environments.is_none() {
Self::overlay_runtime_cwd_on_primary_environment(
&mut effective_environments,
&next.cwd,
);
}
let turn_environments =
self.resolve_turn_environments(&effective_environments)?;
let previous_cwd = state.session_configuration.cwd.clone();
self.resolve_turn_environments(next.environment_selections())?;
let previous_cwd = state.session_configuration.cwd().clone();
let previous_permission_profile =
state.session_configuration.permission_profile();
let next_permission_profile = next.permission_profile();
@@ -656,7 +646,7 @@ impl Session {
self.emit_config_changed_contributors(previous_config.as_ref(), new_config.as_ref());
self.maybe_refresh_shell_snapshot_for_cwd(
&previous_cwd,
&session_configuration.cwd,
session_configuration.cwd(),
&codex_home,
&session_source,
);
@@ -731,7 +721,7 @@ impl Session {
let cwd = primary_turn_environment
.as_ref()
.map(|turn_environment| turn_environment.cwd.clone())
.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 mcp_connection_manager = self.services.mcp_connection_manager.read().await;
@@ -871,29 +861,14 @@ impl Session {
let state = self.state.lock().await;
state.session_configuration.clone()
};
let mut effective_environments = session_configuration.environments.clone();
Self::overlay_runtime_cwd_on_primary_environment(
&mut effective_environments,
&session_configuration.cwd,
);
let turn_environments = match self.resolve_turn_environments(&effective_environments) {
Ok(turn_environments) => turn_environments,
Err(err) => {
warn!("failed to resolve stored session environments: {err}");
ResolvedTurnEnvironments::default()
}
};
let turn_environments =
match self.resolve_turn_environments(session_configuration.environment_selections()) {
Ok(turn_environments) => turn_environments,
Err(err) => {
warn!("failed to resolve stored session environments: {err}");
ResolvedTurnEnvironments::default()
}
};
(session_configuration, turn_environments)
}
fn overlay_runtime_cwd_on_primary_environment(
environments: &mut [TurnEnvironmentSelection],
runtime_cwd: &AbsolutePathBuf,
) {
if let Some(turn_environment) = environments.first_mut()
&& turn_environment.cwd != *runtime_cwd
{
turn_environment.cwd = runtime_cwd.clone();
}
}
}