core: remove redundant TurnContext and Prompt fields (#28638)

## Why

`TurnContext` had accumulated dead fields and cached projections of
values already owned by its per-turn `Config` or `ModelInfo`. Keeping
both copies made ownership unclear and allowed artificial split-brain
states, such as a compatibility hash differing from the model metadata
it came from.

`Prompt` similarly carried a write-only personality after personality
selection had already been materialized into its base instructions.

This makes the canonical owner explicit: configuration-backed values
come from `config`, model-derived values come from `model_info`, and
prompts contain only data consumed by request construction.

## What changed

- Remove the unused `ghost_snapshot`, `codex_self_exe`, and
`thread_source` fields.
- Remove duplicate `comp_hash`, `truncation_policy`, `features`,
`shell_environment_policy`, `codex_linux_sandbox_exe`, `compact_prompt`,
and `tool_mode` fields.
- Read those values directly from `TurnContext::config` or
`TurnContext::model_info` at their consumers.
- Remove the write-only `Prompt::personality` field and its constructor
assignments.
- Preserve review-turn inheritance of the parent turn's shell policy,
Linux sandbox executable, and compact prompt through the review config.

## Testing

- `cargo check -p codex-core --tests`
This commit is contained in:
pakrym-oai
2026-06-16 22:17:24 -07:00
committed by GitHub
Unverified
parent cb15c64760
commit 172b2218a5
42 changed files with 225 additions and 245 deletions
-5
View File
@@ -1,5 +1,4 @@
pub use codex_api::ResponseEvent;
use codex_config::types::Personality;
use codex_protocol::error::Result;
use codex_protocol::models::BaseInstructions;
use codex_protocol::models::ContentItem;
@@ -29,9 +28,6 @@ pub struct Prompt {
pub base_instructions: BaseInstructions,
/// Optionally specify the personality of the model.
pub personality: Option<Personality>,
/// Optional the output schema for the model's response.
pub output_schema: Option<Value>,
@@ -46,7 +42,6 @@ impl Default for Prompt {
tools: Vec::new(),
parallel_tool_calls: false,
base_instructions: BaseInstructions::default(),
personality: None,
output_schema: None,
output_schema_strict: true,
}
+7 -3
View File
@@ -77,7 +77,12 @@ pub(crate) async fn run_inline_auto_compact_task(
reason: CompactionReason,
phase: CompactionPhase,
) -> CodexResult<()> {
let prompt = turn_context.compact_prompt().to_string();
let prompt = turn_context
.config
.compact_prompt
.as_deref()
.unwrap_or(SUMMARIZATION_PROMPT)
.to_string();
let input = vec![UserInput::Text {
text: prompt,
// Compaction prompt is synthesized; no UI element ranges to preserve.
@@ -209,7 +214,7 @@ async fn run_compact_task_inner_impl(
let mut history = sess.clone_history().await;
history.record_items(
&[initial_input_for_turn.into()],
turn_context.truncation_policy,
turn_context.model_info.truncation_policy.into(),
);
let max_retries = turn_context.provider.info().stream_max_retries();
@@ -234,7 +239,6 @@ async fn run_compact_task_inner_impl(
let prompt = Prompt {
input: turn_input,
base_instructions: sess.get_base_instructions().await,
personality: turn_context.personality,
..Default::default()
};
let attempt_result = drain_to_completed(
-1
View File
@@ -228,7 +228,6 @@ async fn run_remote_compact_task_inner_impl(
tools: tool_router.model_visible_specs(),
parallel_tool_calls: turn_context.model_info.supports_parallel_tool_calls,
base_instructions,
personality: turn_context.personality,
output_schema: None,
output_schema_strict: true,
};
-1
View File
@@ -237,7 +237,6 @@ async fn run_remote_compact_task_inner_impl(
tools: tool_router.model_visible_specs(),
parallel_tool_calls: turn_context.model_info.supports_parallel_tool_calls,
base_instructions,
personality: turn_context.personality,
output_schema: None,
output_schema_strict: true,
};
+6 -2
View File
@@ -63,8 +63,12 @@ fn build_permissions_update_item(
exec_policy,
#[allow(deprecated)]
&next.cwd,
next.features.enabled(Feature::ExecPermissionApprovals),
next.features.enabled(Feature::RequestPermissionsTool),
next.config
.features
.enabled(Feature::ExecPermissionApprovals),
next.config
.features
.enabled(Feature::RequestPermissionsTool),
)
.render(),
)
+7 -3
View File
@@ -626,7 +626,11 @@ async fn maybe_request_codex_apps_auth_elicitation(
return result;
}
if !turn_context.features.enabled(Feature::AuthElicitation) {
if !turn_context
.config
.features
.enabled(Feature::AuthElicitation)
{
return result;
}
@@ -723,10 +727,10 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state(
let sandbox_state = serde_json::to_value(SandboxState {
permission_profile: Some(turn_context.permission_profile()),
sandbox_policy: turn_context.sandbox_policy(),
codex_linux_sandbox_exe: turn_context.codex_linux_sandbox_exe.clone(),
codex_linux_sandbox_exe: turn_context.config.codex_linux_sandbox_exe.clone(),
#[allow(deprecated)]
sandbox_cwd: turn_context.cwd.to_path_buf(),
use_legacy_landlock: turn_context.features.use_legacy_landlock(),
use_legacy_landlock: turn_context.config.features.use_legacy_landlock(),
})?;
match meta.as_mut() {
+9 -9
View File
@@ -1316,15 +1316,14 @@ async fn codex_apps_auth_elicitation_non_host_owned_server_returns_original_resu
let (session, mut turn_context, rx_event) = make_session_and_context_with_rx().await;
let mut features = Features::with_defaults();
features.enable(Feature::AuthElicitation);
Arc::get_mut(&mut turn_context)
.expect("single turn context ref")
.features = ManagedFeatures::from(features);
let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref");
Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features);
let result = codex_apps_auth_failure_result();
let metadata = codex_apps_auth_failure_metadata();
let returned = maybe_request_codex_apps_auth_elicitation(
&session,
&turn_context,
turn_context,
"call_123",
CODEX_APPS_MCP_SERVER_NAME,
Some(&metadata),
@@ -1343,7 +1342,7 @@ async fn codex_apps_auth_elicitation_disallowed_by_policy_returns_original_resul
let mut features = Features::with_defaults();
features.enable(Feature::AuthElicitation);
let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref");
turn_context.features = ManagedFeatures::from(features);
Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features);
turn_context
.approval_policy
.set(AskForApproval::Never)
@@ -1372,7 +1371,7 @@ async fn codex_apps_auth_elicitation_granular_mcp_disabled_returns_original_resu
let mut features = Features::with_defaults();
features.enable(Feature::AuthElicitation);
let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref");
turn_context.features = ManagedFeatures::from(features);
Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features);
turn_context
.approval_policy
.set(AskForApproval::Granular(GranularApprovalConfig {
@@ -1407,9 +1406,10 @@ async fn codex_apps_auth_elicitation_feature_enabled_requests_elicitation() {
*session.active_turn.lock().await = Some(ActiveTurn::default());
let mut features = Features::with_defaults();
features.enable(Feature::AuthElicitation);
Arc::get_mut(&mut turn_context)
.expect("single turn context ref")
.features = ManagedFeatures::from(features);
{
let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref");
Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features);
}
let result = codex_apps_auth_failure_result();
let metadata = codex_apps_auth_failure_metadata();
+26 -8
View File
@@ -149,7 +149,6 @@ use codex_thread_store::ReadThreadParams;
use codex_thread_store::ResumeThreadParams;
use codex_thread_store::ThreadPersistenceMetadata;
use codex_thread_store::ThreadStore;
use codex_utils_output_truncation::TruncationPolicy;
use codex_utils_path_uri::PathUri;
use futures::future::BoxFuture;
use futures::future::Shared;
@@ -198,7 +197,6 @@ use codex_config::ConfigLayerSource;
use codex_config::ConfigLayerStackOrdering;
use codex_config::types::McpServerConfig;
use codex_model_provider_info::ModelProviderInfo;
use codex_protocol::config_types::ShellEnvironmentPolicy;
use codex_protocol::error::CodexErr;
use codex_protocol::error::Result as CodexResult;
#[cfg(test)]
@@ -1330,7 +1328,11 @@ impl Session {
} = self
.reconstruct_history_from_rollout(turn_context, rollout_items)
.await;
if turn_context.features.enabled(Feature::ResizeAllImages) {
if turn_context
.config
.features
.enabled(Feature::ResizeAllImages)
{
// Keep the recorded rollout unchanged. Prepare its reconstructed history before
// installing it, so legacy images are processed once for this resume or fork and
// will be processed again if the rollout is reconstructed in a future session.
@@ -2630,7 +2632,11 @@ impl Session {
turn_context: &TurnContext,
items: &'a [ResponseItem],
) -> Cow<'a, [ResponseItem]> {
if !turn_context.features.enabled(Feature::ResizeAllImages) {
if !turn_context
.config
.features
.enabled(Feature::ResizeAllImages)
{
return Cow::Borrowed(items);
}
@@ -2644,7 +2650,11 @@ impl Session {
turn_context: &TurnContext,
input: Vec<UserInput>,
) -> ResponseItem {
let local_image_preparation = if turn_context.features.enabled(Feature::ResizeAllImages) {
let local_image_preparation = if turn_context
.config
.features
.enabled(Feature::ResizeAllImages)
{
LocalImagePreparation::Defer
} else {
LocalImagePreparation::Process
@@ -2664,7 +2674,10 @@ impl Session {
let items = items.as_ref();
{
let mut state = self.state.lock().await;
state.record_items(items.iter(), turn_context.truncation_policy);
state.record_items(
items.iter(),
turn_context.model_info.truncation_policy.into(),
);
}
self.persist_rollout_response_items(items).await;
self.send_raw_response_items(turn_context, items).await;
@@ -2683,7 +2696,10 @@ impl Session {
let items = items.as_ref();
{
let mut state = self.state.lock().await;
state.record_items(items.iter(), turn_context.truncation_policy);
state.record_items(
items.iter(),
turn_context.model_info.truncation_policy.into(),
);
}
self.persist_rollout_items(&[RolloutItem::InterAgentCommunication(communication)])
.await;
@@ -2885,9 +2901,11 @@ impl Session {
#[allow(deprecated)]
&turn_context.cwd,
turn_context
.config
.features
.enabled(Feature::ExecPermissionApprovals),
turn_context
.config
.features
.enabled(Feature::RequestPermissionsTool),
)
@@ -3031,7 +3049,7 @@ impl Session {
contextual_user_sections.push(user_instructions.to_string());
}
// This is full-context metadata. Steady-state context diffs should not re-emit it.
if turn_context.features.enabled(Feature::TokenBudget)
if turn_context.config.features.enabled(Feature::TokenBudget)
&& let Some(model_context_window) = turn_context.model_context_window()
{
developer_sections.push(
+8 -20
View File
@@ -1,6 +1,5 @@
use super::*;
use codex_core_skills::HostLoadedSkills;
use codex_protocol::openai_models::ToolMode;
use std::sync::atomic::AtomicBool;
/// Spawn a review thread using the given prompt.
@@ -47,15 +46,14 @@ pub(super) async fn spawn_review_thread(
let mut per_turn_config = (*config).clone();
per_turn_config.model = Some(model.clone());
per_turn_config.features = review_features.clone();
let tool_mode = model_info.tool_mode.unwrap_or_else(|| {
if per_turn_config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if per_turn_config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
per_turn_config.permissions.shell_environment_policy = parent_turn_context
.config
.permissions
.shell_environment_policy
.clone();
per_turn_config.codex_linux_sandbox_exe =
parent_turn_context.config.codex_linux_sandbox_exe.clone();
per_turn_config.compact_prompt = parent_turn_context.config.compact_prompt.clone();
if let Err(err) = per_turn_config.web_search_mode.set(review_web_search_mode) {
let fallback_value = per_turn_config.web_search_mode.value();
tracing::warn!(
@@ -113,26 +111,20 @@ pub(super) async fn spawn_review_thread(
config: per_turn_config,
auth_manager: auth_manager_for_context,
model_info: model_info.clone(),
comp_hash: model_info.comp_hash.clone(),
tool_mode,
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,
thread_source: parent_turn_context.thread_source.clone(),
environments: parent_turn_context.environments.clone(),
available_models,
unified_exec_shell_mode,
features: review_features,
ghost_snapshot: parent_turn_context.ghost_snapshot.clone(),
current_date: parent_turn_context.current_date.clone(),
timezone: parent_turn_context.timezone.clone(),
app_server_client_name: parent_turn_context.app_server_client_name.clone(),
developer_instructions: None,
user_instructions: None,
compact_prompt: parent_turn_context.compact_prompt.clone(),
collaboration_mode: parent_turn_context.collaboration_mode.clone(),
multi_agent_version: MultiAgentVersion::Disabled,
personality: parent_turn_context.personality,
@@ -140,14 +132,10 @@ pub(super) async fn spawn_review_thread(
permission_profile: parent_turn_context.permission_profile(),
network: parent_turn_context.network.clone(),
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
shell_environment_policy: parent_turn_context.shell_environment_policy.clone(),
#[allow(deprecated)]
cwd: parent_turn_context.cwd.clone(),
final_output_json_schema: None,
codex_self_exe: parent_turn_context.codex_self_exe.clone(),
codex_linux_sandbox_exe: parent_turn_context.codex_linux_sandbox_exe.clone(),
dynamic_tools: parent_turn_context.dynamic_tools.clone(),
truncation_policy: model_info.truncation_policy.into(),
turn_metadata_state,
extension_data,
turn_skills: TurnSkillsContext::new(parent_turn_context.turn_skills.outcome.clone()),
@@ -271,14 +271,14 @@ impl Session {
RolloutItem::ResponseItem(response_item) => {
history.record_items(
std::iter::once(response_item),
turn_context.truncation_policy,
turn_context.model_info.truncation_policy.into(),
);
}
RolloutItem::InterAgentCommunication(communication) => {
let response_item = communication.to_model_input_item();
history.record_items(
std::iter::once(&response_item),
turn_context.truncation_policy,
turn_context.model_info.truncation_policy.into(),
);
}
RolloutItem::Compacted(compacted) => {
+8 -24
View File
@@ -3530,7 +3530,7 @@ async fn includes_timed_out_message() {
};
let (_, turn_context) = make_session_and_context().await;
let out = format_exec_output_str(&exec, turn_context.truncation_policy);
let out = format_exec_output_str(&exec, turn_context.model_info.truncation_policy.into());
assert_eq!(
out,
@@ -3569,10 +3569,6 @@ async fn turn_context_with_model_updates_model_fields() {
updated.config.model_reasoning_effort,
Some(ReasoningEffortConfig::Medium)
);
assert_eq!(
updated.truncation_policy,
expected_model_info.truncation_policy.into()
);
}
#[test]
@@ -8118,18 +8114,6 @@ fn file_system_policy_with_unreadable_glob(turn_context: &TurnContext) -> FileSy
policy
}
#[tokio::test]
async fn turn_context_item_uses_turn_context_comp_hash_snapshot() {
let (_session, mut turn_context) = make_session_and_context().await;
turn_context.comp_hash = Some("turn-context-hash".to_string());
turn_context.model_info.comp_hash = Some("model-info-hash".to_string());
assert_eq!(
turn_context.to_turn_context_item().comp_hash.as_deref(),
Some("turn-context-hash")
);
}
#[tokio::test]
async fn turn_context_item_stores_local_cwd() {
let (_session, mut turn_context) = make_session_and_context().await;
@@ -9706,7 +9690,7 @@ async fn sample_rollout(
}
live_history.record_items(
initial_context.iter(),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
let user1 = ResponseItem::Message {
@@ -9720,7 +9704,7 @@ async fn sample_rollout(
};
live_history.record_items(
std::iter::once(&user1),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
rollout_items.push(RolloutItem::ResponseItem(user1.clone()));
@@ -9735,7 +9719,7 @@ async fn sample_rollout(
};
live_history.record_items(
std::iter::once(&assistant1),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
rollout_items.push(RolloutItem::ResponseItem(assistant1.clone()));
@@ -9763,7 +9747,7 @@ async fn sample_rollout(
};
live_history.record_items(
std::iter::once(&user2),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
rollout_items.push(RolloutItem::ResponseItem(user2.clone()));
@@ -9778,7 +9762,7 @@ async fn sample_rollout(
};
live_history.record_items(
std::iter::once(&assistant2),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
rollout_items.push(RolloutItem::ResponseItem(assistant2.clone()));
@@ -9806,7 +9790,7 @@ async fn sample_rollout(
};
live_history.record_items(
std::iter::once(&user3),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
rollout_items.push(RolloutItem::ResponseItem(user3));
@@ -9821,7 +9805,7 @@ async fn sample_rollout(
};
live_history.record_items(
std::iter::once(&assistant3),
reconstruction_turn.truncation_policy,
reconstruction_turn.model_info.truncation_policy.into(),
);
rollout_items.push(RolloutItem::ResponseItem(assistant3));
@@ -95,11 +95,11 @@ async fn request_permissions_routes_to_guardian_when_reviewer_is_enabled() {
.approval_policy
.set(AskForApproval::OnRequest)
.expect("test setup should allow updating approval policy");
turn_context_raw
let mut config = (*turn_context_raw.config).clone();
config
.features
.enable(Feature::GuardianApproval)
.expect("test setup should allow enabling guardian approvals");
let mut config = (*turn_context_raw.config).clone();
config.approvals_reviewer = ApprovalsReviewer::AutoReview;
config.model_provider.base_url = Some(format!("{}/v1", server.uri()));
let config = Arc::new(config);
@@ -183,11 +183,11 @@ async fn request_permissions_guardian_review_stops_when_cancelled() {
.approval_policy
.set(AskForApproval::OnRequest)
.expect("test setup should allow updating approval policy");
turn_context_raw
let mut config = (*turn_context_raw.config).clone();
config
.features
.enable(Feature::GuardianApproval)
.expect("test setup should allow enabling guardian approvals");
let mut config = (*turn_context_raw.config).clone();
config.approvals_reviewer = ApprovalsReviewer::AutoReview;
config.model_provider.base_url = Some(format!("{}/v1", server.uri()));
let config = Arc::new(config);
@@ -292,21 +292,21 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli
.await;
let (mut session, mut turn_context_raw) = make_session_and_context().await;
turn_context_raw.codex_linux_sandbox_exe = codex_linux_sandbox_exe_or_skip!();
turn_context_raw
.approval_policy
.set(AskForApproval::OnRequest)
.expect("test setup should allow updating approval policy");
turn_context_raw
.features
.enable(Feature::GuardianApproval)
.expect("test setup should allow enabling guardian approvals");
session
.features
.enable(Feature::ExecPermissionApprovals)
.expect("test setup should allow enabling request permissions");
turn_context_raw.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
let mut config = (*turn_context_raw.config).clone();
config.codex_linux_sandbox_exe = codex_linux_sandbox_exe_or_skip!();
config
.features
.enable(Feature::GuardianApproval)
.expect("test setup should allow enabling guardian approvals");
config.model_provider.base_url = Some(format!("{}/v1", server.uri()));
let config = Arc::new(config);
let models_manager = models_manager_with_provider(
@@ -467,7 +467,7 @@ async fn guardian_allows_unified_exec_additional_permissions_requests_past_polic
.approval_policy
.set(AskForApproval::OnRequest)
.expect("test setup should allow updating approval policy");
turn_context_raw
Arc::make_mut(&mut turn_context_raw.config)
.features
.enable(Feature::GuardianApproval)
.expect("test setup should allow enabling guardian approvals");
+1 -1
View File
@@ -11,7 +11,7 @@ pub(super) async fn maybe_record_token_budget_remaining_context(
tokens_before_sampling: i64,
tokens_after_sampling: i64,
) {
if !turn_context.features.enabled(Feature::TokenBudget) {
if !turn_context.config.features.enabled(Feature::TokenBudget) {
return;
}
let Some(model_context_window) = turn_context.model_context_window() else {
+7 -4
View File
@@ -177,7 +177,7 @@ pub(crate) async fn run_turn(
.await;
sess.set_previous_turn_settings(Some(PreviousTurnSettings {
model: turn_context.model_info.slug.clone(),
comp_hash: turn_context.comp_hash.clone(),
comp_hash: turn_context.model_info.comp_hash.clone(),
realtime_active: Some(turn_context.realtime_active),
}))
.await;
@@ -840,7 +840,7 @@ async fn maybe_run_previous_model_inline_compact(
};
let should_compact_for_comp_hash_change = comp_hash_changed(
previous_turn_settings.comp_hash.as_deref(),
turn_context.comp_hash.as_deref(),
turn_context.model_info.comp_hash.as_deref(),
);
let previous_model_turn_context = Arc::new(
turn_context
@@ -913,7 +913,11 @@ async fn run_auto_compact(
phase: CompactionPhase,
) -> CodexResult<()> {
if should_use_remote_compact_task(turn_context.provider.info()) {
if turn_context.features.enabled(Feature::RemoteCompactionV2) {
if turn_context
.config
.features
.enabled(Feature::RemoteCompactionV2)
{
emit_compact_metric(
&sess.services.session_telemetry,
"remote_v2",
@@ -1025,7 +1029,6 @@ pub(crate) fn build_prompt(
tools: router.model_visible_specs(),
parallel_tool_calls: turn_context.model_info.supports_parallel_tool_calls,
base_instructions,
personality: turn_context.personality,
output_schema: turn_context.final_output_json_schema.clone(),
output_schema_strict: !crate::guardian::is_guardian_reviewer_source(
&turn_context.session_source,
+7 -64
View File
@@ -1,7 +1,6 @@
use super::*;
use crate::SkillLoadOutcome;
use crate::agents_md::LoadedAgentsMd;
use crate::config::GhostSnapshotConfig;
use crate::environment_selection::TurnEnvironmentSnapshot;
use crate::shell_snapshot::ShellSnapshotFile;
use codex_core_skills::HostLoadedSkills;
@@ -12,9 +11,7 @@ use codex_protocol::SessionId;
use codex_protocol::ThreadId;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::ToolMode;
use codex_protocol::protocol::MultiAgentVersion;
use codex_protocol::protocol::ThreadSource;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
@@ -111,15 +108,12 @@ pub struct TurnContext {
pub config: Arc<Config>,
pub(crate) auth_manager: Option<Arc<AuthManager>>,
pub(crate) model_info: ModelInfo,
pub(crate) comp_hash: Option<String>,
pub(crate) tool_mode: ToolMode,
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>,
pub(crate) thread_source: Option<ThreadSource>,
pub(crate) environments: TurnEnvironmentSnapshot,
/// The session's absolute working directory. All relative paths provided
/// by the model as well as sandbox policies are resolved against this path
@@ -130,7 +124,6 @@ pub struct TurnContext {
pub(crate) timezone: Option<String>,
pub(crate) app_server_client_name: Option<String>,
pub(crate) developer_instructions: Option<String>,
pub(crate) compact_prompt: Option<String>,
pub(crate) user_instructions: Option<String>,
pub(crate) collaboration_mode: CollaborationMode,
pub(crate) multi_agent_version: MultiAgentVersion,
@@ -139,15 +132,9 @@ pub struct TurnContext {
pub(crate) permission_profile: PermissionProfile,
pub(crate) network: Option<NetworkProxy>,
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
pub(crate) available_models: Vec<ModelPreset>,
pub(crate) unified_exec_shell_mode: UnifiedExecShellMode,
pub features: ManagedFeatures,
pub(crate) ghost_snapshot: GhostSnapshotConfig,
pub(crate) final_output_json_schema: Option<Value>,
pub(crate) codex_self_exe: Option<PathBuf>,
pub(crate) codex_linux_sandbox_exe: Option<PathBuf>,
pub(crate) truncation_policy: TruncationPolicy,
pub(crate) dynamic_tools: Vec<DynamicToolSpec>,
pub(crate) turn_metadata_state: Arc<TurnMetadataState>,
pub(crate) extension_data: Arc<codex_extension_api::ExtensionData>,
@@ -214,7 +201,9 @@ impl TurnContext {
.auth_manager
.as_deref()
.is_some_and(AuthManager::current_auth_uses_codex_backend);
self.features.apps_enabled_for_auth(uses_codex_backend)
self.config
.features
.apps_enabled_for_auth(uses_codex_backend)
}
pub(crate) fn tool_environment_mode(&self) -> ToolEnvironmentMode {
@@ -231,16 +220,6 @@ impl TurnContext {
let model_info = models_manager
.get_model_info(model.as_str(), &config.to_models_manager_config())
.await;
let tool_mode = model_info.tool_mode.unwrap_or_else(|| {
if config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
let truncation_policy = model_info.truncation_policy.into();
let supported_reasoning_levels = model_info
.supported_reasoning_levels
.iter()
@@ -269,7 +248,6 @@ impl TurnContext {
Some(reasoning_effort.clone()),
/*developer_instructions*/ None,
);
let features = self.features.clone();
let available_models = models_manager
.list_models(RefreshStrategy::OnlineIfUncached)
.await;
@@ -281,8 +259,6 @@ impl TurnContext {
config: Arc::new(config),
auth_manager: self.auth_manager.clone(),
model_info: model_info.clone(),
comp_hash: model_info.comp_hash.clone(),
tool_mode,
session_telemetry: self
.session_telemetry
.clone()
@@ -292,7 +268,6 @@ impl TurnContext {
reasoning_summary: self.reasoning_summary,
session_source: self.session_source.clone(),
parent_thread_id: self.parent_thread_id,
thread_source: self.thread_source.clone(),
environments: self.environments.clone(),
#[allow(deprecated)]
cwd: self.cwd.clone(),
@@ -300,7 +275,6 @@ impl TurnContext {
timezone: self.timezone.clone(),
app_server_client_name: self.app_server_client_name.clone(),
developer_instructions: self.developer_instructions.clone(),
compact_prompt: self.compact_prompt.clone(),
user_instructions: self.user_instructions.clone(),
collaboration_mode,
multi_agent_version: self.multi_agent_version,
@@ -309,15 +283,9 @@ impl TurnContext {
permission_profile: self.permission_profile.clone(),
network: self.network.clone(),
windows_sandbox_level: self.windows_sandbox_level,
shell_environment_policy: self.shell_environment_policy.clone(),
available_models,
unified_exec_shell_mode: self.unified_exec_shell_mode.clone(),
features,
ghost_snapshot: self.ghost_snapshot.clone(),
final_output_json_schema: self.final_output_json_schema.clone(),
codex_self_exe: self.codex_self_exe.clone(),
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.clone(),
truncation_policy,
dynamic_tools: self.dynamic_tools.clone(),
turn_metadata_state: self.turn_metadata_state.clone(),
extension_data: Arc::clone(&self.extension_data),
@@ -368,7 +336,7 @@ impl TurnContext {
.config
.permissions
.windows_sandbox_private_desktop,
use_legacy_landlock: self.features.use_legacy_landlock(),
use_legacy_landlock: self.config.features.use_legacy_landlock(),
}
}
@@ -388,12 +356,6 @@ impl TurnContext {
.then_some(file_system_sandbox_policy)
}
pub(crate) fn compact_prompt(&self) -> &str {
self.compact_prompt
.as_deref()
.unwrap_or(compact::SUMMARIZATION_PROMPT)
}
pub(crate) fn to_turn_context_item(&self) -> TurnContextItem {
let workspace_roots = self.config.effective_workspace_roots();
#[allow(deprecated)]
@@ -410,7 +372,7 @@ impl TurnContext {
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.comp_hash.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),
@@ -549,15 +511,6 @@ impl Session {
);
let mut per_turn_config = per_turn_config;
let tool_mode = model_info.tool_mode.unwrap_or_else(|| {
if per_turn_config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if per_turn_config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
per_turn_config.service_tier = get_service_tier(
per_turn_config.service_tier,
per_turn_config.features.enabled(Feature::FastMode),
@@ -583,18 +536,15 @@ impl Session {
sub_id,
trace_id: current_span_trace_id(),
realtime_active: false,
config: per_turn_config.clone(),
config: per_turn_config,
auth_manager: auth_manager_for_context,
model_info: model_info.clone(),
comp_hash: model_info.comp_hash.clone(),
tool_mode,
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,
thread_source: session_configuration.thread_source.clone(),
environments,
#[allow(deprecated)]
cwd,
@@ -602,7 +552,6 @@ impl Session {
timezone: Some(timezone),
app_server_client_name: session_configuration.app_server_client_name.clone(),
developer_instructions: session_configuration.developer_instructions.clone(),
compact_prompt: session_configuration.compact_prompt.clone(),
user_instructions: session_configuration
.loaded_agents_md
.as_ref()
@@ -614,15 +563,9 @@ impl Session {
permission_profile: session_configuration.permission_profile(),
network,
windows_sandbox_level: session_configuration.windows_sandbox_level,
shell_environment_policy: per_turn_config.permissions.shell_environment_policy.clone(),
available_models,
unified_exec_shell_mode,
features: per_turn_config.features.clone(),
ghost_snapshot: per_turn_config.ghost_snapshot.clone(),
final_output_json_schema: None,
codex_self_exe: per_turn_config.codex_self_exe.clone(),
codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(),
truncation_policy: model_info.truncation_policy.into(),
dynamic_tools: session_configuration.dynamic_tools.clone(),
turn_metadata_state,
extension_data,
+7 -1
View File
@@ -31,6 +31,7 @@ impl SessionTask for CompactTask {
let session = session.clone_session();
let _ = if crate::compact::should_use_remote_compact_task(ctx.provider.info()) {
if ctx
.config
.features
.enabled(codex_features::Feature::RemoteCompactionV2)
{
@@ -55,7 +56,12 @@ impl SessionTask for CompactTask {
/*manual*/ true,
);
let input = vec![UserInput::Text {
text: ctx.compact_prompt().to_string(),
text: ctx
.config
.compact_prompt
.as_deref()
.unwrap_or(crate::compact::SUMMARIZATION_PROMPT)
.to_string(),
// Compaction prompt is synthesized; no UI element ranges to preserve.
text_elements: Vec::new(),
}];
+1 -1
View File
@@ -722,7 +722,7 @@ impl Session {
}
emit_turn_memory_metric(
&self.services.session_telemetry,
turn_context.features.enabled(Feature::MemoryTool),
turn_context.config.features.enabled(Feature::MemoryTool),
turn_context.config.memories.use_memories,
turn_had_memory_citation,
);
+8 -4
View File
@@ -157,7 +157,7 @@ pub(crate) async fn execute_user_shell_command(
};
let shell_snapshot_location = turn_environment.shell_snapshot(&cwd);
let mut exec_env_map = create_env(
&turn_context.shell_environment_policy,
&turn_context.config.permissions.shell_environment_policy,
Some(session.thread_id),
);
if exec_env_map.contains_key(PROXY_ACTIVE_ENV_KEY) {
@@ -167,7 +167,11 @@ pub(crate) async fn execute_user_shell_command(
&display_command,
environment_shell,
shell_snapshot_location.as_ref(),
&turn_context.shell_environment_policy.r#set,
&turn_context
.config
.permissions
.shell_environment_policy
.r#set,
&mut exec_env_map,
);
@@ -294,7 +298,7 @@ pub(crate) async fn execute_user_shell_command(
duration: output.duration,
formatted_output: format_exec_output_str(
&output,
turn_context.truncation_policy,
turn_context.model_info.truncation_policy.into(),
),
status: if output.exit_code == 0 {
ExecCommandStatus::Completed
@@ -339,7 +343,7 @@ pub(crate) async fn execute_user_shell_command(
duration: exec_output.duration,
formatted_output: format_exec_output_str(
&exec_output,
turn_context.truncation_policy,
turn_context.model_info.truncation_policy.into(),
),
status: ExecCommandStatus::Failed,
}),
+3 -1
View File
@@ -26,6 +26,7 @@ use crate::tools::ToolRouter;
use crate::tools::context::FunctionToolOutput;
use crate::tools::context::SharedTurnDiffTracker;
use crate::tools::context::ToolPayload;
use crate::tools::effective_tool_mode;
use crate::tools::parallel::ToolCallRuntime;
use crate::tools::router::ToolCall;
use crate::tools::router::ToolCallSource;
@@ -116,7 +117,8 @@ impl CodeModeService {
router: Arc<ToolRouter>,
tracker: SharedTurnDiffTracker,
) -> Option<CodeModeDispatchWorker> {
if !matches!(turn.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly)
let tool_mode = effective_tool_mode(turn);
if !matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly)
|| self.session.is_none()
{
return None;
+5 -2
View File
@@ -346,7 +346,7 @@ impl ToolEmitter {
output: &ExecToolCallOutput,
ctx: ToolEventCtx<'_>,
) -> String {
super::format_exec_output_for_model(output, ctx.turn.truncation_policy)
super::format_exec_output_for_model(output, ctx.turn.model_info.truncation_policy.into())
}
pub async fn finish(
@@ -495,7 +495,10 @@ async fn emit_exec_stage(
aggregated_output: output.aggregated_output.text.clone(),
exit_code: output.exit_code,
duration: output.duration,
formatted_output: format_exec_output_str(&output, ctx.turn.truncation_policy),
formatted_output: format_exec_output_str(
&output,
ctx.turn.model_info.truncation_policy.into(),
),
status: if output.exit_code == 0 {
ExecCommandStatus::Completed
} else {
@@ -82,7 +82,11 @@ impl ToolArgumentDiffConsumer for ApplyPatchArgumentDiffConsumer {
call_id: String,
diff: &str,
) -> Option<EventMsg> {
if !turn.features.enabled(Feature::ApplyPatchStreamingEvents) {
if !turn
.config
.features
.enabled(Feature::ApplyPatchStreamingEvents)
{
return None;
}
@@ -143,7 +143,7 @@ async fn to_extension_call(invocation: &ToolInvocation) -> ExtensionToolCall {
call_id: invocation.call_id.clone(),
tool_name: invocation.tool_name.clone(),
model: invocation.turn.model_info.slug.clone(),
truncation_policy: invocation.turn.truncation_policy,
truncation_policy: invocation.turn.model_info.truncation_policy.into(),
conversation_history,
turn_item_emitter: Arc::new(CoreTurnItemEmitter {
session: Arc::downgrade(&invocation.session),
@@ -315,7 +315,7 @@ mod tests {
let weak_turn = Arc::downgrade(&turn);
let turn_id = turn.sub_id.clone();
let model = turn.model_info.slug.clone();
let truncation_policy = turn.truncation_policy;
let truncation_policy = turn.model_info.truncation_policy.into();
let expected_sandbox_cwds = turn
.environments
.turn_environments
+1 -1
View File
@@ -156,7 +156,7 @@ impl McpHandler {
tool_input: result.tool_input,
wall_time: started.elapsed(),
original_image_detail_supported: can_request_original_image_detail(&turn.model_info),
truncation_policy: turn.truncation_policy,
truncation_policy: turn.model_info.truncation_policy.into(),
}))
}
}
@@ -115,9 +115,10 @@ impl ListMcpResourceTemplatesHandler {
}
}
.await;
let truncation_policy = turn.model_info.truncation_policy.into();
match payload_result {
Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) {
Ok(payload) => match serialize_function_output(payload, truncation_policy) {
Ok(output) => {
let content = function_call_output_content_items_to_text(&output.body)
.unwrap_or_default();
@@ -113,9 +113,10 @@ impl ListMcpResourcesHandler {
}
}
.await;
let truncation_policy = turn.model_info.truncation_policy.into();
match payload_result {
Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) {
Ok(payload) => match serialize_function_output(payload, truncation_policy) {
Ok(output) => {
let content = function_call_output_content_items_to_text(&output.body)
.unwrap_or_default();
@@ -96,9 +96,10 @@ impl ReadMcpResourceHandler {
})
}
.await;
let truncation_policy = turn.model_info.truncation_policy.into();
match payload_result {
Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) {
Ok(payload) => match serialize_function_output(payload, truncation_policy) {
Ok(output) => {
let content = function_call_output_content_items_to_text(&output.body)
.unwrap_or_default();
@@ -228,7 +228,6 @@ fn build_agent_shared_config(turn: &TurnContext) -> Result<Config, FunctionCallE
.or_else(|| turn.model_info.default_reasoning_level.clone());
config.model_reasoning_summary = Some(turn.reasoning_summary);
config.developer_instructions = turn.developer_instructions.clone();
config.compact_prompt = turn.compact_prompt.clone();
apply_spawn_agent_runtime_overrides(&mut config, turn)?;
Ok(config)
@@ -263,8 +262,6 @@ pub(crate) fn apply_spawn_agent_runtime_overrides(
FunctionCallError::RespondToModel(format!("approval_policy is invalid: {err}"))
})?;
config.approvals_reviewer = turn.config.approvals_reviewer;
config.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
config.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
#[allow(deprecated)]
let turn_cwd = turn.cwd.clone();
config.cwd = turn_cwd;
@@ -4467,17 +4467,19 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
text: "base".to_string(),
};
turn.developer_instructions = Some("dev".to_string());
turn.compact_prompt = Some("compact".to_string());
turn.shell_environment_policy = ShellEnvironmentPolicy {
let mut config = (*turn.config).clone();
config.compact_prompt = Some("compact".to_string());
config.permissions.shell_environment_policy = ShellEnvironmentPolicy {
use_profile: true,
..ShellEnvironmentPolicy::default()
};
config.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo"));
turn.config = Arc::new(config);
let temp_dir = tempfile::tempdir().expect("temp dir");
#[allow(deprecated)]
{
turn.cwd = temp_dir.abs();
}
turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo"));
#[allow(deprecated)]
let turn_cwd = turn.cwd.clone();
let sandbox_policy = pick_allowed_sandbox_policy(
@@ -4506,9 +4508,6 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
expected.model_reasoning_effort = turn.reasoning_effort.clone();
expected.model_reasoning_summary = Some(turn.reasoning_summary);
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
#[allow(deprecated)]
{
expected.cwd = turn.cwd.clone();
@@ -4544,9 +4543,6 @@ async fn build_agent_resume_config_clears_base_instructions() {
expected.model_reasoning_effort = turn.reasoning_effort.clone();
expected.model_reasoning_summary = Some(turn.reasoning_summary);
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
#[allow(deprecated)]
{
expected.cwd = turn.cwd.clone();
+9 -2
View File
@@ -80,7 +80,12 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
};
let fs = turn_environment.environment.get_filesystem();
let explicit_env_overrides = turn.shell_environment_policy.r#set.clone();
let explicit_env_overrides = turn
.config
.permissions
.shell_environment_policy
.r#set
.clone();
let exec_permission_approvals_enabled =
session.features().enabled(Feature::ExecPermissionApprovals);
let requested_additional_permissions = additional_permissions.clone();
@@ -223,7 +228,9 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
let post_tool_use_response = out
.as_ref()
.ok()
.map(|output| crate::tools::format_exec_output_str(output, turn.truncation_policy))
.map(|output| {
crate::tools::format_exec_output_str(output, turn.model_info.truncation_policy.into())
})
.map(JsonValue::String);
let content = emitter
.finish(event_ctx, out, /*applied_patch_delta*/ None)
@@ -100,7 +100,10 @@ impl ShellCommandHandler {
cwd,
expiration: params.timeout_ms.into(),
capture_policy: ExecCapturePolicy::ShellTool,
env: create_env(&turn_context.shell_environment_policy, Some(thread_id)),
env: create_env(
&turn_context.config.permissions.shell_environment_policy,
Some(thread_id),
),
network: turn_context.network.clone(),
sandbox_permissions: params.sandbox_permissions.unwrap_or_default(),
windows_sandbox_level: turn_context.windows_sandbox_level,
@@ -83,7 +83,7 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex
#[allow(deprecated)]
let expected_cwd = turn_context.resolve_path(workdir.clone());
let expected_env = create_env(
&turn_context.shell_environment_policy,
&turn_context.config.permissions.shell_environment_policy,
Some(session.thread_id),
);
@@ -265,7 +265,7 @@ impl ExecCommandHandler {
chunk_id: String::new(),
wall_time: std::time::Duration::ZERO,
raw_output: output.into_text().into_bytes(),
truncation_policy: turn.truncation_policy,
truncation_policy: turn.model_info.truncation_policy.into(),
max_output_tokens,
process_id: None,
exit_code: None,
@@ -310,7 +310,7 @@ impl ExecCommandHandler {
chunk_id: generate_chunk_id(),
wall_time: output.duration,
raw_output: output_text.into_bytes(),
truncation_policy: turn.truncation_policy,
truncation_policy: turn.model_info.truncation_policy.into(),
max_output_tokens,
// Sandbox denial is terminal, so there is no live
// process for write_stdin to resume.
@@ -75,7 +75,7 @@ impl WriteStdinHandler {
input: &args.chars,
yield_time_ms: args.yield_time_ms,
max_output_tokens: args.max_output_tokens,
truncation_policy: turn.truncation_policy,
truncation_policy: turn.model_info.truncation_policy.into(),
})
.await
.map_err(|err| {
@@ -195,7 +195,7 @@ impl ViewImageHandler {
DEFAULT_IMAGE_DETAIL
};
let image_url = if turn.features.enabled(Feature::ResizeAllImages) {
let image_url = if turn.config.features.enabled(Feature::ResizeAllImages) {
// The history insertion path owns image decoding and resizing when this is enabled.
data_url_from_bytes("application/octet-stream", &file_bytes)
} else {
+15
View File
@@ -17,7 +17,10 @@ pub(crate) mod tool_dispatch_trace;
use std::borrow::Cow;
use crate::session::turn_context::TurnContext;
use codex_features::Feature;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::openai_models::ToolMode;
use codex_tools::ToolName;
use codex_utils_output_truncation::TruncationPolicy;
use codex_utils_output_truncation::formatted_truncate_text;
@@ -57,6 +60,18 @@ pub(crate) fn tool_user_shell_type(
}
}
fn effective_tool_mode(turn_context: &TurnContext) -> ToolMode {
turn_context.model_info.tool_mode.unwrap_or_else(|| {
if turn_context.config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if turn_context.config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
})
}
/// Format the combined exec output for sending back to the model.
/// Includes exit code and duration metadata; truncates large bodies safely.
pub fn format_exec_output_for_model(
+3 -3
View File
@@ -237,7 +237,7 @@ impl ToolOrchestrator {
};
// Platform-specific flag gating is handled by SandboxManager::select_initial.
let use_legacy_landlock = turn_ctx.features.use_legacy_landlock();
let use_legacy_landlock = turn_ctx.config.features.use_legacy_landlock();
#[allow(deprecated)]
let sandbox_cwd = tool.sandbox_cwd(req).unwrap_or(&turn_ctx.cwd);
let sandbox_policy_cwd = PathUri::from_abs_path(sandbox_cwd);
@@ -249,7 +249,7 @@ impl ToolOrchestrator {
manager: &self.sandbox,
sandbox_cwd: &sandbox_policy_cwd,
workspace_roots: workspace_roots.as_slice(),
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
codex_linux_sandbox_exe: turn_ctx.config.codex_linux_sandbox_exe.as_ref(),
use_legacy_landlock,
windows_sandbox_level: turn_ctx.windows_sandbox_level,
windows_sandbox_private_desktop: turn_ctx
@@ -413,7 +413,7 @@ impl ToolOrchestrator {
let retry_codex_linux_sandbox_exe = if unsandboxed_allowed {
None
} else {
turn_ctx.codex_linux_sandbox_exe.as_ref()
turn_ctx.config.codex_linux_sandbox_exe.as_ref()
};
let retry_attempt = SandboxAttempt {
sandbox: retry_sandbox,
@@ -185,8 +185,8 @@ pub(super) async fn try_run_zsh_fork(
arg0,
sandbox_policy_cwd,
windows_sandbox_workspace_roots,
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
codex_linux_sandbox_exe: ctx.turn.config.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.config.features.use_legacy_landlock(),
};
let main_execve_wrapper_exe = ctx
.session
@@ -286,8 +286,8 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
arg0: exec_request.arg0.clone(),
sandbox_policy_cwd: exec_request.windows_sandbox_policy_cwd.clone(),
windows_sandbox_workspace_roots: exec_request.windows_sandbox_workspace_roots.clone(),
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
codex_linux_sandbox_exe: ctx.turn.config.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.config.features.use_legacy_landlock(),
};
let escalation_policy = CoreShellActionProvider {
policy: Arc::clone(&exec_policy),
+27 -19
View File
@@ -3,6 +3,7 @@ use crate::agent::next_thread_spawn_depth;
use crate::session::turn_context::TurnContext;
use crate::tools::code_mode::execute_spec::create_code_mode_tool;
use crate::tools::context::ToolInvocation;
use crate::tools::effective_tool_mode;
use crate::tools::handlers::ApplyPatchHandler;
use crate::tools::handlers::CodeModeExecuteHandler;
use crate::tools::handlers::CodeModeWaitHandler;
@@ -243,10 +244,9 @@ fn spec_for_model_request(
tool_name: &ToolName,
spec: ToolSpec,
) -> ToolSpec {
if matches!(
turn_context.tool_mode,
ToolMode::CodeMode | ToolMode::CodeModeOnly
) && exposure != ToolExposure::DirectModelOnly
let tool_mode = effective_tool_mode(turn_context);
if matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly)
&& exposure != ToolExposure::DirectModelOnly
&& !is_excluded_from_code_mode(turn_context, tool_name)
&& codex_code_mode::is_code_mode_nested_tool(spec.name())
{
@@ -298,7 +298,7 @@ pub(crate) fn search_tool_enabled(turn_context: &TurnContext) -> bool {
}
pub(crate) fn tool_suggest_enabled(turn_context: &TurnContext) -> bool {
let features = turn_context.features.get();
let features = turn_context.config.features.get();
features.enabled(Feature::ToolSuggest)
&& features.enabled(Feature::Apps)
&& features.enabled(Feature::Plugins)
@@ -324,7 +324,12 @@ fn collab_tools_enabled(turn_context: &TurnContext) -> bool {
}
fn agent_jobs_tools_enabled(turn_context: &TurnContext) -> bool {
turn_context.features.get().enabled(Feature::SpawnCsv) && collab_tools_enabled(turn_context)
turn_context
.config
.features
.get()
.enabled(Feature::SpawnCsv)
&& collab_tools_enabled(turn_context)
}
fn agent_jobs_worker_tools_enabled(turn_context: &TurnContext) -> bool {
@@ -339,6 +344,7 @@ fn agent_jobs_worker_tools_enabled(turn_context: &TurnContext) -> bool {
fn image_generation_tool_enabled(turn_context: &TurnContext) -> bool {
image_generation_runtime_enabled(turn_context)
&& turn_context
.config
.features
.get()
.enabled(Feature::ImageGeneration)
@@ -365,7 +371,11 @@ fn standalone_image_generation_model_visible(turn_context: &TurnContext) -> bool
return true;
}
turn_context.features.get().enabled(Feature::ImageGenExt)
turn_context
.config
.features
.get()
.enabled(Feature::ImageGenExt)
}
fn standalone_image_generation_available(
@@ -412,7 +422,8 @@ fn is_hidden_by_code_mode_only(
tool_name: &ToolName,
exposure: ToolExposure,
) -> bool {
turn_context.tool_mode == ToolMode::CodeModeOnly
let tool_mode = effective_tool_mode(turn_context);
tool_mode == ToolMode::CodeModeOnly
&& exposure != ToolExposure::DirectModelOnly
&& codex_code_mode::is_code_mode_nested_tool(&codex_tools::code_mode_name_for_tool_name(
tool_name,
@@ -433,10 +444,8 @@ fn build_code_mode_executors(
turn_context: &TurnContext,
executors: &[Arc<dyn CoreToolRuntime>],
) -> Vec<Arc<dyn CoreToolRuntime>> {
if !matches!(
turn_context.tool_mode,
ToolMode::CodeMode | ToolMode::CodeModeOnly
) {
let tool_mode = effective_tool_mode(turn_context);
if !matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) {
return vec![];
}
@@ -482,7 +491,7 @@ fn build_code_mode_executors(
create_code_mode_tool(
&enabled_tools,
&namespace_descriptions,
turn_context.tool_mode == ToolMode::CodeModeOnly,
tool_mode == ToolMode::CodeModeOnly,
deferred_tools_available,
),
code_mode_nested_tool_specs,
@@ -577,6 +586,7 @@ fn standalone_web_search_enabled(turn_context: &TurnContext) -> bool {
namespace_tools_enabled(turn_context)
&& (turn_context.model_info.use_responses_lite
|| turn_context
.config
.features
.get()
.enabled(Feature::StandaloneWebSearch))
@@ -584,7 +594,7 @@ fn standalone_web_search_enabled(turn_context: &TurnContext) -> bool {
fn add_shell_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) {
let turn_context = context.turn_context;
let features = turn_context.features.get();
let features = turn_context.config.features.get();
let environment_mode = turn_context.tool_environment_mode();
if !environment_mode.has_environment() {
return;
@@ -643,7 +653,7 @@ fn add_mcp_resource_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut
fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) {
let turn_context = context.turn_context;
let features = turn_context.features.get();
let features = turn_context.config.features.get();
let environment_mode = turn_context.tool_environment_mode();
planned_tools.add(PlanHandler);
@@ -915,10 +925,8 @@ fn append_extension_tool_executors(
.iter()
.map(|executor| executor.tool_name())
.collect::<HashSet<_>>();
if matches!(
turn_context.tool_mode,
ToolMode::CodeMode | ToolMode::CodeModeOnly
) {
let tool_mode = effective_tool_mode(turn_context);
if matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) {
reserved_tool_names.insert(ToolName::plain(codex_code_mode::PUBLIC_TOOL_NAME));
reserved_tool_names.insert(ToolName::plain(codex_code_mode::WAIT_TOOL_NAME));
}
@@ -197,16 +197,6 @@ async fn probe(configure_turn: impl FnOnce(&mut TurnContext)) -> ToolPlanProbe {
}
fn set_feature(turn: &mut TurnContext, feature: Feature, enabled: bool) {
if enabled {
turn.features
.enable(feature)
.expect("test feature should be enableable");
} else {
turn.features
.disable(feature)
.expect("test feature should be disableable");
}
let mut config = (*turn.config).clone();
if enabled {
config
@@ -221,15 +211,6 @@ fn set_feature(turn: &mut TurnContext, feature: Feature, enabled: bool) {
}
turn.multi_agent_version = config.multi_agent_version_from_features();
turn.config = Arc::new(config);
turn.tool_mode = turn.model_info.tool_mode.unwrap_or_else(|| {
if turn.config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if turn.config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
}
fn set_features(turn: &mut TurnContext, features: &[Feature]) {
@@ -1196,7 +1177,6 @@ async fn tool_mode_selector_overrides_feature_flags() {
let direct = probe(|turn| {
set_features(turn, &[Feature::CodeMode, Feature::CodeModeOnly]);
turn.model_info.tool_mode = Some(ToolMode::Direct);
turn.tool_mode = ToolMode::Direct;
})
.await;
direct.assert_visible_lacks(&[
+1 -1
View File
@@ -190,7 +190,7 @@ async fn exec_command_with_tty(
chunk_id: generate_chunk_id(),
wall_time,
raw_output: collected,
truncation_policy: turn.truncation_policy,
truncation_policy: turn.model_info.truncation_policy.into(),
max_output_tokens: None,
process_id: response_process_id,
exit_code,
@@ -602,7 +602,7 @@ impl UnifiedExecProcessManager {
chunk_id,
wall_time,
raw_output: collected,
truncation_policy: context.turn.truncation_policy,
truncation_policy: context.turn.model_info.truncation_policy.into(),
max_output_tokens: request.max_output_tokens,
process_id: response_process_id,
exit_code,
@@ -1030,7 +1030,7 @@ impl UnifiedExecProcessManager {
context: &UnifiedExecContext,
) -> Result<(UnifiedExecProcess, Option<DeferredNetworkApproval>), UnifiedExecError> {
let local_policy_env = create_env(
&context.turn.shell_environment_policy,
&context.turn.config.permissions.shell_environment_policy,
/*thread_id*/ None,
);
let mut env = local_policy_env.clone();
@@ -1040,7 +1040,9 @@ impl UnifiedExecProcessManager {
);
let env = apply_unified_exec_env(env);
let exec_server_env_config = ExecServerEnvConfig {
policy: exec_env_policy_from_shell_policy(&context.turn.shell_environment_policy),
policy: exec_env_policy_from_shell_policy(
&context.turn.config.permissions.shell_environment_policy,
),
local_policy_env,
};
let mut orchestrator = ToolOrchestrator::new();
@@ -1072,7 +1074,13 @@ impl UnifiedExecProcessManager {
turn_environment: request.turn_environment.clone(),
env,
exec_server_env_config: Some(exec_server_env_config),
explicit_env_overrides: context.turn.shell_environment_policy.r#set.clone(),
explicit_env_overrides: context
.turn
.config
.permissions
.shell_environment_policy
.r#set
.clone(),
network: request.network.clone(),
tty: request.tty,
sandbox_permissions: request.sandbox_permissions,
+4 -1
View File
@@ -11,7 +11,10 @@ fn user_shell_command_fragment(
exec_output: &ExecToolCallOutput,
turn_context: &TurnContext,
) -> UserShellCommand {
let output = format_exec_output_str(exec_output, turn_context.truncation_policy);
let output = format_exec_output_str(
exec_output,
turn_context.model_info.truncation_policy.into(),
);
UserShellCommand::new(command, exec_output.exit_code, exec_output.duration, output)
}