mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
## Summary - rename the core codex module root to session/mod.rs without using #[path] - move the codex module directory and tests under core/src/session - remove session/mod.rs reexports so call sites use explicit child module paths ## Testing - cargo test -p codex-core --lib - cargo check -p codex-core --tests - just fmt - just fix -p codex-core - git diff --check
618 lines
26 KiB
Rust
618 lines
26 KiB
Rust
use super::*;
|
|
use codex_model_provider::SharedModelProvider;
|
|
use codex_model_provider::create_model_provider;
|
|
|
|
pub(super) fn image_generation_tool_auth_allowed(auth_manager: Option<&AuthManager>) -> bool {
|
|
matches!(
|
|
auth_manager.and_then(AuthManager::auth_mode),
|
|
Some(AuthMode::Chatgpt)
|
|
)
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct TurnSkillsContext {
|
|
pub(crate) outcome: Arc<SkillLoadOutcome>,
|
|
pub(crate) implicit_invocation_seen_skills: Arc<Mutex<HashSet<String>>>,
|
|
}
|
|
|
|
impl TurnSkillsContext {
|
|
pub(crate) fn new(outcome: Arc<SkillLoadOutcome>) -> Self {
|
|
Self {
|
|
outcome,
|
|
implicit_invocation_seen_skills: Arc::new(Mutex::new(HashSet::new())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The context needed for a single turn of the thread.
|
|
#[derive(Debug)]
|
|
pub(crate) struct TurnContext {
|
|
pub(crate) sub_id: String,
|
|
pub(crate) trace_id: Option<String>,
|
|
pub(crate) realtime_active: bool,
|
|
pub(crate) config: Arc<Config>,
|
|
pub(crate) auth_manager: Option<Arc<AuthManager>>,
|
|
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) environment: Option<Arc<Environment>>,
|
|
/// 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()`.
|
|
pub(crate) cwd: AbsolutePathBuf,
|
|
pub(crate) current_date: Option<String>,
|
|
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) personality: Option<Personality>,
|
|
pub(crate) approval_policy: Constrained<AskForApproval>,
|
|
pub(crate) sandbox_policy: Constrained<SandboxPolicy>,
|
|
pub(crate) file_system_sandbox_policy: FileSystemSandboxPolicy,
|
|
pub(crate) network_sandbox_policy: NetworkSandboxPolicy,
|
|
pub(crate) network: Option<NetworkProxy>,
|
|
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
|
|
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
|
|
pub(crate) tools_config: ToolsConfig,
|
|
pub(crate) 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) tool_call_gate: Arc<ReadinessFlag>,
|
|
pub(crate) truncation_policy: TruncationPolicy,
|
|
pub(crate) js_repl: Arc<JsReplHandle>,
|
|
pub(crate) dynamic_tools: Vec<DynamicToolSpec>,
|
|
pub(crate) turn_metadata_state: Arc<TurnMetadataState>,
|
|
pub(crate) turn_skills: TurnSkillsContext,
|
|
pub(crate) turn_timing_state: Arc<TurnTimingState>,
|
|
}
|
|
impl TurnContext {
|
|
pub(crate) fn model_context_window(&self) -> Option<i64> {
|
|
let effective_context_window_percent = self.model_info.effective_context_window_percent;
|
|
self.model_info.context_window.map(|context_window| {
|
|
context_window.saturating_mul(effective_context_window_percent) / 100
|
|
})
|
|
}
|
|
|
|
pub(crate) fn apps_enabled(&self) -> bool {
|
|
let is_chatgpt_auth = self
|
|
.auth_manager
|
|
.as_deref()
|
|
.and_then(AuthManager::auth_cached)
|
|
.as_ref()
|
|
.is_some_and(CodexAuth::is_chatgpt_auth);
|
|
self.features.apps_enabled_for_auth(is_chatgpt_auth)
|
|
}
|
|
|
|
pub(crate) async fn with_model(&self, model: String, models_manager: &ModelsManager) -> 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 truncation_policy = model_info.truncation_policy.into();
|
|
let supported_reasoning_levels = model_info
|
|
.supported_reasoning_levels
|
|
.iter()
|
|
.map(|preset| preset.effort)
|
|
.collect::<Vec<_>>();
|
|
let reasoning_effort = if let Some(current_reasoning_effort) = self.reasoning_effort {
|
|
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)
|
|
.copied()
|
|
.or(model_info.default_reasoning_level)
|
|
}
|
|
} else {
|
|
supported_reasoning_levels
|
|
.get(supported_reasoning_levels.len().saturating_sub(1) / 2)
|
|
.copied()
|
|
.or(model_info.default_reasoning_level)
|
|
};
|
|
config.model_reasoning_effort = reasoning_effort;
|
|
|
|
let collaboration_mode = self.collaboration_mode.with_updates(
|
|
Some(model.clone()),
|
|
Some(reasoning_effort),
|
|
/*developer_instructions*/ None,
|
|
);
|
|
let features = self.features.clone();
|
|
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
|
model_info: &model_info,
|
|
available_models: &models_manager
|
|
.list_models(RefreshStrategy::OnlineIfUncached)
|
|
.await,
|
|
features: &features,
|
|
image_generation_tool_auth_allowed: image_generation_tool_auth_allowed(
|
|
self.auth_manager.as_deref(),
|
|
),
|
|
web_search_mode: self.tools_config.web_search_mode,
|
|
session_source: self.session_source.clone(),
|
|
sandbox_policy: self.sandbox_policy.get(),
|
|
windows_sandbox_level: self.windows_sandbox_level,
|
|
})
|
|
.with_unified_exec_shell_mode(self.tools_config.unified_exec_shell_mode.clone())
|
|
.with_web_search_config(self.tools_config.web_search_config.clone())
|
|
.with_allow_login_shell(self.tools_config.allow_login_shell)
|
|
.with_has_environment(self.tools_config.has_environment)
|
|
.with_spawn_agent_usage_hint(config.multi_agent_v2.usage_hint_enabled)
|
|
.with_spawn_agent_usage_hint_text(config.multi_agent_v2.usage_hint_text.clone())
|
|
.with_hide_spawn_agent_metadata(config.multi_agent_v2.hide_spawn_agent_metadata)
|
|
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
|
&config.agent_roles,
|
|
));
|
|
|
|
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(),
|
|
environment: self.environment.clone(),
|
|
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(),
|
|
compact_prompt: self.compact_prompt.clone(),
|
|
user_instructions: self.user_instructions.clone(),
|
|
collaboration_mode,
|
|
personality: self.personality,
|
|
approval_policy: self.approval_policy.clone(),
|
|
sandbox_policy: self.sandbox_policy.clone(),
|
|
file_system_sandbox_policy: self.file_system_sandbox_policy.clone(),
|
|
network_sandbox_policy: self.network_sandbox_policy,
|
|
network: self.network.clone(),
|
|
windows_sandbox_level: self.windows_sandbox_level,
|
|
shell_environment_policy: self.shell_environment_policy.clone(),
|
|
tools_config,
|
|
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(),
|
|
tool_call_gate: Arc::new(ReadinessFlag::new()),
|
|
truncation_policy,
|
|
js_repl: Arc::clone(&self.js_repl),
|
|
dynamic_tools: self.dynamic_tools.clone(),
|
|
turn_metadata_state: self.turn_metadata_state.clone(),
|
|
turn_skills: self.turn_skills.clone(),
|
|
turn_timing_state: Arc::clone(&self.turn_timing_state),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn resolve_path(&self, path: Option<String>) -> AbsolutePathBuf {
|
|
path.as_ref()
|
|
.map_or_else(|| self.cwd.clone(), |path| self.cwd.join(path))
|
|
}
|
|
|
|
pub(crate) fn file_system_sandbox_context(
|
|
&self,
|
|
additional_permissions: Option<PermissionProfile>,
|
|
) -> FileSystemSandboxContext {
|
|
FileSystemSandboxContext {
|
|
sandbox_policy: self.sandbox_policy.get().clone(),
|
|
windows_sandbox_level: self.windows_sandbox_level,
|
|
windows_sandbox_private_desktop: self
|
|
.config
|
|
.permissions
|
|
.windows_sandbox_private_desktop,
|
|
use_legacy_landlock: self.features.use_legacy_landlock(),
|
|
additional_permissions,
|
|
}
|
|
}
|
|
|
|
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 legacy_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy(
|
|
self.sandbox_policy.get(),
|
|
&self.cwd,
|
|
);
|
|
// 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 file_system_sandbox_policy = (self.file_system_sandbox_policy
|
|
!= legacy_file_system_sandbox_policy)
|
|
.then(|| self.file_system_sandbox_policy.clone());
|
|
|
|
TurnContextItem {
|
|
turn_id: Some(self.sub_id.clone()),
|
|
trace_id: self.trace_id.clone(),
|
|
cwd: self.cwd.to_path_buf(),
|
|
current_date: self.current_date.clone(),
|
|
timezone: self.timezone.clone(),
|
|
approval_policy: self.approval_policy.value(),
|
|
sandbox_policy: self.sandbox_policy.get().clone(),
|
|
network: self.turn_context_network_item(),
|
|
file_system_sandbox_policy,
|
|
model: self.model_info.slug.clone(),
|
|
personality: self.personality,
|
|
collaboration_mode: Some(self.collaboration_mode.clone()),
|
|
realtime_active: Some(self.realtime_active),
|
|
effort: self.reasoning_effort,
|
|
summary: self.reasoning_summary,
|
|
user_instructions: self.user_instructions.clone(),
|
|
developer_instructions: self.developer_instructions.clone(),
|
|
final_output_json_schema: self.final_output_json_schema.clone(),
|
|
truncation_policy: Some(self.truncation_policy),
|
|
}
|
|
}
|
|
|
|
fn turn_context_network_item(&self) -> Option<TurnContextNetworkItem> {
|
|
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) -> 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 = session_configuration.cwd.clone();
|
|
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;
|
|
per_turn_config.personality = session_configuration.personality;
|
|
per_turn_config.approvals_reviewer = session_configuration.approvals_reviewer;
|
|
let resolved_web_search_mode = resolve_web_search_mode_for_turn(
|
|
&per_turn_config.web_search_mode,
|
|
session_configuration.sandbox_policy.get(),
|
|
);
|
|
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
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(crate) fn make_turn_context(
|
|
conversation_id: ThreadId,
|
|
auth_manager: Option<Arc<AuthManager>>,
|
|
session_telemetry: &SessionTelemetry,
|
|
provider: ModelProviderInfo,
|
|
session_configuration: &SessionConfiguration,
|
|
user_shell: &shell::Shell,
|
|
shell_zsh_path: Option<&PathBuf>,
|
|
main_execve_wrapper_exe: Option<&PathBuf>,
|
|
per_turn_config: Config,
|
|
model_info: ModelInfo,
|
|
models_manager: &ModelsManager,
|
|
network: Option<NetworkProxy>,
|
|
environment: Option<Arc<Environment>>,
|
|
sub_id: String,
|
|
js_repl: Arc<JsReplHandle>,
|
|
skills_outcome: Arc<SkillLoadOutcome>,
|
|
) -> 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 image_generation_tool_auth_allowed =
|
|
image_generation_tool_auth_allowed(auth_manager.as_deref());
|
|
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 tools_config = ToolsConfig::new(&ToolsConfigParams {
|
|
model_info: &model_info,
|
|
available_models: &models_manager.try_list_models().unwrap_or_default(),
|
|
features: &per_turn_config.features,
|
|
image_generation_tool_auth_allowed,
|
|
web_search_mode: Some(per_turn_config.web_search_mode.value()),
|
|
session_source: session_source.clone(),
|
|
sandbox_policy: session_configuration.sandbox_policy.get(),
|
|
windows_sandbox_level: session_configuration.windows_sandbox_level,
|
|
})
|
|
.with_unified_exec_shell_mode_for_session(
|
|
crate::tools::spec::tool_user_shell_type(user_shell),
|
|
shell_zsh_path,
|
|
main_execve_wrapper_exe,
|
|
)
|
|
.with_web_search_config(per_turn_config.web_search_config.clone())
|
|
.with_allow_login_shell(per_turn_config.permissions.allow_login_shell)
|
|
.with_has_environment(environment.is_some())
|
|
.with_spawn_agent_usage_hint(per_turn_config.multi_agent_v2.usage_hint_enabled)
|
|
.with_spawn_agent_usage_hint_text(per_turn_config.multi_agent_v2.usage_hint_text.clone())
|
|
.with_hide_spawn_agent_metadata(per_turn_config.multi_agent_v2.hide_spawn_agent_metadata)
|
|
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
|
&per_turn_config.agent_roles,
|
|
));
|
|
|
|
let cwd = session_configuration.cwd.clone();
|
|
|
|
let per_turn_config = Arc::new(per_turn_config);
|
|
let turn_metadata_state = Arc::new(TurnMetadataState::new(
|
|
conversation_id.to_string(),
|
|
&session_source,
|
|
sub_id.clone(),
|
|
cwd.clone(),
|
|
session_configuration.sandbox_policy.get(),
|
|
session_configuration.windows_sandbox_level,
|
|
));
|
|
let (current_date, timezone) = local_time_context();
|
|
TurnContext {
|
|
sub_id,
|
|
trace_id: current_span_trace_id(),
|
|
realtime_active: false,
|
|
config: per_turn_config.clone(),
|
|
auth_manager: auth_manager_for_context,
|
|
model_info: model_info.clone(),
|
|
session_telemetry: session_telemetry_for_context,
|
|
provider: provider_for_context,
|
|
reasoning_effort,
|
|
reasoning_summary,
|
|
session_source,
|
|
environment,
|
|
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(),
|
|
compact_prompt: session_configuration.compact_prompt.clone(),
|
|
user_instructions: session_configuration.user_instructions.clone(),
|
|
collaboration_mode: session_configuration.collaboration_mode.clone(),
|
|
personality: session_configuration.personality,
|
|
approval_policy: session_configuration.approval_policy.clone(),
|
|
sandbox_policy: session_configuration.sandbox_policy.clone(),
|
|
file_system_sandbox_policy: session_configuration.file_system_sandbox_policy.clone(),
|
|
network_sandbox_policy: session_configuration.network_sandbox_policy,
|
|
network,
|
|
windows_sandbox_level: session_configuration.windows_sandbox_level,
|
|
shell_environment_policy: per_turn_config.permissions.shell_environment_policy.clone(),
|
|
tools_config,
|
|
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(),
|
|
tool_call_gate: Arc::new(ReadinessFlag::new()),
|
|
truncation_policy: model_info.truncation_policy.into(),
|
|
js_repl,
|
|
dynamic_tools: session_configuration.dynamic_tools.clone(),
|
|
turn_metadata_state,
|
|
turn_skills: TurnSkillsContext::new(skills_outcome),
|
|
turn_timing_state: Arc::new(TurnTimingState::default()),
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn new_turn_with_sub_id(
|
|
&self,
|
|
sub_id: String,
|
|
updates: SessionSettingsUpdate,
|
|
) -> ConstraintResult<Arc<TurnContext>> {
|
|
let (
|
|
session_configuration,
|
|
sandbox_policy_changed,
|
|
previous_cwd,
|
|
codex_home,
|
|
session_source,
|
|
) = {
|
|
let mut state = self.state.lock().await;
|
|
match state.session_configuration.clone().apply(&updates) {
|
|
Ok(next) => {
|
|
let previous_cwd = state.session_configuration.cwd.clone();
|
|
let sandbox_policy_changed =
|
|
state.session_configuration.sandbox_policy != next.sandbox_policy;
|
|
let codex_home = next.codex_home.clone();
|
|
let session_source = next.session_source.clone();
|
|
state.session_configuration = next.clone();
|
|
(
|
|
next,
|
|
sandbox_policy_changed,
|
|
previous_cwd,
|
|
codex_home,
|
|
session_source,
|
|
)
|
|
}
|
|
Err(err) => {
|
|
drop(state);
|
|
self.send_event_raw(Event {
|
|
id: sub_id.clone(),
|
|
msg: EventMsg::Error(ErrorEvent {
|
|
message: err.to_string(),
|
|
codex_error_info: Some(CodexErrorInfo::BadRequest),
|
|
}),
|
|
})
|
|
.await;
|
|
return Err(err);
|
|
}
|
|
}
|
|
};
|
|
|
|
self.maybe_refresh_shell_snapshot_for_cwd(
|
|
&previous_cwd,
|
|
&session_configuration.cwd,
|
|
&codex_home,
|
|
&session_source,
|
|
);
|
|
|
|
if sandbox_policy_changed {
|
|
self.refresh_managed_network_proxy_for_current_sandbox_policy()
|
|
.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<Option<Value>>,
|
|
) -> Arc<TurnContext> {
|
|
let per_turn_config = Self::build_per_turn_config(&session_configuration);
|
|
{
|
|
let mcp_connection_manager = self.services.mcp_connection_manager.read().await;
|
|
mcp_connection_manager.set_approval_policy(&session_configuration.approval_policy);
|
|
mcp_connection_manager
|
|
.set_sandbox_policy(per_turn_config.permissions.sandbox_policy.get());
|
|
}
|
|
|
|
let model_info = self
|
|
.services
|
|
.models_manager
|
|
.get_model_info(
|
|
session_configuration.collaboration_mode.model(),
|
|
&per_turn_config.to_models_manager_config(),
|
|
)
|
|
.await;
|
|
let plugin_outcome = self
|
|
.services
|
|
.plugins_manager
|
|
.plugins_for_config(&per_turn_config)
|
|
.await;
|
|
let effective_skill_roots = plugin_outcome.effective_skill_roots();
|
|
let skills_input = skills_load_input_from_config(&per_turn_config, effective_skill_roots);
|
|
let fs = self
|
|
.services
|
|
.environment
|
|
.as_ref()
|
|
.map(|environment| environment.get_filesystem());
|
|
let skills_outcome = Arc::new(
|
|
self.services
|
|
.skills_manager
|
|
.skills_for_config(&skills_input, fs)
|
|
.await,
|
|
);
|
|
let mut turn_context: TurnContext = Self::make_turn_context(
|
|
self.conversation_id,
|
|
Some(Arc::clone(&self.services.auth_manager)),
|
|
&self.services.session_telemetry,
|
|
session_configuration.provider.clone(),
|
|
&session_configuration,
|
|
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
|
|
.as_ref()
|
|
.and_then(|started_proxy| {
|
|
Self::managed_network_proxy_active_for_sandbox_policy(
|
|
session_configuration.sandbox_policy.get(),
|
|
)
|
|
.then(|| started_proxy.proxy())
|
|
}),
|
|
self.services.environment.clone(),
|
|
sub_id,
|
|
Arc::clone(&self.js_repl),
|
|
skills_outcome,
|
|
);
|
|
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);
|
|
turn_context.turn_metadata_state.spawn_git_enrichment_task();
|
|
turn_context
|
|
}
|
|
|
|
pub(crate) async fn maybe_emit_unknown_model_warning_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;
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn new_default_turn(&self) -> Arc<TurnContext> {
|
|
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<TurnContext> {
|
|
let session_configuration = {
|
|
let state = self.state.lock().await;
|
|
state.session_configuration.clone()
|
|
};
|
|
self.new_turn_from_configuration(
|
|
sub_id,
|
|
session_configuration,
|
|
/*final_output_json_schema*/ None,
|
|
)
|
|
.await
|
|
}
|
|
}
|