Skip to content

Commit c5b8161

Browse files
committed
app-server: preserve local environment cwd fallback
1 parent 6507c02 commit c5b8161

3 files changed

Lines changed: 32 additions & 6 deletions

File tree

‎codex-rs/app-server/src/request_processors.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ use codex_core_plugins::remote::RemotePluginShareContext as RemoteCatalogPluginS
368368
use codex_core_plugins::remote::RemotePluginShareSummary as RemoteCatalogPluginShareSummary;
369369
use codex_core_plugins::remote::RemotePluginSummary as RemoteCatalogPluginSummary;
370370
use codex_exec_server::EnvironmentManager;
371+
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
371372
use codex_exec_server::LOCAL_FS;
372373
use codex_features::FEATURES;
373374
use codex_features::Feature;

‎codex-rs/app-server/src/request_processors/turn_processor.rs‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,14 @@ impl TurnRequestProcessor {
590590
if let Some(environment_selections) = environment_selections {
591591
let legacy_fallback_cwd = match cwd {
592592
Some(cwd) => cwd,
593-
None => thread.config_snapshot().await.cwd().clone(),
593+
None => match environment_selections
594+
.iter()
595+
.find(|selection| selection.environment_id == LOCAL_ENVIRONMENT_ID)
596+
.and_then(|selection| selection.cwd.to_abs_path().ok())
597+
{
598+
Some(cwd) => cwd,
599+
None => thread.config_snapshot().await.cwd().clone(),
600+
},
594601
};
595602
return Some(TurnEnvironmentSelections::new(
596603
legacy_fallback_cwd,

‎codex-rs/app-server/tests/suite/v2/turn_start.rs‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use codex_app_server_protocol::ThreadDeletedNotification;
4848
use codex_app_server_protocol::ThreadItem;
4949
use codex_app_server_protocol::ThreadLoadedListParams;
5050
use codex_app_server_protocol::ThreadLoadedListResponse;
51+
use codex_app_server_protocol::ThreadSettingsUpdatedNotification;
5152
use codex_app_server_protocol::ThreadSource;
5253
use codex_app_server_protocol::ThreadStartParams;
5354
use codex_app_server_protocol::ThreadStartResponse;
@@ -64,6 +65,7 @@ use codex_app_server_protocol::WarningNotification;
6465
use codex_config::config_toml::ConfigToml;
6566
use codex_core::personality_migration::PERSONALITY_MIGRATION_FILENAME;
6667
use codex_core::test_support::all_model_presets;
68+
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
6769
use codex_features::FEATURES;
6870
use codex_features::Feature;
6971
use codex_protocol::config_types::CollaborationMode;
@@ -2532,7 +2534,7 @@ async fn turn_start_exec_approval_decline_v2() -> Result<()> {
25322534
}
25332535

25342536
#[tokio::test]
2535-
async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
2537+
async fn turn_start_explicit_local_environment_updates_legacy_cwd_between_turns() -> Result<()> {
25362538
// TODO(anp): Materialize cwd and shell-display fixtures in the selected remote environment.
25372539
skip_if_remote!(Ok(()), "cwd fixtures are only materialized on the host");
25382540
skip_if_no_network!(Ok(()));
@@ -2636,10 +2638,15 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
26362638
.await??;
26372639
mcp.clear_message_buffer();
26382640

2639-
// second turn with workspace-write and second_cwd, ensure exec begins in second_cwd
2641+
// Select a new local cwd without the top-level compatibility parameter. The inherited
2642+
// workspace-write sandbox must follow the local environment cwd.
26402643
let second_turn = mcp
26412644
.send_turn_start_request(TurnStartParams {
2642-
environments: None,
2645+
environments: Some(vec![TurnEnvironmentParams {
2646+
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
2647+
cwd: second_cwd.abs().into(),
2648+
runtime_workspace_roots: None,
2649+
}]),
26432650
thread_id: thread.id.clone(),
26442651
client_user_message_id: None,
26452652
input: vec![V2UserInput::Text {
@@ -2648,11 +2655,11 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
26482655
}],
26492656
responsesapi_client_metadata: None,
26502657
additional_context: None,
2651-
cwd: Some(second_cwd.clone()),
2658+
cwd: None,
26522659
runtime_workspace_roots: None,
26532660
approval_policy: Some(codex_app_server_protocol::AskForApproval::Never),
26542661
approvals_reviewer: None,
2655-
sandbox_policy: Some(codex_app_server_protocol::SandboxPolicy::DangerFullAccess),
2662+
sandbox_policy: None,
26562663
permissions: None,
26572664
model: Some("mock-model".to_string()),
26582665
effort: Some(ReasoningEffort::Medium),
@@ -2669,6 +2676,17 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
26692676
mcp.read_stream_until_response_message(RequestId::Integer(second_turn)),
26702677
)
26712678
.await??;
2679+
let settings_updated = timeout(
2680+
DEFAULT_READ_TIMEOUT,
2681+
mcp.read_stream_until_notification_message("thread/settings/updated"),
2682+
)
2683+
.await??;
2684+
let settings_updated: ThreadSettingsUpdatedNotification = serde_json::from_value(
2685+
settings_updated
2686+
.params
2687+
.context("thread/settings/updated should include params")?,
2688+
)?;
2689+
assert_eq!(settings_updated.thread_settings.cwd, second_cwd.abs());
26722690

26732691
let command_exec_item = timeout(DEFAULT_READ_TIMEOUT, async {
26742692
loop {

0 commit comments

Comments
 (0)