This commit is contained in:
Ahmed Ibrahim
2026-07-08 20:59:47 -07:00
parent 3fa90665fe
commit e4c7bdc387
42 changed files with 391 additions and 146 deletions
+21
View File
@@ -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::StepContext;
use crate::session::turn_context::TurnContext;
use codex_login::AuthManager;
use codex_models_manager::manager::SharedModelsManager;
@@ -79,6 +80,7 @@ pub(crate) async fn run_codex_thread_interactive(
models_manager: SharedModelsManager,
parent_session: Arc<Session>,
parent_ctx: Arc<TurnContext>,
step_context: Arc<StepContext>,
cancel_token: CancellationToken,
subagent_source: SubAgentSource,
initial_history: Option<InitialHistory>,
@@ -157,6 +159,7 @@ pub(crate) async fn run_codex_thread_interactive(
// routing them to the parent session for decisions.
let parent_session_clone = Arc::clone(&parent_session);
let parent_ctx_clone = Arc::clone(&parent_ctx);
let step_context_clone = Arc::clone(&step_context);
let codex_for_events = Arc::clone(&codex);
// Cache the child call's MCP metadata at begin time. The later legacy
// RequestUserInput approval event only carries a call_id and question metadata.
@@ -168,6 +171,7 @@ pub(crate) async fn run_codex_thread_interactive(
tx_sub,
parent_session_clone,
parent_ctx_clone,
step_context_clone,
pending_mcp_invocations,
cancel_token_events,
)
@@ -200,6 +204,7 @@ pub(crate) async fn run_codex_thread_one_shot(
input: Vec<UserInput>,
parent_session: Arc<Session>,
parent_ctx: Arc<TurnContext>,
step_context: Arc<StepContext>,
cancel_token: CancellationToken,
subagent_source: SubAgentSource,
final_output_json_schema: Option<Value>,
@@ -214,6 +219,7 @@ pub(crate) async fn run_codex_thread_one_shot(
models_manager,
parent_session,
parent_ctx,
step_context,
child_cancel.clone(),
subagent_source,
initial_history,
@@ -279,6 +285,7 @@ async fn forward_events(
tx_sub: Sender<Event>,
parent_session: Arc<Session>,
parent_ctx: Arc<TurnContext>,
step_context: Arc<StepContext>,
pending_mcp_invocations: Arc<Mutex<HashMap<String, PendingMcpInvocation>>>,
cancel_token: CancellationToken,
) {
@@ -315,6 +322,7 @@ async fn forward_events(
id,
&parent_session,
&parent_ctx,
Arc::clone(&step_context),
event,
&cancel_token,
)
@@ -329,6 +337,7 @@ async fn forward_events(
id,
&parent_session,
&parent_ctx,
Arc::clone(&step_context),
event,
&cancel_token,
)
@@ -342,6 +351,7 @@ async fn forward_events(
&codex,
&parent_session,
&parent_ctx,
&step_context,
event,
&cancel_token,
)
@@ -356,6 +366,7 @@ async fn forward_events(
id,
&parent_session,
&parent_ctx,
Arc::clone(&step_context),
&pending_mcp_invocations,
event,
&cancel_token,
@@ -493,6 +504,7 @@ async fn handle_exec_approval(
turn_id: String,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
step_context: Arc<StepContext>,
event: ExecApprovalRequestEvent,
cancel_token: &CancellationToken,
) {
@@ -515,6 +527,7 @@ async fn handle_exec_approval(
let review_rx = spawn_approval_request_review(
Arc::clone(parent_session),
Arc::clone(parent_ctx),
step_context,
new_guardian_review_id(),
GuardianApprovalRequest::Shell {
id: call_id.clone(),
@@ -578,6 +591,7 @@ async fn handle_patch_approval(
_id: String,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
step_context: Arc<StepContext>,
event: ApplyPatchApprovalRequestEvent,
cancel_token: &CancellationToken,
) {
@@ -628,6 +642,7 @@ async fn handle_patch_approval(
let review_rx = spawn_approval_request_review(
Arc::clone(parent_session),
Arc::clone(parent_ctx),
step_context,
new_guardian_review_id(),
GuardianApprovalRequest::ApplyPatch {
id: approval_id.clone(),
@@ -680,6 +695,7 @@ async fn handle_request_user_input(
id: String,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
step_context: Arc<StepContext>,
pending_mcp_invocations: &Arc<Mutex<HashMap<String, PendingMcpInvocation>>>,
event: RequestUserInputEvent,
cancel_token: &CancellationToken,
@@ -687,6 +703,7 @@ async fn handle_request_user_input(
if let Some(response) = maybe_auto_review_mcp_request_user_input(
parent_session,
parent_ctx,
step_context,
pending_mcp_invocations,
&event,
cancel_token,
@@ -723,6 +740,7 @@ async fn handle_request_user_input(
async fn maybe_auto_review_mcp_request_user_input(
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
step_context: Arc<StepContext>,
pending_mcp_invocations: &Arc<Mutex<HashMap<String, PendingMcpInvocation>>>,
event: &RequestUserInputEvent,
cancel_token: &CancellationToken,
@@ -750,6 +768,7 @@ async fn maybe_auto_review_mcp_request_user_input(
let review_rx = spawn_approval_request_review(
Arc::clone(parent_session),
Arc::clone(parent_ctx),
step_context,
new_guardian_review_id(),
build_guardian_mcp_tool_review_request(&event.call_id, &invocation, metadata.as_ref()),
/*retry_reason*/ None,
@@ -796,6 +815,7 @@ async fn handle_request_permissions(
codex: &Codex,
parent_session: &Arc<Session>,
parent_ctx: &Arc<TurnContext>,
step_context: &Arc<StepContext>,
event: RequestPermissionsEvent,
cancel_token: &CancellationToken,
) {
@@ -811,6 +831,7 @@ async fn handle_request_permissions(
});
let response_fut = parent_session.request_permissions_for_cwd(
parent_ctx,
step_context,
call_id.clone(),
args,
cwd,
+20 -4
View File
@@ -15,6 +15,7 @@ use crate::responses_metadata::CompactionTurnMetadata;
#[cfg(test)]
use crate::session::PreviousTurnSettings;
use crate::session::session::Session;
use crate::session::step_context::StepContext;
use crate::session::turn::get_last_assistant_message_from_turn;
use crate::session::turn_context::TurnContext;
use crate::util::backoff;
@@ -70,13 +71,18 @@ 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(
turn_context,
world_state.as_ref(),
step_context,
)
.await;
(items, Some(Arc::clone(world_state)))
}
@@ -91,6 +97,7 @@ pub(crate) fn should_use_remote_compact_task(provider: &ModelProviderInfo) -> bo
pub(crate) async fn run_inline_auto_compact_task(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
step_context: Arc<StepContext>,
initial_context_injection: InitialContextInjection,
reason: CompactionReason,
phase: CompactionPhase,
@@ -110,6 +117,7 @@ pub(crate) async fn run_inline_auto_compact_task(
run_compact_task_inner(
sess,
turn_context,
step_context,
input,
initial_context_injection,
CompactionTrigger::Auto,
@@ -123,6 +131,7 @@ pub(crate) async fn run_inline_auto_compact_task(
pub(crate) async fn run_compact_task(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
step_context: Arc<StepContext>,
input: Vec<UserInput>,
) -> CodexResult<()> {
let start_event = EventMsg::TurnStarted(TurnStartedEvent {
@@ -130,12 +139,13 @@ pub(crate) async fn run_compact_task(
trace_id: turn_context.trace_id.clone(),
started_at: turn_context.turn_timing_state.started_at_unix_secs().await,
model_context_window: turn_context.model_context_window(),
collaboration_mode_kind: turn_context.collaboration_mode.mode,
collaboration_mode_kind: turn_context.mode,
});
sess.send_event(&turn_context, start_event).await;
run_compact_task_inner(
sess.clone(),
turn_context,
step_context,
input,
InitialContextInjection::DoNotInject,
CompactionTrigger::Manual,
@@ -149,6 +159,7 @@ pub(crate) async fn run_compact_task(
async fn run_compact_task_inner(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
step_context: Arc<StepContext>,
input: Vec<UserInput>,
initial_context_injection: InitialContextInjection,
trigger: CompactionTrigger,
@@ -185,6 +196,7 @@ async fn run_compact_task_inner(
let result = run_compact_task_inner_impl(
Arc::clone(&sess),
Arc::clone(&turn_context),
step_context,
input,
initial_context_injection,
compaction_metadata,
@@ -220,6 +232,7 @@ async fn run_compact_task_inner(
async fn run_compact_task_inner_impl(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
step_context: Arc<StepContext>,
input: Vec<UserInput>,
initial_context_injection: InitialContextInjection,
compaction_metadata: CompactionTurnMetadata,
@@ -262,6 +275,7 @@ async fn run_compact_task_inner_impl(
let attempt_result = drain_to_completed(
&sess,
turn_context.as_ref(),
step_context.as_ref(),
&mut client_session,
&responses_metadata,
&prompt,
@@ -336,6 +350,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;
@@ -346,7 +361,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(turn_context.to_turn_context_item(&step_context))
}
};
let compacted_item = CompactedItem {
@@ -661,6 +676,7 @@ fn build_compacted_history_with_limit(
async fn drain_to_completed(
sess: &Session,
turn_context: &TurnContext,
step_context: &StepContext,
client_session: &mut ModelClientSession,
responses_metadata: &CodexResponsesMetadata,
prompt: &Prompt,
@@ -670,7 +686,7 @@ async fn drain_to_completed(
prompt,
&turn_context.model_info,
&turn_context.session_telemetry,
turn_context.reasoning_effort.clone(),
step_context.effective_reasoning_effort(),
turn_context.reasoning_summary,
turn_context.config.service_tier.clone(),
responses_metadata,
+11 -4
View File
@@ -81,7 +81,7 @@ pub(crate) async fn run_remote_compact_task(
trace_id: turn_context.trace_id.clone(),
started_at: turn_context.turn_timing_state.started_at_unix_secs().await,
model_context_window: turn_context.model_context_window(),
collaboration_mode_kind: turn_context.collaboration_mode.mode,
collaboration_mode_kind: turn_context.mode,
});
sess.send_event(&turn_context, start_event).await;
@@ -260,6 +260,7 @@ async fn run_remote_compact_task_inner_impl(
let (new_history, world_state_baseline) = process_compacted_history(
sess.as_ref(),
compaction_turn_context.as_ref(),
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_turn_context.to_turn_context_item(step_context))
}
};
let compacted_item = CompactedItem {
@@ -304,14 +305,20 @@ 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>>) {
// Mid-turn compaction is the only path that must inject initial context above the last user
// 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;
let (initial_context, world_state_baseline) = build_compaction_initial_context(
sess,
turn_context,
step_context,
initial_context_injection,
)
.await;
compacted_history.retain(should_keep_compacted_history_item);
(
+1 -1
View File
@@ -87,7 +87,7 @@ pub(super) async fn run_remote_compact_attempt(
&turn_context.model_info,
turn_state,
CompactConversationRequestSettings {
effort: turn_context.reasoning_effort.clone(),
effort: step_context.effective_reasoning_effort().clone(),
summary: turn_context.reasoning_summary,
service_tier: if sess.services.auth_manager.auth_mode() == Some(AuthMode::ApiKey) {
None
+5 -3
View File
@@ -92,7 +92,7 @@ pub(crate) async fn run_remote_compact_task(
trace_id: turn_context.trace_id.clone(),
started_at: turn_context.turn_timing_state.started_at_unix_secs().await,
model_context_window: turn_context.model_context_window(),
collaboration_mode_kind: turn_context.collaboration_mode.mode,
collaboration_mode_kind: turn_context.mode,
});
sess.send_event(&turn_context, start_event).await;
@@ -284,6 +284,7 @@ async fn run_remote_compact_task_inner_impl(
let (new_history, world_state_baseline) = process_compacted_history(
sess.as_ref(),
compaction_turn_context.as_ref(),
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_turn_context.to_turn_context_item(step_context))
}
};
let compacted_item = CompactedItem {
@@ -330,6 +331,7 @@ struct RemoteCompactionV2Output {
async fn run_remote_compaction_request_v2(
sess: &Session,
turn_context: &TurnContext,
step_context: &StepContext,
client_session: &mut ModelClientSession,
prompt: &Prompt,
responses_metadata: &CodexResponsesMetadata,
@@ -346,7 +348,7 @@ async fn run_remote_compaction_request_v2(
prompt,
&turn_context.model_info,
&turn_context.session_telemetry,
turn_context.reasoning_effort.clone(),
step_context.effective_reasoning_effort(),
turn_context.reasoning_summary,
turn_context.config.service_tier.clone(),
responses_metadata,
@@ -102,6 +102,7 @@ pub(super) async fn run_remote_compact_v2_attempt(
let compaction_output_result = run_remote_compaction_request_v2(
sess,
turn_context.as_ref(),
step_context.as_ref(),
client_session,
&prompt,
&responses_metadata,
+20 -5
View File
@@ -24,6 +24,7 @@ use codex_protocol::protocol::TurnStartedEvent;
/// observe the same lifecycle as local or remote compaction.
pub(crate) async fn run_manual_compact_task(
sess: Arc<Session>,
step_context: Arc<StepContext>,
turn_context: Arc<TurnContext>,
) -> CodexResult<()> {
let start_event = EventMsg::TurnStarted(TurnStartedEvent {
@@ -31,14 +32,20 @@ pub(crate) async fn run_manual_compact_task(
trace_id: turn_context.trace_id.clone(),
started_at: turn_context.turn_timing_state.started_at_unix_secs().await,
model_context_window: turn_context.model_context_window(),
collaboration_mode_kind: turn_context.collaboration_mode.mode,
collaboration_mode_kind: turn_context.mode,
});
sess.send_event(&turn_context, start_event).await;
// 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,
&turn_context,
&step_context,
world_state,
CompactionTrigger::Manual,
)
.await
}
/// Runs token-budget inline auto-compaction as a normal compaction lifecycle.
@@ -58,12 +65,20 @@ pub(crate) async fn run_inline_auto_compact_task(
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,
turn_context,
&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<()> {
@@ -76,7 +91,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(turn_context.as_ref(), world_state, step_context.as_ref())
.await;
sess.emit_turn_item_completed(turn_context, compaction_item)
.await;
+9 -7
View File
@@ -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;
@@ -70,13 +71,11 @@ fn build_collaboration_mode_update_item(
}
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
}
@@ -85,8 +84,10 @@ fn build_collaboration_mode_update_item(
fn build_multi_agent_mode_update_item(
previous: Option<&TurnContextItem>,
next: &TurnContext,
step_context: &StepContext,
) -> Option<String> {
let effective_multi_agent_mode = crate::session::multi_agents::effective_multi_agent_mode(next);
let effective_multi_agent_mode =
crate::session::multi_agents::effective_multi_agent_mode(next, step_context);
let previous = previous?;
if previous.multi_agent_mode == effective_multi_agent_mode {
return None;
@@ -241,6 +242,7 @@ pub(crate) fn build_settings_update_items(
previous: Option<&TurnContextItem>,
previous_turn_settings: Option<&PreviousTurnSettings>,
next: &TurnContext,
step_context: &StepContext,
exec_policy: &Policy,
personality_feature_enabled: bool,
) -> Vec<ResponseItem> {
@@ -254,7 +256,7 @@ pub(crate) fn build_settings_update_items(
build_model_instructions_update_item(previous_turn_settings, next),
build_permissions_update_item(previous, next, exec_policy),
build_collaboration_mode_update_item(previous, next),
build_multi_agent_mode_update_item(previous, next),
build_multi_agent_mode_update_item(previous, next, step_context),
build_realtime_update_item(previous, previous_turn_settings, next),
build_personality_update_item(previous, next, personality_feature_enabled),
]
+22 -2
View File
@@ -25,6 +25,7 @@ use tokio::time::sleep_until;
use tokio_util::sync::CancellationToken;
use crate::session::session::Session;
use crate::session::step_context::StepContext;
use crate::session::turn_context::TurnContext;
use crate::turn_timing::now_unix_timestamp_ms;
use crate::util::backoff;
@@ -275,6 +276,7 @@ pub(crate) async fn record_guardian_denial_for_test(
async fn run_guardian_review(
session: Arc<Session>,
turn: Arc<TurnContext>,
step_context: Arc<StepContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
@@ -359,6 +361,7 @@ async fn run_guardian_review(
let (outcome, analytics_result) = Box::pin(run_guardian_review_session_with_retry(
session.clone(),
turn.clone(),
step_context.clone(),
request,
retry_reason.clone(),
schema,
@@ -594,6 +597,7 @@ async fn run_guardian_review(
pub(crate) async fn review_approval_request(
session: &Arc<Session>,
turn: &Arc<TurnContext>,
step_context: Arc<StepContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
@@ -603,6 +607,7 @@ pub(crate) async fn review_approval_request(
Box::pin(run_guardian_review(
Arc::clone(session),
Arc::clone(turn),
step_context,
review_id,
request,
retry_reason,
@@ -615,6 +620,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: Arc<StepContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
@@ -624,6 +630,7 @@ pub(crate) async fn review_approval_request_with_cancel(
run_guardian_review(
Arc::clone(session),
Arc::clone(turn),
step_context,
review_id,
request,
retry_reason,
@@ -636,6 +643,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: Arc<StepContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
@@ -654,6 +662,7 @@ pub(crate) fn spawn_approval_request_review(
let decision = runtime.block_on(review_approval_request_with_cancel(
&session,
&turn,
step_context,
review_id,
request,
retry_reason,
@@ -678,6 +687,7 @@ pub(super) struct GuardianReviewSessionConfig {
pub(super) async fn guardian_review_session_config(
session: &Session,
turn: &TurnContext,
step_context: &StepContext,
) -> anyhow::Result<GuardianReviewSessionConfig> {
let network_proxy = session.services.network_proxy.load_full();
let live_network_config = match network_proxy.as_ref() {
@@ -722,7 +732,8 @@ pub(super) async fn guardian_review_session_config(
.supported_reasoning_levels
.iter()
.any(|preset| preset.effort == codex_protocol::openai_models::ReasoningEffort::Low),
turn.reasoning_effort
step_context
.reasoning_effort
.clone()
.or_else(|| turn.model_info.default_reasoning_level.clone()),
);
@@ -768,13 +779,19 @@ pub(super) async fn guardian_review_session_config(
async fn run_guardian_review_session_before_deadline(
session: Arc<Session>,
turn: Arc<TurnContext>,
step_context: Arc<StepContext>,
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
let session_config = match guardian_review_session_config(
session.as_ref(),
turn.as_ref(),
step_context.as_ref(),
)
.await
{
Ok(session_config) => session_config,
Err(err) => {
@@ -790,6 +807,7 @@ async fn run_guardian_review_session_before_deadline(
.run_review(GuardianReviewSessionParams {
parent_session: Arc::clone(&session),
parent_turn: turn.clone(),
step_context,
spawn_config: session_config.spawn_config,
request,
retry_reason,
@@ -862,6 +880,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: Arc<StepContext>,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
schema: serde_json::Value,
@@ -875,6 +894,7 @@ pub(super) async fn run_guardian_review_session_with_retry(
let (outcome, mut analytics_result) = run_guardian_review_session_before_deadline(
Arc::clone(&session),
Arc::clone(&turn),
Arc::clone(&step_context),
request.clone(),
retry_reason.clone(),
schema.clone(),
+17 -4
View File
@@ -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::StepContext;
use crate::session::turn_context::TurnContext;
use codex_config::types::McpServerConfig;
use codex_features::Feature;
@@ -75,6 +76,7 @@ pub(crate) enum GuardianReviewSessionOutcome {
pub(crate) struct GuardianReviewSessionParams {
pub(crate) parent_session: Arc<Session>,
pub(crate) parent_turn: Arc<TurnContext>,
pub(crate) step_context: Arc<StepContext>,
pub(crate) spawn_config: Config,
pub(crate) request: GuardianApprovalRequest,
pub(crate) retry_reason: Option<String>,
@@ -296,12 +298,14 @@ impl GuardianReviewSessionManager {
&self,
parent_session: Arc<Session>,
parent_turn: Arc<TurnContext>,
step_context: Arc<StepContext>,
) -> 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, &parent_turn, &step_context)
.await?
.spawn_config;
let reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(
&spawn_config,
parent_session.user_instructions().await,
@@ -311,6 +315,7 @@ impl GuardianReviewSessionManager {
let review_session = spawn_guardian_review_session(
&parent_session,
&parent_turn,
Arc::clone(&step_context),
spawn_config,
reuse_key,
spawn_cancel_token.clone(),
@@ -396,6 +401,7 @@ impl GuardianReviewSessionManager {
Box::pin(spawn_guardian_review_session(
&params.parent_session,
&params.parent_turn,
Arc::clone(&params.step_context),
params.spawn_config.clone(),
next_reuse_key.clone(),
spawn_cancel_token.clone(),
@@ -607,6 +613,7 @@ impl GuardianReviewSessionManager {
Box::pin(spawn_guardian_review_session(
&params.parent_session,
&params.parent_turn,
Arc::clone(&params.step_context),
fork_config,
reuse_key,
spawn_cancel_token.clone(),
@@ -649,6 +656,7 @@ impl GuardianReviewSessionManager {
async fn spawn_guardian_review_session(
parent_session: &Arc<Session>,
parent_turn: &Arc<TurnContext>,
step_context: Arc<StepContext>,
spawn_config: Config,
reuse_key: GuardianReviewSessionReuseKey,
cancel_token: CancellationToken,
@@ -668,6 +676,7 @@ async fn spawn_guardian_review_session(
parent_session.services.models_manager.clone(),
Arc::clone(parent_session),
Arc::clone(parent_turn),
step_context,
cancel_token.clone(),
SubAgentSource::Other(GUARDIAN_REVIEWER_NAME.to_string()),
initial_history,
@@ -1217,9 +1226,13 @@ mod tests {
)
.expect("guardian config");
let turn = Arc::new(turn);
let step_context = StepContext::for_test(Arc::clone(&turn));
GuardianReviewSessionParams {
parent_session: Arc::new(session),
parent_turn: Arc::new(turn),
parent_turn: Arc::clone(&turn),
step_context,
spawn_config,
request: GuardianApprovalRequest::Shell {
id: "shell-1".to_string(),
+5 -3
View File
@@ -395,7 +395,7 @@ async fn handle_approved_mcp_tool_call(
let result = async {
let rewritten_arguments = rewrite?;
let request_meta =
build_mcp_tool_call_request_meta(turn_context, &server, call_id, metadata);
build_mcp_tool_call_request_meta(step_context, &server, call_id, metadata);
execute_mcp_tool_call(
sess,
step_context,
@@ -1081,18 +1081,19 @@ async fn custom_mcp_tool_approval_mode(
}
fn build_mcp_tool_call_request_meta(
turn_context: &TurnContext,
step_context: &StepContext,
server: &str,
call_id: &str,
metadata: Option<&McpToolApprovalMetadata>,
) -> Option<serde_json::Value> {
let turn_context = step_context.turn.as_ref();
let mut request_meta = serde_json::Map::new();
if let Some(turn_metadata) = turn_context
.turn_metadata_state
.current_meta_value_for_mcp_request(McpTurnMetadataContext {
model: turn_context.model_info.slug.as_str(),
reasoning_effort: turn_context.effective_reasoning_effort(),
reasoning_effort: step_context.effective_reasoning_effort(),
})
{
request_meta.insert(
@@ -1274,6 +1275,7 @@ async fn maybe_request_mcp_tool_approval(
let decision = review_approval_request(
sess,
turn_context,
Arc::clone(step_context),
review_id.clone(),
build_guardian_mcp_tool_review_request(call_id, invocation, metadata),
/*retry_reason*/ None,
+1 -1
View File
@@ -86,7 +86,7 @@ impl Session {
let turn_context = self
.new_default_turn_with_sub_id(uuid::Uuid::new_v4().to_string())
.await;
if turn_context.collaboration_mode.mode == ModeKind::Plan {
if turn_context.mode == ModeKind::Plan {
self.clear_reserved_idle_turn(&turn_state).await;
self.maybe_start_turn_for_pending_work().await;
return Err(TryStartTurnIfIdleError::new(
+4 -2
View File
@@ -588,11 +588,12 @@ async fn review_guardian_mcp_elicitation(
session: Arc<Session>,
request: ElicitationReviewRequest,
) -> anyhow::Result<Option<ElicitationResponse>> {
let Some((turn_context, _cancellation_token)) =
session.active_turn_context_and_cancellation_token().await
let Some((step_context, _cancellation_token)) =
session.active_step_context_and_cancellation_token().await
else {
return Ok(None);
};
let turn_context = Arc::clone(&step_context.turn);
let approvals_reviewer = crate::connectors::mcp_approvals_reviewer(
turn_context.config.as_ref(),
@@ -624,6 +625,7 @@ async fn review_guardian_mcp_elicitation(
let decision = crate::guardian::review_approval_request(
&session,
&turn_context,
step_context,
review_id.clone(),
guardian_request,
/*retry_reason*/ None,
+48 -10
View File
@@ -344,7 +344,6 @@ use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::LocalImagePreparation;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::CodexErrorInfo;
@@ -1730,6 +1729,7 @@ impl Session {
&self,
reference_context_item: Option<&TurnContextItem>,
current_context: &TurnContext,
step_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
@@ -1744,6 +1744,7 @@ impl Session {
reference_context_item,
previous_turn_settings.as_ref(),
current_context,
step_context,
exec_policy.as_ref(),
self.features.enabled(Feature::Personality),
)
@@ -2033,6 +2034,27 @@ impl Session {
))
}
pub(crate) async fn set_active_step_context(&self, step_context: Arc<StepContext>) {
let mut active = self.active_turn.lock().await;
let Some(task) = active.as_mut().and_then(|turn| turn.task.as_mut()) else {
return;
};
if task.turn_context.sub_id == step_context.turn.sub_id {
task.step_context = Some(step_context);
}
}
async fn active_step_context_and_cancellation_token(
&self,
) -> Option<(Arc<StepContext>, CancellationToken)> {
let active = self.active_turn.lock().await;
let task = active.as_ref()?.task.as_ref()?;
Some((
Arc::clone(task.step_context.as_ref()?),
task.cancellation_token.child_token(),
))
}
pub(crate) async fn record_execpolicy_amendment_message(
&self,
sub_id: &str,
@@ -2268,6 +2290,7 @@ impl Session {
pub(crate) async fn request_permissions_for_environment(
self: &Arc<Self>,
turn_context: &Arc<TurnContext>,
step_context: &Arc<StepContext>,
call_id: String,
args: RequestPermissionsArgs,
environment: TurnEnvironmentSelection,
@@ -2326,6 +2349,7 @@ impl Session {
let review_rx = crate::guardian::spawn_approval_request_review(
session,
turn,
step_context.clone(),
review_id,
request,
/*retry_reason*/ None,
@@ -2436,6 +2460,7 @@ impl Session {
pub(crate) async fn request_permissions_for_cwd(
self: &Arc<Self>,
turn_context: &Arc<TurnContext>,
step_context: &Arc<StepContext>,
call_id: String,
args: RequestPermissionsArgs,
cwd: AbsolutePathBuf,
@@ -2460,6 +2485,7 @@ impl Session {
environment.cwd = PathUri::from_abs_path(&cwd);
self.request_permissions_for_environment(
turn_context,
step_context,
call_id,
args,
environment,
@@ -3165,9 +3191,9 @@ impl Session {
&self,
turn_context: &TurnContext,
world_state: &WorldState,
step_context: &StepContext,
) -> 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(turn_context, world_state, step_context)
.await
}
@@ -3175,8 +3201,9 @@ impl Session {
&self,
turn_context: &TurnContext,
world_state: &WorldState,
mcp: &McpRuntimeSnapshot,
step_context: &StepContext,
) -> Vec<ResponseItem> {
let mcp = step_context.mcp.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();
@@ -3447,7 +3474,9 @@ 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(turn_context, step_context)
{
items.push(ContextualUserFragment::into(
MultiAgentModeInstructions::new(multi_agent_mode),
));
@@ -3516,6 +3545,7 @@ impl Session {
&self,
turn_context: &TurnContext,
world_state: Arc<WorldState>,
step_context: &StepContext,
) -> u64 {
let window = {
let mut state = self.state.lock().await;
@@ -3523,9 +3553,13 @@ impl Session {
};
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(
turn_context,
world_state.as_ref(),
step_context,
)
.await;
let turn_context_item = turn_context.to_turn_context_item();
let turn_context_item = turn_context.to_turn_context_item(step_context);
self.replace_compacted_history(
turn_context,
context_items,
@@ -3573,7 +3607,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 = turn_context.to_turn_context_item(step_context);
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 +3617,7 @@ impl Session {
.build_initial_context_with_world_state_and_mcp(
turn_context,
world_state.as_ref(),
step_context.mcp.as_ref(),
step_context,
)
.await;
let snapshot = world_state.snapshot();
@@ -3600,7 +3634,11 @@ 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(),
turn_context,
step_context,
)
.await;
let (world_state_items, world_state_item) = {
let mut state = self.state.lock().await;
+6 -2
View File
@@ -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,10 @@ 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(
turn_context: &TurnContext,
step_context: &StepContext,
) -> Option<MultiAgentMode> {
if turn_context.multi_agent_version != MultiAgentVersion::V2 {
return None;
}
@@ -49,7 +53,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,
},
+4 -3
View File
@@ -70,7 +70,6 @@ pub(super) async fn spawn_review_thread(
let auth_manager_for_context = auth_manager.clone();
let provider_for_context = provider.clone();
let session_telemetry_for_context = session_telemetry.clone();
let reasoning_effort = per_turn_config.model_reasoning_effort.clone();
let reasoning_summary = per_turn_config
.model_reasoning_summary
.unwrap_or(model_info.default_reasoning_summary);
@@ -114,7 +113,6 @@ pub(super) async fn spawn_review_thread(
model_info: model_info.clone(),
session_telemetry: session_telemetry_for_context,
provider: provider_for_context,
reasoning_effort,
reasoning_summary,
session_source,
parent_thread_id: parent_turn_context.parent_thread_id,
@@ -126,7 +124,10 @@ pub(super) async fn spawn_review_thread(
timezone: parent_turn_context.timezone.clone(),
app_server_client_name: parent_turn_context.app_server_client_name.clone(),
developer_instructions: None,
collaboration_mode: parent_turn_context.collaboration_mode.clone(),
mode: parent_turn_context.mode,
collaboration_mode_developer_instructions: parent_turn_context
.collaboration_mode_developer_instructions
.clone(),
multi_agent_version: MultiAgentVersion::Disabled,
personality: parent_turn_context.personality,
approval_policy: parent_turn_context.approval_policy.clone(),
@@ -179,7 +179,7 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -225,7 +225,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif
model: previous_model.to_string(),
comp_hash: Some("comp-hash-a".to_string()),
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -1259,7 +1259,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -1345,7 +1345,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -1376,7 +1376,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -1502,7 +1502,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
model: current_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -1623,7 +1623,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
@@ -1790,7 +1790,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
+21
View File
@@ -12,6 +12,7 @@ use tokio::sync::OnceCell;
#[derive(Debug)]
pub(crate) struct StepContext {
pub(crate) turn: Arc<TurnContext>,
pub(crate) reasoning_effort: Option<codex_protocol::openai_models::ReasoningEffort>,
pub(crate) environments: TurnEnvironmentSnapshot,
/// Capability roots bound to ready environments in this exact step.
pub(crate) selected_capability_roots: Vec<ResolvedSelectedCapabilityRoot>,
@@ -24,6 +25,24 @@ pub(crate) struct StepContext {
}
impl StepContext {
pub(crate) fn effective_reasoning_effort(
&self,
) -> Option<codex_protocol::openai_models::ReasoningEffort> {
if self.turn.model_info.supports_reasoning_summaries {
self.reasoning_effort
.clone()
.or_else(|| self.turn.model_info.default_reasoning_level.clone())
} else {
None
}
}
pub(crate) fn effective_reasoning_effort_for_tracing(&self) -> String {
self.effective_reasoning_effort()
.map(|effort| effort.to_string())
.unwrap_or_else(|| "default".to_string())
}
pub(crate) fn new(
turn: Arc<TurnContext>,
environments: TurnEnvironmentSnapshot,
@@ -31,8 +50,10 @@ impl StepContext {
mcp: Arc<McpRuntimeSnapshot>,
loaded_agents_md: Option<Arc<LoadedAgentsMd>>,
) -> Self {
let reasoning_effort = turn.config.model_reasoning_effort.clone();
Self {
turn,
reasoning_effort,
environments,
selected_capability_roots,
mcp,
+2 -2
View File
@@ -2523,7 +2523,7 @@ async fn turn_start_lifecycle_exposes_turn_metadata_and_token_baseline() {
thread_level_id: session.thread_id.to_string(),
turn_level_id: turn_context.sub_id.clone(),
turn_id: turn_context.sub_id.clone(),
collaboration_mode: turn_context.collaboration_mode.clone(),
collaboration_mode: turn_context.collaboration_mode(),
token_usage_at_turn_start,
saw_session_store: true,
saw_thread_store: true,
@@ -3089,7 +3089,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
collaboration_mode: Some(turn_context.collaboration_mode()),
multi_agent_version: None,
multi_agent_mode: None,
realtime_active: Some(turn_context.realtime_active),
+22 -8
View File
@@ -166,6 +166,8 @@ pub(crate) async fn run_turn(
// run_turn owns the step used to seed context and make the first sampling request.
let first_step_context = sess.capture_step_context(Arc::clone(&turn_context)).await;
sess.set_active_step_context(Arc::clone(&first_step_context))
.await;
// Keep the exact model-visible state used by this turn and its inline compactions.
let (mut world_state, display_roots) = tokio::join!(
sess.record_context_updates_and_set_reference_context_item(first_step_context.as_ref()),
@@ -204,7 +206,8 @@ pub(crate) async fn run_turn(
.await;
}
track_turn_resolved_config_analytics(&sess, &turn_context, &input).await;
track_turn_resolved_config_analytics(&sess, &turn_context, &input, first_step_context.clone())
.await;
let mut last_agent_message: Option<String> = None;
let mut stop_hook_active = false;
@@ -247,7 +250,12 @@ pub(crate) async fn run_turn(
// Capture once so context, advertised tools, and tool calls share one request view.
let step_context = match next_step_context.take() {
Some(step_context) => step_context,
None => sess.capture_step_context(Arc::clone(&turn_context)).await,
None => {
let step_context = sess.capture_step_context(Arc::clone(&turn_context)).await;
sess.set_active_step_context(Arc::clone(&step_context))
.await;
step_context
}
};
let sampling_request_result: CodexResult<_> = async {
super::time_reminder::maybe_record_current_time_reminder(
@@ -744,6 +752,7 @@ async fn track_turn_resolved_config_analytics(
sess: &Session,
turn_context: &TurnContext,
input: &[TurnInput],
first_step_context: Arc<StepContext>,
) {
let thread_config = {
let state = sess.state.lock().await;
@@ -777,7 +786,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.reasoning_effort.clone(),
reasoning_effort: first_step_context.reasoning_effort.clone(),
reasoning_summary: Some(turn_context.reasoning_summary),
service_tier: turn_context
.config
@@ -787,7 +796,7 @@ async fn track_turn_resolved_config_analytics(
approval_policy: turn_context.approval_policy.value(),
approvals_reviewer: turn_context.config.approvals_reviewer,
sandbox_network_access: turn_context.network_sandbox_policy().is_enabled(),
collaboration_mode: turn_context.collaboration_mode.mode,
collaboration_mode: turn_context.mode,
personality: turn_context.personality,
workspace_kind: turn_context.turn_metadata_state.workspace_kind(),
is_first_turn,
@@ -808,6 +817,8 @@ async fn run_pre_sampling_compact(
if token_status.token_limit_reached {
// Pre-turn compaction runs before run_turn creates the normal sampling step.
let step_context = sess.capture_step_context(Arc::clone(turn_context)).await;
sess.set_active_step_context(Arc::clone(&step_context))
.await;
run_auto_compact(
sess,
step_context,
@@ -1020,6 +1031,7 @@ async fn run_auto_compact(
run_inline_auto_compact_task(
Arc::clone(sess),
Arc::clone(turn_context),
Arc::clone(&step_context),
initial_context_injection,
reason,
phase,
@@ -1158,6 +1170,7 @@ async fn run_sampling_request(
tool_runtime.clone(),
Arc::clone(&sess),
Arc::clone(&turn_context),
step_context.clone(),
Arc::clone(&turn_store),
client_session,
responses_metadata,
@@ -1928,6 +1941,7 @@ async fn try_run_sampling_request(
tool_runtime: ToolCallRuntime,
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
step_context: Arc<StepContext>,
turn_store: Arc<codex_extension_api::ExtensionData>,
client_session: &mut ModelClientSession,
responses_metadata: &CodexResponsesMetadata,
@@ -1939,7 +1953,7 @@ async fn try_run_sampling_request(
model = turn_context.model_info.slug.clone(),
approval_policy = turn_context.approval_policy.value(),
sandbox_policy = &turn_context.sandbox_policy(),
effort = turn_context.reasoning_effort,
effort = step_context.effective_reasoning_effort_for_tracing(),
auth_mode = sess.services.auth_manager.auth_mode(),
features = sess.features.enabled_features(),
);
@@ -1959,7 +1973,7 @@ async fn try_run_sampling_request(
prompt,
&turn_context.model_info,
&turn_context.session_telemetry,
turn_context.reasoning_effort.clone(),
step_context.effective_reasoning_effort(),
turn_context.reasoning_summary,
turn_context.config.service_tier.clone(),
responses_metadata,
@@ -1979,8 +1993,8 @@ async fn try_run_sampling_request(
)> = None;
let mut should_emit_turn_diff = false;
let mut should_emit_token_count = false;
let reasoning_effort = turn_context.effective_reasoning_effort_for_tracing();
let plan_mode = turn_context.collaboration_mode.mode == ModeKind::Plan;
let reasoning_effort = step_context.effective_reasoning_effort_for_tracing();
let plan_mode = turn_context.mode == ModeKind::Plan;
let mut assistant_message_stream_parsers = AssistantMessageStreamParsers::new(plan_mode);
let mut plan_mode_state = plan_mode.then(|| PlanModeStreamState::new(&turn_context.sub_id));
let defer_streamed_turn_items_for_contributors =
+45 -43
View File
@@ -108,7 +108,6 @@ pub struct TurnContext {
pub(crate) model_info: ModelInfo,
pub(crate) session_telemetry: SessionTelemetry,
pub(crate) provider: SharedModelProvider,
pub(crate) reasoning_effort: Option<ReasoningEffortConfig>,
pub(crate) reasoning_summary: ReasoningSummaryConfig,
pub(crate) session_source: SessionSource,
pub(crate) parent_thread_id: Option<ThreadId>,
@@ -123,7 +122,8 @@ pub struct TurnContext {
pub(crate) timezone: Option<String>,
pub(crate) app_server_client_name: Option<String>,
pub(crate) developer_instructions: Option<String>,
pub(crate) collaboration_mode: CollaborationMode,
pub(crate) mode: ModeKind,
pub(crate) collaboration_mode_developer_instructions: Option<String>,
pub(crate) multi_agent_version: MultiAgentVersion,
pub(crate) personality: Option<Personality>,
pub(crate) approval_policy: Constrained<AskForApproval>,
@@ -149,6 +149,17 @@ enum TurnMultiAgentRuntime {
}
impl TurnContext {
pub(crate) fn collaboration_mode(&self) -> CollaborationMode {
CollaborationMode {
mode: self.mode,
settings: Settings {
model: self.model_info.slug.clone(),
reasoning_effort: self.config.model_reasoning_effort.clone(),
developer_instructions: self.collaboration_mode_developer_instructions.clone(),
},
}
}
pub(crate) fn permission_profile(&self) -> PermissionProfile {
self.permission_profile.clone()
}
@@ -169,22 +180,6 @@ impl TurnContext {
)
}
pub(crate) fn effective_reasoning_effort(&self) -> Option<ReasoningEffortConfig> {
if self.model_info.supports_reasoning_summaries {
self.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()
.map(|effort| effort.to_string())
.unwrap_or_else(|| "default".to_string())
}
pub(crate) fn model_context_window(&self) -> Option<i64> {
let effective_context_window_percent = self.model_info.effective_context_window_percent;
self.model_info
@@ -220,29 +215,24 @@ impl TurnContext {
.iter()
.map(|preset| preset.effort.clone())
.collect::<Vec<_>>();
let reasoning_effort = if let Some(current_reasoning_effort) = self.reasoning_effort.clone()
{
if supported_reasoning_levels.contains(&current_reasoning_effort) {
Some(current_reasoning_effort)
let reasoning_effort =
if let Some(current_reasoning_effort) = self.config.model_reasoning_effort.clone() {
if supported_reasoning_levels.contains(&current_reasoning_effort) {
Some(current_reasoning_effort)
} else {
supported_reasoning_levels
.get(supported_reasoning_levels.len().saturating_sub(1) / 2)
.cloned()
.or_else(|| model_info.default_reasoning_level.clone())
}
} else {
supported_reasoning_levels
.get(supported_reasoning_levels.len().saturating_sub(1) / 2)
.cloned()
.or_else(|| model_info.default_reasoning_level.clone())
}
} else {
supported_reasoning_levels
.get(supported_reasoning_levels.len().saturating_sub(1) / 2)
.cloned()
.or_else(|| model_info.default_reasoning_level.clone())
};
};
config.model_reasoning_effort = reasoning_effort.clone();
let collaboration_mode = self.collaboration_mode.with_updates(
Some(model.clone()),
Some(reasoning_effort.clone()),
/*developer_instructions*/ None,
);
let available_models = models_manager
.list_models(RefreshStrategy::OnlineIfUncached)
.await;
@@ -259,7 +249,6 @@ impl TurnContext {
.clone()
.with_model(model.as_str(), model_info.slug.as_str()),
provider: self.provider.clone(),
reasoning_effort,
reasoning_summary: self.reasoning_summary,
session_source: self.session_source.clone(),
parent_thread_id: self.parent_thread_id,
@@ -271,7 +260,10 @@ impl TurnContext {
timezone: self.timezone.clone(),
app_server_client_name: self.app_server_client_name.clone(),
developer_instructions: self.developer_instructions.clone(),
collaboration_mode,
mode: self.mode,
collaboration_mode_developer_instructions: self
.collaboration_mode_developer_instructions
.clone(),
multi_agent_version: self.multi_agent_version,
personality: self.personality,
approval_policy: self.approval_policy.clone(),
@@ -350,7 +342,7 @@ impl TurnContext {
.then_some(file_system_sandbox_policy)
}
pub(crate) fn to_turn_context_item(&self) -> TurnContextItem {
pub(crate) fn to_turn_context_item(&self, step_context: &StepContext) -> TurnContextItem {
let workspace_roots = self.config.effective_workspace_roots();
#[allow(deprecated)]
let cwd = self.cwd.clone();
@@ -368,11 +360,18 @@ impl TurnContext {
model: self.model_info.slug.clone(),
comp_hash: self.model_info.comp_hash.clone(),
personality: self.personality,
collaboration_mode: Some(self.collaboration_mode.clone()),
collaboration_mode: Some(CollaborationMode {
mode: self.mode,
settings: Settings {
model: self.model_info.slug.clone(),
reasoning_effort: step_context.reasoning_effort.clone(),
developer_instructions: self.collaboration_mode_developer_instructions.clone(),
},
}),
multi_agent_version: Some(self.multi_agent_version),
multi_agent_mode: super::multi_agents::effective_multi_agent_mode(self),
multi_agent_mode: super::multi_agents::effective_multi_agent_mode(self, step_context),
realtime_active: Some(self.realtime_active),
effort: self.reasoning_effort.clone(),
effort: step_context.effective_reasoning_effort(),
summary: ReasoningSummaryConfig::Auto,
}
}
@@ -485,7 +484,7 @@ impl Session {
sub_id: String,
skills_snapshot: HostSkillsSnapshot,
) -> TurnContext {
let reasoning_effort = session_configuration.collaboration_mode.reasoning_effort();
let collaboration_mode = &session_configuration.collaboration_mode;
let reasoning_summary = session_configuration
.model_reasoning_summary
.unwrap_or(model_info.default_reasoning_summary);
@@ -537,7 +536,6 @@ impl Session {
model_info,
session_telemetry: session_telemetry_for_context,
provider: provider_for_context,
reasoning_effort,
reasoning_summary,
session_source,
parent_thread_id: session_configuration.parent_thread_id,
@@ -549,7 +547,11 @@ impl Session {
timezone: Some(timezone),
app_server_client_name: session_configuration.app_server_client_name.clone(),
developer_instructions: session_configuration.developer_instructions.clone(),
collaboration_mode: session_configuration.collaboration_mode.clone(),
mode: collaboration_mode.mode,
collaboration_mode_developer_instructions: collaboration_mode
.settings
.developer_instructions
.clone(),
multi_agent_version,
personality: session_configuration.personality,
approval_policy: session_configuration.approval_policy.clone(),
+10 -5
View File
@@ -251,13 +251,21 @@ async fn schedule_startup_prewarm_inner(
prewarm_started_at.elapsed(),
/*status*/ None,
);
let step_context = session
.capture_step_context(Arc::clone(&startup_turn_context))
.await;
if routes_approval_to_guardian(&startup_turn_context) {
let guardian_session = Arc::clone(&session);
let guardian_parent_turn = Arc::clone(&startup_turn_context);
let guardian_step_context = Arc::clone(&step_context);
drop(tokio::spawn(async move {
if let Err(err) = guardian_session
.guardian_review_session
.initialize(Arc::clone(&guardian_session), guardian_parent_turn)
.initialize(
Arc::clone(&guardian_session),
guardian_parent_turn,
guardian_step_context,
)
.await
{
warn!("failed to initialize guardian review session: {err:#}");
@@ -267,9 +275,6 @@ async fn schedule_startup_prewarm_inner(
let startup_cancellation_token = CancellationToken::new();
let built_tools_started_at = Instant::now();
// Startup prewarm runs before run_turn and needs its own tool-building snapshot.
let step_context = session
.capture_step_context(Arc::clone(&startup_turn_context))
.await;
let startup_router = built_tools(
session.as_ref(),
step_context.as_ref(),
@@ -310,7 +315,7 @@ async fn schedule_startup_prewarm_inner(
&startup_prompt,
&startup_turn_context.model_info,
&startup_turn_context.session_telemetry,
startup_turn_context.reasoning_effort.clone(),
step_context.effective_reasoning_effort(),
startup_turn_context.reasoning_summary,
startup_turn_context.config.service_tier.clone(),
&responses_metadata,
+2
View File
@@ -20,6 +20,7 @@ use tokio::sync::oneshot;
use crate::agent::control::AgentExecutionGuard;
use crate::session::TurnInputQueue;
use crate::session::step_context::StepContext;
use crate::session::turn_context::TurnContext;
use crate::tasks::AnySessionTask;
use codex_protocol::models::AdditionalPermissionProfile;
@@ -76,6 +77,7 @@ pub(crate) struct RunningTask {
pub(crate) cancellation_token: CancellationToken,
pub(crate) handle: AbortOnDropHandle<()>,
pub(crate) turn_context: Arc<TurnContext>,
pub(crate) step_context: Option<Arc<StepContext>>,
pub(crate) turn_extension_data: Arc<ExtensionData>,
pub(crate) _agent_execution_guard: Option<AgentExecutionGuard>,
// Timer recorded when the task drops to capture the full turn duration.
+2 -2
View File
@@ -214,7 +214,7 @@ pub(crate) async fn record_completed_response_item_with_finalized_facts(
|| {
completed_item_defers_mailbox_delivery_to_next_turn(
item,
turn_context.collaboration_mode.mode == ModeKind::Plan,
turn_context.mode == ModeKind::Plan,
)
},
|facts| facts.defers_mailbox_delivery_to_next_turn,
@@ -408,7 +408,7 @@ pub(crate) async fn handle_output_item_done(
previously_active_item: Option<TurnItem>,
) -> Result<OutputItemResult> {
let mut output = OutputItemResult::default();
let plan_mode = ctx.turn_context.collaboration_mode.mode == ModeKind::Plan;
let plan_mode = ctx.turn_context.mode == ModeKind::Plan;
match ToolRouter::build_tool_call(item.clone()) {
// The model emitted a tool call; log it, persist the item immediately, and queue the tool execution.
+7 -2
View File
@@ -32,8 +32,13 @@ impl SessionTask for CompactTask {
_cancellation_token: CancellationToken,
) -> SessionTaskResult {
let session = session.clone_session();
let step_context = session.capture_step_context(Arc::clone(&ctx)).await;
session
.set_active_step_context(Arc::clone(&step_context))
.await;
if ctx.config.features.enabled(Feature::TokenBudget) {
crate::compact_token_budget::run_manual_compact_task(session, ctx).await?;
crate::compact_token_budget::run_manual_compact_task(session, step_context, ctx)
.await?;
return Ok(None);
}
@@ -73,7 +78,7 @@ impl SessionTask for CompactTask {
// Compaction prompt is synthesized; no UI element ranges to preserve.
text_elements: Vec::new(),
}];
crate::compact::run_compact_task(session.clone(), ctx, input).await
crate::compact::run_compact_task(session.clone(), ctx, step_context, input).await
};
if let Err(err @ CodexErr::TurnAborted) = result {
return Err(err);
+2 -1
View File
@@ -12,11 +12,12 @@ impl Session {
turn_context: &TurnContext,
token_usage_at_turn_start: &TokenUsage,
) {
let collaboration_mode = turn_context.collaboration_mode();
for contributor in self.services.extensions.turn_lifecycle_contributors() {
contributor
.on_turn_start(codex_extension_api::TurnStartInput {
turn_id: turn_context.sub_id.as_str(),
collaboration_mode: &turn_context.collaboration_mode,
collaboration_mode: &collaboration_mode,
token_usage_at_turn_start,
session_store: &self.services.session_extension_data,
thread_store: &self.services.thread_extension_data,
+7 -1
View File
@@ -383,7 +383,12 @@ 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
.config
.model_reasoning_effort
.as_ref()
.map(ToString::to_string)
.unwrap_or_else(|| "default".to_string());
let task_span = info_span!(
"turn",
otel.name = span_name,
@@ -443,6 +448,7 @@ impl Session {
task,
cancellation_token,
turn_context: Arc::clone(&turn_context),
step_context: None,
turn_extension_data,
_agent_execution_guard: agent_execution_guard,
_timer: timer,
+1 -1
View File
@@ -52,7 +52,7 @@ impl SessionTask for RegularTask {
trace_id: ctx.trace_id.clone(),
started_at: ctx.turn_timing_state.started_at_unix_secs().await,
model_context_window: ctx.model_context_window(),
collaboration_mode_kind: ctx.collaboration_mode.mode,
collaboration_mode_kind: ctx.mode,
});
sess.send_event(ctx.as_ref(), event).await;
sess.set_server_reasoning_included(/*included*/ false).await;
+5
View File
@@ -22,6 +22,7 @@ use crate::codex_delegate::run_codex_thread_one_shot;
use crate::config::Constrained;
use crate::session::TurnInput;
use crate::session::session::Session;
use crate::session::step_context::StepContext;
use crate::session::turn_context::TurnContext;
use crate::state::TaskKind;
use codex_features::Feature;
@@ -70,11 +71,13 @@ impl SessionTask for ReviewTask {
}
}
let step_context = session.session.capture_step_context(Arc::clone(&ctx)).await;
// Start sub-codex conversation and get the receiver for events.
let output = match start_review_conversation(
session.clone(),
ctx.clone(),
user_input,
step_context,
cancellation_token.clone(),
)
.await
@@ -97,6 +100,7 @@ async fn start_review_conversation(
session: Arc<SessionTaskContext>,
ctx: Arc<TurnContext>,
input: Vec<UserInput>,
step_context: Arc<StepContext>,
cancellation_token: CancellationToken,
) -> Option<async_channel::Receiver<Event>> {
let config = ctx.config.clone();
@@ -129,6 +133,7 @@ async fn start_review_conversation(
input,
session.clone_session(),
ctx.clone(),
step_context,
cancellation_token,
SubAgentSource::Review,
/*final_output_json_schema*/ None,
+1 -1
View File
@@ -120,7 +120,7 @@ pub(crate) async fn execute_user_shell_command(
trace_id: turn_context.trace_id.clone(),
started_at: turn_context.turn_timing_state.started_at_unix_secs().await,
model_context_window: turn_context.model_context_window(),
collaboration_mode_kind: turn_context.collaboration_mode.mode,
collaboration_mode_kind: turn_context.mode,
});
session.send_event(turn_context.as_ref(), event).await;
}
@@ -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;
@@ -108,6 +109,7 @@ 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 multi_agent_version = turn.multi_agent_version;
@@ -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, turn.as_ref(), step_context)?;
Ok(JobRunnerOptions {
max_concurrency,
spawn_config,
@@ -36,6 +36,7 @@ impl SpawnAgentsOnCsvHandler {
let ToolInvocation {
session,
turn,
step_context,
payload,
..
} = invocation;
@@ -49,7 +50,7 @@ impl SpawnAgentsOnCsvHandler {
}
};
handle(session, turn, arguments)
handle(session, turn, step_context, arguments)
.await
.map(boxed_tool_output)
}
@@ -69,6 +70,7 @@ impl CoreToolRuntime for SpawnAgentsOnCsvHandler {
pub async fn handle(
session: Arc<Session>,
turn: Arc<TurnContext>,
step_context: Arc<StepContext>,
arguments: String,
) -> Result<FunctionToolOutput, FunctionCallError> {
let args: SpawnAgentsOnCsvArgs = parse_arguments(arguments.as_str())?;
@@ -182,7 +184,14 @@ 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 {
let options = match build_runner_options(
&session,
&turn,
step_context.as_ref(),
requested_concurrency,
)
.await
{
Ok(options) => options,
Err(err) => {
let error_message = err.to_string();
@@ -1,6 +1,7 @@
use super::*;
use crate::agent::next_thread_spawn_depth;
use crate::session::session::Session;
use crate::session::step_context::StepContext;
use crate::session::turn_context::TurnContext;
use crate::tools::handlers::multi_agents_spec::create_resume_agent_tool;
use codex_tools::ToolSpec;
@@ -35,6 +36,7 @@ async fn handle_resume_agent(
let ToolInvocation {
session,
turn,
step_context,
payload,
call_id,
..
@@ -88,6 +90,7 @@ async fn handle_resume_agent(
match Box::pin(try_resume_closed_agent(
&session,
&turn,
step_context.as_ref(),
receiver_thread_id,
child_depth,
))
@@ -188,10 +191,11 @@ 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 config = build_agent_resume_config(turn.as_ref(), 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(turn.as_ref(), 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,11 @@ 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,
turn.as_ref(),
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;
@@ -161,28 +162,32 @@ pub(crate) fn parse_collab_input(
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(turn, 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(
turn: &TurnContext,
step_context: &StepContext,
) -> Result<Config, FunctionCallError> {
let mut config = build_agent_shared_config(turn, 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(
turn: &TurnContext,
step_context: &StepContext,
) -> Result<Config, FunctionCallError> {
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
.reasoning_effort
.clone()
.or_else(|| turn.model_info.default_reasoning_level.clone());
config.model_reasoning_effort = step_context.effective_reasoning_effort().clone();
config.model_reasoning_summary = Some(turn.reasoning_summary);
config.developer_instructions = turn.developer_instructions.clone();
apply_spawn_agent_runtime_overrides(&mut config, turn)?;
@@ -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(turn.as_ref(), 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,11 @@ 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,
turn.as_ref(),
step_context.as_ref(),
)?;
if let Some(service_tier) = args.service_tier.as_ref() {
config.service_tier = Some(service_tier.clone());
}
+1 -1
View File
@@ -81,7 +81,7 @@ impl PlanHandler {
}
};
if turn.collaboration_mode.mode == ModeKind::Plan {
if turn.mode == ModeKind::Plan {
return Err(FunctionCallError::RespondToModel(
"update_plan is a TODO/checklist tool and is not allowed in Plan mode".to_string(),
));
@@ -95,6 +95,7 @@ impl RequestPermissionsHandler {
let response = session
.request_permissions_for_environment(
&turn,
&step_context,
call_id,
args,
turn_environment.selection(),
+2
View File
@@ -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;
@@ -384,6 +385,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,
}
+9 -2
View File
@@ -1,10 +1,12 @@
set working-directory := "codex-rs"
set positional-arguments
set positional-arguments := true
export JUST_SHELL := justfile_directory() / "scripts/just-shell.py"
set shell := ["python3", "-c", 'import os, runpy; runpy.run_path(os.environ["JUST_SHELL"], run_name="__main__")']
set windows-shell := ["python", "-c", 'import os, runpy; runpy.run_path(os.environ["JUST_SHELL"], run_name="__main__")']
rust_min_stack := "8388608" # 8 MiB
rust_min_stack := "8388608"
python := if os_family() == "windows" { "python" } else { "python3" }
# Display help
@@ -12,7 +14,9 @@ help:
just -l
# `codex`
alias c := codex
codex *args:
cargo run --bin codex -- {args}
@@ -76,6 +80,7 @@ install:
#
# Run `cargo install --locked cargo-nextest` if you don't have it installed.
# Prefer this for routine local runs. Workspace crate features are banned, so
# there should be no need to add `--all-features`.
[unix]
test *args:
@@ -86,6 +91,7 @@ test *args:
$env:RUST_MIN_STACK = "{{ rust_min_stack }}"; $env:NEXTEST_PROFILE = "local"; cargo nextest run --no-fail-fast @($args | Select-Object -Skip 1)
# Run from the repository root so scripts that resolve paths from `cwd` see
# the same layout they use in GitHub Actions.
[no-cd]
test-github-scripts:
@@ -101,6 +107,7 @@ bench-smoke:
# Build and run Codex from source using Bazel.
# On Unix, use `[no-cd]` and `--run_under="cd $PWD &&"` to ensure Bazel runs
# the command in the current working directory.
[no-cd]
[unix]