From 4454e1411b82dd755e487e4b7ecd4d949c0ed336 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 13 May 2026 11:15:25 -0700 Subject: [PATCH] Deprecate TurnContext cwd and resolve_path (#22519) ## Why `TurnContext::cwd` and `TurnContext::resolve_path` are being phased out in favor of using the selected turn environment cwd directly. Deprecating both APIs makes any new direct dependency visible while preserving the existing migration path for current callers. ## What Changed - Marked `TurnContext::cwd` and `TurnContext::resolve_path` as deprecated with guidance to use the selected turn environment cwd instead. - Added exact `#[allow(deprecated)]` suppressions at each existing direct usage site, including tests, rather than adding crate-wide suppression. - Kept the change behavior-preserving: current cwd reads, writes, and path resolution continue to use the same values. ## Verification - `just fmt` - `cargo check -p codex-core` - `cargo check -p codex-core --tests` - `git diff --check` --- codex-rs/core/src/codex_delegate.rs | 11 ++++- codex-rs/core/src/codex_delegate_tests.rs | 1 + codex-rs/core/src/context_manager/updates.rs | 1 + codex-rs/core/src/guardian/review_session.rs | 2 + codex-rs/core/src/hook_runtime.rs | 7 +++ codex-rs/core/src/mcp_openai_file.rs | 16 +++++-- codex-rs/core/src/mcp_tool_call.rs | 1 + codex-rs/core/src/mcp_tool_call_tests.rs | 18 ++++++-- codex-rs/core/src/memory_usage.rs | 21 ++++----- codex-rs/core/src/session/handlers.rs | 1 + codex-rs/core/src/session/mcp.rs | 1 + codex-rs/core/src/session/mod.rs | 3 ++ codex-rs/core/src/session/review.rs | 2 + .../session/rollout_reconstruction_tests.rs | 8 ++++ codex-rs/core/src/session/tests.rs | 45 ++++++++++++++----- .../core/src/session/tests/guardian_tests.rs | 12 +++-- codex-rs/core/src/session/turn.rs | 5 +++ codex-rs/core/src/session/turn_context.rs | 9 ++++ codex-rs/core/src/tasks/user_shell.rs | 2 + .../agent_jobs/spawn_agents_on_csv.rs | 6 ++- .../src/tools/handlers/multi_agents_common.rs | 4 +- .../src/tools/handlers/multi_agents_tests.rs | 22 ++++++--- .../src/tools/handlers/request_permissions.rs | 1 + codex-rs/core/src/tools/handlers/shell.rs | 2 + .../src/tools/handlers/shell/shell_command.rs | 6 ++- .../core/src/tools/handlers/shell_tests.rs | 1 + .../core/src/tools/handlers/view_image.rs | 5 ++- codex-rs/core/src/tools/network_approval.rs | 1 + codex-rs/core/src/tools/orchestrator.rs | 1 + codex-rs/core/src/tools/registry.rs | 1 + codex-rs/core/src/unified_exec/mod_tests.rs | 9 +++- .../src/unified_exec/process_manager_tests.rs | 3 ++ 32 files changed, 183 insertions(+), 45 deletions(-) diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 5a01384bf0..493771e28c 100644 --- a/codex-rs/core/src/codex_delegate.rs +++ b/codex-rs/core/src/codex_delegate.rs @@ -530,7 +530,10 @@ async fn handle_patch_approval( let guardian_decision = if routes_approval_to_guardian(parent_ctx) { let files = changes .keys() - .map(|path| parent_ctx.cwd.join(path)) + .map(|path| { + #[allow(deprecated)] + parent_ctx.cwd.join(path) + }) .collect::>(); let review_cancel = cancel_token.child_token(); let patch = changes @@ -566,6 +569,7 @@ async fn handle_patch_approval( new_guardian_review_id(), GuardianApprovalRequest::ApplyPatch { id: approval_id.clone(), + #[allow(deprecated)] cwd: parent_ctx.cwd.clone(), files, patch, @@ -739,7 +743,10 @@ async fn handle_request_permissions( reason: event.reason, permissions: event.permissions, }; - let cwd = event.cwd.unwrap_or_else(|| parent_ctx.cwd.clone()); + let cwd = event.cwd.unwrap_or_else(|| { + #[allow(deprecated)] + parent_ctx.cwd.clone() + }); let response_fut = parent_session.request_permissions_for_cwd( parent_ctx, call_id.clone(), diff --git a/codex-rs/core/src/codex_delegate_tests.rs b/codex-rs/core/src/codex_delegate_tests.rs index ecd392e3e7..66cde8d1ea 100644 --- a/codex-rs/core/src/codex_delegate_tests.rs +++ b/codex-rs/core/src/codex_delegate_tests.rs @@ -207,6 +207,7 @@ async fn handle_request_permissions_uses_tool_call_id_for_round_trip() { scope: PermissionGrantScope::Turn, strict_auto_review: false, }; + #[allow(deprecated)] let delegated_cwd = parent_ctx.cwd.join("delegated-cwd"); let cancel_token = CancellationToken::new(); let request_call_id = call_id.clone(); diff --git a/codex-rs/core/src/context_manager/updates.rs b/codex-rs/core/src/context_manager/updates.rs index 0cdbc0b010..d7302bbbf8 100644 --- a/codex-rs/core/src/context_manager/updates.rs +++ b/codex-rs/core/src/context_manager/updates.rs @@ -61,6 +61,7 @@ fn build_permissions_update_item( next.approval_policy.value(), next.config.approvals_reviewer, exec_policy, + #[allow(deprecated)] &next.cwd, next.features.enabled(Feature::ExecPermissionApprovals), next.features.enabled(Feature::RequestPermissionsTool), diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index a419d7cbfa..d3fae0f2e5 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -708,6 +708,7 @@ async fn run_review_on_session( Box::pin(review_session.codex.submit(Op::UserTurn { environments: None, items: prompt_items.items, + #[allow(deprecated)] cwd: params.parent_turn.cwd.to_path_buf(), approval_policy: AskForApproval::Never, approvals_reviewer: None, @@ -1086,6 +1087,7 @@ mod tests { let reasoning_effort = turn.reasoning_effort; let reasoning_summary = turn.reasoning_summary; let personality = turn.personality; + #[allow(deprecated)] let cwd = turn.cwd.clone(); let spawn_config = build_guardian_review_session_config( turn.config.as_ref(), diff --git a/codex-rs/core/src/hook_runtime.rs b/codex-rs/core/src/hook_runtime.rs index 3d72e4a7c6..78fc24833e 100644 --- a/codex-rs/core/src/hook_runtime.rs +++ b/codex-rs/core/src/hook_runtime.rs @@ -116,6 +116,7 @@ pub(crate) async fn run_pending_session_start_hooks( let request = codex_hooks::SessionStartRequest { session_id: sess.session_id().into(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -150,6 +151,7 @@ pub(crate) async fn run_pre_tool_use_hooks( let request = PreToolUseRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -209,6 +211,7 @@ pub(crate) async fn run_permission_request_hooks( let request = PermissionRequestRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -249,6 +252,7 @@ pub(crate) async fn run_post_tool_use_hooks( let request = PostToolUseRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -276,6 +280,7 @@ pub(crate) async fn run_pre_compact_hooks( let request = codex_hooks::PreCompactRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -313,6 +318,7 @@ pub(crate) async fn run_post_compact_hooks( let request = codex_hooks::PostCompactRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -338,6 +344,7 @@ pub(crate) async fn run_user_prompt_submit_hooks( let request = UserPromptSubmitRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), diff --git a/codex-rs/core/src/mcp_openai_file.rs b/codex-rs/core/src/mcp_openai_file.rs index 0e0d4a6008..ae44515c62 100644 --- a/codex-rs/core/src/mcp_openai_file.rs +++ b/codex-rs/core/src/mcp_openai_file.rs @@ -102,6 +102,7 @@ async fn build_uploaded_local_argument_value( index: Option, file_path: &str, ) -> Result { + #[allow(deprecated)] let resolved_path = turn_context.resolve_path(Some(file_path.to_string())); let Some(auth) = auth else { return Err( @@ -216,7 +217,10 @@ mod tests { tokio::fs::write(&local_path, b"hello") .await .expect("write local file"); - turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + #[allow(deprecated)] + { + turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + } let mut config = (*turn_context.config).clone(); config.chatgpt_base_url = format!("{}/backend-api", server.uri()); @@ -297,7 +301,10 @@ mod tests { tokio::fs::write(&local_path, b"hello") .await .expect("write local file"); - turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + #[allow(deprecated)] + { + turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + } let mut config = (*turn_context.config).clone(); config.chatgpt_base_url = format!("{}/backend-api", server.uri()); @@ -411,7 +418,10 @@ mod tests { tokio::fs::write(dir.path().join("two.csv"), b"two") .await .expect("write second local file"); - turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + #[allow(deprecated)] + { + turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + } let mut config = (*turn_context.config).clone(); config.chatgpt_base_url = format!("{}/backend-api", server.uri()); diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 8855aae62e..d1faa421d9 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -732,6 +732,7 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state( permission_profile: Some(turn_context.permission_profile()), sandbox_policy: turn_context.sandbox_policy(), codex_linux_sandbox_exe: turn_context.codex_linux_sandbox_exe.clone(), + #[allow(deprecated)] sandbox_cwd: turn_context.cwd.to_path_buf(), use_legacy_landlock: turn_context.features.use_legacy_landlock(), })?; diff --git a/codex-rs/core/src/mcp_tool_call_tests.rs b/codex-rs/core/src/mcp_tool_call_tests.rs index 65f8977283..8b4f1ba223 100644 --- a/codex-rs/core/src/mcp_tool_call_tests.rs +++ b/codex-rs/core/src/mcp_tool_call_tests.rs @@ -1221,7 +1221,10 @@ async fn install_host_owned_codex_apps_manager(session: &Session, turn_context: turn_context.sub_id.clone(), session.get_tx_event(), turn_context.permission_profile(), - codex_mcp::McpRuntimeEnvironment::new(environment, turn_context.cwd.to_path_buf()), + codex_mcp::McpRuntimeEnvironment::new(environment, { + #[allow(deprecated)] + turn_context.cwd.to_path_buf() + }), turn_context.config.codex_home.to_path_buf(), codex_mcp::codex_apps_tools_cache_key(auth.as_ref()), /*host_owned_codex_apps_enabled*/ true, @@ -2230,7 +2233,10 @@ async fn maybe_persist_mcp_tool_approval_writes_project_config_for_project_serve .build() .await .expect("load project config"); - turn_context.cwd = config.cwd.clone(); + #[allow(deprecated)] + { + turn_context.cwd = config.cwd.clone(); + } turn_context.config = Arc::new(config); let key = McpToolApprovalKey { server: "docs".to_string(), @@ -2431,12 +2437,14 @@ async fn permission_request_hook_allows_mcp_tool_call() { .lines() .map(|line| serde_json::from_str::(line).expect("parse hook input")) .collect::>(); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); assert_eq!( inputs, vec![serde_json::json!({ "session_id": session.session_id(), "turn_id": "turn_id", - "cwd": turn_context.cwd, + "cwd": turn_cwd, "transcript_path": null, "model": turn_context.model_info.slug, "permission_mode": "default", @@ -2491,12 +2499,14 @@ async fn permission_request_hook_uses_hook_tool_name_without_metadata() { .lines() .map(|line| serde_json::from_str::(line).expect("parse hook input")) .collect::>(); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); assert_eq!( inputs, vec![serde_json::json!({ "session_id": session.session_id(), "turn_id": "turn_id", - "cwd": turn_context.cwd, + "cwd": turn_cwd, "transcript_path": null, "model": turn_context.model_info.slug, "permission_mode": "default", diff --git a/codex-rs/core/src/memory_usage.rs b/codex-rs/core/src/memory_usage.rs index bd856d5e6f..b5bc6eef53 100644 --- a/codex-rs/core/src/memory_usage.rs +++ b/codex-rs/core/src/memory_usage.rs @@ -44,10 +44,9 @@ fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec Option<(Vec serde_json::from_str::(arguments) .ok() @@ -71,10 +69,9 @@ fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec None, } diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index ab40cb8eed..5cb37f9828 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -699,6 +699,7 @@ pub async fn review( .await; sess.refresh_mcp_servers_if_requested(&turn_context, Some(sess.mcp_elicitation_reviewer())) .await; + #[allow(deprecated)] match resolve_review_request(review_request, &turn_context.cwd) { Ok(resolved) => { spawn_review_thread( diff --git a/codex-rs/core/src/session/mcp.rs b/codex-rs/core/src/session/mcp.rs index d32ecf2b19..fcaaa17c57 100644 --- a/codex-rs/core/src/session/mcp.rs +++ b/codex-rs/core/src/session/mcp.rs @@ -296,6 +296,7 @@ impl Session { .environment_manager .default_environment() .unwrap_or_else(|| self.services.environment_manager.local_environment()), + #[allow(deprecated)] turn_context.cwd.to_path_buf(), ), }; diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 3ef9466812..fd3f31771d 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -2051,6 +2051,7 @@ impl Session { turn_context, call_id, args, + #[allow(deprecated)] turn_context.cwd.clone(), cancellation_token, ) @@ -2630,6 +2631,7 @@ impl Session { turn_context.approval_policy.value(), turn_context.config.approvals_reviewer, self.services.exec_policy.current().as_ref(), + #[allow(deprecated)] &turn_context.cwd, turn_context .features @@ -2763,6 +2765,7 @@ impl Session { contextual_user_sections.push( UserInstructions { text: user_instructions.to_string(), + #[allow(deprecated)] directory: turn_context.cwd.to_string_lossy().into_owned(), } .render(), diff --git a/codex-rs/core/src/session/review.rs b/codex-rs/core/src/session/review.rs index 083aac8c04..e84465508a 100644 --- a/codex-rs/core/src/session/review.rs +++ b/codex-rs/core/src/session/review.rs @@ -107,6 +107,7 @@ pub(super) async fn spawn_review_thread( sess.thread_id().to_string(), parent_turn_context.thread_source, review_turn_id.clone(), + #[allow(deprecated)] parent_turn_context.cwd.clone(), &parent_turn_context.permission_profile, parent_turn_context.windows_sandbox_level, @@ -143,6 +144,7 @@ pub(super) async fn spawn_review_thread( network: parent_turn_context.network.clone(), windows_sandbox_level: parent_turn_context.windows_sandbox_level, shell_environment_policy: parent_turn_context.shell_environment_policy.clone(), + #[allow(deprecated)] cwd: parent_turn_context.cwd.clone(), final_output_json_schema: None, codex_self_exe: parent_turn_context.codex_self_exe.clone(), diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index 5cfcc38053..143b23d3a3 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -60,6 +60,7 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -101,6 +102,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif let mut previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -911,6 +913,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -989,6 +992,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis serde_json::to_value(Some(TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1020,6 +1024,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1135,6 +1140,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo let current_context_item = TurnContextItem { turn_id: Some(current_turn_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1249,6 +1255,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1401,6 +1408,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 9ef67bcb8e..577b2fa5ec 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -2216,6 +2216,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() { let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -3769,6 +3770,7 @@ async fn session_configuration_apply_preserves_absolute_cwd_write_root_on_cwd_up #[tokio::test] async fn session_update_settings_does_not_rewrite_sticky_environment_cwds() { let (session, turn_context) = make_session_and_context().await; + #[allow(deprecated)] let updated_cwd = turn_context.cwd.join("project"); std::fs::create_dir_all(updated_cwd.as_path()).expect("create project dir"); @@ -3788,8 +3790,12 @@ async fn session_update_settings_does_not_rewrite_sticky_environment_cwds() { let next_turn = session.new_default_turn().await; assert_eq!(session_cwd, updated_cwd); - assert_eq!(config.cwd, turn_context.cwd); - assert_eq!(next_turn.cwd, updated_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + #[allow(deprecated)] + let next_turn_cwd = next_turn.cwd.clone(); + assert_eq!(config.cwd, turn_cwd); + assert_eq!(next_turn_cwd, updated_cwd); assert_eq!(next_turn.config.cwd, updated_cwd); } @@ -3873,7 +3879,9 @@ async fn absolute_cwd_update_with_turn_environment_is_allowed() { .await .expect("absolute cwd with explicit environments should succeed"); - assert_eq!(turn_context.cwd, absolute_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, absolute_cwd); assert_eq!(turn_context.config.cwd, absolute_cwd); assert_eq!(turn_context.environments.turn_environments.len(), 1); } @@ -4674,7 +4682,9 @@ async fn request_permissions_emits_event_when_granular_policy_allows_requests() panic!("expected request_permissions event"); }; assert_eq!(request.call_id, call_id); - assert_eq!(request.cwd, Some(turn_context.cwd.clone())); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(request.cwd, Some(turn_cwd)); session .notify_request_permissions_response(&request.call_id, expected_response.clone()) @@ -5103,7 +5113,9 @@ async fn turn_environments_set_primary_environment() { &turn_environments.turn_environments[0].environment )); assert!(!turn_context.environments.turn_environments.is_empty()); - assert_eq!(turn_context.cwd.as_path(), selected_cwd.as_path()); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd.as_path(), selected_cwd.as_path()); assert_eq!(turn_context.config.cwd.as_path(), selected_cwd.as_path()); } @@ -5134,7 +5146,9 @@ async fn default_turn_overlays_session_cwd_onto_stored_thread_environments() { &turn_environment.environment, &turn_environments.turn_environments[0].environment )); - assert_eq!(turn_context.cwd, session_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, session_cwd); assert_eq!(turn_context.config.cwd, session_cwd); } @@ -5152,7 +5166,9 @@ async fn default_turn_honors_empty_stored_thread_environments() { assert!(turn_context.environments.primary().is_none()); assert!(turn_context.environments.turn_environments.is_empty()); - assert_eq!(turn_context.cwd, session_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, session_cwd); assert_eq!(turn_context.config.cwd, session_cwd); assert_eq!(turn_context.environments.turn_environments.len(), 0); } @@ -5161,6 +5177,7 @@ async fn default_turn_honors_empty_stored_thread_environments() { async fn primary_environment_uses_first_turn_environment() { let (_session, mut turn_context) = make_session_and_context().await; let first_environment = turn_context.environments.turn_environments[0].clone(); + #[allow(deprecated)] let second_cwd = turn_context.cwd.join("second"); turn_context .environments @@ -5214,7 +5231,9 @@ async fn empty_turn_environments_clear_primary_environment() { assert!(turn_context.environments.primary().is_none()); assert!(turn_context.environments.turn_environments.is_empty()); - assert_eq!(turn_context.cwd, session.get_config().await.cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, session.get_config().await.cwd); assert_eq!(turn_context.config.cwd, session.get_config().await.cwd); } @@ -7033,13 +7052,16 @@ async fn build_initial_context_restates_realtime_start_when_reference_context_is } fn file_system_policy_with_unreadable_glob(turn_context: &TurnContext) -> FileSystemSandboxPolicy { + #[allow(deprecated)] let mut policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( &turn_context.sandbox_policy(), &turn_context.cwd, ); + #[allow(deprecated)] + let cwd_display = turn_context.cwd.as_path().display().to_string(); policy.entries.push(FileSystemSandboxEntry { path: FileSystemPath::GlobPattern { - pattern: format!("{}/**/*.env", turn_context.cwd.as_path().display()), + pattern: format!("{cwd_display}/**/*.env"), }, access: FileSystemAccessMode::None, }); @@ -9381,6 +9403,8 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() { let call_id = "test-call".to_string(); let handler = ShellCommandHandler::from(ShellCommandBackendConfig::Classic); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -9393,7 +9417,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() { payload: ToolPayload::Function { arguments: serde_json::json!({ "command": command_script, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, "timeout_ms": timeout_ms, "sandbox_permissions": sandbox_permissions, "justification": Some("test"), @@ -9433,6 +9457,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() { approval_policy: turn_context.approval_policy.value(), permission_profile: turn_context.permission_profile(), file_system_sandbox_policy: &file_system_sandbox_policy, + #[allow(deprecated)] sandbox_cwd: turn_context.cwd.as_path(), sandbox_permissions: SandboxPermissions::UseDefault, prefix_rule: None, diff --git a/codex-rs/core/src/session/tests/guardian_tests.rs b/codex-rs/core/src/session/tests/guardian_tests.rs index ffb0d94d80..718b9bf05f 100644 --- a/codex-rs/core/src/session/tests/guardian_tests.rs +++ b/codex-rs/core/src/session/tests/guardian_tests.rs @@ -291,6 +291,8 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli let handler = crate::tools::handlers::ShellCommandHandler::from( codex_tools::ShellCommandBackendConfig::Classic, ); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -304,7 +306,7 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli arguments: serde_json::json!({ "command": "echo hi", "login": false, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, "timeout_ms": expiration_ms, "sandbox_permissions": SandboxPermissions::WithAdditionalPermissions, "additional_permissions": PermissionProfile { @@ -392,6 +394,8 @@ async fn strict_auto_review_turn_grant_forces_guardian_for_shell_command_policy_ let handler = crate::tools::handlers::ShellCommandHandler::from( codex_tools::ShellCommandBackendConfig::Classic, ); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -405,7 +409,7 @@ async fn strict_auto_review_turn_grant_forces_guardian_for_shell_command_policy_ arguments: serde_json::json!({ "command": "echo hi", "login": false, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, "timeout_ms": 1_000_u64, }) .to_string(), @@ -558,6 +562,8 @@ async fn shell_command_allows_sticky_turn_permissions_without_inline_request_per let handler = crate::tools::handlers::ShellCommandHandler::from( codex_tools::ShellCommandBackendConfig::Classic, ); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -572,7 +578,7 @@ async fn shell_command_allows_sticky_turn_permissions_without_inline_request_per "command": "echo hi", "login": false, "timeout_ms": 1_000_u64, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, }) .to_string(), }, diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index 2dd84213ed..d910e03b21 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -366,6 +366,7 @@ pub(crate) async fn run_turn( let mut stop_hook_active = false; // Although from the perspective of codex.rs, TurnDiffTracker has the lifecycle of a Task which contains // many turns, from the perspective of the user, it is a single turn. + #[allow(deprecated)] let display_root = get_git_repo_root(turn_context.cwd.as_path()) .unwrap_or_else(|| turn_context.cwd.clone().into_path_buf()); let turn_diff_tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::with_display_root( @@ -524,6 +525,7 @@ pub(crate) async fn run_turn( let stop_request = codex_hooks::StopRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -573,6 +575,7 @@ pub(crate) async fn run_turn( .hooks() .dispatch(HookPayload { session_id: sess.session_id().into(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), client: turn_context.app_server_client_name.clone(), triggered_at: chrono::Utc::now(), @@ -697,6 +700,7 @@ async fn track_turn_resolved_config_analytics( model: turn_context.model_info.slug.clone(), model_provider: turn_context.config.model_provider_id.clone(), permission_profile: turn_context.permission_profile(), + #[allow(deprecated)] permission_profile_cwd: turn_context.cwd.to_path_buf(), reasoning_effort: turn_context.reasoning_effort, reasoning_summary: Some(turn_context.reasoning_summary), @@ -993,6 +997,7 @@ pub(crate) fn build_prompt( } #[allow(clippy::too_many_arguments)] +#[allow(deprecated)] #[instrument(level = "trace", skip_all, fields( diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index 7b22ddcc39..8caec7aaea 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -69,6 +69,7 @@ pub struct TurnContext { /// The session's absolute working directory. All relative paths provided /// by the model as well as sandbox policies are resolved against this path /// instead of `std::env::current_dir()`. + #[deprecated(note = "use the selected turn environment cwd instead")] pub(crate) cwd: AbsolutePathBuf, pub(crate) current_date: Option, pub(crate) timezone: Option, @@ -118,6 +119,7 @@ impl TurnContext { &self.permission_profile, &file_system_sandbox_policy, network_sandbox_policy, + #[allow(deprecated)] &self.cwd, ) } @@ -253,6 +255,7 @@ impl TurnContext { session_source: self.session_source.clone(), thread_source: self.thread_source, environments: self.environments.clone(), + #[allow(deprecated)] cwd: self.cwd.clone(), current_date: self.current_date.clone(), timezone: self.timezone.clone(), @@ -288,7 +291,9 @@ impl TurnContext { } } + #[deprecated(note = "resolve paths from the selected turn environment cwd instead")] pub(crate) fn resolve_path(&self, path: Option) -> AbsolutePathBuf { + #[allow(deprecated)] path.as_ref() .map_or_else(|| self.cwd.clone(), |path| self.cwd.join(path)) } @@ -314,6 +319,7 @@ impl TurnContext { ); FileSystemSandboxContext { permissions, + #[allow(deprecated)] cwd: Some(self.cwd.clone()), windows_sandbox_level: self.windows_sandbox_level, windows_sandbox_private_desktop: self @@ -332,6 +338,7 @@ impl TurnContext { let legacy_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( &self.sandbox_policy(), + #[allow(deprecated)] &self.cwd, ); let file_system_sandbox_policy = self.file_system_sandbox_policy(); @@ -349,6 +356,7 @@ impl TurnContext { TurnContextItem { turn_id: Some(self.sub_id.clone()), trace_id: self.trace_id.clone(), + #[allow(deprecated)] cwd: self.cwd.to_path_buf(), current_date: self.current_date.clone(), timezone: self.timezone.clone(), @@ -566,6 +574,7 @@ impl Session { session_source, thread_source: session_configuration.thread_source, environments, + #[allow(deprecated)] cwd, current_date: Some(current_date), timezone: Some(timezone), diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index 683856b90e..23f3882eda 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -148,6 +148,7 @@ pub(crate) async fn execute_user_shell_command( let exec_command = maybe_wrap_shell_lc_with_snapshot( &display_command, session_shell.as_ref(), + #[allow(deprecated)] &turn_context.cwd, &turn_context.shell_environment_policy.r#set, &exec_env_map, @@ -155,6 +156,7 @@ pub(crate) async fn execute_user_shell_command( let call_id = Uuid::new_v4().to_string(); let raw_command = command; + #[allow(deprecated)] let cwd = turn_context.cwd.clone(); let parsed_cmd = parse_command(&display_command); diff --git a/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs b/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs index 6dd7ea2b31..e1a37be6b6 100644 --- a/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs +++ b/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs @@ -68,6 +68,7 @@ pub async fn handle( } let db = required_state_db(&session)?; + #[allow(deprecated)] let input_path = turn.resolve_path(Some(args.csv_path)); let input_path_display = input_path.display().to_string(); let csv_content = tokio::fs::read_to_string(&input_path) @@ -141,7 +142,10 @@ pub async fn handle( let job_id = Uuid::new_v4().to_string(); let output_csv_path = args.output_csv_path.map_or_else( || default_output_csv_path(&input_path, job_id.as_str()), - |path| turn.resolve_path(Some(path)), + |path| { + #[allow(deprecated)] + turn.resolve_path(Some(path)) + }, ); let job_suffix = &job_id[..8]; let job_name = format!("agent-job-{job_suffix}"); diff --git a/codex-rs/core/src/tools/handlers/multi_agents_common.rs b/codex-rs/core/src/tools/handlers/multi_agents_common.rs index cc4e9ebfca..216a420e32 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_common.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_common.rs @@ -268,7 +268,9 @@ pub(crate) fn apply_spawn_agent_runtime_overrides( })?; config.permissions.shell_environment_policy = turn.shell_environment_policy.clone(); config.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone(); - config.cwd = turn.cwd.clone(); + #[allow(deprecated)] + let turn_cwd = turn.cwd.clone(); + config.cwd = turn_cwd; config .permissions .set_permission_profile(turn.permission_profile()) diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index f051fb0f70..1a58561737 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -2088,6 +2088,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() { let manager = thread_manager(); session.services.agent_control = manager.agent_control(); let expected_sandbox = turn.config.legacy_sandbox_policy(); + #[allow(deprecated)] let mut expected_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&expected_sandbox, &turn.cwd); expected_file_system_sandbox_policy @@ -3767,15 +3768,20 @@ async fn build_agent_spawn_config_uses_turn_context_values() { ..ShellEnvironmentPolicy::default() }; let temp_dir = tempfile::tempdir().expect("temp dir"); - turn.cwd = temp_dir.abs(); + #[allow(deprecated)] + { + turn.cwd = temp_dir.abs(); + } turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo")); + #[allow(deprecated)] + let turn_cwd = turn.cwd.clone(); let sandbox_policy = pick_allowed_sandbox_policy( &turn.config.permissions.permission_profile, turn.config.legacy_sandbox_policy(), - turn.cwd.as_path(), + turn_cwd.as_path(), ); let file_system_sandbox_policy = - FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, &turn.cwd); + FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, &turn_cwd); let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy); let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement( SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy), @@ -3798,7 +3804,10 @@ async fn build_agent_spawn_config_uses_turn_context_values() { expected.compact_prompt = turn.compact_prompt.clone(); expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone(); expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone(); - expected.cwd = turn.cwd.clone(); + #[allow(deprecated)] + { + expected.cwd = turn.cwd.clone(); + } expected .permissions .approval_policy @@ -3849,7 +3858,10 @@ async fn build_agent_resume_config_clears_base_instructions() { expected.compact_prompt = turn.compact_prompt.clone(); expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone(); expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone(); - expected.cwd = turn.cwd.clone(); + #[allow(deprecated)] + { + expected.cwd = turn.cwd.clone(); + } expected .permissions .approval_policy diff --git a/codex-rs/core/src/tools/handlers/request_permissions.rs b/codex-rs/core/src/tools/handlers/request_permissions.rs index 243ed52d15..5d49ad861b 100644 --- a/codex-rs/core/src/tools/handlers/request_permissions.rs +++ b/codex-rs/core/src/tools/handlers/request_permissions.rs @@ -47,6 +47,7 @@ impl ToolExecutor for RequestPermissionsHandler { } }; + #[allow(deprecated)] let mut args: RequestPermissionsArgs = parse_arguments_with_base_path(&arguments, &turn.cwd)?; args.permissions = normalize_additional_permissions(args.permissions.into()) diff --git a/codex-rs/core/src/tools/handlers/shell.rs b/codex-rs/core/src/tools/handlers/shell.rs index e6ce6908f3..84f400854d 100644 --- a/codex-rs/core/src/tools/handlers/shell.rs +++ b/codex-rs/core/src/tools/handlers/shell.rs @@ -92,6 +92,7 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result Result for ShellCommandHandler { ))); }; + #[allow(deprecated)] let cwd = resolve_workdir_base_path(&arguments, &turn.cwd)?; let params: ShellCommandToolCallParams = parse_arguments_with_base_path(&arguments, &cwd)?; + #[allow(deprecated)] let workdir = turn.resolve_path(params.workdir.clone()); maybe_emit_implicit_skill_invocation( session.as_ref(), diff --git a/codex-rs/core/src/tools/handlers/shell_tests.rs b/codex-rs/core/src/tools/handlers/shell_tests.rs index 9db561d577..660bac6789 100644 --- a/codex-rs/core/src/tools/handlers/shell_tests.rs +++ b/codex-rs/core/src/tools/handlers/shell_tests.rs @@ -88,6 +88,7 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex let expected_command = session .user_shell() .derive_exec_args(&command, /*use_login_shell*/ true); + #[allow(deprecated)] let expected_cwd = turn_context.resolve_path(workdir.clone()); let expected_env = create_env( &turn_context.shell_environment_policy, diff --git a/codex-rs/core/src/tools/handlers/view_image.rs b/codex-rs/core/src/tools/handlers/view_image.rs index 3efbedc0ee..bb74adb86d 100644 --- a/codex-rs/core/src/tools/handlers/view_image.rs +++ b/codex-rs/core/src/tools/handlers/view_image.rs @@ -282,7 +282,10 @@ mod tests { let (session, mut turn) = make_session_and_context().await; let image_dir = tempfile::tempdir().expect("create image temp dir"); let image_cwd = image_dir.abs(); - turn.cwd = image_cwd.clone(); + #[allow(deprecated)] + { + turn.cwd = image_cwd.clone(); + } turn.environments .turn_environments .first_mut() diff --git a/codex-rs/core/src/tools/network_approval.rs b/codex-rs/core/src/tools/network_approval.rs index 14af2c9c5f..39441d96f5 100644 --- a/codex-rs/core/src/tools/network_approval.rs +++ b/codex-rs/core/src/tools/network_approval.rs @@ -525,6 +525,7 @@ impl NetworkApprovalService { guardian_approval_id, /*approval_id*/ None, prompt_command, + #[allow(deprecated)] turn_context.cwd.clone(), Some(prompt_reason), Some(network_approval_context.clone()), diff --git a/codex-rs/core/src/tools/orchestrator.rs b/codex-rs/core/src/tools/orchestrator.rs index 648d3f79e8..deb9ae596e 100644 --- a/codex-rs/core/src/tools/orchestrator.rs +++ b/codex-rs/core/src/tools/orchestrator.rs @@ -234,6 +234,7 @@ impl ToolOrchestrator { // Platform-specific flag gating is handled by SandboxManager::select_initial. let use_legacy_landlock = turn_ctx.features.use_legacy_landlock(); + #[allow(deprecated)] let sandbox_cwd = tool.sandbox_cwd(req).unwrap_or(&turn_ctx.cwd); let initial_attempt = SandboxAttempt { sandbox: initial_sandbox, diff --git a/codex-rs/core/src/tools/registry.rs b/codex-rs/core/src/tools/registry.rs index 85c346f48f..6486e7db71 100644 --- a/codex-rs/core/src/tools/registry.rs +++ b/codex-rs/core/src/tools/registry.rs @@ -409,6 +409,7 @@ impl ToolRegistry { "sandbox_policy", permission_profile_policy_tag( &invocation.turn.permission_profile, + #[allow(deprecated)] invocation.turn.cwd.as_path(), ), ), diff --git a/codex-rs/core/src/unified_exec/mod_tests.rs b/codex-rs/core/src/unified_exec/mod_tests.rs index f8a1480af8..6926f9cbf1 100644 --- a/codex-rs/core/src/unified_exec/mod_tests.rs +++ b/codex-rs/core/src/unified_exec/mod_tests.rs @@ -83,6 +83,7 @@ async fn exec_command_with_tty( ) -> Result { let manager = &session.services.unified_exec_manager; let process_id = manager.allocate_process_id().await; + #[allow(deprecated)] let cwd = workdir .as_ref() .map_or_else(|| turn.cwd.clone(), |workdir| turn.cwd.join(workdir)); @@ -501,10 +502,12 @@ async fn reusing_completed_process_returns_unknown_process() -> anyhow::Result<( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> { let (_, turn) = make_session_and_context().await; + #[allow(deprecated)] + let cwd = turn.cwd.clone(); let request = test_exec_request( &turn, vec!["bash".to_string(), "-lc".to_string(), "exit 17".to_string()], - turn.cwd.clone(), + cwd, shell_env(), ); @@ -598,10 +601,12 @@ async fn remote_exec_server_rejects_inherited_fd_launches() -> anyhow::Result<() turn.environments.turn_environments[0].environment = Arc::new(remote_test_env.environment().clone()); + #[allow(deprecated)] + let cwd = turn.cwd.clone(); let request = test_exec_request( &turn, vec!["bash".to_string(), "-lc".to_string(), "echo ok".to_string()], - turn.cwd.clone(), + cwd, shell_env(), ); diff --git a/codex-rs/core/src/unified_exec/process_manager_tests.rs b/codex-rs/core/src/unified_exec/process_manager_tests.rs index 5ef5994030..87d9553dd3 100644 --- a/codex-rs/core/src/unified_exec/process_manager_tests.rs +++ b/codex-rs/core/src/unified_exec/process_manager_tests.rs @@ -175,7 +175,9 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() { process_id: 123, yield_time_ms: 1000, max_output_tokens: None, + #[allow(deprecated)] cwd: turn.cwd.clone(), + #[allow(deprecated)] sandbox_cwd: turn.cwd.clone(), environment: turn .environments @@ -200,6 +202,7 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() { /*process_started_alive*/ false, &context, &request, + #[allow(deprecated)] turn.cwd.clone(), transcript, "PRE_DENIAL_MARKER".to_string(),