Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions codex-rs/core/src/agents_md_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ async fn agents_md_paths(config: &TestConfig) -> std::io::Result<Vec<AbsolutePat
fn resolved_local_environments<const N: usize>(
environments: [(&str, AbsolutePathBuf); N],
) -> TurnEnvironmentSnapshot {
TurnEnvironmentSnapshot {
turn_environments: environments
TurnEnvironmentSnapshot::from_turn_environments(
environments
.into_iter()
.map(|(environment_id, cwd)| {
TurnEnvironment::new(
Expand All @@ -272,7 +272,7 @@ fn resolved_local_environments<const N: usize>(
)
})
.collect(),
}
)
}

fn project_provenance(path: AbsolutePathBuf, cwd: AbsolutePathBuf) -> InstructionProvenance {
Expand Down
31 changes: 29 additions & 2 deletions codex-rs/core/src/codex_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use tokio::time::timeout;
use tokio_util::sync::CancellationToken;

use crate::config::Config;
use crate::environment_selection::TurnEnvironmentSnapshot;
use crate::guardian::GuardianApprovalRequest;
use crate::guardian::new_guardian_review_id;
use crate::guardian::routes_approval_to_guardian;
Expand Down Expand Up @@ -72,6 +73,7 @@ pub(crate) async fn run_codex_thread_interactive(
models_manager: SharedModelsManager,
parent_session: Arc<Session>,
parent_ctx: Arc<TurnContext>,
parent_environments: TurnEnvironmentSnapshot,
cancel_token: CancellationToken,
subagent_source: SubAgentSource,
initial_history: Option<InitialHistory>,
Expand All @@ -84,6 +86,11 @@ pub(crate) async fn run_codex_thread_interactive(
instructions: parent_session.user_instructions().await,
warnings: Vec::new(),
};
// Child threads inherit only environments attached for this request.
let attached_environments = TurnEnvironmentSnapshot {
turn_environments: parent_environments.turn_environments.clone(),
starting: Vec::new(),
};
let CodexSpawnOk { codex, .. } = Box::pin(Codex::spawn(CodexSpawnArgs {
config,
user_instructions,
Expand All @@ -107,11 +114,11 @@ pub(crate) async fn run_codex_thread_interactive(
dynamic_tools: Vec::new(),
metrics_service_name: None,
user_shell_override: None,
inherited_environments: Some(parent_ctx.environments.clone()),
inherited_environments: Some(attached_environments.clone()),
inherited_exec_policy: Some(Arc::clone(&parent_session.services.exec_policy)),
parent_rollout_thread_trace: codex_rollout_trace::ThreadTraceContext::disabled(),
parent_trace: None,
environment_selections: parent_ctx.environments.to_selections(),
environment_selections: attached_environments.to_selections(),
thread_extension_init: codex_extension_api::ExtensionDataInit::default(),
analytics_events_client: Some(parent_session.services.analytics_events_client.clone()),
thread_store: Arc::clone(&parent_session.services.thread_store),
Expand Down Expand Up @@ -141,6 +148,7 @@ pub(crate) async fn run_codex_thread_interactive(
// routing them to the parent session for decisions.
let parent_session_clone = Arc::clone(&parent_session);
let parent_ctx_clone = Arc::clone(&parent_ctx);
let parent_environments_clone = parent_environments.clone();
let codex_for_events = Arc::clone(&codex);
// Cache delegated MCP invocations so guardian can recover the full tool call
// context when the later legacy RequestUserInput approval event only carries
Expand All @@ -152,6 +160,7 @@ pub(crate) async fn run_codex_thread_interactive(
tx_sub,
parent_session_clone,
parent_ctx_clone,
parent_environments_clone,
pending_mcp_invocations,
cancel_token_events,
)
Expand Down Expand Up @@ -184,6 +193,7 @@ pub(crate) async fn run_codex_thread_one_shot(
input: Vec<UserInput>,
parent_session: Arc<Session>,
parent_ctx: Arc<TurnContext>,
parent_environments: TurnEnvironmentSnapshot,
cancel_token: CancellationToken,
subagent_source: SubAgentSource,
final_output_json_schema: Option<Value>,
Expand All @@ -198,6 +208,7 @@ pub(crate) async fn run_codex_thread_one_shot(
models_manager,
parent_session,
parent_ctx,
parent_environments,
child_cancel.clone(),
subagent_source,
initial_history,
Expand Down Expand Up @@ -263,6 +274,7 @@ async fn forward_events(
tx_sub: Sender<Event>,
parent_session: Arc<Session>,
parent_ctx: Arc<TurnContext>,
parent_environments: TurnEnvironmentSnapshot,
pending_mcp_invocations: Arc<Mutex<HashMap<String, McpInvocation>>>,
cancel_token: CancellationToken,
) {
Expand Down Expand Up @@ -299,6 +311,7 @@ async fn forward_events(
id,
&parent_session,
&parent_ctx,
&parent_environments,
event,
&cancel_token,
)
Expand All @@ -313,6 +326,7 @@ async fn forward_events(
id,
&parent_session,
&parent_ctx,
&parent_environments,
event,
&cancel_token,
)
Expand All @@ -326,6 +340,7 @@ async fn forward_events(
&codex,
&parent_session,
&parent_ctx,
&parent_environments,
event,
&cancel_token,
)
Expand All @@ -340,6 +355,7 @@ async fn forward_events(
id,
&parent_session,
&parent_ctx,
&parent_environments,
&pending_mcp_invocations,
event,
&cancel_token,
Expand Down Expand Up @@ -453,6 +469,7 @@ async fn handle_exec_approval(
turn_id: String,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
parent_environments: &TurnEnvironmentSnapshot,
event: ExecApprovalRequestEvent,
cancel_token: &CancellationToken,
) {
Expand All @@ -475,6 +492,7 @@ async fn handle_exec_approval(
let review_rx = spawn_approval_request_review(
Arc::clone(parent_session),
Arc::clone(parent_ctx),
parent_environments.clone(),
new_guardian_review_id(),
GuardianApprovalRequest::Shell {
id: call_id.clone(),
Expand Down Expand Up @@ -538,6 +556,7 @@ async fn handle_patch_approval(
_id: String,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
parent_environments: &TurnEnvironmentSnapshot,
event: ApplyPatchApprovalRequestEvent,
cancel_token: &CancellationToken,
) {
Expand Down Expand Up @@ -588,6 +607,7 @@ async fn handle_patch_approval(
let review_rx = spawn_approval_request_review(
Arc::clone(parent_session),
Arc::clone(parent_ctx),
parent_environments.clone(),
new_guardian_review_id(),
GuardianApprovalRequest::ApplyPatch {
id: approval_id.clone(),
Expand Down Expand Up @@ -636,18 +656,21 @@ async fn handle_patch_approval(
.await;
}

#[allow(clippy::too_many_arguments)]
async fn handle_request_user_input(
codex: &Codex,
id: String,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
parent_environments: &TurnEnvironmentSnapshot,
pending_mcp_invocations: &Arc<Mutex<HashMap<String, McpInvocation>>>,
event: RequestUserInputEvent,
cancel_token: &CancellationToken,
) {
if let Some(response) = maybe_auto_review_mcp_request_user_input(
parent_session,
parent_ctx,
parent_environments,
pending_mcp_invocations,
&event,
cancel_token,
Expand Down Expand Up @@ -684,6 +707,7 @@ async fn handle_request_user_input(
async fn maybe_auto_review_mcp_request_user_input(
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
parent_environments: &TurnEnvironmentSnapshot,
pending_mcp_invocations: &Arc<Mutex<HashMap<String, McpInvocation>>>,
event: &RequestUserInputEvent,
cancel_token: &CancellationToken,
Expand Down Expand Up @@ -716,6 +740,7 @@ async fn maybe_auto_review_mcp_request_user_input(
let review_rx = spawn_approval_request_review(
Arc::clone(parent_session),
Arc::clone(parent_ctx),
parent_environments.clone(),
new_guardian_review_id(),
build_guardian_mcp_tool_review_request(&event.call_id, &invocation, metadata.as_ref()),
/*retry_reason*/ None,
Expand Down Expand Up @@ -762,6 +787,7 @@ async fn handle_request_permissions(
codex: &Codex,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
parent_environments: &TurnEnvironmentSnapshot,
event: RequestPermissionsEvent,
cancel_token: &CancellationToken,
) {
Expand All @@ -777,6 +803,7 @@ async fn handle_request_permissions(
});
let response_fut = parent_session.request_permissions_for_cwd(
parent_ctx,
parent_environments,
call_id.clone(),
args,
cwd,
Expand Down
28 changes: 25 additions & 3 deletions codex-rs/core/src/codex_delegate_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ async fn forward_events_cancelled_while_send_blocked_shuts_down_delegate() {
let (tx_sub, rx_sub) = bounded(SUBMISSION_CHANNEL_CAPACITY);
let (_agent_status_tx, agent_status) = watch::channel(AgentStatus::PendingInit);
let (session, ctx, _rx_evt) = crate::session::tests::make_session_and_context_with_rx().await;
let step = crate::session::tests::step_context_for_session(session.as_ref()).await;
let codex = Arc::new(Codex {
tx_sub,
rx_event: rx_events,
Expand Down Expand Up @@ -67,6 +68,7 @@ async fn forward_events_cancelled_while_send_blocked_shuts_down_delegate() {
tx_out.clone(),
session,
ctx,
step.environments.clone(),
Arc::new(Mutex::new(HashMap::new())),
cancel.clone(),
));
Expand Down Expand Up @@ -160,6 +162,8 @@ async fn forward_ops_preserves_submission_trace_context() {
async fn run_codex_thread_interactive_respects_pre_cancelled_spawn() {
let (parent_session, parent_ctx, _rx_events) =
crate::session::tests::make_session_and_context_with_rx().await;
let parent_step =
crate::session::tests::step_context_for_session(parent_session.as_ref()).await;
let cancel_token = CancellationToken::new();
cancel_token.cancel();

Expand All @@ -171,6 +175,7 @@ async fn run_codex_thread_interactive_respects_pre_cancelled_spawn() {
Arc::clone(&parent_session.services.models_manager),
parent_session,
parent_ctx,
parent_step.environments.clone(),
cancel_token,
SubAgentSource::Review,
/*initial_history*/ None,
Expand All @@ -184,11 +189,16 @@ async fn run_codex_thread_interactive_respects_pre_cancelled_spawn() {

#[tokio::test]
async fn handle_request_permissions_uses_tool_call_id_for_round_trip() {
let (parent_session, mut parent_ctx, rx_events) =
let (parent_session, parent_ctx, rx_events) =
crate::session::tests::make_session_and_context_with_rx().await;
let mut parent_step =
crate::session::tests::step_context_for_session(parent_session.as_ref()).await;
*parent_session.active_turn.lock().await = Some(crate::state::ActiveTurn::default());
let parent_ctx_mut = Arc::get_mut(&mut parent_ctx).expect("single turn context ref");
parent_ctx_mut.environments.turn_environments[0].environment_id = "remote".to_string();
Arc::get_mut(&mut parent_step)
.expect("single step context ref")
.environments
.turn_environments[0]
.environment_id = "remote".to_string();

let (tx_sub, rx_sub) = bounded(SUBMISSION_CHANNEL_CAPACITY);
let (_tx_events, rx_events_child) = bounded(SUBMISSION_CHANNEL_CAPACITY);
Expand Down Expand Up @@ -222,12 +232,14 @@ async fn handle_request_permissions_uses_tool_call_id_for_round_trip() {
let codex = Arc::clone(&codex);
let parent_session = Arc::clone(&parent_session);
let parent_ctx = Arc::clone(&parent_ctx);
let parent_step = Arc::clone(&parent_step);
let cancel_token = cancel_token.clone();
async move {
handle_request_permissions(
codex.as_ref(),
&parent_session,
&parent_ctx,
&parent_step.environments,
RequestPermissionsEvent {
call_id: request_call_id,
turn_id: "child-turn-1".to_string(),
Expand Down Expand Up @@ -307,17 +319,21 @@ async fn handle_exec_approval_uses_call_id_for_guardian_review_and_approval_id_f
});

let cancel_token = CancellationToken::new();
let parent_step =
crate::session::tests::step_context_for_session(parent_session.as_ref()).await;
let handle = tokio::spawn({
let codex = Arc::clone(&codex);
let parent_session = Arc::clone(&parent_session);
let parent_ctx = Arc::clone(&parent_ctx);
let parent_step = Arc::clone(&parent_step);
let cancel_token = cancel_token.clone();
async move {
handle_exec_approval(
codex.as_ref(),
"child-turn-1".to_string(),
&parent_session,
&parent_ctx,
&parent_step.environments,
ExecApprovalRequestEvent {
call_id: "command-item-1".to_string(),
approval_id: Some("callback-approval-1".to_string()),
Expand Down Expand Up @@ -419,10 +435,13 @@ async fn delegated_mcp_guardian_abort_returns_synthetic_decline_answer() {
)])));
let cancel_token = CancellationToken::new();
cancel_token.cancel();
let parent_step =
crate::session::tests::step_context_for_session(parent_session.as_ref()).await;

let response = maybe_auto_review_mcp_request_user_input(
&parent_session,
&parent_ctx,
&parent_step.environments,
&pending_mcp_invocations,
&RequestUserInputEvent {
call_id: "call-1".to_string(),
Expand Down Expand Up @@ -467,6 +486,8 @@ async fn delegated_mcp_user_reviewer_returns_none_without_metadata() {
},
)])));
let cancel_token = CancellationToken::new();
let parent_step =
crate::session::tests::step_context_for_session(parent_session.as_ref()).await;

let event = RequestUserInputEvent {
call_id: "call-1".to_string(),
Expand All @@ -484,6 +505,7 @@ async fn delegated_mcp_user_reviewer_returns_none_without_metadata() {
let response = maybe_auto_review_mcp_request_user_input(
&parent_session,
&parent_ctx,
&parent_step.environments,
&pending_mcp_invocations,
&event,
&cancel_token,
Expand Down
6 changes: 5 additions & 1 deletion codex-rs/core/src/codex_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,9 +456,13 @@ impl CodexThread {

let turn_context = self.codex.session.new_default_turn().await;
if self.codex.session.reference_context_item().await.is_none() {
let step_context = self.codex.session.prepare_step_for_request().await;
self.codex
.session
.record_context_updates_and_set_reference_context_item(turn_context.as_ref())
.record_context_updates_and_set_reference_context_item(
turn_context.as_ref(),
step_context.as_ref(),
)
.await;
}
self.codex
Expand Down
Loading
Loading