use super::*; use crate::environment_selection::TurnEnvironmentSnapshot; use crate::shell_snapshot::ShellSnapshotFile; use codex_core_skills::HostSkillsSnapshot; use codex_file_system::FileSystemSandboxContext; use codex_model_provider::SharedModelProvider; use codex_model_provider::create_model_provider; use codex_protocol::SessionId; use codex_protocol::ThreadId; use codex_protocol::models::AdditionalPermissionProfile; use codex_protocol::openai_models::ModelInfo; use codex_protocol::protocol::MultiAgentVersion; use codex_protocol::protocol::ThreadHistoryMode; use codex_protocol::protocol::TurnEnvironmentSelection; use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy; use codex_sandboxing::policy_transforms::effective_network_sandbox_policy; use codex_utils_path_uri::PathUri; use futures::FutureExt; use futures::future::BoxFuture; use futures::future::Shared; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use tracing::instrument; #[derive(Clone, Debug)] pub(crate) struct TurnSkillsContext { pub(crate) snapshot: HostSkillsSnapshot, pub(crate) implicit_invocation_seen_skills: Arc>>, } impl TurnSkillsContext { pub(crate) fn new(snapshot: HostSkillsSnapshot) -> Self { Self { snapshot, implicit_invocation_seen_skills: Arc::new(Mutex::new(HashSet::new())), } } } pub(crate) type ShellSnapshotTask = Shared>>>; #[derive(Clone)] pub(crate) struct TurnEnvironment { pub(crate) environment_id: String, pub(crate) environment: Arc, cwd: PathUri, workspace_roots: Vec, pub(crate) shell: Option, pub(crate) shell_snapshot: ShellSnapshotTask, } impl TurnEnvironment { pub(crate) fn new( environment_id: String, environment: Arc, cwd: PathUri, workspace_roots: Vec, shell: Option, ) -> Self { Self { environment_id, environment, cwd, workspace_roots, shell, shell_snapshot: futures::future::ready(None).boxed().shared(), } } pub(crate) fn shell_snapshot(&self, cwd: &AbsolutePathBuf) -> Option { if self.cwd != PathUri::from_abs_path(cwd) { return None; } self.shell_snapshot .peek()? .as_deref() .map(ShellSnapshotFile::path) } pub(crate) fn cwd(&self) -> &PathUri { &self.cwd } pub(crate) fn workspace_roots(&self) -> &[PathUri] { &self.workspace_roots } pub(crate) fn selection(&self) -> TurnEnvironmentSelection { TurnEnvironmentSelection { environment_id: self.environment_id.clone(), cwd: self.cwd.clone(), workspace_roots: self.workspace_roots.clone(), } } } impl std::fmt::Debug for TurnEnvironment { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TurnEnvironment") .field("environment_id", &self.environment_id) .field("environment", &self.environment) .field("cwd", &self.cwd) .field("workspace_roots", &self.workspace_roots) .field("shell", &self.shell) .finish_non_exhaustive() } } /// The context needed for a single turn of the thread. #[derive(Debug)] pub struct TurnContext { pub(crate) sub_id: String, pub(crate) trace_id: Option, pub(crate) realtime_active: bool, pub config: Arc, pub(crate) auth_manager: Option>, pub(crate) model_info: ModelInfo, pub(crate) session_telemetry: SessionTelemetry, pub(crate) provider: SharedModelProvider, pub(crate) reasoning_effort: Option, pub(crate) reasoning_summary: ReasoningSummaryConfig, pub(crate) session_source: SessionSource, pub(crate) history_mode: ThreadHistoryMode, pub(crate) parent_thread_id: Option, pub(crate) originator: String, 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 /// instead of `std::env::current_dir()`. #[deprecated(note = "use the selected turn environment cwd instead")] pub(crate) cwd: AbsolutePathBuf, pub(crate) current_date: Option, pub(crate) timezone: Option, pub(crate) app_server_client_name: Option, pub(crate) developer_instructions: Option, pub(crate) collaboration_mode: CollaborationMode, pub(crate) multi_agent_version: MultiAgentVersion, pub(crate) personality: Option, pub(crate) approval_policy: Constrained, pub(crate) permission_profile: PermissionProfile, pub(crate) network: Option, pub(crate) windows_sandbox_level: WindowsSandboxLevel, pub(crate) available_models: Vec, pub(crate) unified_exec_shell_mode: UnifiedExecShellMode, pub(crate) final_output_json_schema: Option, pub(crate) dynamic_tools: Vec, pub(crate) turn_metadata_state: Arc, pub(crate) extension_data: Arc, pub(crate) turn_skills: TurnSkillsContext, pub(crate) turn_timing_state: Arc, pub(crate) terminal_error: Arc>>, pub(crate) server_model_warning_emitted: AtomicBool, pub(crate) model_verification_emitted: AtomicBool, } enum TurnMultiAgentRuntime { ResolveAndStore, Preview, } impl TurnContext { pub(crate) fn item_ids_enabled(&self) -> bool { self.config.features.enabled(Feature::ItemIds) || matches!(self.history_mode, ThreadHistoryMode::Paginated) } pub(crate) fn permission_profile(&self) -> PermissionProfile { self.permission_profile.clone() } pub(crate) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy { self.permission_profile.file_system_sandbox_policy() } pub(crate) fn network_sandbox_policy(&self) -> NetworkSandboxPolicy { self.permission_profile.network_sandbox_policy() } pub(crate) fn sandbox_policy(&self) -> SandboxPolicy { compatibility_sandbox_policy_for_permission_profile( &self.permission_profile, #[allow(deprecated)] &self.cwd, ) } pub(crate) fn effective_reasoning_effort(&self) -> Option { 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 { let effective_context_window_percent = self.model_info.effective_context_window_percent; self.model_info .resolved_context_window() .map(|context_window| { context_window.saturating_mul(effective_context_window_percent) / 100 }) } pub(crate) fn apps_enabled(&self) -> bool { let uses_codex_backend = self .auth_manager .as_deref() .is_some_and(AuthManager::current_auth_uses_codex_backend); self.config .features .apps_enabled_for_auth(uses_codex_backend) && self.config.orchestrator_mcp_enabled } pub(crate) async fn with_model( &self, model: String, models_manager: &SharedModelsManager, ) -> Self { let mut config = (*self.config).clone(); config.model = Some(model.clone()); let model_info = models_manager .get_model_info(model.as_str(), &config.to_models_manager_config()) .await; let supported_reasoning_levels = model_info .supported_reasoning_levels .iter() .map(|preset| preset.effort.clone()) .collect::>(); let reasoning_effort = if let Some(current_reasoning_effort) = self.reasoning_effort.clone() { if supported_reasoning_levels.contains(¤t_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()) }; 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, config.http_client_factory(), ) .await; Self { sub_id: self.sub_id.clone(), trace_id: self.trace_id.clone(), realtime_active: self.realtime_active, config: Arc::new(config), auth_manager: self.auth_manager.clone(), model_info: model_info.clone(), session_telemetry: self .session_telemetry .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(), history_mode: self.history_mode, parent_thread_id: self.parent_thread_id, originator: self.originator.clone(), environments: self.environments.clone(), #[allow(deprecated)] cwd: self.cwd.clone(), current_date: self.current_date.clone(), timezone: self.timezone.clone(), app_server_client_name: self.app_server_client_name.clone(), developer_instructions: self.developer_instructions.clone(), collaboration_mode, multi_agent_version: self.multi_agent_version, personality: self.personality, approval_policy: self.approval_policy.clone(), permission_profile: self.permission_profile.clone(), network: self.network.clone(), windows_sandbox_level: self.windows_sandbox_level, available_models, unified_exec_shell_mode: self.unified_exec_shell_mode.clone(), final_output_json_schema: self.final_output_json_schema.clone(), dynamic_tools: self.dynamic_tools.clone(), turn_metadata_state: self.turn_metadata_state.clone(), extension_data: Arc::clone(&self.extension_data), turn_skills: self.turn_skills.clone(), turn_timing_state: Arc::clone(&self.turn_timing_state), terminal_error: Arc::clone(&self.terminal_error), server_model_warning_emitted: AtomicBool::new( self.server_model_warning_emitted.load(Ordering::Relaxed), ), model_verification_emitted: AtomicBool::new( self.model_verification_emitted.load(Ordering::Relaxed), ), } } pub(crate) fn file_system_sandbox_context( &self, additional_permissions: Option, environment: &TurnEnvironment, ) -> FileSystemSandboxContext { let permission_profile = self.config.permissions.permission_profile(); let (base_file_system_sandbox_policy, base_network_sandbox_policy) = permission_profile.to_runtime_permissions(); let file_system_sandbox_policy = effective_file_system_sandbox_policy( &base_file_system_sandbox_policy, additional_permissions.as_ref(), ); let network_sandbox_policy = effective_network_sandbox_policy( base_network_sandbox_policy, additional_permissions.as_ref(), ); let permissions = PermissionProfile::from_runtime_permissions_with_enforcement( permission_profile.enforcement(), &file_system_sandbox_policy, network_sandbox_policy, ); FileSystemSandboxContext { permissions: permissions.into(), cwd: Some(environment.cwd().clone()), workspace_roots: environment.workspace_roots().to_vec(), windows_sandbox_level: self.windows_sandbox_level, windows_sandbox_private_desktop: self .config .permissions .windows_sandbox_private_desktop, use_legacy_landlock: self.config.features.use_legacy_landlock(), } } fn non_legacy_file_system_sandbox_policy(&self) -> Option { // Omit the derived split filesystem policy when it is equivalent to // the legacy sandbox policy. This keeps turn-context payloads stable // while both fields exist; once callers consume only the split policy, // this comparison and the legacy projection should go away. let legacy_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( &self.sandbox_policy(), #[allow(deprecated)] &self.cwd, ); let file_system_sandbox_policy = self.file_system_sandbox_policy(); (file_system_sandbox_policy != legacy_file_system_sandbox_policy) .then_some(file_system_sandbox_policy) } pub(crate) fn to_turn_context_item(&self) -> TurnContextItem { let workspace_roots = self.config.effective_workspace_roots(); #[allow(deprecated)] let cwd = self.cwd.clone(); TurnContextItem { turn_id: Some(self.sub_id.clone()), cwd, workspace_roots: (!workspace_roots.is_empty()).then_some(workspace_roots), current_date: self.current_date.clone(), timezone: self.timezone.clone(), approval_policy: self.approval_policy.value(), approvals_reviewer: Some(self.config.approvals_reviewer), sandbox_policy: self.sandbox_policy(), permission_profile: Some(self.permission_profile()), network: self.turn_context_network_item(), file_system_sandbox_policy: self.non_legacy_file_system_sandbox_policy(), model: self.model_info.slug.clone(), comp_hash: self.model_info.comp_hash.clone(), personality: self.personality, collaboration_mode: Some(self.collaboration_mode.clone()), multi_agent_version: Some(self.multi_agent_version), multi_agent_mode: super::multi_agents::effective_multi_agent_mode(self), realtime_active: Some(self.realtime_active), effort: self.reasoning_effort.clone(), summary: ReasoningSummaryConfig::Auto, } } fn turn_context_network_item(&self) -> Option { let network = self .config .config_layer_stack .requirements() .network .as_ref()?; Some(TurnContextNetworkItem { allowed_domains: network .domains .as_ref() .and_then(codex_config::NetworkDomainPermissionsToml::allowed_domains) .unwrap_or_default(), denied_domains: network .domains .as_ref() .and_then(codex_config::NetworkDomainPermissionsToml::denied_domains) .unwrap_or_default(), }) } } fn local_time_context() -> (String, String) { match iana_time_zone::get_timezone() { Ok(timezone) => (Local::now().format("%Y-%m-%d").to_string(), timezone), Err(_) => ( Utc::now().format("%Y-%m-%d").to_string(), "Etc/UTC".to_string(), ), } } impl Session { /// Don't expand the number of mutated arguments on config. We are in the process of getting rid of it. pub(crate) fn build_per_turn_config( session_configuration: &SessionConfiguration, cwd: AbsolutePathBuf, workspace_roots: Vec, ) -> Config { // todo(aibrahim): store this state somewhere else so we don't need to mut config let config = session_configuration.original_config_do_not_use.clone(); let mut per_turn_config = (*config).clone(); per_turn_config.cwd = cwd; per_turn_config.workspace_roots = workspace_roots.clone(); per_turn_config .permissions .set_workspace_roots(workspace_roots); per_turn_config.model_reasoning_effort = session_configuration.collaboration_mode.reasoning_effort(); per_turn_config.model_reasoning_summary = session_configuration.model_reasoning_summary; per_turn_config.service_tier = session_configuration.service_tier.clone(); per_turn_config.personality = session_configuration.personality; per_turn_config.approvals_reviewer = session_configuration.approvals_reviewer; session_configuration .apply_permission_profile_to_permissions(&mut per_turn_config.permissions); let permission_profile = session_configuration.permission_profile(); let resolved_web_search_mode = resolve_web_search_mode_for_turn(&per_turn_config.web_search_mode, &permission_profile); if let Err(err) = per_turn_config .web_search_mode .set(resolved_web_search_mode) { let fallback_value = per_turn_config.web_search_mode.value(); tracing::warn!( error = %err, ?resolved_web_search_mode, ?fallback_value, "resolved web_search_mode is disallowed by requirements; keeping constrained value" ); } per_turn_config.features = config.features.clone(); per_turn_config } pub(crate) fn build_effective_session_config( session_configuration: &SessionConfiguration, ) -> Config { let workspace_roots = session_configuration.workspace_roots(); let mut config = Self::build_per_turn_config( session_configuration, session_configuration.cwd().clone(), workspace_roots.clone(), ); config.model = Some(session_configuration.collaboration_mode.model().to_string()); config.permissions.approval_policy = session_configuration.approval_policy.clone(); config.workspace_roots = workspace_roots.clone(); config.permissions.set_workspace_roots(workspace_roots); config } #[allow(clippy::too_many_arguments)] pub(crate) fn make_turn_context( thread_id: ThreadId, session_id: SessionId, auth_manager: Option>, session_telemetry: &SessionTelemetry, provider: ModelProviderInfo, session_configuration: &SessionConfiguration, multi_agent_version: MultiAgentVersion, user_shell: &shell::Shell, shell_zsh_path: Option<&PathBuf>, main_execve_wrapper_exe: Option<&PathBuf>, per_turn_config: Config, model_info: ModelInfo, models_manager: &SharedModelsManager, network: Option, environments: TurnEnvironmentSnapshot, cwd: AbsolutePathBuf, sub_id: String, skills_snapshot: HostSkillsSnapshot, ) -> TurnContext { let reasoning_effort = session_configuration.collaboration_mode.reasoning_effort(); let reasoning_summary = session_configuration .model_reasoning_summary .unwrap_or(model_info.default_reasoning_summary); let session_telemetry = session_telemetry.clone().with_model( session_configuration.collaboration_mode.model(), model_info.slug.as_str(), ); let session_source = session_configuration.session_source.clone(); let auth_manager_for_context = auth_manager.clone(); let provider_for_context = create_model_provider(provider, auth_manager); let session_telemetry_for_context = session_telemetry; let available_models = models_manager.try_list_models().unwrap_or_default(); let unified_exec_shell_mode = UnifiedExecShellMode::for_session( codex_tools::unified_exec_feature_mode_for_features(per_turn_config.features.get()), crate::tools::tool_user_shell_type(user_shell), shell_zsh_path, main_execve_wrapper_exe, ); let mut per_turn_config = per_turn_config; per_turn_config.service_tier = get_service_tier( per_turn_config.service_tier, per_turn_config.features.enabled(Feature::FastMode), &model_info, ); let permission_profile = per_turn_config.permissions.effective_permission_profile(); let per_turn_config = Arc::new(per_turn_config); let turn_metadata_state = Arc::new(TurnMetadataState::new( session_id.to_string(), thread_id.to_string(), session_configuration.forked_from_thread_id, session_configuration.parent_thread_id, &session_configuration.session_source, session_configuration.thread_source.clone(), sub_id.clone(), cwd.clone(), &permission_profile, session_configuration.windows_sandbox_level, network.is_some(), )); let (current_date, timezone) = local_time_context(); let extension_data = Arc::new(codex_extension_api::ExtensionData::new(sub_id.clone())); extension_data.insert(skills_snapshot.clone()); TurnContext { sub_id, trace_id: current_span_trace_id(), realtime_active: false, config: per_turn_config, auth_manager: auth_manager_for_context, model_info, session_telemetry: session_telemetry_for_context, provider: provider_for_context, reasoning_effort, reasoning_summary, session_source, history_mode: session_configuration.history_mode, parent_thread_id: session_configuration.parent_thread_id, originator: session_configuration.originator.clone(), environments, #[allow(deprecated)] cwd, current_date: Some(current_date), 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(), multi_agent_version, personality: session_configuration.personality, approval_policy: session_configuration.approval_policy.clone(), permission_profile, network, windows_sandbox_level: session_configuration.windows_sandbox_level, available_models, unified_exec_shell_mode, final_output_json_schema: None, dynamic_tools: session_configuration.dynamic_tools.clone(), turn_metadata_state, extension_data, turn_skills: TurnSkillsContext::new(skills_snapshot), turn_timing_state: Arc::new(TurnTimingState::default()), terminal_error: Arc::new(Mutex::new(None)), server_model_warning_emitted: AtomicBool::new(false), model_verification_emitted: AtomicBool::new(false), } } pub(crate) async fn new_turn_with_sub_id( &self, sub_id: String, updates: SessionSettingsUpdate, ) -> CodexResult> { let notify_config_contributors = !self.services.extensions.config_contributors().is_empty(); let update_result: CodexResult<_> = { let mut state = self.state.lock().await; match state.session_configuration.clone().apply(&updates) { Ok(next) => { let previous_permission_profile = state.session_configuration.permission_profile(); let next_permission_profile = next.permission_profile(); let permission_profile_changed = previous_permission_profile != next_permission_profile; let previous_config = notify_config_contributors.then(|| { Self::build_effective_session_config(&state.session_configuration) }); let new_config = notify_config_contributors .then(|| Self::build_effective_session_config(&next)); if updates.environments.is_some() { self.services .turn_environments .update_selections(next.environment_selections()); } state.session_configuration = next.clone(); Ok(( next, permission_profile_changed, previous_config, new_config, )) } Err(err) => Err(CodexErr::InvalidRequest(err.to_string())), } }; let (session_configuration, permission_profile_changed, previous_config, new_config) = match update_result { Ok(update) => update, Err(err) => { let message = err.to_string(); self.send_event_raw(Event { id: sub_id.clone(), msg: EventMsg::Error(ErrorEvent { message: message.clone(), codex_error_info: Some(CodexErrorInfo::BadRequest), }), }) .await; return Err(CodexErr::InvalidRequest(message)); } }; self.emit_config_changed_contributors(previous_config.as_ref(), new_config.as_ref()); if permission_profile_changed { self.refresh_managed_network_proxy_for_current_permission_profile() .await; } Ok(self .new_turn_from_configuration( sub_id, session_configuration, updates.final_output_json_schema, ) .await) } async fn new_turn_from_configuration( &self, sub_id: String, session_configuration: SessionConfiguration, final_output_json_schema: Option>, ) -> Arc { self.new_turn_context_from_configuration( sub_id, session_configuration, final_output_json_schema, TurnMultiAgentRuntime::ResolveAndStore, ) .await } async fn new_startup_prewarm_turn_from_configuration( &self, sub_id: String, session_configuration: SessionConfiguration, ) -> Arc { self.new_turn_context_from_configuration( sub_id, session_configuration, /*final_output_json_schema*/ None, TurnMultiAgentRuntime::Preview, ) .await } #[instrument(name = "turn_context.build", level = "trace", skip_all)] async fn new_turn_context_from_configuration( &self, sub_id: String, session_configuration: SessionConfiguration, final_output_json_schema: Option>, multi_agent_runtime: TurnMultiAgentRuntime, ) -> Arc { let turn_environments = self.services.turn_environments.snapshot().await; let primary_turn_environment = turn_environments.primary().cloned(); // TODO(anp): Migrate per-turn config and legacy TurnContext cwd consumers to PathUri so // a foreign primary environment does not fall back to the session's host cwd. let cwd = primary_turn_environment .as_ref() .and_then(|turn_environment| turn_environment.cwd().to_abs_path().ok()) .unwrap_or_else(|| session_configuration.cwd().clone()); let workspace_roots = primary_turn_environment .as_ref() .map(TurnEnvironment::workspace_roots) .map(|workspace_roots| { workspace_roots .iter() .map(PathUri::to_abs_path) .collect::>>() }) .transpose() .ok() .flatten() .unwrap_or_else(|| session_configuration.workspace_roots()); let per_turn_config = Self::build_per_turn_config(&session_configuration, cwd.clone(), workspace_roots); { let mcp_runtime = self.services.latest_mcp_runtime(); let mcp_connection_manager = mcp_runtime.manager(); mcp_connection_manager.set_approval_policy(&session_configuration.approval_policy); mcp_connection_manager .set_permission_profile(session_configuration.permission_profile()); } let model_info = self .services .models_manager .get_model_info( session_configuration.collaboration_mode.model(), &per_turn_config.to_models_manager_config(), ) .await; self.services .thread_extension_data .insert(model_info.clone()); let multi_agent_version = match multi_agent_runtime { TurnMultiAgentRuntime::ResolveAndStore => { self.resolve_multi_agent_version_for_model(&model_info, &per_turn_config) } TurnMultiAgentRuntime::Preview => self .multi_agent_version() .or(model_info.multi_agent_version) .unwrap_or_else(|| per_turn_config.multi_agent_version_from_features()), }; let plugins_input = per_turn_config.plugins_config_input(); let plugin_outcome = self .services .plugins_manager .plugins_for_config(&plugins_input) .await; let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots(); let plugin_skill_snapshots = self .services .plugins_manager .plugin_skill_snapshots_for_config(&plugins_input); let skills_input = skills_load_input_from_config(&per_turn_config, effective_skill_roots) .with_plugin_skill_snapshots(plugin_skill_snapshots); let fs = primary_turn_environment .map(|turn_environment| turn_environment.environment.get_filesystem()); let skills_snapshot = self .services .skills_service .snapshot_for_config(&skills_input, fs) .await; let mut turn_context: TurnContext = Self::make_turn_context( self.thread_id(), self.session_id(), Some(Arc::clone(&self.services.auth_manager)), &self.services.session_telemetry, session_configuration.provider.clone(), &session_configuration, multi_agent_version, self.services.user_shell.as_ref(), self.services.shell_zsh_path.as_ref(), self.services.main_execve_wrapper_exe.as_ref(), per_turn_config, model_info, &self.services.models_manager, self.services .network_proxy .load_full() .as_ref() .and_then(|started_proxy| { Self::managed_network_proxy_active_for_permission_profile( &session_configuration.permission_profile(), ) .then(|| started_proxy.proxy()) }), turn_environments, cwd, sub_id, skills_snapshot, ); turn_context.realtime_active = self.conversation.running_state().await.is_some(); if let Some(final_schema) = final_output_json_schema { turn_context.final_output_json_schema = final_schema; } let turn_context = Arc::new(turn_context); if turn_context .environments .single_local_environment_cwd() .is_some() { turn_context.turn_metadata_state.spawn_git_enrichment_task(); } turn_context } pub(crate) async fn maybe_emit_model_warnings_for_turn(&self, tc: &TurnContext) { if tc.model_info.used_fallback_model_metadata { self.send_event( tc, EventMsg::Warning(WarningEvent { message: format!( "Model metadata for `{}` not found. Defaulting to fallback metadata; this can degrade performance and cause issues.", tc.model_info.slug ), }), ) .await; } if let Some(message) = unsupported_code_mode_warning(&tc.model_info, tc.config.features.get()) { self.send_event(tc, EventMsg::Warning(WarningEvent { message })) .await; } } pub(crate) async fn new_default_turn(&self) -> Arc { self.new_default_turn_with_sub_id(self.next_internal_sub_id()) .await } pub(crate) async fn new_default_turn_with_sub_id(&self, sub_id: String) -> Arc { let session_configuration = self.default_turn_configuration().await; self.new_turn_from_configuration( sub_id, session_configuration, /*final_output_json_schema*/ None, ) .await } pub(crate) async fn new_startup_prewarm_turn_with_sub_id( &self, sub_id: String, ) -> Arc { let session_configuration = self.default_turn_configuration().await; self.new_startup_prewarm_turn_from_configuration(sub_id, session_configuration) .await } async fn default_turn_configuration(&self) -> SessionConfiguration { let state = self.state.lock().await; state.session_configuration.clone() } }