Separate environment requests from runtime selections (#51221)

## What changed

Introduce `TurnEnvironmentRequest` for caller-supplied environment input and construct `TurnEnvironmentSelection` at session startup and app-server settings boundaries. Update thread startup, default environments, and inherited environment handoffs to use requests, with explicit conversions when reusing runtime selections.

Move the environment types into `protocol::environment` while retaining their `protocol` re-exports. Update test fixtures and existing tests to build request inputs separately from runtime selections.

GitOrigin-RevId: 12e7daaece95aae136afae8b055b4dbaebdffc75
This commit is contained in:
Adam Perry @ OpenAI
2026-10-06 01:39:50 +00:00
committed by copyberry
parent 28b91c7c31
commit 2dbcab90e2
46 changed files with 373 additions and 212 deletions
+68 -1
View File
@@ -1,4 +1,5 @@
//! Environment attachment authority, including the shared Full Access decision.
//! Environment requests, thread selections, and attachment authority.
//! Input APIs accept requests; runtime code uses selections constructed by the thread.
use crate::capabilities::SelectedCapabilityRoot;
use crate::config_types::ShellEnvironmentPolicy;
@@ -10,8 +11,74 @@ use crate::protocol::AskForApproval;
use crate::sandbox::SandboxType;
use codex_execpolicy::RequirementsExecPolicy;
use codex_network_proxy::EnvironmentNetworkPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
/// An environment requested by a caller, before construction of thread-owned state.
///
/// Input APIs accept requests so the receiving thread has a construction boundary for
/// attaching its state before passing a selection to runtime code.
#[derive(Debug, Clone, PartialEq)]
pub struct TurnEnvironmentRequest {
pub environment_id: String,
pub cwd: PathUri,
pub workspace_roots: Vec<PathUri>,
pub config: EnvironmentConfigState,
}
/// An environment selected by a thread for runtime use.
/// Constructed from caller input at the receiving thread's startup or settings boundary.
#[derive(Debug, Clone, PartialEq)]
pub struct TurnEnvironmentSelection {
pub environment_id: String,
pub cwd: PathUri,
pub workspace_roots: Vec<PathUri>,
pub config: EnvironmentConfigState,
}
impl TurnEnvironmentSelection {
/// Constructs runtime state at the receiving thread boundary.
pub fn new(request: TurnEnvironmentRequest) -> Self {
Self {
environment_id: request.environment_id,
cwd: request.cwd,
workspace_roots: request.workspace_roots,
config: request.config,
}
}
/// Requests this environment again; the receiving thread supplies its own roots.
/// This explicit conversion discards thread-owned state. Test inputs should instead
/// construct requests directly so they do not depend on runtime selection fields.
pub fn into_request(self) -> TurnEnvironmentRequest {
TurnEnvironmentRequest {
environment_id: self.environment_id,
cwd: self.cwd,
workspace_roots: self.workspace_roots,
config: self.config,
}
}
}
/// The environments captured by a thread and its fallback working directory.
#[derive(Debug, Clone, PartialEq)]
pub struct TurnEnvironmentSelections {
pub legacy_fallback_cwd: AbsolutePathBuf,
pub environments: Vec<TurnEnvironmentSelection>,
}
impl TurnEnvironmentSelections {
pub fn new(
legacy_fallback_cwd: AbsolutePathBuf,
environments: Vec<TurnEnvironmentSelection>,
) -> Self {
Self {
legacy_fallback_cwd,
environments,
}
}
}
/// Configuration supplied for a thread's selected environment.
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
+4 -29
View File
@@ -99,6 +99,9 @@ pub use crate::approvals::NetworkPolicyAmendment;
pub use crate::approvals::NetworkPolicyRuleAction;
pub use crate::environment::EnvironmentConfig;
pub use crate::environment::EnvironmentConfigState;
pub use crate::environment::TurnEnvironmentRequest;
pub use crate::environment::TurnEnvironmentSelection;
pub use crate::environment::TurnEnvironmentSelections;
pub use crate::environment::has_full_access;
pub use crate::legacy_events::HasLegacyEvent;
pub use crate::permissions::FileSystemAccessMode;
@@ -149,34 +152,6 @@ pub fn strip_user_message_prefix(text: &str) -> &str {
}
}
// TODO(anp): Replace `TurnEnvironmentSelection` with `PathUri` once path URIs carry environment
// identifiers.
#[derive(Debug, Clone, PartialEq)]
pub struct TurnEnvironmentSelection {
pub environment_id: String,
pub cwd: PathUri,
pub workspace_roots: Vec<PathUri>,
pub config: EnvironmentConfigState,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TurnEnvironmentSelections {
pub legacy_fallback_cwd: AbsolutePathBuf,
pub environments: Vec<TurnEnvironmentSelection>,
}
impl TurnEnvironmentSelections {
pub fn new(
legacy_fallback_cwd: AbsolutePathBuf,
environments: Vec<TurnEnvironmentSelection>,
) -> Self {
Self {
legacy_fallback_cwd,
environments,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, TS)]
#[serde(transparent)]
#[ts(type = "string")]
@@ -513,7 +488,7 @@ pub struct ThreadSettingsOverrides {
pub environments: Option<TurnEnvironmentSelections>,
/// Updated top-level runtime workspace roots for default environments.
/// Explicit environment selections own their roots separately.
/// Explicit environment requests own their workspace roots separately.
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
/// Updated profile-defined workspace roots for status summaries and