mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
core: route downstream reasoning effort through steps
This commit is contained in:
@@ -11,6 +11,7 @@ use crate::config::ConfigBuilder;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use crate::context::SubagentNotification;
|
||||
use crate::init_state_db;
|
||||
use crate::session::tests::turn_context_item_for_test;
|
||||
use crate::thread_manager::StartThreadOptions;
|
||||
use assert_matches::assert_matches;
|
||||
use codex_extension_api::ExtensionDataInit;
|
||||
@@ -983,7 +984,7 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let parent_reference_context_item = turn_context.to_turn_context_item();
|
||||
let parent_reference_context_item = turn_context_item_for_test(&turn_context);
|
||||
parent_thread
|
||||
.codex
|
||||
.session
|
||||
@@ -1187,7 +1188,7 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history() {
|
||||
previous_window_id: None,
|
||||
window_id: None,
|
||||
}),
|
||||
RolloutItem::TurnContext(turn_context.to_turn_context_item()),
|
||||
RolloutItem::TurnContext(turn_context_item_for_test(&turn_context)),
|
||||
RolloutItem::ResponseItem(spawn_agent_call(&parent_spawn_call_id)),
|
||||
])
|
||||
.await;
|
||||
@@ -1374,9 +1375,9 @@ async fn spawn_agent_fork_last_n_turns_keeps_only_recent_turns() {
|
||||
parent_thread
|
||||
.codex
|
||||
.session
|
||||
.persist_rollout_items(&[RolloutItem::TurnContext(
|
||||
spawn_turn_context.to_turn_context_item(),
|
||||
)])
|
||||
.persist_rollout_items(&[RolloutItem::TurnContext(turn_context_item_for_test(
|
||||
&spawn_turn_context,
|
||||
))])
|
||||
.await;
|
||||
parent_thread
|
||||
.codex
|
||||
|
||||
@@ -51,6 +51,7 @@ use crate::session::CodexSpawnOk;
|
||||
use crate::session::SUBMISSION_CHANNEL_CAPACITY;
|
||||
use crate::session::emit_subagent_session_started;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_login::AuthManager;
|
||||
use codex_models_manager::manager::SharedModelsManager;
|
||||
@@ -514,7 +515,7 @@ async fn handle_exec_approval(
|
||||
let review_cancel = cancel_token.child_token();
|
||||
let review_rx = spawn_approval_request_review(
|
||||
Arc::clone(parent_session),
|
||||
Arc::clone(parent_ctx),
|
||||
StepContextSeed::from_turn(Arc::clone(parent_ctx)),
|
||||
new_guardian_review_id(),
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: call_id.clone(),
|
||||
@@ -627,7 +628,7 @@ async fn handle_patch_approval(
|
||||
.join("\n");
|
||||
let review_rx = spawn_approval_request_review(
|
||||
Arc::clone(parent_session),
|
||||
Arc::clone(parent_ctx),
|
||||
StepContextSeed::from_turn(Arc::clone(parent_ctx)),
|
||||
new_guardian_review_id(),
|
||||
GuardianApprovalRequest::ApplyPatch {
|
||||
id: approval_id.clone(),
|
||||
@@ -749,7 +750,7 @@ async fn maybe_auto_review_mcp_request_user_input(
|
||||
let review_cancel = cancel_token.child_token();
|
||||
let review_rx = spawn_approval_request_review(
|
||||
Arc::clone(parent_session),
|
||||
Arc::clone(parent_ctx),
|
||||
StepContextSeed::from_turn(Arc::clone(parent_ctx)),
|
||||
new_guardian_review_id(),
|
||||
build_guardian_mcp_tool_review_request(&event.call_id, &invocation, metadata.as_ref()),
|
||||
/*retry_reason*/ None,
|
||||
@@ -809,8 +810,9 @@ async fn handle_request_permissions(
|
||||
#[allow(deprecated)]
|
||||
parent_ctx.cwd.clone()
|
||||
});
|
||||
let step_context_seed = StepContextSeed::from_turn(Arc::clone(parent_ctx));
|
||||
let response_fut = parent_session.request_permissions_for_cwd(
|
||||
parent_ctx,
|
||||
&step_context_seed,
|
||||
call_id.clone(),
|
||||
args,
|
||||
cwd,
|
||||
|
||||
@@ -70,14 +70,14 @@ pub(crate) enum InitialContextInjection {
|
||||
|
||||
pub(crate) async fn build_compaction_initial_context(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
initial_context_injection: &InitialContextInjection,
|
||||
) -> (Vec<ResponseItem>, Option<Arc<WorldState>>) {
|
||||
// Return the rendered state with its items so history and its baseline stay identical.
|
||||
match initial_context_injection {
|
||||
InitialContextInjection::BeforeLastUserMessage(world_state) => {
|
||||
let items = sess
|
||||
.build_initial_context_with_world_state(turn_context, world_state.as_ref())
|
||||
.build_initial_context_with_world_state(step_context, world_state.as_ref())
|
||||
.await;
|
||||
(items, Some(Arc::clone(world_state)))
|
||||
}
|
||||
@@ -337,7 +337,7 @@ async fn run_compact_task_inner_impl(
|
||||
|
||||
let (initial_context, world_state_baseline) = build_compaction_initial_context(
|
||||
sess.as_ref(),
|
||||
turn_context.as_ref(),
|
||||
step_context.as_ref(),
|
||||
&initial_context_injection,
|
||||
)
|
||||
.await;
|
||||
@@ -348,7 +348,7 @@ async fn run_compact_task_inner_impl(
|
||||
let reference_context_item = match initial_context_injection {
|
||||
InitialContextInjection::DoNotInject => None,
|
||||
InitialContextInjection::BeforeLastUserMessage(_) => {
|
||||
Some(turn_context.to_turn_context_item())
|
||||
Some(step_context.to_turn_context_item())
|
||||
}
|
||||
};
|
||||
let compacted_item = CompactedItem {
|
||||
|
||||
@@ -212,8 +212,8 @@ async fn run_remote_compact_task_inner_impl(
|
||||
analytics_details,
|
||||
)
|
||||
.await;
|
||||
let (attempt, compaction_turn_context) = match attempt {
|
||||
Ok(attempt) => (attempt, turn_context),
|
||||
let (attempt, compaction_step_context) = match attempt {
|
||||
Ok(attempt) => (attempt, step_context),
|
||||
Err(error) => {
|
||||
let Some(fallback_step_context) = fallback_step_context else {
|
||||
return Err(error);
|
||||
@@ -247,11 +247,12 @@ async fn run_remote_compact_task_inner_impl(
|
||||
fallback_result.as_ref().err(),
|
||||
);
|
||||
match fallback_result {
|
||||
Ok(attempt) => (attempt, fallback_turn_context),
|
||||
Ok(attempt) => (attempt, fallback_step_context),
|
||||
Err(_) => return Err(error),
|
||||
}
|
||||
}
|
||||
};
|
||||
let compaction_turn_context = compaction_step_context.turn.as_ref();
|
||||
let RemoteCompactAttempt {
|
||||
new_history,
|
||||
trace_input_history,
|
||||
@@ -259,7 +260,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
let (new_window_number, new_window_ids) = sess.advance_auto_compact_window().await;
|
||||
let (new_history, world_state_baseline) = process_compacted_history(
|
||||
sess.as_ref(),
|
||||
compaction_turn_context.as_ref(),
|
||||
compaction_step_context.as_ref(),
|
||||
new_history,
|
||||
&initial_context_injection,
|
||||
)
|
||||
@@ -268,7 +269,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
let reference_context_item = match initial_context_injection {
|
||||
InitialContextInjection::DoNotInject => None,
|
||||
InitialContextInjection::BeforeLastUserMessage(_) => {
|
||||
Some(compaction_turn_context.to_turn_context_item())
|
||||
Some(compaction_step_context.to_turn_context_item())
|
||||
}
|
||||
};
|
||||
let compacted_item = CompactedItem {
|
||||
@@ -287,7 +288,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
replacement_history: &new_history,
|
||||
});
|
||||
sess.replace_compacted_history(
|
||||
compaction_turn_context.as_ref(),
|
||||
compaction_turn_context,
|
||||
new_history,
|
||||
reference_context_item,
|
||||
world_state_baseline,
|
||||
@@ -303,7 +304,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
|
||||
pub(crate) async fn process_compacted_history(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
mut compacted_history: Vec<ResponseItem>,
|
||||
initial_context_injection: &InitialContextInjection,
|
||||
) -> (Vec<ResponseItem>, Option<Arc<WorldState>>) {
|
||||
@@ -311,7 +312,7 @@ pub(crate) async fn process_compacted_history(
|
||||
// message in the replacement history. Pre-turn compaction instead injects context after the
|
||||
// compaction item, but mid-turn compaction keeps the compaction item last for model training.
|
||||
let (initial_context, world_state_baseline) =
|
||||
build_compaction_initial_context(sess, turn_context, initial_context_injection).await;
|
||||
build_compaction_initial_context(sess, step_context, initial_context_injection).await;
|
||||
|
||||
compacted_history.retain(should_keep_compacted_history_item);
|
||||
(
|
||||
|
||||
@@ -224,8 +224,8 @@ async fn run_remote_compact_task_inner_impl(
|
||||
analytics_details,
|
||||
)
|
||||
.await;
|
||||
let (attempt, compaction_turn_context) = match attempt {
|
||||
Ok(attempt) => (attempt, turn_context),
|
||||
let (attempt, compaction_step_context) = match attempt {
|
||||
Ok(attempt) => (attempt, step_context),
|
||||
Err(error) => {
|
||||
let Some(fallback_step_context) = fallback_step_context else {
|
||||
return Err(error);
|
||||
@@ -259,11 +259,12 @@ async fn run_remote_compact_task_inner_impl(
|
||||
fallback_result.as_ref().err(),
|
||||
);
|
||||
match fallback_result {
|
||||
Ok(attempt) => (attempt, fallback_turn_context),
|
||||
Ok(attempt) => (attempt, fallback_step_context),
|
||||
Err(_) => return Err(error),
|
||||
}
|
||||
}
|
||||
};
|
||||
let compaction_turn_context = compaction_step_context.turn.as_ref();
|
||||
let RemoteCompactV2Attempt {
|
||||
trace_input_history,
|
||||
prompt_input,
|
||||
@@ -283,7 +284,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
let (new_window_number, new_window_ids) = sess.advance_auto_compact_window().await;
|
||||
let (new_history, world_state_baseline) = process_compacted_history(
|
||||
sess.as_ref(),
|
||||
compaction_turn_context.as_ref(),
|
||||
compaction_step_context.as_ref(),
|
||||
compacted_history,
|
||||
&initial_context_injection,
|
||||
)
|
||||
@@ -292,7 +293,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
let reference_context_item = match initial_context_injection {
|
||||
InitialContextInjection::DoNotInject => None,
|
||||
InitialContextInjection::BeforeLastUserMessage(_) => {
|
||||
Some(compaction_turn_context.to_turn_context_item())
|
||||
Some(compaction_step_context.to_turn_context_item())
|
||||
}
|
||||
};
|
||||
let compacted_item = CompactedItem {
|
||||
@@ -308,7 +309,7 @@ async fn run_remote_compact_task_inner_impl(
|
||||
replacement_history: &new_history,
|
||||
});
|
||||
sess.replace_compacted_history(
|
||||
compaction_turn_context.as_ref(),
|
||||
compaction_turn_context,
|
||||
new_history,
|
||||
reference_context_item,
|
||||
world_state_baseline,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::tests::build_world_state_from_turn_context;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::WireApi;
|
||||
@@ -13,17 +14,18 @@ async fn process_compacted_history_with_test_session(
|
||||
) -> (Vec<ResponseItem>, Vec<ResponseItem>) {
|
||||
let (session, turn_context) = crate::session::tests::make_session_and_context().await;
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
session
|
||||
.set_previous_turn_settings(previous_turn_settings.cloned())
|
||||
.await;
|
||||
let world_state = Arc::new(build_world_state_from_turn_context(&session, &turn_context).await);
|
||||
let initial_context = session
|
||||
.build_initial_context_with_world_state(&turn_context, world_state.as_ref())
|
||||
.build_initial_context_with_world_state(&step_context, world_state.as_ref())
|
||||
.await;
|
||||
let initial_context_injection = InitialContextInjection::BeforeLastUserMessage(world_state);
|
||||
let (refreshed, _) = crate::compact_remote::process_compacted_history(
|
||||
&session,
|
||||
&turn_context,
|
||||
&step_context,
|
||||
compacted_history,
|
||||
&initial_context_injection,
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ pub(crate) async fn run_manual_compact_task(
|
||||
// Manual compaction runs outside run_turn, so it captures its own current step.
|
||||
let step_context = sess.capture_step_context(Arc::clone(&turn_context)).await;
|
||||
let world_state = Arc::new(sess.build_world_state_for_step(&step_context).await);
|
||||
run_compact_task_inner(&sess, &turn_context, world_state, CompactionTrigger::Manual).await
|
||||
run_compact_task_inner(&sess, &step_context, world_state, CompactionTrigger::Manual).await
|
||||
}
|
||||
|
||||
/// Runs token-budget inline auto-compaction as a normal compaction lifecycle.
|
||||
@@ -51,22 +51,22 @@ pub(crate) async fn run_inline_auto_compact_task(
|
||||
step_context: Arc<StepContext>,
|
||||
initial_context_injection: InitialContextInjection,
|
||||
) -> CodexResult<()> {
|
||||
let turn_context = &step_context.turn;
|
||||
let world_state = match initial_context_injection {
|
||||
InitialContextInjection::BeforeLastUserMessage(world_state) => world_state,
|
||||
InitialContextInjection::DoNotInject => {
|
||||
Arc::new(sess.build_world_state_for_step(&step_context).await)
|
||||
}
|
||||
};
|
||||
run_compact_task_inner(&sess, turn_context, world_state, CompactionTrigger::Auto).await
|
||||
run_compact_task_inner(&sess, &step_context, world_state, CompactionTrigger::Auto).await
|
||||
}
|
||||
|
||||
async fn run_compact_task_inner(
|
||||
sess: &Arc<Session>,
|
||||
turn_context: &Arc<TurnContext>,
|
||||
step_context: &Arc<StepContext>,
|
||||
world_state: Arc<WorldState>,
|
||||
trigger: CompactionTrigger,
|
||||
) -> CodexResult<()> {
|
||||
let turn_context = &step_context.turn;
|
||||
let pre_compact_outcome = run_pre_compact_hooks(sess, turn_context, trigger).await;
|
||||
match pre_compact_outcome {
|
||||
PreCompactHookOutcome::Continue => {}
|
||||
@@ -76,7 +76,7 @@ async fn run_compact_task_inner(
|
||||
let compaction_item = TurnItem::ContextCompaction(ContextCompactionItem::new());
|
||||
sess.emit_turn_item_started(turn_context, &compaction_item)
|
||||
.await;
|
||||
sess.start_new_context_window(turn_context.as_ref(), world_state)
|
||||
sess.start_new_context_window(step_context.as_ref(), world_state)
|
||||
.await;
|
||||
sess.emit_turn_item_completed(turn_context, compaction_item)
|
||||
.await;
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::context::RealtimeEndInstructions;
|
||||
use crate::context::RealtimeStartInstructions;
|
||||
use crate::context::RealtimeStartWithInstructions;
|
||||
use crate::session::PreviousTurnSettings;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_execpolicy::Policy;
|
||||
use codex_features::Feature;
|
||||
@@ -63,20 +64,18 @@ fn build_permissions_update_item(
|
||||
|
||||
fn build_collaboration_mode_update_item(
|
||||
previous: Option<&TurnContextItem>,
|
||||
next: &TurnContext,
|
||||
next: &StepContext,
|
||||
) -> Option<String> {
|
||||
if !next.config.include_collaboration_mode_instructions {
|
||||
if !next.turn.config.include_collaboration_mode_instructions {
|
||||
return None;
|
||||
}
|
||||
|
||||
let prev = previous?;
|
||||
if prev.collaboration_mode.as_ref() != Some(&next.collaboration_mode) {
|
||||
let collaboration_mode = next.collaboration_mode();
|
||||
if prev.collaboration_mode.as_ref() != Some(&collaboration_mode) {
|
||||
// If the next mode has empty developer instructions, this returns None and we emit no
|
||||
// update, so prior collaboration instructions remain in the prompt history.
|
||||
Some(
|
||||
CollaborationModeInstructions::from_collaboration_mode(&next.collaboration_mode)?
|
||||
.render(),
|
||||
)
|
||||
Some(CollaborationModeInstructions::from_collaboration_mode(&collaboration_mode)?.render())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -84,7 +83,7 @@ fn build_collaboration_mode_update_item(
|
||||
|
||||
fn build_multi_agent_mode_update_item(
|
||||
previous: Option<&TurnContextItem>,
|
||||
next: &TurnContext,
|
||||
next: &StepContext,
|
||||
) -> Option<String> {
|
||||
let effective_multi_agent_mode = crate::session::multi_agents::effective_multi_agent_mode(next);
|
||||
let previous = previous?;
|
||||
@@ -240,10 +239,11 @@ fn build_text_message(role: &str, text_sections: Vec<String>) -> Option<Response
|
||||
pub(crate) fn build_settings_update_items(
|
||||
previous: Option<&TurnContextItem>,
|
||||
previous_turn_settings: Option<&PreviousTurnSettings>,
|
||||
next: &TurnContext,
|
||||
next: &StepContext,
|
||||
exec_policy: &Policy,
|
||||
personality_feature_enabled: bool,
|
||||
) -> Vec<ResponseItem> {
|
||||
let turn_context = next.turn.as_ref();
|
||||
// TODO(ccunningham): build_settings_update_items still does not cover every
|
||||
// model-visible item emitted by build_initial_context. Persist the remaining
|
||||
// inputs or add explicit replay events so fork/resume can diff everything
|
||||
@@ -251,12 +251,12 @@ pub(crate) fn build_settings_update_items(
|
||||
let developer_update_sections = [
|
||||
// Keep model-switch instructions first so model-specific guidance is read before
|
||||
// any other context diffs on this turn.
|
||||
build_model_instructions_update_item(previous_turn_settings, next),
|
||||
build_permissions_update_item(previous, next, exec_policy),
|
||||
build_model_instructions_update_item(previous_turn_settings, turn_context),
|
||||
build_permissions_update_item(previous, turn_context, exec_policy),
|
||||
build_collaboration_mode_update_item(previous, next),
|
||||
build_multi_agent_mode_update_item(previous, next),
|
||||
build_realtime_update_item(previous, previous_turn_settings, next),
|
||||
build_personality_update_item(previous, next, personality_feature_enabled),
|
||||
build_realtime_update_item(previous, previous_turn_settings, turn_context),
|
||||
build_personality_update_item(previous, turn_context, personality_feature_enabled),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
|
||||
@@ -25,6 +25,7 @@ use tokio::time::sleep_until;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::turn_timing::now_unix_timestamp_ms;
|
||||
use crate::util::backoff;
|
||||
@@ -274,13 +275,14 @@ pub(crate) async fn record_guardian_denial_for_test(
|
||||
/// caller as distinct from explicit guardian denials.
|
||||
async fn run_guardian_review(
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context_seed: StepContextSeed,
|
||||
review_id: String,
|
||||
request: GuardianApprovalRequest,
|
||||
retry_reason: Option<String>,
|
||||
approval_request_source: GuardianApprovalRequestSource,
|
||||
external_cancel: Option<CancellationToken>,
|
||||
) -> ReviewDecision {
|
||||
let turn = Arc::clone(&step_context_seed.turn);
|
||||
let target_item_id = guardian_request_target_item_id(&request).map(str::to_string);
|
||||
let assessment_turn_id = guardian_request_turn_id(&request, &turn.sub_id).to_string();
|
||||
let action_summary = guardian_assessment_action(&request);
|
||||
@@ -358,7 +360,7 @@ async fn run_guardian_review(
|
||||
let terminal_action = action_summary.clone();
|
||||
let (outcome, analytics_result) = Box::pin(run_guardian_review_session_with_retry(
|
||||
session.clone(),
|
||||
turn.clone(),
|
||||
step_context_seed.clone(),
|
||||
request,
|
||||
retry_reason.clone(),
|
||||
schema,
|
||||
@@ -593,7 +595,7 @@ async fn run_guardian_review(
|
||||
/// Public entrypoint for approval requests that should be reviewed by guardian.
|
||||
pub(crate) async fn review_approval_request(
|
||||
session: &Arc<Session>,
|
||||
turn: &Arc<TurnContext>,
|
||||
step_context_seed: &StepContextSeed,
|
||||
review_id: String,
|
||||
request: GuardianApprovalRequest,
|
||||
retry_reason: Option<String>,
|
||||
@@ -602,7 +604,7 @@ pub(crate) async fn review_approval_request(
|
||||
// guardian session state machine into their own async stack.
|
||||
Box::pin(run_guardian_review(
|
||||
Arc::clone(session),
|
||||
Arc::clone(turn),
|
||||
step_context_seed.clone(),
|
||||
review_id,
|
||||
request,
|
||||
retry_reason,
|
||||
@@ -614,7 +616,7 @@ pub(crate) async fn review_approval_request(
|
||||
|
||||
pub(crate) async fn review_approval_request_with_cancel(
|
||||
session: &Arc<Session>,
|
||||
turn: &Arc<TurnContext>,
|
||||
step_context_seed: &StepContextSeed,
|
||||
review_id: String,
|
||||
request: GuardianApprovalRequest,
|
||||
retry_reason: Option<String>,
|
||||
@@ -623,7 +625,7 @@ pub(crate) async fn review_approval_request_with_cancel(
|
||||
) -> ReviewDecision {
|
||||
run_guardian_review(
|
||||
Arc::clone(session),
|
||||
Arc::clone(turn),
|
||||
step_context_seed.clone(),
|
||||
review_id,
|
||||
request,
|
||||
retry_reason,
|
||||
@@ -635,7 +637,7 @@ pub(crate) async fn review_approval_request_with_cancel(
|
||||
|
||||
pub(crate) fn spawn_approval_request_review(
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context_seed: StepContextSeed,
|
||||
review_id: String,
|
||||
request: GuardianApprovalRequest,
|
||||
retry_reason: Option<String>,
|
||||
@@ -653,7 +655,7 @@ pub(crate) fn spawn_approval_request_review(
|
||||
};
|
||||
let decision = runtime.block_on(review_approval_request_with_cancel(
|
||||
&session,
|
||||
&turn,
|
||||
&step_context_seed,
|
||||
review_id,
|
||||
request,
|
||||
retry_reason,
|
||||
@@ -677,8 +679,9 @@ pub(super) struct GuardianReviewSessionConfig {
|
||||
|
||||
pub(super) async fn guardian_review_session_config(
|
||||
session: &Session,
|
||||
turn: &TurnContext,
|
||||
step_context_seed: &StepContextSeed,
|
||||
) -> anyhow::Result<GuardianReviewSessionConfig> {
|
||||
let turn = step_context_seed.turn.as_ref();
|
||||
let network_proxy = session.services.network_proxy.load_full();
|
||||
let live_network_config = match network_proxy.as_ref() {
|
||||
Some(network_proxy) => Some(network_proxy.proxy().current_cfg().await?),
|
||||
@@ -722,8 +725,8 @@ pub(super) async fn guardian_review_session_config(
|
||||
.supported_reasoning_levels
|
||||
.iter()
|
||||
.any(|preset| preset.effort == codex_protocol::openai_models::ReasoningEffort::Low),
|
||||
turn.config
|
||||
.model_reasoning_effort
|
||||
step_context_seed
|
||||
.reasoning_effort
|
||||
.clone()
|
||||
.or_else(|| turn.model_info.default_reasoning_level.clone()),
|
||||
);
|
||||
@@ -768,29 +771,29 @@ pub(super) async fn guardian_review_session_config(
|
||||
/// rules.
|
||||
async fn run_guardian_review_session_before_deadline(
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context_seed: StepContextSeed,
|
||||
request: GuardianApprovalRequest,
|
||||
retry_reason: Option<String>,
|
||||
schema: serde_json::Value,
|
||||
external_cancel: Option<CancellationToken>,
|
||||
deadline: Instant,
|
||||
) -> (GuardianReviewOutcome, GuardianReviewAnalyticsResult) {
|
||||
let session_config = match guardian_review_session_config(session.as_ref(), turn.as_ref()).await
|
||||
{
|
||||
Ok(session_config) => session_config,
|
||||
Err(err) => {
|
||||
return (
|
||||
GuardianReviewOutcome::Error(GuardianReviewError::prompt_build(err)),
|
||||
GuardianReviewAnalyticsResult::without_session(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let session_config =
|
||||
match guardian_review_session_config(session.as_ref(), &step_context_seed).await {
|
||||
Ok(session_config) => session_config,
|
||||
Err(err) => {
|
||||
return (
|
||||
GuardianReviewOutcome::Error(GuardianReviewError::prompt_build(err)),
|
||||
GuardianReviewAnalyticsResult::without_session(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let (session_outcome, session_analytics_result) = Box::pin(
|
||||
session
|
||||
.guardian_review_session
|
||||
.run_review(GuardianReviewSessionParams {
|
||||
parent_session: Arc::clone(&session),
|
||||
parent_turn: turn.clone(),
|
||||
parent_turn: Arc::clone(&step_context_seed.turn),
|
||||
spawn_config: session_config.spawn_config,
|
||||
request,
|
||||
retry_reason,
|
||||
@@ -801,8 +804,8 @@ async fn run_guardian_review_session_before_deadline(
|
||||
guardian_catalog_contains_auto_review: session_config.catalog_contains_auto_review,
|
||||
guardian_review_model_overridden: session_config.model_overridden,
|
||||
guardian_review_model_override: session_config.model_override,
|
||||
reasoning_summary: turn.reasoning_summary,
|
||||
personality: turn.personality,
|
||||
reasoning_summary: step_context_seed.turn.reasoning_summary,
|
||||
personality: step_context_seed.turn.personality,
|
||||
external_cancel,
|
||||
deadline,
|
||||
}),
|
||||
@@ -862,7 +865,7 @@ async fn run_guardian_review_session_before_deadline(
|
||||
|
||||
pub(super) async fn run_guardian_review_session_with_retry(
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context_seed: StepContextSeed,
|
||||
request: GuardianApprovalRequest,
|
||||
retry_reason: Option<String>,
|
||||
schema: serde_json::Value,
|
||||
@@ -875,7 +878,7 @@ pub(super) async fn run_guardian_review_session_with_retry(
|
||||
loop {
|
||||
let (outcome, mut analytics_result) = run_guardian_review_session_before_deadline(
|
||||
Arc::clone(&session),
|
||||
Arc::clone(&turn),
|
||||
step_context_seed.clone(),
|
||||
request.clone(),
|
||||
retry_reason.clone(),
|
||||
schema.clone(),
|
||||
|
||||
@@ -44,6 +44,7 @@ use crate::context::ContextualUserFragment;
|
||||
use crate::context::GuardianFollowupReviewReminder;
|
||||
use crate::session::Codex;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_features::Feature;
|
||||
@@ -299,9 +300,12 @@ impl GuardianReviewSessionManager {
|
||||
) -> BoxFuture<'_, anyhow::Result<()>> {
|
||||
// Boxing breaks the Session::new -> Guardian -> Session::new future recursion.
|
||||
Box::pin(async move {
|
||||
let spawn_config = guardian_review_session_config(&parent_session, &parent_turn)
|
||||
.await?
|
||||
.spawn_config;
|
||||
let spawn_config = guardian_review_session_config(
|
||||
&parent_session,
|
||||
&StepContextSeed::from_turn(Arc::clone(&parent_turn)),
|
||||
)
|
||||
.await?
|
||||
.spawn_config;
|
||||
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
|
||||
&spawn_config,
|
||||
parent_session.user_instructions().await,
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::config::NetworkProxySpec;
|
||||
use crate::config::test_config;
|
||||
use crate::guardian::approval_request::guardian_request_target_item_id;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::test_support;
|
||||
use codex_analytics::GuardianApprovalRequestSource;
|
||||
@@ -1151,7 +1152,7 @@ async fn cancelled_guardian_review_emits_terminal_abort_without_warning() {
|
||||
|
||||
let decision = review_approval_request_with_cancel(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-cancelled-guardian".to_string(),
|
||||
GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
@@ -2321,7 +2322,7 @@ async fn guardian_review_surfaces_responses_api_errors_in_rejection_reason() ->
|
||||
|
||||
let decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-shell-guardian-error".to_string(),
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-guardian-error".to_string(),
|
||||
@@ -2461,7 +2462,7 @@ async fn guardian_review_does_not_retry_missing_assessment_payload() -> anyhow::
|
||||
|
||||
let decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-missing-assessment".to_string(),
|
||||
guardian_shell_request("shell-missing-assessment"),
|
||||
/*retry_reason*/ None,
|
||||
@@ -2565,7 +2566,7 @@ async fn guardian_review_exhausts_three_failures_with_one_terminal_event() -> an
|
||||
|
||||
let decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-exhausted-retry".to_string(),
|
||||
guardian_shell_request("shell-exhausted-retry"),
|
||||
/*retry_reason*/ None,
|
||||
@@ -2616,7 +2617,7 @@ async fn guardian_review_does_not_retry_valid_denial() -> anyhow::Result<()> {
|
||||
|
||||
let decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-valid-denial".to_string(),
|
||||
guardian_shell_request("shell-valid-denial"),
|
||||
/*retry_reason*/ None,
|
||||
@@ -2719,7 +2720,7 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
||||
assert_eq!(
|
||||
review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-shell-guardian-1".to_string(),
|
||||
initial_request,
|
||||
/*retry_reason*/ None
|
||||
@@ -2773,7 +2774,7 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
||||
let mut second_review = tokio::spawn(async move {
|
||||
review_approval_request(
|
||||
&session_for_second,
|
||||
&turn_for_second,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_for_second)),
|
||||
"review-shell-guardian-2".to_string(),
|
||||
second_request,
|
||||
Some("trunk follow-up".to_string()),
|
||||
@@ -2820,7 +2821,7 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
||||
|
||||
let third_decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn)),
|
||||
"review-shell-guardian-3".to_string(),
|
||||
third_request,
|
||||
Some("parallel follow-up".to_string()),
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::mcp_tool_approval_templates::RenderedMcpToolApprovalParam;
|
||||
use crate::mcp_tool_approval_templates::render_mcp_tool_approval_template;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::tools::hook_names::HookToolName;
|
||||
use crate::tools::sandboxing::PermissionRequestPayload;
|
||||
@@ -1274,7 +1275,7 @@ async fn maybe_request_mcp_tool_approval(
|
||||
let review_id = new_guardian_review_id();
|
||||
let decision = review_approval_request(
|
||||
sess,
|
||||
turn_context,
|
||||
&StepContextSeed::from(step_context.as_ref()),
|
||||
review_id.clone(),
|
||||
build_guardian_mcp_tool_review_request(call_id, invocation, metadata),
|
||||
/*retry_reason*/ None,
|
||||
|
||||
@@ -93,10 +93,11 @@ fn approval_metadata(
|
||||
}
|
||||
}
|
||||
|
||||
fn mcp_turn_metadata_context(turn_context: &TurnContext) -> McpTurnMetadataContext<'_> {
|
||||
fn mcp_turn_metadata_context(step_context: &StepContext) -> McpTurnMetadataContext<'_> {
|
||||
let turn_context = step_context.turn.as_ref();
|
||||
McpTurnMetadataContext {
|
||||
model: turn_context.model_info.slug.as_str(),
|
||||
reasoning_effort: turn_context.effective_reasoning_effort(),
|
||||
reasoning_effort: step_context.effective_reasoning_effort(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,12 +1092,17 @@ fn truncate_mcp_tool_result_for_event_bounds_large_error() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn mcp_tool_call_request_meta_includes_turn_metadata_for_custom_server() {
|
||||
let (_, turn_context) = make_session_and_context().await;
|
||||
let (_, mut turn_context) = make_session_and_context().await;
|
||||
Arc::make_mut(&mut turn_context.config).model_reasoning_effort =
|
||||
Some(codex_protocol::openai_models::ReasoningEffort::High);
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let mut step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
Arc::get_mut(&mut step_context)
|
||||
.expect("step context should be uniquely owned")
|
||||
.reasoning_effort = Some(codex_protocol::openai_models::ReasoningEffort::Low);
|
||||
let expected_turn_metadata = turn_context
|
||||
.turn_metadata_state
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&turn_context))
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&step_context))
|
||||
.expect("turn metadata");
|
||||
|
||||
let meta = build_mcp_tool_call_request_meta(
|
||||
@@ -1204,7 +1210,7 @@ async fn plugin_mcp_tool_call_request_meta_includes_plugin_id() {
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let expected_turn_metadata = turn_context
|
||||
.turn_metadata_state
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&turn_context))
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&step_context))
|
||||
.expect("turn metadata");
|
||||
let mut metadata = approval_metadata(
|
||||
/*connector_id*/ None, /*connector_name*/ None,
|
||||
@@ -1325,7 +1331,7 @@ async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let expected_turn_metadata = turn_context
|
||||
.turn_metadata_state
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&turn_context))
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&step_context))
|
||||
.expect("turn metadata");
|
||||
let metadata = McpToolApprovalMetadata {
|
||||
annotations: None,
|
||||
@@ -1378,7 +1384,7 @@ async fn codex_apps_tool_call_request_meta_includes_call_id_without_existing_cod
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let expected_turn_metadata = turn_context
|
||||
.turn_metadata_state
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&turn_context))
|
||||
.current_meta_value_for_mcp_request(mcp_turn_metadata_context(&step_context))
|
||||
.expect("turn metadata");
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -11,6 +11,7 @@ use tokio::sync::watch;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use super::tests::make_session_and_context_with_rx;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::state::ActiveTurn;
|
||||
|
||||
async fn wait_until_held(pause_state: &mut watch::Receiver<bool>) {
|
||||
@@ -114,7 +115,7 @@ async fn permission_request_holds_an_elicitation_until_response() {
|
||||
.selection();
|
||||
session
|
||||
.request_permissions_for_environment(
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
"call-1".to_string(),
|
||||
RequestPermissionsArgs {
|
||||
environment_id: None,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::step_context::StepContextSeed;
|
||||
use super::*;
|
||||
use crate::mcp::McpRuntimeProjection;
|
||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||
@@ -623,7 +624,7 @@ async fn review_guardian_mcp_elicitation(
|
||||
let review_id = crate::guardian::new_guardian_review_id();
|
||||
let decision = crate::guardian::review_approval_request(
|
||||
&session,
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
review_id.clone(),
|
||||
guardian_request,
|
||||
/*retry_reason*/ None,
|
||||
|
||||
@@ -40,6 +40,7 @@ use crate::image_preparation::prepare_response_items;
|
||||
use crate::parse_turn_item;
|
||||
use crate::realtime_conversation::RealtimeConversationManager;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::session_prefix::format_inter_agent_completion_message;
|
||||
use crate::skills::SkillRenderSideEffects;
|
||||
@@ -1729,7 +1730,7 @@ impl Session {
|
||||
async fn build_settings_update_items(
|
||||
&self,
|
||||
reference_context_item: Option<&TurnContextItem>,
|
||||
current_context: &TurnContext,
|
||||
current_context: &StepContext,
|
||||
) -> Vec<ResponseItem> {
|
||||
// TODO: Make context updates a pure diff of persisted previous/current TurnContextItem
|
||||
// state so replay/backtracking is deterministic. Runtime inputs that affect model-visible
|
||||
@@ -2267,12 +2268,13 @@ impl Session {
|
||||
)]
|
||||
pub(crate) async fn request_permissions_for_environment(
|
||||
self: &Arc<Self>,
|
||||
turn_context: &Arc<TurnContext>,
|
||||
step_context_seed: &StepContextSeed,
|
||||
call_id: String,
|
||||
args: RequestPermissionsArgs,
|
||||
environment: TurnEnvironmentSelection,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Option<RequestPermissionsResponse> {
|
||||
let turn_context = &step_context_seed.turn;
|
||||
match turn_context.as_ref().approval_policy.value() {
|
||||
AskForApproval::Never => {
|
||||
return Some(RequestPermissionsResponse {
|
||||
@@ -2316,7 +2318,6 @@ impl Session {
|
||||
};
|
||||
let review_id = crate::guardian::new_guardian_review_id();
|
||||
let session = Arc::clone(self);
|
||||
let turn = Arc::clone(turn_context);
|
||||
let request = crate::guardian::GuardianApprovalRequest::RequestPermissions {
|
||||
id: call_id,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
@@ -2325,7 +2326,7 @@ impl Session {
|
||||
};
|
||||
let review_rx = crate::guardian::spawn_approval_request_review(
|
||||
session,
|
||||
turn,
|
||||
step_context_seed.clone(),
|
||||
review_id,
|
||||
request,
|
||||
/*retry_reason*/ None,
|
||||
@@ -2435,12 +2436,13 @@ impl Session {
|
||||
|
||||
pub(crate) async fn request_permissions_for_cwd(
|
||||
self: &Arc<Self>,
|
||||
turn_context: &Arc<TurnContext>,
|
||||
step_context_seed: &StepContextSeed,
|
||||
call_id: String,
|
||||
args: RequestPermissionsArgs,
|
||||
cwd: AbsolutePathBuf,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Option<RequestPermissionsResponse> {
|
||||
let turn_context = &step_context_seed.turn;
|
||||
let turn_environment = match args.environment_id.as_deref() {
|
||||
Some(environment_id) => turn_context
|
||||
.environments
|
||||
@@ -2459,7 +2461,7 @@ impl Session {
|
||||
let mut environment = turn_environment.selection();
|
||||
environment.cwd = PathUri::from_abs_path(&cwd);
|
||||
self.request_permissions_for_environment(
|
||||
turn_context,
|
||||
step_context_seed,
|
||||
call_id,
|
||||
args,
|
||||
environment,
|
||||
@@ -3165,27 +3167,27 @@ impl Session {
|
||||
|
||||
pub(crate) async fn build_initial_context_with_world_state(
|
||||
&self,
|
||||
turn_context: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
world_state: &WorldState,
|
||||
) -> Vec<ResponseItem> {
|
||||
let mcp = self.services.latest_mcp_runtime();
|
||||
self.build_initial_context_with_world_state_and_mcp(turn_context, world_state, &mcp)
|
||||
self.build_initial_context_with_world_state_and_mcp(step_context, world_state, &mcp)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn build_initial_context_with_world_state_and_mcp(
|
||||
&self,
|
||||
turn_context: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
world_state: &WorldState,
|
||||
mcp: &McpRuntimeSnapshot,
|
||||
) -> Vec<ResponseItem> {
|
||||
let turn_context = step_context.turn.as_ref();
|
||||
let mut developer_sections = Vec::<String>::with_capacity(8);
|
||||
let mut contextual_user_sections = Vec::<String>::with_capacity(2);
|
||||
let mut separate_developer_sections = Vec::<String>::new();
|
||||
let (
|
||||
reference_context_item,
|
||||
previous_turn_settings,
|
||||
collaboration_mode,
|
||||
base_instructions,
|
||||
session_source,
|
||||
auto_compact_window_ids,
|
||||
@@ -3194,12 +3196,12 @@ impl Session {
|
||||
(
|
||||
state.reference_context_item(),
|
||||
state.previous_turn_settings(),
|
||||
state.session_configuration.collaboration_mode.clone(),
|
||||
state.session_configuration.base_instructions.clone(),
|
||||
state.session_configuration.session_source.clone(),
|
||||
state.auto_compact_window_ids(),
|
||||
)
|
||||
};
|
||||
let collaboration_mode = step_context.collaboration_mode();
|
||||
if let Some(model_switch_message) =
|
||||
crate::context_manager::updates::build_model_instructions_update_item(
|
||||
previous_turn_settings.as_ref(),
|
||||
@@ -3449,7 +3451,7 @@ impl Session {
|
||||
{
|
||||
items.push(usage_hint_message);
|
||||
}
|
||||
if let Some(multi_agent_mode) = multi_agents::effective_multi_agent_mode(turn_context) {
|
||||
if let Some(multi_agent_mode) = multi_agents::effective_multi_agent_mode(step_context) {
|
||||
items.push(ContextualUserFragment::into(
|
||||
MultiAgentModeInstructions::new(multi_agent_mode),
|
||||
));
|
||||
@@ -3516,18 +3518,19 @@ impl Session {
|
||||
|
||||
pub(crate) async fn start_new_context_window(
|
||||
&self,
|
||||
turn_context: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
world_state: Arc<WorldState>,
|
||||
) -> u64 {
|
||||
let turn_context = step_context.turn.as_ref();
|
||||
let window = {
|
||||
let mut state = self.state.lock().await;
|
||||
state.start_new_context_window()
|
||||
};
|
||||
let (window_number, window_ids) = window;
|
||||
let context_items = self
|
||||
.build_initial_context_with_world_state(turn_context, world_state.as_ref())
|
||||
.build_initial_context_with_world_state(step_context, world_state.as_ref())
|
||||
.await;
|
||||
let turn_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context_item = step_context.to_turn_context_item();
|
||||
self.replace_compacted_history(
|
||||
turn_context,
|
||||
context_items,
|
||||
@@ -3575,7 +3578,7 @@ impl Session {
|
||||
let state = self.state.lock().await;
|
||||
state.reference_context_item()
|
||||
};
|
||||
let turn_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context_item = step_context.to_turn_context_item();
|
||||
let turn_context_changed = reference_context_item.as_ref() != Some(&turn_context_item);
|
||||
let should_inject_full_context = reference_context_item.is_none();
|
||||
let world_state = Arc::new(self.build_world_state_for_step(step_context).await);
|
||||
@@ -3583,7 +3586,7 @@ impl Session {
|
||||
let (mut context_items, world_state_item) = if should_inject_full_context {
|
||||
let context_items = self
|
||||
.build_initial_context_with_world_state_and_mcp(
|
||||
turn_context,
|
||||
step_context,
|
||||
world_state.as_ref(),
|
||||
step_context.mcp.as_ref(),
|
||||
)
|
||||
@@ -3602,7 +3605,7 @@ impl Session {
|
||||
// Steady-state path: append only built-in context diffs here; turn-scoped extension
|
||||
// context is added below.
|
||||
let mut context_items = self
|
||||
.build_settings_update_items(reference_context_item.as_ref(), turn_context)
|
||||
.build_settings_update_items(reference_context_item.as_ref(), step_context)
|
||||
.await;
|
||||
let (world_state_items, world_state_item) = {
|
||||
let mut state = self.state.lock().await;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::config::MultiAgentV2Config;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
@@ -36,7 +37,8 @@ fn configured_usage_hint_text_for_source<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn effective_multi_agent_mode(turn_context: &TurnContext) -> Option<MultiAgentMode> {
|
||||
pub(crate) fn effective_multi_agent_mode(step_context: &StepContext) -> Option<MultiAgentMode> {
|
||||
let turn_context = step_context.turn.as_ref();
|
||||
if turn_context.multi_agent_version != MultiAgentVersion::V2 {
|
||||
return None;
|
||||
}
|
||||
@@ -49,7 +51,7 @@ pub(crate) fn effective_multi_agent_mode(turn_context: &TurnContext) -> Option<M
|
||||
.multi_agent_mode_hint_text
|
||||
{
|
||||
Some(hint_text) => MultiAgentMode::Custom(hint_text.clone()),
|
||||
None => match turn_context.effective_reasoning_effort() {
|
||||
None => match step_context.effective_reasoning_effort() {
|
||||
Some(ReasoningEffort::Ultra) => MultiAgentMode::Proactive,
|
||||
_ => MultiAgentMode::ExplicitRequestOnly,
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::*;
|
||||
|
||||
use super::tests::build_world_state_from_turn_context;
|
||||
use super::tests::make_session_and_context;
|
||||
use super::tests::turn_context_item_for_test;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::ContentItem;
|
||||
@@ -138,7 +139,7 @@ async fn record_initial_history_restores_world_state_baseline() {
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let world_state = build_world_state_from_turn_context(&session, &turn_context).await;
|
||||
let rollout_items = completed_user_turn_rollout(
|
||||
turn_context.to_turn_context_item(),
|
||||
turn_context_item_for_test(&turn_context),
|
||||
vec![RolloutItem::WorldState(WorldStateItem::full(
|
||||
world_state.snapshot().into_value(),
|
||||
))],
|
||||
@@ -291,7 +292,8 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif
|
||||
#[tokio::test]
|
||||
async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_completed_turns() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let first_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let first_context_item = turn_context_item_for_test(&turn_context);
|
||||
let first_turn_id = first_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -414,7 +416,8 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
|
||||
#[tokio::test]
|
||||
async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_incomplete_turn() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let first_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let first_context_item = turn_context_item_for_test(&turn_context);
|
||||
let first_turn_id = first_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -508,7 +511,8 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_inc
|
||||
#[tokio::test]
|
||||
async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metadata() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let first_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let first_context_item = turn_context_item_for_test(&turn_context);
|
||||
let first_turn_id = first_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -634,7 +638,8 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
|
||||
#[tokio::test]
|
||||
async fn reconstruct_history_rollback_counts_inter_agent_assistant_turns() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let first_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let first_context_item = turn_context_item_for_test(&turn_context);
|
||||
let first_turn_id = first_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -735,7 +740,8 @@ async fn reconstruct_history_rollback_counts_inter_agent_assistant_turns() {
|
||||
#[tokio::test]
|
||||
async fn reconstruct_history_rollback_clears_history_and_metadata_when_exceeding_user_turns() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let only_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let only_context_item = turn_context_item_for_test(&turn_context);
|
||||
let only_turn_id = only_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -789,7 +795,8 @@ async fn reconstruct_history_rollback_clears_history_and_metadata_when_exceeding
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
let user_turn_id = previous_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -864,7 +871,8 @@ async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_resumed_rollback_drops_incomplete_user_turn_compaction_metadata() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
let previous_turn_id = previous_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -960,7 +968,8 @@ async fn record_initial_history_resumed_rollback_drops_incomplete_user_turn_comp
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_resumed_bare_turn_context_does_not_seed_reference_context_item() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
let rollout_items = vec![RolloutItem::TurnContext(previous_context_item.clone())];
|
||||
|
||||
session
|
||||
@@ -977,7 +986,8 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_seed_referenc
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_resumed_does_not_seed_reference_context_item_after_compaction() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
let rollout_items = vec![
|
||||
RolloutItem::TurnContext(previous_context_item),
|
||||
RolloutItem::Compacted(CompactedItem {
|
||||
@@ -1078,8 +1088,9 @@ async fn reconstruct_history_prefers_compacted_window_over_session_meta() {
|
||||
#[tokio::test]
|
||||
async fn reconstruct_history_replays_world_state_from_latest_compaction_window() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let rollout_items = completed_user_turn_rollout(
|
||||
turn_context.to_turn_context_item(),
|
||||
turn_context_item_for_test(&turn_context),
|
||||
vec![
|
||||
RolloutItem::WorldState(WorldStateItem::full(json!({
|
||||
"environment": {"status": "old"}
|
||||
@@ -1186,7 +1197,8 @@ async fn reconstruct_history_legacy_compaction_without_replacement_history_does_
|
||||
async fn reconstruct_history_legacy_compaction_without_replacement_history_clears_later_reference_context_item()
|
||||
{
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let current_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let current_context_item = turn_context_item_for_test(&turn_context);
|
||||
let current_turn_id = current_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -1479,7 +1491,8 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
|
||||
async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_for_later_turn_context()
|
||||
{
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
let previous_turn_id = previous_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -1717,7 +1730,8 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_resumed_trailing_incomplete_turn_preserves_turn_context_item() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let current_context_item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let current_context_item = turn_context_item_for_test(&turn_context);
|
||||
let current_turn_id = current_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
|
||||
@@ -6,7 +6,10 @@ use crate::session::McpRuntimeSnapshot;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||
use codex_mcp::ToolInfo;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::TurnContextItem;
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
/// Request-scoped state that may change between model sampling requests.
|
||||
@@ -66,4 +69,65 @@ impl StepContext {
|
||||
.map(|effort| effort.to_string())
|
||||
.unwrap_or_else(|| "default".to_string())
|
||||
}
|
||||
|
||||
pub(crate) fn collaboration_mode(&self) -> CollaborationMode {
|
||||
self.turn.collaboration_mode.with_updates(
|
||||
/*model*/ None,
|
||||
Some(self.reasoning_effort.clone()),
|
||||
/*developer_instructions*/ None,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn to_turn_context_item(&self) -> TurnContextItem {
|
||||
let workspace_roots = self.turn.config.effective_workspace_roots();
|
||||
#[allow(deprecated)]
|
||||
let cwd = self.turn.cwd.clone();
|
||||
TurnContextItem {
|
||||
turn_id: Some(self.turn.sub_id.clone()),
|
||||
cwd,
|
||||
workspace_roots: (!workspace_roots.is_empty()).then_some(workspace_roots),
|
||||
current_date: self.turn.current_date.clone(),
|
||||
timezone: self.turn.timezone.clone(),
|
||||
approval_policy: self.turn.approval_policy.value(),
|
||||
sandbox_policy: self.turn.sandbox_policy(),
|
||||
permission_profile: Some(self.turn.permission_profile()),
|
||||
network: self.turn.turn_context_network_item(),
|
||||
file_system_sandbox_policy: self.turn.non_legacy_file_system_sandbox_policy(),
|
||||
model: self.turn.model_info.slug.clone(),
|
||||
comp_hash: self.turn.model_info.comp_hash.clone(),
|
||||
personality: self.turn.personality,
|
||||
collaboration_mode: Some(self.collaboration_mode()),
|
||||
multi_agent_version: Some(self.turn.multi_agent_version),
|
||||
multi_agent_mode: super::multi_agents::effective_multi_agent_mode(self),
|
||||
realtime_active: Some(self.turn.realtime_active),
|
||||
effort: self.reasoning_effort.clone(),
|
||||
summary: ReasoningSummaryConfig::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frozen step inputs needed by downstream work that can outlive a sampling request.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct StepContextSeed {
|
||||
pub(crate) turn: Arc<TurnContext>,
|
||||
pub(crate) reasoning_effort: Option<ReasoningEffortConfig>,
|
||||
}
|
||||
|
||||
impl StepContextSeed {
|
||||
pub(crate) fn from_turn(turn: Arc<TurnContext>) -> Self {
|
||||
let reasoning_effort = turn.config.model_reasoning_effort.clone();
|
||||
Self {
|
||||
turn,
|
||||
reasoning_effort,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&StepContext> for StepContextSeed {
|
||||
fn from(step_context: &StepContext) -> Self {
|
||||
Self {
|
||||
turn: Arc::clone(&step_context.turn),
|
||||
reasoning_effort: step_context.reasoning_effort.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,10 @@ impl StepContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn turn_context_item_for_test(turn: &Arc<TurnContext>) -> TurnContextItem {
|
||||
StepContext::for_test(Arc::clone(turn)).to_turn_context_item()
|
||||
}
|
||||
|
||||
mod guardian_tests;
|
||||
|
||||
struct InstructionsTestCase {
|
||||
@@ -1134,6 +1138,7 @@ async fn danger_full_access_tool_attempts_do_not_enforce_managed_network() -> an
|
||||
let tool_ctx = crate::tools::sandboxing::ToolCtx {
|
||||
session: Arc::clone(&session),
|
||||
turn: Arc::clone(&turn),
|
||||
step_context: StepContext::for_test(Arc::clone(&turn)),
|
||||
call_id: "probe-call".to_string(),
|
||||
tool_name: codex_tools::ToolName::plain("probe"),
|
||||
};
|
||||
@@ -2790,9 +2795,10 @@ async fn start_new_context_window_assigns_and_persists_item_ids() {
|
||||
attach_thread_persistence(Arc::get_mut(&mut session).expect("unique session")).await;
|
||||
let world_state =
|
||||
Arc::new(build_world_state_from_turn_context(session.as_ref(), &turn_context).await);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
|
||||
session
|
||||
.start_new_context_window(turn_context.as_ref(), world_state)
|
||||
.start_new_context_window(step_context.as_ref(), world_state)
|
||||
.await;
|
||||
|
||||
let live_history = session.clone_history().await;
|
||||
@@ -3176,7 +3182,7 @@ async fn thread_rollback_drops_last_turn_from_history() {
|
||||
full_history.extend(initial_context.clone());
|
||||
full_history.extend(turn_1.clone());
|
||||
full_history.extend(turn_2);
|
||||
sess.replace_history(full_history.clone(), Some(tc.to_turn_context_item()))
|
||||
sess.replace_history(full_history.clone(), Some(turn_context_item_for_test(&tc)))
|
||||
.await;
|
||||
let rollout_items: Vec<RolloutItem> = full_history
|
||||
.into_iter()
|
||||
@@ -3191,7 +3197,7 @@ async fn thread_rollback_drops_last_turn_from_history() {
|
||||
.await;
|
||||
{
|
||||
let mut state = sess.state.lock().await;
|
||||
state.set_reference_context_item(Some(tc.to_turn_context_item()));
|
||||
state.set_reference_context_item(Some(turn_context_item_for_test(&tc)));
|
||||
}
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
@@ -3236,7 +3242,7 @@ async fn thread_rollback_clears_history_when_num_turns_exceeds_existing_turns()
|
||||
let mut full_history = Vec::new();
|
||||
full_history.extend(initial_context.clone());
|
||||
full_history.extend(turn_1);
|
||||
sess.replace_history(full_history.clone(), Some(tc.to_turn_context_item()))
|
||||
sess.replace_history(full_history.clone(), Some(turn_context_item_for_test(&tc)))
|
||||
.await;
|
||||
let rollout_items: Vec<RolloutItem> = full_history
|
||||
.into_iter()
|
||||
@@ -3283,7 +3289,7 @@ async fn thread_rollback_recomputes_previous_turn_settings_and_reference_context
|
||||
)
|
||||
.await;
|
||||
|
||||
let first_context_item = tc.to_turn_context_item();
|
||||
let first_context_item = turn_context_item_for_test(&tc);
|
||||
let first_turn_id = first_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -3405,7 +3411,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
|
||||
)
|
||||
.await;
|
||||
|
||||
let first_context_item = tc.to_turn_context_item();
|
||||
let first_context_item = turn_context_item_for_test(&tc);
|
||||
let first_turn_id = first_context_item
|
||||
.turn_id
|
||||
.clone()
|
||||
@@ -3547,7 +3553,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
|
||||
Arc::get_mut(&mut sess).expect("session should not have additional references"),
|
||||
)
|
||||
.await;
|
||||
let turn_context_item = tc.to_turn_context_item();
|
||||
let turn_context_item = turn_context_item_for_test(&tc);
|
||||
|
||||
sess.persist_rollout_items(&[
|
||||
RolloutItem::EventMsg(EventMsg::TurnStarted(
|
||||
@@ -5320,8 +5326,9 @@ async fn build_initial_context(
|
||||
turn_context: &Arc<TurnContext>,
|
||||
) -> Vec<ResponseItem> {
|
||||
let world_state = build_world_state_from_turn_context(session, turn_context).await;
|
||||
let step_context = StepContext::for_test(Arc::clone(turn_context));
|
||||
session
|
||||
.build_initial_context_with_world_state(turn_context.as_ref(), &world_state)
|
||||
.build_initial_context_with_world_state(step_context.as_ref(), &world_state)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -6118,7 +6125,7 @@ async fn request_permissions_emits_event_when_granular_policy_allows_requests()
|
||||
.selection();
|
||||
session
|
||||
.request_permissions_for_environment(
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
call_id,
|
||||
codex_protocol::request_permissions::RequestPermissionsArgs {
|
||||
environment_id: None,
|
||||
@@ -6357,7 +6364,7 @@ async fn request_permissions_response_materializes_session_cwd_grants_before_rec
|
||||
.selection();
|
||||
session
|
||||
.request_permissions_for_environment(
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
call_id,
|
||||
codex_protocol::request_permissions::RequestPermissionsArgs {
|
||||
environment_id: None,
|
||||
@@ -6448,7 +6455,7 @@ async fn request_permissions_is_auto_denied_when_granular_policy_blocks_tool_req
|
||||
.selection();
|
||||
let response = session
|
||||
.request_permissions_for_environment(
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
call_id,
|
||||
codex_protocol::request_permissions::RequestPermissionsArgs {
|
||||
environment_id: None,
|
||||
@@ -8147,10 +8154,11 @@ async fn build_settings_update_items_emits_realtime_start_when_session_becomes_l
|
||||
)
|
||||
.await;
|
||||
current_context.realtime_active = true;
|
||||
let current_context = StepContext::for_test(Arc::new(current_context));
|
||||
|
||||
let update_items = session
|
||||
.build_settings_update_items(
|
||||
Some(&previous_context.to_turn_context_item()),
|
||||
Some(&turn_context_item_for_test(&previous_context)),
|
||||
¤t_context,
|
||||
)
|
||||
.await;
|
||||
@@ -8168,6 +8176,7 @@ async fn build_settings_update_items_emits_realtime_start_when_session_becomes_l
|
||||
async fn build_settings_update_items_emits_realtime_end_when_session_stops_being_live() {
|
||||
let (session, mut previous_context) = make_session_and_context().await;
|
||||
previous_context.realtime_active = true;
|
||||
let previous_context = Arc::new(previous_context);
|
||||
let mut current_context = previous_context
|
||||
.with_model(
|
||||
previous_context.model_info.slug.clone(),
|
||||
@@ -8175,10 +8184,11 @@ async fn build_settings_update_items_emits_realtime_end_when_session_stops_being
|
||||
)
|
||||
.await;
|
||||
current_context.realtime_active = false;
|
||||
let current_context = StepContext::for_test(Arc::new(current_context));
|
||||
|
||||
let update_items = session
|
||||
.build_settings_update_items(
|
||||
Some(&previous_context.to_turn_context_item()),
|
||||
Some(&turn_context_item_for_test(&previous_context)),
|
||||
¤t_context,
|
||||
)
|
||||
.await;
|
||||
@@ -8195,7 +8205,8 @@ async fn build_settings_update_items_emits_realtime_end_when_session_stops_being
|
||||
#[tokio::test]
|
||||
async fn build_settings_update_items_uses_previous_turn_settings_for_realtime_end() {
|
||||
let (session, previous_context) = make_session_and_context().await;
|
||||
let mut previous_context_item = previous_context.to_turn_context_item();
|
||||
let previous_context = Arc::new(previous_context);
|
||||
let mut previous_context_item = turn_context_item_for_test(&previous_context);
|
||||
previous_context_item.realtime_active = None;
|
||||
let previous_turn_settings = PreviousTurnSettings {
|
||||
model: previous_context.model_info.slug.clone(),
|
||||
@@ -8209,6 +8220,7 @@ async fn build_settings_update_items_uses_previous_turn_settings_for_realtime_en
|
||||
)
|
||||
.await;
|
||||
current_context.realtime_active = false;
|
||||
let current_context = StepContext::for_test(Arc::new(current_context));
|
||||
|
||||
session
|
||||
.set_previous_turn_settings(Some(previous_turn_settings))
|
||||
@@ -8241,7 +8253,7 @@ async fn build_initial_context_uses_previous_realtime_state() {
|
||||
"expected initial context to describe active realtime state, got {developer_texts:?}"
|
||||
);
|
||||
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.set_reference_context_item(Some(previous_context_item));
|
||||
@@ -8398,9 +8410,9 @@ async fn record_context_updates_includes_turn_context_fragments_on_steady_state_
|
||||
.insert(TurnContextExtensionTestState {
|
||||
expected_model_context_window: Some(50),
|
||||
});
|
||||
let mut previous_context_item = turn_context.to_turn_context_item();
|
||||
previous_context_item.turn_id = Some("previous-turn-id".to_string());
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let mut previous_context_item = turn_context_item_for_test(&turn_context);
|
||||
previous_context_item.turn_id = Some("previous-turn-id".to_string());
|
||||
let world_state = build_world_state_from_turn_context(&session, &turn_context).await;
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
@@ -8974,14 +8986,16 @@ async fn turn_context_item_stores_local_cwd() {
|
||||
|
||||
#[allow(deprecated)]
|
||||
let local_cwd = turn_context.cwd.clone();
|
||||
assert_eq!(turn_context.to_turn_context_item().cwd, local_cwd);
|
||||
let turn_context = Arc::new(turn_context);
|
||||
assert_eq!(turn_context_item_for_test(&turn_context).cwd, local_cwd);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy() {
|
||||
let (_session, turn_context) = make_session_and_context().await;
|
||||
let turn_context = Arc::new(turn_context);
|
||||
|
||||
let item = turn_context.to_turn_context_item();
|
||||
let item = turn_context_item_for_test(&turn_context);
|
||||
|
||||
assert_eq!(item.file_system_sandbox_policy, None);
|
||||
assert_eq!(
|
||||
@@ -8990,6 +9004,27 @@ async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn turn_context_item_uses_step_reasoning_effort() {
|
||||
let (_session, mut turn_context) = make_session_and_context().await;
|
||||
Arc::make_mut(&mut turn_context.config).model_reasoning_effort =
|
||||
Some(ReasoningEffortConfig::High);
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let mut step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
Arc::get_mut(&mut step_context)
|
||||
.expect("step context should be uniquely owned")
|
||||
.reasoning_effort = Some(ReasoningEffortConfig::Low);
|
||||
|
||||
let item = step_context.to_turn_context_item();
|
||||
assert_eq!(item.effort, Some(ReasoningEffortConfig::Low));
|
||||
assert_eq!(
|
||||
item.collaboration_mode
|
||||
.expect("turn context should have collaboration mode")
|
||||
.reasoning_effort(),
|
||||
Some(ReasoningEffortConfig::Low)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn turn_context_item_stores_split_file_system_sandbox_policy_when_different() {
|
||||
let (_session, mut turn_context) = make_session_and_context().await;
|
||||
@@ -9000,7 +9035,8 @@ async fn turn_context_item_stores_split_file_system_sandbox_policy_when_differen
|
||||
turn_context.network_sandbox_policy(),
|
||||
);
|
||||
|
||||
let item = turn_context.to_turn_context_item();
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let item = turn_context_item_for_test(&turn_context);
|
||||
|
||||
assert_eq!(
|
||||
item.file_system_sandbox_policy,
|
||||
@@ -9028,7 +9064,7 @@ async fn record_context_updates_and_set_reference_context_item_injects_full_cont
|
||||
let current_context = session.reference_context_item().await;
|
||||
assert_eq!(
|
||||
serde_json::to_value(current_context).expect("serialize current context item"),
|
||||
serde_json::to_value(Some(turn_context.to_turn_context_item()))
|
||||
serde_json::to_value(Some(step_context.to_turn_context_item()))
|
||||
.expect("serialize expected context item")
|
||||
);
|
||||
}
|
||||
@@ -9080,8 +9116,8 @@ async fn record_context_updates_and_set_reference_context_item_reinjects_full_co
|
||||
async fn record_context_updates_and_set_reference_context_item_persists_baseline_without_emitting_diffs()
|
||||
{
|
||||
let (mut session, turn_context) = make_session_and_context().await;
|
||||
let previous_context_item = turn_context.to_turn_context_item();
|
||||
let previous_context = Arc::new(turn_context);
|
||||
let previous_context_item = turn_context_item_for_test(&previous_context);
|
||||
let world_state = build_world_state_from_turn_context(&session, &previous_context).await;
|
||||
let mut turn_context = Arc::try_unwrap(previous_context)
|
||||
.unwrap_or_else(|_| panic!("previous turn context should have no remaining references"));
|
||||
@@ -9095,13 +9131,14 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
}
|
||||
let rollout_path = attach_thread_persistence(&mut session).await;
|
||||
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
|
||||
let update_items = session
|
||||
.build_settings_update_items(Some(&previous_context_item), &turn_context)
|
||||
.build_settings_update_items(Some(&previous_context_item), &step_context)
|
||||
.await;
|
||||
assert_eq!(update_items, Vec::new());
|
||||
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
session
|
||||
.record_context_updates_and_set_reference_context_item(&step_context)
|
||||
.await;
|
||||
@@ -9113,7 +9150,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
assert_eq!(
|
||||
serde_json::to_value(session.reference_context_item().await)
|
||||
.expect("serialize current context item"),
|
||||
serde_json::to_value(Some(turn_context.to_turn_context_item()))
|
||||
serde_json::to_value(Some(step_context.to_turn_context_item()))
|
||||
.expect("serialize expected context item")
|
||||
);
|
||||
session.ensure_rollout_materialized().await;
|
||||
@@ -9132,7 +9169,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
assert_eq!(
|
||||
serde_json::to_value(persisted_turn_context)
|
||||
.expect("serialize persisted turn context item"),
|
||||
serde_json::to_value(Some(turn_context.to_turn_context_item()))
|
||||
serde_json::to_value(Some(step_context.to_turn_context_item()))
|
||||
.expect("serialize expected turn context item")
|
||||
);
|
||||
}
|
||||
@@ -9258,7 +9295,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_full_rei
|
||||
assert_eq!(
|
||||
serde_json::to_value(persisted_turn_context)
|
||||
.expect("serialize persisted turn context item"),
|
||||
serde_json::to_value(Some(turn_context.to_turn_context_item()))
|
||||
serde_json::to_value(Some(step_context.to_turn_context_item()))
|
||||
.expect("serialize expected turn context item")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::exec_policy::ExecPolicyManager;
|
||||
use crate::guardian::GUARDIAN_REVIEWER_NAME;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::test_support::models_manager_with_provider;
|
||||
use crate::tools::context::ToolCallSource;
|
||||
use crate::tools::context::ToolOutput;
|
||||
@@ -132,7 +133,7 @@ async fn request_permissions_routes_to_guardian_when_reviewer_is_enabled() {
|
||||
let response = tokio::time::timeout(
|
||||
Duration::from_secs(45),
|
||||
session.request_permissions_for_environment(
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
"perm-call-1".to_string(),
|
||||
RequestPermissionsArgs {
|
||||
environment_id: None,
|
||||
@@ -227,7 +228,7 @@ async fn request_permissions_guardian_review_stops_when_cancelled() {
|
||||
.selection();
|
||||
session
|
||||
.request_permissions_for_environment(
|
||||
&turn_context,
|
||||
&StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
"perm-call-cancelled".to_string(),
|
||||
RequestPermissionsArgs {
|
||||
environment_id: None,
|
||||
@@ -531,12 +532,13 @@ async fn process_compacted_history_preserves_separate_guardian_developer_message
|
||||
turn_context.session_source = guardian_source;
|
||||
turn_context.developer_instructions = Some(guardian_policy.clone());
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let world_state = Arc::new(build_world_state_from_turn_context(&session, &turn_context).await);
|
||||
let initial_context_injection = InitialContextInjection::BeforeLastUserMessage(world_state);
|
||||
|
||||
let (refreshed, _) = crate::compact_remote::process_compacted_history(
|
||||
&session,
|
||||
&turn_context,
|
||||
&step_context,
|
||||
vec![
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
|
||||
@@ -204,7 +204,7 @@ pub(crate) async fn run_turn(
|
||||
.await;
|
||||
}
|
||||
|
||||
track_turn_resolved_config_analytics(&sess, &turn_context, &input).await;
|
||||
track_turn_resolved_config_analytics(&sess, first_step_context.as_ref(), &input).await;
|
||||
|
||||
let mut last_agent_message: Option<String> = None;
|
||||
let mut stop_hook_active = false;
|
||||
@@ -742,9 +742,10 @@ async fn build_extension_turn_input_items(
|
||||
)]
|
||||
async fn track_turn_resolved_config_analytics(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
input: &[TurnInput],
|
||||
) {
|
||||
let turn_context = step_context.turn.as_ref();
|
||||
let thread_config = {
|
||||
let state = sess.state.lock().await;
|
||||
state.session_configuration.thread_config_snapshot()
|
||||
@@ -777,7 +778,7 @@ async fn track_turn_resolved_config_analytics(
|
||||
permission_profile: turn_context.permission_profile(),
|
||||
#[allow(deprecated)]
|
||||
permission_profile_cwd: turn_context.cwd.to_path_buf(),
|
||||
reasoning_effort: turn_context.config.model_reasoning_effort.clone(),
|
||||
reasoning_effort: step_context.reasoning_effort.clone(),
|
||||
reasoning_summary: Some(turn_context.reasoning_summary),
|
||||
service_tier: turn_context
|
||||
.config
|
||||
@@ -1935,7 +1936,7 @@ async fn try_run_sampling_request(
|
||||
prompt: &Prompt,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> CodexResult<SamplingRequestResult> {
|
||||
let turn_context = &step_context.turn;
|
||||
let turn_context = Arc::clone(&step_context.turn);
|
||||
feedback_tags!(
|
||||
model = turn_context.model_info.slug.clone(),
|
||||
approval_policy = turn_context.approval_policy.value(),
|
||||
|
||||
@@ -168,19 +168,17 @@ impl TurnContext {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn effective_reasoning_effort(&self) -> Option<ReasoningEffortConfig> {
|
||||
if self.model_info.supports_reasoning_summaries {
|
||||
/// Resolves effort for the turn span, which is created before the first step is captured.
|
||||
pub(crate) fn turn_start_reasoning_effort_for_tracing(&self) -> String {
|
||||
let reasoning_effort = if self.model_info.supports_reasoning_summaries {
|
||||
self.config
|
||||
.model_reasoning_effort
|
||||
.clone()
|
||||
.or_else(|| self.model_info.default_reasoning_level.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn effective_reasoning_effort_for_tracing(&self) -> String {
|
||||
self.effective_reasoning_effort()
|
||||
};
|
||||
reasoning_effort
|
||||
.map(|effort| effort.to_string())
|
||||
.unwrap_or_else(|| "default".to_string())
|
||||
}
|
||||
@@ -333,7 +331,7 @@ impl TurnContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn non_legacy_file_system_sandbox_policy(&self) -> Option<FileSystemSandboxPolicy> {
|
||||
pub(super) fn non_legacy_file_system_sandbox_policy(&self) -> Option<FileSystemSandboxPolicy> {
|
||||
// Omit the derived split filesystem policy when it is equivalent to
|
||||
// the legacy sandbox policy. This keeps turn-context payloads stable
|
||||
// while both fields exist; once callers consume only the split policy,
|
||||
@@ -349,34 +347,7 @@ impl TurnContext {
|
||||
.then_some(file_system_sandbox_policy)
|
||||
}
|
||||
|
||||
pub(crate) fn to_turn_context_item(&self) -> TurnContextItem {
|
||||
let workspace_roots = self.config.effective_workspace_roots();
|
||||
#[allow(deprecated)]
|
||||
let cwd = self.cwd.clone();
|
||||
TurnContextItem {
|
||||
turn_id: Some(self.sub_id.clone()),
|
||||
cwd,
|
||||
workspace_roots: (!workspace_roots.is_empty()).then_some(workspace_roots),
|
||||
current_date: self.current_date.clone(),
|
||||
timezone: self.timezone.clone(),
|
||||
approval_policy: self.approval_policy.value(),
|
||||
sandbox_policy: self.sandbox_policy(),
|
||||
permission_profile: Some(self.permission_profile()),
|
||||
network: self.turn_context_network_item(),
|
||||
file_system_sandbox_policy: self.non_legacy_file_system_sandbox_policy(),
|
||||
model: self.model_info.slug.clone(),
|
||||
comp_hash: self.model_info.comp_hash.clone(),
|
||||
personality: self.personality,
|
||||
collaboration_mode: Some(self.collaboration_mode.clone()),
|
||||
multi_agent_version: Some(self.multi_agent_version),
|
||||
multi_agent_mode: super::multi_agents::effective_multi_agent_mode(self),
|
||||
realtime_active: Some(self.realtime_active),
|
||||
effort: self.config.model_reasoning_effort.clone(),
|
||||
summary: ReasoningSummaryConfig::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_context_network_item(&self) -> Option<TurnContextNetworkItem> {
|
||||
pub(super) fn turn_context_network_item(&self) -> Option<TurnContextNetworkItem> {
|
||||
let network = self
|
||||
.config
|
||||
.config_layer_stack
|
||||
|
||||
@@ -383,7 +383,7 @@ impl Session {
|
||||
let task_cancellation_token = cancellation_token.child_token();
|
||||
// Task-owned turn spans keep a core-owned span open for the
|
||||
// full task lifecycle after the submission dispatch span ends.
|
||||
let reasoning_effort = turn_context.effective_reasoning_effort_for_tracing();
|
||||
let reasoning_effort = turn_context.turn_start_reasoning_effort_for_tracing();
|
||||
let task_span = info_span!(
|
||||
"turn",
|
||||
otel.name = span_name,
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::init_state_db;
|
||||
use crate::installation_id::INSTALLATION_ID_FILENAME;
|
||||
use crate::rollout::RolloutRecorder;
|
||||
use crate::session::session::SessionSettingsUpdate;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::tests::build_world_state_from_turn_context;
|
||||
use crate::session::tests::make_session_and_context;
|
||||
use crate::tasks::InterruptedTurnHistoryMarker;
|
||||
@@ -319,9 +320,10 @@ fn out_of_range_truncation_drops_pre_user_active_turn_prefix() {
|
||||
async fn ignores_session_prefix_messages_when_truncating() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let world_state = build_world_state_from_turn_context(&session, &turn_context).await;
|
||||
let mut items = session
|
||||
.build_initial_context_with_world_state(&turn_context, &world_state)
|
||||
.build_initial_context_with_world_state(&step_context, &world_state)
|
||||
.await;
|
||||
items.push(user_msg("feature request"));
|
||||
items.push(assistant_msg("ack"));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::tests::build_world_state_from_turn_context;
|
||||
use crate::session::tests::make_session_and_context;
|
||||
use codex_protocol::AgentPath;
|
||||
@@ -270,9 +271,10 @@ fn truncates_rollout_from_start_applies_thread_rollback_markers() {
|
||||
async fn ignores_session_prefix_messages_when_truncating_rollout_from_start() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let world_state = build_world_state_from_turn_context(&session, &turn_context).await;
|
||||
let mut items = session
|
||||
.build_initial_context_with_world_state(&turn_context, &world_state)
|
||||
.build_initial_context_with_world_state(&step_context, &world_state)
|
||||
.await;
|
||||
items.push(user_msg("feature request"));
|
||||
items.push(assistant_msg("ack"));
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::agent::status::is_final;
|
||||
use crate::config::Config;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::tools::handlers::multi_agents::build_agent_spawn_config;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
@@ -107,9 +108,10 @@ fn required_state_db(
|
||||
|
||||
async fn build_runner_options(
|
||||
session: &Arc<Session>,
|
||||
turn: &Arc<TurnContext>,
|
||||
step_context: &StepContext,
|
||||
requested_concurrency: Option<usize>,
|
||||
) -> Result<JobRunnerOptions, FunctionCallError> {
|
||||
let turn = &step_context.turn;
|
||||
let multi_agent_version = turn.multi_agent_version;
|
||||
if multi_agent_version == MultiAgentVersion::Disabled {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
@@ -124,7 +126,7 @@ async fn build_runner_options(
|
||||
}
|
||||
let max_concurrency = normalize_concurrency(requested_concurrency, agent_max_threads);
|
||||
let base_instructions = session.get_base_instructions().await;
|
||||
let spawn_config = build_agent_spawn_config(&base_instructions, turn.as_ref())?;
|
||||
let spawn_config = build_agent_spawn_config(&base_instructions, step_context)?;
|
||||
Ok(JobRunnerOptions {
|
||||
max_concurrency,
|
||||
spawn_config,
|
||||
|
||||
@@ -35,7 +35,7 @@ impl SpawnAgentsOnCsvHandler {
|
||||
) -> Result<Box<dyn crate::tools::context::ToolOutput>, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
payload,
|
||||
..
|
||||
} = invocation;
|
||||
@@ -49,7 +49,7 @@ impl SpawnAgentsOnCsvHandler {
|
||||
}
|
||||
};
|
||||
|
||||
handle(session, turn, arguments)
|
||||
handle(session, step_context, arguments)
|
||||
.await
|
||||
.map(boxed_tool_output)
|
||||
}
|
||||
@@ -68,9 +68,10 @@ impl CoreToolRuntime for SpawnAgentsOnCsvHandler {
|
||||
/// `report_agent_job_result`, then exported to CSV on completion.
|
||||
pub async fn handle(
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context: Arc<StepContext>,
|
||||
arguments: String,
|
||||
) -> Result<FunctionToolOutput, FunctionCallError> {
|
||||
let turn = Arc::clone(&step_context.turn);
|
||||
let args: SpawnAgentsOnCsvArgs = parse_arguments(arguments.as_str())?;
|
||||
if args.instruction.trim().is_empty() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
@@ -182,16 +183,17 @@ pub async fn handle(
|
||||
})?;
|
||||
|
||||
let requested_concurrency = args.max_concurrency.or(args.max_workers);
|
||||
let options = match build_runner_options(&session, &turn, requested_concurrency).await {
|
||||
Ok(options) => options,
|
||||
Err(err) => {
|
||||
let error_message = err.to_string();
|
||||
let _ = db
|
||||
.mark_agent_job_failed(job_id.as_str(), error_message.as_str())
|
||||
.await;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let options =
|
||||
match build_runner_options(&session, step_context.as_ref(), requested_concurrency).await {
|
||||
Ok(options) => options,
|
||||
Err(err) => {
|
||||
let error_message = err.to_string();
|
||||
let _ = db
|
||||
.mark_agent_job_failed(job_id.as_str(), error_message.as_str())
|
||||
.await;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
db.mark_agent_job_running(job_id.as_str())
|
||||
.await
|
||||
.map_err(|err| {
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::apply_patch::InternalApplyPatchInvocation;
|
||||
use crate::apply_patch::convert_apply_patch_to_protocol;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::tools::context::ApplyPatchToolOutput;
|
||||
@@ -445,6 +446,7 @@ impl ApplyPatchHandler {
|
||||
let tool_ctx = ToolCtx {
|
||||
session: session.clone(),
|
||||
turn: turn.clone(),
|
||||
step_context: step_context.clone(),
|
||||
call_id: call_id.clone(),
|
||||
tool_name: tool_name.clone(),
|
||||
};
|
||||
@@ -550,6 +552,7 @@ pub(crate) async fn intercept_apply_patch(
|
||||
turn_environment: TurnEnvironment,
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context: Arc<StepContext>,
|
||||
tracker: Option<&SharedTurnDiffTracker>,
|
||||
call_id: &str,
|
||||
tool_name: &str,
|
||||
@@ -608,6 +611,7 @@ pub(crate) async fn intercept_apply_patch(
|
||||
let tool_ctx = ToolCtx {
|
||||
session: session.clone(),
|
||||
turn: turn.clone(),
|
||||
step_context,
|
||||
call_id: call_id.to_string(),
|
||||
tool_name: ToolName::plain(tool_name),
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use crate::agent::next_thread_spawn_depth;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::tools::handlers::multi_agents_spec::create_resume_agent_tool;
|
||||
use codex_tools::ToolSpec;
|
||||
use std::sync::Arc;
|
||||
@@ -35,6 +35,7 @@ async fn handle_resume_agent(
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
payload,
|
||||
call_id,
|
||||
..
|
||||
@@ -87,7 +88,7 @@ async fn handle_resume_agent(
|
||||
let (receiver_agent, error) = if matches!(status, AgentStatus::NotFound) {
|
||||
match Box::pin(try_resume_closed_agent(
|
||||
&session,
|
||||
&turn,
|
||||
step_context.as_ref(),
|
||||
receiver_thread_id,
|
||||
child_depth,
|
||||
))
|
||||
@@ -187,11 +188,12 @@ impl ToolOutput for ResumeAgentResult {
|
||||
|
||||
async fn try_resume_closed_agent(
|
||||
session: &Arc<Session>,
|
||||
turn: &Arc<TurnContext>,
|
||||
step_context: &StepContext,
|
||||
receiver_thread_id: ThreadId,
|
||||
child_depth: i32,
|
||||
) -> Result<(), FunctionCallError> {
|
||||
let config = build_agent_resume_config(turn.as_ref())?;
|
||||
let turn = step_context.turn.as_ref();
|
||||
let config = build_agent_resume_config(step_context)?;
|
||||
Box::pin(session.services.agent_control.resume_agent_from_rollout(
|
||||
config,
|
||||
receiver_thread_id,
|
||||
|
||||
@@ -34,6 +34,7 @@ impl Handler {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
payload,
|
||||
call_id,
|
||||
..
|
||||
@@ -48,7 +49,7 @@ impl Handler {
|
||||
.agent_control
|
||||
.get_agent_metadata(receiver_thread_id);
|
||||
if receiver_agent.is_some() {
|
||||
let resume_config = build_agent_resume_config(turn.as_ref())?;
|
||||
let resume_config = build_agent_resume_config(step_context.as_ref())?;
|
||||
session
|
||||
.services
|
||||
.agent_control
|
||||
|
||||
@@ -48,6 +48,7 @@ async fn handle_spawn_agent(
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
payload,
|
||||
call_id,
|
||||
..
|
||||
@@ -86,8 +87,10 @@ async fn handle_spawn_agent(
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let mut config =
|
||||
build_agent_spawn_config(&session.get_base_instructions().await, turn.as_ref())?;
|
||||
let mut config = build_agent_spawn_config(
|
||||
&session.get_base_instructions().await,
|
||||
step_context.as_ref(),
|
||||
)?;
|
||||
if let Some(service_tier) = args.service_tier.as_ref() {
|
||||
config.service_tier = Some(service_tier.clone());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::config::DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS;
|
||||
use crate::config::HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::tools::context::FunctionToolOutput;
|
||||
use crate::tools::context::ToolOutput;
|
||||
@@ -154,34 +155,36 @@ pub(crate) fn parse_collab_input(
|
||||
/// Builds the base config snapshot for a newly spawned sub-agent.
|
||||
///
|
||||
/// The returned config starts from the parent's effective config and then refreshes the
|
||||
/// runtime-owned fields carried on `turn`, including model selection, reasoning settings,
|
||||
/// runtime-owned fields carried on the turn and step contexts, including model selection, reasoning settings,
|
||||
/// approval policy, sandbox, and cwd. Role-specific overrides are layered after this step;
|
||||
/// skipping this helper and cloning stale config state directly can send the child agent out with
|
||||
/// the wrong provider or runtime policy.
|
||||
pub(crate) fn build_agent_spawn_config(
|
||||
base_instructions: &BaseInstructions,
|
||||
turn: &TurnContext,
|
||||
step_context: &StepContext,
|
||||
) -> Result<Config, FunctionCallError> {
|
||||
let mut config = build_agent_shared_config(turn)?;
|
||||
let mut config = build_agent_shared_config(step_context)?;
|
||||
config.base_instructions = Some(base_instructions.text.clone());
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub(crate) fn build_agent_resume_config(turn: &TurnContext) -> Result<Config, FunctionCallError> {
|
||||
let mut config = build_agent_shared_config(turn)?;
|
||||
pub(crate) fn build_agent_resume_config(
|
||||
step_context: &StepContext,
|
||||
) -> Result<Config, FunctionCallError> {
|
||||
let mut config = build_agent_shared_config(step_context)?;
|
||||
// For resume, keep base instructions sourced from rollout/session metadata.
|
||||
config.base_instructions = None;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn build_agent_shared_config(turn: &TurnContext) -> Result<Config, FunctionCallError> {
|
||||
fn build_agent_shared_config(step_context: &StepContext) -> Result<Config, FunctionCallError> {
|
||||
let turn = step_context.turn.as_ref();
|
||||
let base_config = turn.config.clone();
|
||||
let mut config = (*base_config).clone();
|
||||
config.model = Some(turn.model_info.slug.clone());
|
||||
config.model_provider = turn.provider.info().clone();
|
||||
config.model_reasoning_effort = turn
|
||||
.config
|
||||
.model_reasoning_effort
|
||||
config.model_reasoning_effort = step_context
|
||||
.reasoning_effort
|
||||
.clone()
|
||||
.or_else(|| turn.model_info.default_reasoning_level.clone());
|
||||
config.model_reasoning_summary = Some(turn.reasoning_summary);
|
||||
|
||||
@@ -4487,6 +4487,7 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
|
||||
..ShellEnvironmentPolicy::default()
|
||||
};
|
||||
config.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo"));
|
||||
config.model_reasoning_effort = Some(ReasoningEffort::High);
|
||||
turn.config = Arc::new(config);
|
||||
let temp_dir = tempfile::tempdir().expect("temp dir");
|
||||
#[allow(deprecated)]
|
||||
@@ -4513,12 +4514,18 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("approval policy set");
|
||||
|
||||
let config = build_agent_spawn_config(&base_instructions, &turn).expect("spawn config");
|
||||
let turn = Arc::new(turn);
|
||||
let mut step_context = StepContext::for_test(Arc::clone(&turn));
|
||||
Arc::get_mut(&mut step_context)
|
||||
.expect("step context should be uniquely owned")
|
||||
.reasoning_effort = Some(ReasoningEffort::Low);
|
||||
|
||||
let config = build_agent_spawn_config(&base_instructions, &step_context).expect("spawn config");
|
||||
let mut expected = (*turn.config).clone();
|
||||
expected.base_instructions = Some(base_instructions.text);
|
||||
expected.model = Some(turn.model_info.slug.clone());
|
||||
expected.model_provider = turn.provider.info().clone();
|
||||
expected.model_reasoning_effort = turn.config.model_reasoning_effort.clone();
|
||||
expected.model_reasoning_effort = Some(ReasoningEffort::Low);
|
||||
expected.model_reasoning_summary = Some(turn.reasoning_summary);
|
||||
expected.developer_instructions = turn.developer_instructions.clone();
|
||||
#[allow(deprecated)]
|
||||
@@ -4542,18 +4549,25 @@ async fn build_agent_resume_config_clears_base_instructions() {
|
||||
let (_session, mut turn) = make_session_and_context().await;
|
||||
let mut base_config = (*turn.config).clone();
|
||||
base_config.base_instructions = Some("caller-base".to_string());
|
||||
base_config.model_reasoning_effort = Some(ReasoningEffort::High);
|
||||
turn.config = Arc::new(base_config);
|
||||
turn.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("approval policy set");
|
||||
|
||||
let config = build_agent_resume_config(&turn).expect("resume config");
|
||||
let turn = Arc::new(turn);
|
||||
let mut step_context = StepContext::for_test(Arc::clone(&turn));
|
||||
Arc::get_mut(&mut step_context)
|
||||
.expect("step context should be uniquely owned")
|
||||
.reasoning_effort = Some(ReasoningEffort::Low);
|
||||
|
||||
let config = build_agent_resume_config(&step_context).expect("resume config");
|
||||
|
||||
let mut expected = (*turn.config).clone();
|
||||
expected.base_instructions = None;
|
||||
expected.model = Some(turn.model_info.slug.clone());
|
||||
expected.model_provider = turn.provider.info().clone();
|
||||
expected.model_reasoning_effort = turn.config.model_reasoning_effort.clone();
|
||||
expected.model_reasoning_effort = Some(ReasoningEffort::Low);
|
||||
expected.model_reasoning_summary = Some(turn.reasoning_summary);
|
||||
expected.developer_instructions = turn.developer_instructions.clone();
|
||||
#[allow(deprecated)]
|
||||
|
||||
@@ -67,6 +67,7 @@ pub(crate) async fn handle_message_string_tool(
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
call_id,
|
||||
..
|
||||
} = invocation;
|
||||
@@ -89,7 +90,7 @@ pub(crate) async fn handle_message_string_tool(
|
||||
let receiver_agent_path = receiver_agent.agent_path.clone().ok_or_else(|| {
|
||||
FunctionCallError::RespondToModel("target agent is missing an agent_path".to_string())
|
||||
})?;
|
||||
let resume_config = build_agent_resume_config(turn.as_ref())?;
|
||||
let resume_config = build_agent_resume_config(step_context.as_ref())?;
|
||||
session
|
||||
.services
|
||||
.agent_control
|
||||
|
||||
@@ -43,6 +43,7 @@ async fn handle_spawn_agent(
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
payload,
|
||||
call_id,
|
||||
..
|
||||
@@ -59,8 +60,10 @@ async fn handle_spawn_agent(
|
||||
let message = message_content(args.message)?;
|
||||
let session_source = turn.session_source.clone();
|
||||
let child_depth = next_thread_spawn_depth(&session_source);
|
||||
let mut config =
|
||||
build_agent_spawn_config(&session.get_base_instructions().await, turn.as_ref())?;
|
||||
let mut config = build_agent_spawn_config(
|
||||
&session.get_base_instructions().await,
|
||||
step_context.as_ref(),
|
||||
)?;
|
||||
if let Some(service_tier) = args.service_tier.as_ref() {
|
||||
config.service_tier = Some(service_tier.clone());
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use codex_protocol::request_permissions::RequestPermissionsArgs;
|
||||
use codex_sandboxing::policy_transforms::normalize_additional_permissions;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::tools::context::FunctionToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolPayload;
|
||||
@@ -46,7 +47,6 @@ impl RequestPermissionsHandler {
|
||||
) -> Result<Box<dyn crate::tools::context::ToolOutput>, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
cancellation_token,
|
||||
call_id,
|
||||
@@ -94,7 +94,7 @@ impl RequestPermissionsHandler {
|
||||
|
||||
let response = session
|
||||
.request_permissions_for_environment(
|
||||
&turn,
|
||||
&StepContextSeed::from(step_context.as_ref()),
|
||||
call_id,
|
||||
args,
|
||||
turn_environment.selection(),
|
||||
|
||||
@@ -7,6 +7,7 @@ use tokio_util::sync::CancellationToken;
|
||||
use crate::exec::ExecParams;
|
||||
use crate::exec_policy::ExecApprovalRequest;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::shell::ShellType;
|
||||
@@ -54,6 +55,7 @@ struct RunExecLikeArgs {
|
||||
prefix_rule: Option<Vec<String>>,
|
||||
session: Arc<crate::session::session::Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context: Arc<StepContext>,
|
||||
turn_environment: TurnEnvironment,
|
||||
tracker: crate::tools::context::SharedTurnDiffTracker,
|
||||
call_id: String,
|
||||
@@ -71,6 +73,7 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
|
||||
prefix_rule,
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
turn_environment,
|
||||
tracker,
|
||||
call_id,
|
||||
@@ -146,6 +149,7 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
|
||||
turn_environment.clone(),
|
||||
session.clone(),
|
||||
turn.clone(),
|
||||
step_context.clone(),
|
||||
Some(&tracker),
|
||||
&call_id,
|
||||
tool_name.name.as_str(),
|
||||
@@ -206,6 +210,7 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
|
||||
let tool_ctx = ToolCtx {
|
||||
session: session.clone(),
|
||||
turn: turn.clone(),
|
||||
step_context,
|
||||
call_id: call_id.clone(),
|
||||
tool_name,
|
||||
};
|
||||
|
||||
@@ -227,6 +227,7 @@ impl ShellCommandHandler {
|
||||
prefix_rule,
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
turn_environment,
|
||||
tracker,
|
||||
call_id,
|
||||
|
||||
@@ -127,7 +127,12 @@ impl ExecCommandHandler {
|
||||
};
|
||||
|
||||
let manager: &UnifiedExecProcessManager = &session.services.unified_exec_manager;
|
||||
let context = UnifiedExecContext::new(session.clone(), turn.clone(), call_id.clone());
|
||||
let context = UnifiedExecContext::new(
|
||||
session.clone(),
|
||||
turn.clone(),
|
||||
step_context.clone(),
|
||||
call_id.clone(),
|
||||
);
|
||||
let environment_args: ExecCommandEnvironmentArgs = parse_arguments(&arguments)?;
|
||||
let Some(turn_environment) = resolve_tool_environment(
|
||||
&step_context.environments,
|
||||
@@ -318,6 +323,7 @@ impl ExecCommandHandler {
|
||||
turn_environment.clone(),
|
||||
context.session.clone(),
|
||||
context.turn.clone(),
|
||||
context.step_context.clone(),
|
||||
Some(&tracker),
|
||||
&context.call_id,
|
||||
"exec_command",
|
||||
|
||||
@@ -8,6 +8,8 @@ use crate::guardian::routes_approval_to_guardian;
|
||||
use crate::hook_runtime::run_permission_request_hooks;
|
||||
use crate::network_policy_decision::denied_network_policy_message;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::tools::sandboxing::PermissionRequestPayload;
|
||||
use crate::tools::sandboxing::ToolError;
|
||||
use codex_hooks::PermissionRequestDecision;
|
||||
@@ -22,6 +24,7 @@ use codex_protocol::approvals::NetworkApprovalContext;
|
||||
use codex_protocol::approvals::NetworkApprovalProtocol;
|
||||
use codex_protocol::approvals::NetworkPolicyRuleAction;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
@@ -243,6 +246,7 @@ struct ActiveNetworkApprovalCall {
|
||||
command: String,
|
||||
environment_id: String,
|
||||
cancellation_token: CancellationToken,
|
||||
reasoning_effort: Option<ReasoningEffortConfig>,
|
||||
}
|
||||
|
||||
enum ActiveNetworkApprovalAttribution {
|
||||
@@ -298,6 +302,7 @@ impl NetworkApprovalService {
|
||||
command: String,
|
||||
environment_id: String,
|
||||
cancellation_token: CancellationToken,
|
||||
reasoning_effort: Option<ReasoningEffortConfig>,
|
||||
) {
|
||||
let mut calls = self.calls.lock().await;
|
||||
let key = registration_id.clone();
|
||||
@@ -310,6 +315,7 @@ impl NetworkApprovalService {
|
||||
command,
|
||||
environment_id,
|
||||
cancellation_token,
|
||||
reasoning_effort,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -632,9 +638,16 @@ impl NetworkApprovalService {
|
||||
let use_guardian = routes_approval_to_guardian(&turn_context);
|
||||
let guardian_review_id = use_guardian.then(new_guardian_review_id);
|
||||
let approval_decision = if let Some(review_id) = guardian_review_id.clone() {
|
||||
let step_context_seed = owner_call.as_ref().map_or_else(
|
||||
|| StepContextSeed::from_turn(Arc::clone(&turn_context)),
|
||||
|call| StepContextSeed {
|
||||
turn: Arc::clone(&turn_context),
|
||||
reasoning_effort: call.reasoning_effort.clone(),
|
||||
},
|
||||
);
|
||||
review_approval_request(
|
||||
&session,
|
||||
&turn_context,
|
||||
&step_context_seed,
|
||||
review_id,
|
||||
GuardianApprovalRequest::NetworkAccess {
|
||||
id: guardian_approval_id.clone(),
|
||||
@@ -849,7 +862,7 @@ pub(crate) fn build_network_policy_decider(
|
||||
|
||||
pub(crate) async fn begin_network_approval(
|
||||
session: &Session,
|
||||
turn_id: &str,
|
||||
step_context: &StepContext,
|
||||
managed_network_active: bool,
|
||||
selected_sandbox: SandboxType,
|
||||
spec: Option<NetworkApprovalSpec>,
|
||||
@@ -890,11 +903,12 @@ pub(crate) async fn begin_network_approval(
|
||||
.network_approval
|
||||
.register_call(
|
||||
registration_id.clone(),
|
||||
turn_id.to_string(),
|
||||
step_context.turn.sub_id.clone(),
|
||||
trigger,
|
||||
command,
|
||||
environment_id,
|
||||
cancellation_token.clone(),
|
||||
step_context.reasoning_effort.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
|
||||
@@ -313,6 +313,7 @@ async fn register_call_with_default_shell_trigger(
|
||||
"curl https://example.com".to_string(),
|
||||
"local".to_string(),
|
||||
cancellation_token.clone(),
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.await;
|
||||
cancellation_token
|
||||
@@ -340,6 +341,7 @@ async fn active_call_preserves_triggering_command_context() {
|
||||
"curl https://example.com".to_string(),
|
||||
"remote".to_string(),
|
||||
CancellationToken::new(),
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::guardian::review_approval_request;
|
||||
use crate::guardian::routes_approval_to_guardian;
|
||||
use crate::hook_runtime::run_permission_request_hooks;
|
||||
use crate::network_policy_decision::network_approval_context_from_payload;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::tools::flat_tool_name;
|
||||
use crate::tools::network_approval::ActiveNetworkApproval;
|
||||
use crate::tools::network_approval::DeferredNetworkApproval;
|
||||
@@ -71,7 +72,7 @@ impl ToolOrchestrator {
|
||||
{
|
||||
let network_approval = match begin_network_approval(
|
||||
&tool_ctx.session,
|
||||
&tool_ctx.turn.sub_id,
|
||||
tool_ctx.step_context.as_ref(),
|
||||
managed_network_active,
|
||||
attempt.sandbox,
|
||||
tool.network_approval_spec(req, tool_ctx),
|
||||
@@ -85,6 +86,7 @@ impl ToolOrchestrator {
|
||||
let attempt_tool_ctx = ToolCtx {
|
||||
session: tool_ctx.session.clone(),
|
||||
turn: tool_ctx.turn.clone(),
|
||||
step_context: tool_ctx.step_context.clone(),
|
||||
call_id: tool_ctx.call_id.clone(),
|
||||
tool_name: tool_ctx.tool_name.clone(),
|
||||
};
|
||||
@@ -172,6 +174,7 @@ impl ToolOrchestrator {
|
||||
let approval_ctx = ApprovalCtx {
|
||||
session: &tool_ctx.session,
|
||||
turn: &tool_ctx.turn,
|
||||
step_context: &tool_ctx.step_context,
|
||||
call_id: &tool_ctx.call_id,
|
||||
guardian_review_id: guardian_review_id.clone(),
|
||||
retry_reason: None,
|
||||
@@ -207,6 +210,7 @@ impl ToolOrchestrator {
|
||||
let approval_ctx = ApprovalCtx {
|
||||
session: &tool_ctx.session,
|
||||
turn: &tool_ctx.turn,
|
||||
step_context: &tool_ctx.step_context,
|
||||
call_id: &tool_ctx.call_id,
|
||||
guardian_review_id: guardian_review_id.clone(),
|
||||
retry_reason: reason.clone(),
|
||||
@@ -404,6 +408,7 @@ impl ToolOrchestrator {
|
||||
let approval_ctx = ApprovalCtx {
|
||||
session: &tool_ctx.session,
|
||||
turn: &tool_ctx.turn,
|
||||
step_context: &tool_ctx.step_context,
|
||||
call_id: &tool_ctx.call_id,
|
||||
guardian_review_id: guardian_review_id.clone(),
|
||||
retry_reason: Some(retry_reason),
|
||||
@@ -579,7 +584,7 @@ impl ToolOrchestrator {
|
||||
Ok(action) => {
|
||||
review_approval_request(
|
||||
approval_ctx.session,
|
||||
approval_ctx.turn,
|
||||
&StepContextSeed::from(approval_ctx.step_context.as_ref()),
|
||||
review_id,
|
||||
action,
|
||||
approval_ctx.retry_reason.clone(),
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::hook_runtime::run_permission_request_hooks;
|
||||
use crate::sandboxing::ExecOptions;
|
||||
use crate::sandboxing::ExecRequest;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::session::step_context::StepContextSeed;
|
||||
use crate::shell::ShellType;
|
||||
use crate::tools::runtimes::build_sandbox_command;
|
||||
use crate::tools::runtimes::exec_env_for_sandbox_permissions;
|
||||
@@ -38,6 +39,7 @@ use codex_protocol::exec_output::ExecToolCallOutput;
|
||||
use codex_protocol::exec_output::StreamOutput;
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
@@ -236,6 +238,7 @@ pub(super) async fn try_run_zsh_fork(
|
||||
policy: Arc::clone(&exec_policy),
|
||||
session: Arc::clone(&ctx.session),
|
||||
turn: Arc::clone(&ctx.turn),
|
||||
reasoning_effort: ctx.step_context.reasoning_effort.clone(),
|
||||
call_id: ctx.call_id.clone(),
|
||||
environment_id: req.turn_environment.environment_id.clone(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
@@ -320,6 +323,7 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
|
||||
policy: Arc::clone(&exec_policy),
|
||||
session: Arc::clone(&ctx.session),
|
||||
turn: Arc::clone(&ctx.turn),
|
||||
reasoning_effort: ctx.step_context.reasoning_effort.clone(),
|
||||
call_id: ctx.call_id.clone(),
|
||||
environment_id: req.turn_environment.environment_id.clone(),
|
||||
tool_name: GuardianCommandSource::UnifiedExec,
|
||||
@@ -355,6 +359,7 @@ struct CoreShellActionProvider {
|
||||
policy: Arc<RwLock<Policy>>,
|
||||
session: Arc<crate::session::session::Session>,
|
||||
turn: Arc<crate::session::turn_context::TurnContext>,
|
||||
reasoning_effort: Option<ReasoningEffortConfig>,
|
||||
call_id: String,
|
||||
environment_id: String,
|
||||
tool_name: GuardianCommandSource,
|
||||
@@ -451,6 +456,10 @@ impl CoreShellActionProvider {
|
||||
let workdir = workdir.clone();
|
||||
let session = self.session.clone();
|
||||
let turn = self.turn.clone();
|
||||
let step_context_seed = StepContextSeed {
|
||||
turn: Arc::clone(&turn),
|
||||
reasoning_effort: self.reasoning_effort.clone(),
|
||||
};
|
||||
let call_id = self.call_id.clone();
|
||||
let approval_id = Some(Uuid::new_v4().to_string());
|
||||
let environment_id = Some(self.environment_id.clone());
|
||||
@@ -493,7 +502,7 @@ impl CoreShellActionProvider {
|
||||
if let Some(review_id) = guardian_review_id.clone() {
|
||||
let decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
&step_context_seed,
|
||||
review_id.clone(),
|
||||
GuardianApprovalRequest::Execve {
|
||||
id: call_id.clone(),
|
||||
|
||||
@@ -425,6 +425,7 @@ async fn preapproved_additional_permissions_escalate_intercepted_exec() -> anyho
|
||||
policy: Arc::new(RwLock::new(codex_execpolicy::Policy::empty())),
|
||||
session: Arc::new(session),
|
||||
turn: Arc::new(turn_context),
|
||||
reasoning_effort: None,
|
||||
call_id: "preapproved-additional-permissions".to_string(),
|
||||
environment_id: "local".to_string(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
@@ -561,6 +562,7 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
|
||||
policy: std::sync::Arc::new(RwLock::new(codex_execpolicy::Policy::empty())),
|
||||
session: std::sync::Arc::new(session),
|
||||
turn: std::sync::Arc::new(turn_context),
|
||||
reasoning_effort: None,
|
||||
call_id: "execve-hook-call".to_string(),
|
||||
environment_id: "local".to_string(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
@@ -772,6 +774,7 @@ prefix_rule(pattern = ["{cat_path_literal}"], decision = "allow")
|
||||
policy: Arc::new(RwLock::new(policy)),
|
||||
session: Arc::new(session),
|
||||
turn: Arc::new(turn_context),
|
||||
reasoning_effort: None,
|
||||
call_id: "deny-read-prefix-allow".to_string(),
|
||||
environment_id: "local".to_string(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
@@ -809,6 +812,7 @@ async fn denied_reads_keep_granular_sandbox_rejection_for_escalation() -> anyhow
|
||||
policy: Arc::new(RwLock::new(PolicyParser::new().build())),
|
||||
session: Arc::new(session),
|
||||
turn: Arc::new(turn_context),
|
||||
reasoning_effort: None,
|
||||
call_id: "deny-read-granular-sandbox-reject".to_string(),
|
||||
environment_id: "local".to_string(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
use crate::sandboxing::ExecOptions;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::state::SessionServices;
|
||||
use crate::tools::hook_names::HookToolName;
|
||||
@@ -121,6 +122,7 @@ where
|
||||
pub(crate) struct ApprovalCtx<'a> {
|
||||
pub session: &'a Arc<Session>,
|
||||
pub turn: &'a Arc<TurnContext>,
|
||||
pub step_context: &'a Arc<StepContext>,
|
||||
pub call_id: &'a str,
|
||||
/// Guardian review lifecycle ID for this approval, when guardian is reviewing it.
|
||||
///
|
||||
@@ -384,6 +386,7 @@ pub(crate) trait Sandboxable {
|
||||
pub(crate) struct ToolCtx {
|
||||
pub session: Arc<Session>,
|
||||
pub turn: Arc<TurnContext>,
|
||||
pub step_context: Arc<StepContext>,
|
||||
pub call_id: String,
|
||||
pub tool_name: ToolName,
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ use tokio::sync::Mutex;
|
||||
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::step_context::StepContext;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::shell::ShellType;
|
||||
@@ -75,14 +76,21 @@ pub(crate) const MAX_UNIFIED_EXEC_PROCESSES: usize = 64;
|
||||
pub(crate) struct UnifiedExecContext {
|
||||
pub session: Arc<Session>,
|
||||
pub turn: Arc<TurnContext>,
|
||||
pub step_context: Arc<StepContext>,
|
||||
pub call_id: String,
|
||||
}
|
||||
|
||||
impl UnifiedExecContext {
|
||||
pub fn new(session: Arc<Session>, turn: Arc<TurnContext>, call_id: String) -> Self {
|
||||
pub fn new(
|
||||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
step_context: Arc<StepContext>,
|
||||
call_id: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
session,
|
||||
turn,
|
||||
step_context,
|
||||
call_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +121,12 @@ async fn exec_command_with_tty(
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
let context =
|
||||
UnifiedExecContext::new(Arc::clone(session), Arc::clone(turn), "call".to_string());
|
||||
let context = UnifiedExecContext::new(
|
||||
Arc::clone(session),
|
||||
Arc::clone(turn),
|
||||
StepContext::for_test(Arc::clone(turn)),
|
||||
"call".to_string(),
|
||||
);
|
||||
let started_at = Instant::now();
|
||||
let process_started_alive = !process.has_exited() && process.exit_code().is_none();
|
||||
if process_started_alive {
|
||||
|
||||
@@ -1171,6 +1171,7 @@ impl UnifiedExecProcessManager {
|
||||
let tool_ctx = ToolCtx {
|
||||
session: context.session.clone(),
|
||||
turn: context.turn.clone(),
|
||||
step_context: context.step_context.clone(),
|
||||
call_id: context.call_id.clone(),
|
||||
tool_name: ToolName::plain("exec_command"),
|
||||
};
|
||||
|
||||
@@ -262,6 +262,7 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() {
|
||||
let context = UnifiedExecContext::new(
|
||||
Arc::clone(&session),
|
||||
Arc::clone(&turn),
|
||||
StepContext::for_test(Arc::clone(&turn)),
|
||||
"call-unified-denied".to_string(),
|
||||
);
|
||||
let request = ExecCommandRequest {
|
||||
|
||||
Reference in New Issue
Block a user