diff --git a/codex-rs/core/src/session/mcp.rs b/codex-rs/core/src/session/mcp.rs index 4a2e02f426..b984867a8f 100644 --- a/codex-rs/core/src/session/mcp.rs +++ b/codex-rs/core/src/session/mcp.rs @@ -204,8 +204,7 @@ impl Session { .iter() .chain( environments - .turn_environments - .iter() + .turn_environments() .flat_map(|environment| environment.environment.selected_capability_roots()), ) .enumerate() diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 9a10164534..c7ead4c21e 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -2849,22 +2849,12 @@ impl Session { self: &Arc, turn_context: Arc, ) -> Arc { - let deferred_executor_enabled = turn_context - .config - .features - .enabled(Feature::DeferredExecutor); - let environments = if deferred_executor_enabled { - // Keep selections fixed for the turn while allowing their startup work to finish. - turn_context.environments.refresh_readiness() - } else { - turn_context.environments.clone() - }; - if deferred_executor_enabled { - self.services - .agents_md_manager - .refresh(&turn_context.config, &environments) - .await; - } + // Keep selections fixed for the turn while allowing their startup work to finish. + let environments = turn_context.environments.refresh_readiness(); + self.services + .agents_md_manager + .refresh(&turn_context.config, &environments) + .await; let loaded_agents_md = self.services.agents_md_manager.get_loaded().await; let selected_capability_roots = self .resolve_selected_capability_roots_for_step(&environments) diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 889b1d59f4..808ad1f47d 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -7793,14 +7793,13 @@ async fn deferred_environment_roots_refresh_plugin_availability() { .primary() .expect("ready local environment"); let environments = TurnEnvironmentSnapshot { - turn_environments: vec![TurnEnvironment::new( + environments: vec![TurnEnvironmentState::Ready(TurnEnvironment::new( "executor".to_string(), environment, local_environment.cwd().clone(), local_environment.workspace_roots().to_vec(), local_environment.shell.clone(), - )], - starting: Vec::new(), + ))], }; let resolved_roots = session .resolve_selected_capability_roots_for_step(&environments) @@ -7873,8 +7872,10 @@ async fn conflicting_ready_environment_root_ids_keep_first_location() { )); } let environments = TurnEnvironmentSnapshot { - turn_environments, - starting: Vec::new(), + environments: turn_environments + .into_iter() + .map(TurnEnvironmentState::Ready) + .collect(), }; let resolved_roots = session diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index 72c7cba9e0..ce59f1fa99 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -259,15 +259,9 @@ pub(crate) async fn run_turn( ) .await?; - if turn_context - .config - .features - .enabled(Feature::DeferredExecutor) - { - world_state = sess - .record_step_world_state_if_changed(&world_state, step_context.as_ref()) - .await; - } + world_state = sess + .record_step_world_state_if_changed(&world_state, step_context.as_ref()) + .await; // Construct the input that we will send to the model. let sampling_request_input: Vec = async { diff --git a/codex-rs/core/tests/suite/mcp_tool_exposure.rs b/codex-rs/core/tests/suite/mcp_tool_exposure.rs index 51ab7842ee..954c590c7b 100644 --- a/codex-rs/core/tests/suite/mcp_tool_exposure.rs +++ b/codex-rs/core/tests/suite/mcp_tool_exposure.rs @@ -142,10 +142,6 @@ async fn apps_guidance_appears_after_background_recovery_within_a_turn() -> Resu .await; let mut builder = apps_enabled_builder(apps_server.chatgpt_base_url.clone()).with_config(|config| { - config - .features - .enable(Feature::DeferredExecutor) - .expect("test config should allow feature update"); config .features .enable(Feature::DefaultModeRequestUserInput) diff --git a/codex-rs/core/tests/suite/model_visible_layout.rs b/codex-rs/core/tests/suite/model_visible_layout.rs index 4e68303f2c..44b7c9b098 100644 --- a/codex-rs/core/tests/suite/model_visible_layout.rs +++ b/codex-rs/core/tests/suite/model_visible_layout.rs @@ -261,9 +261,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -// TODO(ccunningham): Diff `user_instructions` and emit updates when AGENTS.md content changes -// (for example after cwd changes), then update this test to assert refreshed AGENTS content. -async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> Result<()> { +async fn snapshot_model_visible_layout_cwd_change_refreshes_agents() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; @@ -371,18 +369,18 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R assert_eq!(requests.len(), 2, "expected two requests"); assert_eq!( user_instructions_wrapper_count(&requests[0]), - 0, - "expected first request to omit the serialized user-instructions wrapper when cwd-only project docs are introduced after session init" + 1, + "expected first request to include AGENTS.md from its selected cwd" ); assert_eq!( user_instructions_wrapper_count(&requests[1]), - 0, - "expected second request to keep omitting the serialized user-instructions wrapper after cwd change with the current session-scoped project doc behavior" + 2, + "expected second request to retain the original AGENTS.md item and append its replacement" ); insta::assert_snapshot!( - "model_visible_layout_cwd_change_does_not_refresh_agents", + "model_visible_layout_cwd_change_refreshes_agents", format_labeled_requests_snapshot( - "Second turn changes cwd to a directory with different AGENTS.md; current behavior does not emit refreshed AGENTS instructions.", + "Second turn changes cwd to a directory with different AGENTS.md and refreshes the model-visible instructions.", &[ ("First Request (agents_one)", &requests[0]), ("Second Request (agents_two cwd)", &requests[1]), diff --git a/codex-rs/core/tests/suite/remote_env.rs b/codex-rs/core/tests/suite/remote_env.rs index 6af56301ba..3d62201514 100644 --- a/codex-rs/core/tests/suite/remote_env.rs +++ b/codex-rs/core/tests/suite/remote_env.rs @@ -338,32 +338,37 @@ async fn explicit_remote_shell_runs_in_remote_cwd() -> Result<()> { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn deferred_executor_does_not_duplicate_initial_environment_context() -> Result<()> { - let server = start_mock_server().await; - let response_mock = mount_sse_once( - &server, - sse(vec![ - ev_response_created("resp-1"), - ev_assistant_message("msg-1", "done"), - ev_completed("resp-1"), - ]), - ) - .await; - let mut builder = test_codex().with_config(|config| { - assert!(config.features.enable(Feature::DeferredExecutor).is_ok()); - }); - let test = builder.build(&server).await?; +async fn step_world_state_does_not_duplicate_initial_environment_context() -> Result<()> { + for deferred_executor_enabled in [false, true] { + let server = start_mock_server().await; + let response_mock = mount_sse_once( + &server, + sse(vec![ + ev_response_created("resp-1"), + ev_assistant_message("msg-1", "done"), + ev_completed("resp-1"), + ]), + ) + .await; + let mut builder = test_codex().with_config(move |config| { + if deferred_executor_enabled { + assert!(config.features.enable(Feature::DeferredExecutor).is_ok()); + } + }); + let test = builder.build(&server).await?; - test.submit_turn("report the environment").await?; + test.submit_turn("report the environment").await?; - let user_context = response_mock.single_request().message_input_texts("user"); - assert_eq!( - user_context - .iter() - .filter(|text| text.contains("")) - .count(), - 1 - ); + let user_context = response_mock.single_request().message_input_texts("user"); + assert_eq!( + user_context + .iter() + .filter(|text| text.contains("")) + .count(), + 1, + "deferred executor enabled: {deferred_executor_enabled}", + ); + } Ok(()) } diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__model_visible_layout__model_visible_layout_cwd_change_does_not_refresh_agents.snap b/codex-rs/core/tests/suite/snapshots/all__suite__model_visible_layout__model_visible_layout_cwd_change_refreshes_agents.snap similarity index 54% rename from codex-rs/core/tests/suite/snapshots/all__suite__model_visible_layout__model_visible_layout_cwd_change_does_not_refresh_agents.snap rename to codex-rs/core/tests/suite/snapshots/all__suite__model_visible_layout__model_visible_layout_cwd_change_refreshes_agents.snap index 9efdd98f77..5285d6ea52 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__model_visible_layout__model_visible_layout_cwd_change_does_not_refresh_agents.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__model_visible_layout__model_visible_layout_cwd_change_refreshes_agents.snap @@ -1,22 +1,28 @@ --- source: core/tests/suite/model_visible_layout.rs -expression: "format_labeled_requests_snapshot(\"Second turn changes cwd to a directory with different AGENTS.md; current behavior does not emit refreshed AGENTS instructions.\",\n&[(\"First Request (agents_one)\", &requests[0]),\n(\"Second Request (agents_two cwd)\", &requests[1]),])" +expression: "format_labeled_requests_snapshot(\"Second turn changes cwd to a directory with different AGENTS.md and refreshes the model-visible instructions.\",\n&[(\"First Request (agents_one)\", &requests[0]),\n(\"Second Request (agents_two cwd)\", &requests[1]),])" --- -Scenario: Second turn changes cwd to a directory with different AGENTS.md; current behavior does not emit refreshed AGENTS instructions. +Scenario: Second turn changes cwd to a directory with different AGENTS.md and refreshes the model-visible instructions. ## First Request (agents_one) 00:message/developer[2]: [01] [02] -01:message/user:> +01:message/user[2]: + [01] + [02] > 02:message/user:first turn in agents_one ## Second Request (agents_two cwd) 00:message/developer[2]: [01] [02] -01:message/user:> +01:message/user[2]: + [01] + [02] > 02:message/user:first turn in agents_one 03:message/assistant:turn one complete -04:message/user:> +04:message/user[2]: + [01] + [02] > 05:message/user:second turn in agents_two