mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
Use step environment snapshots for subagent inheritance (#55331)
## What changed - Remove `subagent_thread_environments` and the separate configured environment bindings. Spawned agents inherit the invoking step's environment snapshot. - Retain environment selections across child eviction without retaining executor bindings. Reload V2 children against the parent's captured environments, requiring matching environment IDs, working directories, and workspace roots while preserving permission validation. GitOrigin-RevId: d820c63ea20f5834ed88071d16c60683f2545406
This commit is contained in:
@@ -23,7 +23,6 @@ use crate::session::session::SessionConfiguration;
|
||||
use crate::session::session::SessionSettingsUpdate;
|
||||
|
||||
/// Defaults for environments that inherit their configuration from the running turn.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ThreadEnvironmentDefaults {
|
||||
pub(crate) common: EnvironmentConfig,
|
||||
// Chosen once when the session starts; it does not apply to remote environments.
|
||||
@@ -350,11 +349,6 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
if update_future {
|
||||
self.services
|
||||
.turn_environments
|
||||
.update_configured_selections(&future);
|
||||
}
|
||||
state.session_configuration.environments = future;
|
||||
if update_current {
|
||||
// Invalidate MCP before installed configuration can wake a waiting turn.
|
||||
|
||||
@@ -2040,14 +2040,6 @@ impl Session {
|
||||
// Save new environment defaults for future turns. The running turn keeps its own.
|
||||
windows_sandbox_changed =
|
||||
state.session_configuration.windows_sandbox_level != updated.windows_sandbox_level;
|
||||
self.services
|
||||
.turn_environments
|
||||
.set_configured_thread_defaults(updated.inferred_environment_config());
|
||||
if updated.environments != state.session_configuration.environments {
|
||||
self.services
|
||||
.turn_environments
|
||||
.update_configured_selections(&updated.environments);
|
||||
}
|
||||
state.session_configuration = updated;
|
||||
if root_service_tier_changed {
|
||||
self.services.agent_control.propagate_config_update(
|
||||
@@ -2121,18 +2113,6 @@ impl Session {
|
||||
.thread_config_snapshot(state.session_configuration.environments.clone())
|
||||
}
|
||||
|
||||
/// Captures configured child bindings, or the active turn's bindings for legacy inheritance.
|
||||
pub(crate) async fn inherited_environment_snapshot(&self) -> TurnEnvironmentSnapshot {
|
||||
let snapshot = {
|
||||
let _state = self.state.lock().await;
|
||||
if self.features.enabled(Feature::SubagentThreadEnvironments) {
|
||||
return self.services.turn_environments.configured_snapshot_now();
|
||||
}
|
||||
self.services.turn_environments.snapshot()
|
||||
};
|
||||
snapshot.await
|
||||
}
|
||||
|
||||
pub(crate) async fn configured_environment_selections(&self) -> Vec<TurnEnvironmentSelection> {
|
||||
let state = self.state.lock().await;
|
||||
state.session_configuration.environments.clone()
|
||||
@@ -3885,10 +3865,6 @@ impl Session {
|
||||
.await
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::await_holding_invalid_type,
|
||||
reason = "step settings and both environment snapshots must be captured under active_turn then state"
|
||||
)]
|
||||
#[tracing::instrument(name = "step_context.capture", level = "info", skip_all)]
|
||||
async fn capture_step_context_inner(
|
||||
self: &Arc<Self>,
|
||||
@@ -3898,21 +3874,16 @@ impl Session {
|
||||
required_plugins: &HashSet<String>,
|
||||
) -> CodexResult<Arc<StepContext>> {
|
||||
// Read the step's model and record its environments together so an update cannot split them.
|
||||
// Match apply_turn_settings and activate_turn_environments: active_turn before state.
|
||||
// Wait for executor startup below, after releasing both locks.
|
||||
let (mut settings, environments, inherited_environments) = {
|
||||
// Wait for executor startup below, after releasing the lock.
|
||||
let (mut settings, environments) = {
|
||||
let _active = self
|
||||
.active_turn
|
||||
.lock()
|
||||
.or_cancel(cancellation_token)
|
||||
.await?;
|
||||
let _state = self.state.lock().or_cancel(cancellation_token).await?;
|
||||
(
|
||||
turn_context.next_step_settings.load_full(),
|
||||
self.services.turn_environments.snapshot(),
|
||||
self.features
|
||||
.enabled(Feature::SubagentThreadEnvironments)
|
||||
.then(|| self.services.turn_environments.configured_snapshot_now()),
|
||||
)
|
||||
};
|
||||
if matches!(
|
||||
@@ -3940,7 +3911,6 @@ impl Session {
|
||||
);
|
||||
let session_telemetry = settings.telemetry(&turn_context.session_telemetry);
|
||||
let environments = environments.or_cancel(cancellation_token).await?;
|
||||
let inherited_environments = inherited_environments.unwrap_or_else(|| environments.clone());
|
||||
// Keep both preparation futures off caller stacks while they are live together.
|
||||
let load_agents_md = Box::pin(async {
|
||||
let (loaded_agents_md, warnings) = self
|
||||
@@ -4073,7 +4043,6 @@ impl Session {
|
||||
turn: turn_context,
|
||||
extension_data,
|
||||
environments,
|
||||
inherited_environments,
|
||||
selected_capability_roots,
|
||||
executor_capability_discovery,
|
||||
executor_capability_discovery_v2,
|
||||
|
||||
@@ -1483,7 +1483,6 @@ impl Session {
|
||||
inherited_environments.clone(),
|
||||
config.features.enabled(Feature::DeferredExecutor),
|
||||
));
|
||||
turn_environments.update_configured_selections(environment_selections);
|
||||
turn_environments.update_selections(environment_selections);
|
||||
session_configuration.environments = turn_environments.selections();
|
||||
let resolved_environments = turn_environments.snapshot().await;
|
||||
|
||||
@@ -189,27 +189,14 @@ async fn next_step_waits_for_environment_publication_before_capturing() {
|
||||
.turn_environments
|
||||
.update_selections(&selections);
|
||||
}
|
||||
{
|
||||
// Thread attachments publish under state independently of active-turn settings.
|
||||
// A capture waiting for that publication must not retain the previous attachments.
|
||||
let mut state = session.state.lock().await;
|
||||
let mut context = std::task::Context::from_waker(futures::task::noop_waker_ref());
|
||||
assert!(std::future::Future::poll(capture.as_mut(), &mut context).is_pending());
|
||||
session
|
||||
.services
|
||||
.turn_environments
|
||||
.update_configured_selections(&selections);
|
||||
state.session_configuration.environments = selections.clone();
|
||||
}
|
||||
|
||||
let step = capture.await.expect("capture after publication");
|
||||
assert_eq!(
|
||||
(
|
||||
step.settings.model_info.slug.as_str(),
|
||||
step.environments.primary().map(TurnEnvironment::cwd),
|
||||
step.inherited_environments.all_selections(),
|
||||
),
|
||||
("next-step-model", Some(&next_cwd), selections),
|
||||
("next-step-model", Some(&next_cwd)),
|
||||
);
|
||||
session.abort_all_tasks(TurnAbortReason::Interrupted).await;
|
||||
}
|
||||
@@ -422,7 +409,6 @@ async fn activation_fixture(models: Vec<ModelInfo>) -> ActivationFixture {
|
||||
let (models, lookup) = GatedModelsManager::new(models);
|
||||
mutable.services.models_manager = models;
|
||||
for feature in [
|
||||
Feature::SubagentThreadEnvironments,
|
||||
Feature::StepModelSwitching,
|
||||
Feature::FastMode,
|
||||
Feature::TokenBudget,
|
||||
|
||||
@@ -35,8 +35,6 @@ pub(crate) struct StepContext {
|
||||
/// Telemetry context tagged with this sampling request's model.
|
||||
pub(crate) session_telemetry: SessionTelemetry,
|
||||
pub(crate) environments: TurnEnvironmentSnapshot,
|
||||
/// Child bindings captured with this step; configured-only when opted in.
|
||||
pub(crate) inherited_environments: TurnEnvironmentSnapshot,
|
||||
/// Capability roots bound to ready environments in this exact step.
|
||||
pub(crate) selected_capability_roots: Vec<ResolvedSelectedCapabilityRoot>,
|
||||
/// Executor-materialized capability files shared by MCP and skills in this exact step.
|
||||
|
||||
@@ -289,7 +289,6 @@ impl StepContext {
|
||||
mode_instructions: None,
|
||||
},
|
||||
turn: Arc::clone(&turn),
|
||||
inherited_environments: environments.clone(),
|
||||
environments,
|
||||
selected_capability_roots: Vec::new(),
|
||||
executor_capability_discovery: None,
|
||||
|
||||
Reference in New Issue
Block a user