mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Preserve environment configuration ownership (#38678)
## Why Environment attachments can either inherit configuration from their thread or provide their own. Later thread setting updates must refresh inherited configuration without overwriting attachment-owned permissions and capability roots. ## What changed - Resolve each attachment's configuration when it is selected and retain whether it came from the thread or the attachment owner. - Apply subsequent thread configuration updates only to thread-owned attachments, while preserving ownership across snapshots and child threads. - Keep the resolved configuration with the environment selection so runtime consumers use a single canonical value. ## Testing - Extend remote-environment coverage to verify that thread-owned permissions follow thread updates while owner-provided read-only permissions remain in effect. - Cover configuration inheritance, attachment replacement, and owner-configured capability roots. GitOrigin-RevId: fe70c4be5f151432b69bf4b141e316faa89036ae
This commit is contained in:
1 parent
233739e76a
commit
22bf16a37e
23 files changed
+343
-192
No files matched your search
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::environment_selection::EnvironmentConfigOrigin;
|
||||
use crate::environment_selection::TurnEnvironmentSnapshot;
|
||||
use crate::exec_policy::AllowPrefixRules;
|
||||
use crate::shell_snapshot::ShellSnapshotFile;
|
||||
@@ -15,6 +16,7 @@ use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_protocol::openai_models::MODEL_SPECIALTY_CYBER;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::protocol::EnvironmentConfig;
|
||||
use codex_protocol::protocol::EnvironmentConfigState;
|
||||
use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::MultiAgentVersion;
|
||||
use codex_protocol::protocol::ThreadHistoryMode;
|
||||
@@ -34,28 +36,44 @@ pub(crate) type ShellSnapshotTask = Shared<BoxFuture<'static, Option<Arc<ShellSn
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct TurnEnvironment {
|
||||
pub(crate) selection: TurnEnvironmentSelection,
|
||||
pub(crate) config_origin: EnvironmentConfigOrigin,
|
||||
pub(crate) environment: Arc<Environment>,
|
||||
pub(crate) shell: Option<shell::Shell>,
|
||||
pub(crate) config: EnvironmentConfig,
|
||||
pub(crate) shell_snapshot: ShellSnapshotTask,
|
||||
}
|
||||
|
||||
impl TurnEnvironment {
|
||||
pub(crate) fn new(
|
||||
selection: TurnEnvironmentSelection,
|
||||
config_origin: EnvironmentConfigOrigin,
|
||||
environment: Arc<Environment>,
|
||||
shell: Option<shell::Shell>,
|
||||
config: EnvironmentConfig,
|
||||
) -> Self {
|
||||
debug_assert!(matches!(selection.config, EnvironmentConfigState::Ready(_)));
|
||||
Self {
|
||||
selection,
|
||||
config_origin,
|
||||
environment,
|
||||
shell,
|
||||
config,
|
||||
shell_snapshot: futures::future::ready(None).boxed().shared(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn config(&self) -> &EnvironmentConfig {
|
||||
let EnvironmentConfigState::Ready(config) = &self.selection.config else {
|
||||
unreachable!("ready turn environments always carry resolved configuration")
|
||||
};
|
||||
config
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn config_mut(&mut self) -> &mut EnvironmentConfig {
|
||||
let EnvironmentConfigState::Ready(config) = &mut self.selection.config else {
|
||||
unreachable!("ready turn environments always carry resolved configuration")
|
||||
};
|
||||
config
|
||||
}
|
||||
|
||||
pub(crate) fn shell_snapshot(&self, cwd: &AbsolutePathBuf) -> Option<AbsolutePathBuf> {
|
||||
if self.selection.cwd != PathUri::from_abs_path(cwd) {
|
||||
return None;
|
||||
@@ -75,11 +93,11 @@ impl TurnEnvironment {
|
||||
}
|
||||
|
||||
pub(crate) fn permission_profile(&self) -> &PermissionProfile {
|
||||
self.config.permission_profile.permission_profile()
|
||||
self.config().permission_profile.permission_profile()
|
||||
}
|
||||
|
||||
pub(crate) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {
|
||||
self.config.permission_profile.active_permission_profile()
|
||||
self.config().permission_profile.active_permission_profile()
|
||||
}
|
||||
|
||||
pub(crate) fn permission_profile_with_workspace_roots(&self) -> PermissionProfile {
|
||||
@@ -94,7 +112,8 @@ impl TurnEnvironment {
|
||||
}
|
||||
|
||||
pub(crate) fn selection(&self) -> TurnEnvironmentSelection {
|
||||
self.selection.clone()
|
||||
self.config_origin
|
||||
.into_input_selection(self.selection.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +125,8 @@ impl std::fmt::Debug for TurnEnvironment {
|
||||
.field("cwd", &self.selection.cwd)
|
||||
.field("workspace_roots", &self.selection.workspace_roots)
|
||||
.field("shell", &self.shell)
|
||||
.field("config", &self.config)
|
||||
.field("config", self.config())
|
||||
.field("config_origin", &self.config_origin)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -690,7 +710,7 @@ impl Session {
|
||||
{
|
||||
self.services
|
||||
.turn_environments
|
||||
.update_environment_configs(&environment_config);
|
||||
.update_thread_config(&environment_config);
|
||||
}
|
||||
if mcp_inputs_changed {
|
||||
self.mark_mcp_runtime_dirty();
|
||||
|
||||
Reference in new issue
Block a user