mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
bacfc5e4c0
## Why We have some large gaps in our thread start, resume, and pre-sampling traces that make it hard to tell where latency is coming from. ## What Changed - Added coarse spans around thread start/resume, turn context construction, rollout reconstruction, skill/plugin loading, and tool preparation. - Added a breakdown of discoverable-tool preparation across connector loading, plugin discovery, and local plugin details. ## Testing - `cargo check -p codex-app-server -p codex-core -p codex-core-skills -p codex-core-plugins` - Built the app-server locally and exercised thread start, first turn, follow-up turn, server restart, thread resume, and a resumed turn.
372 lines
13 KiB
Rust
372 lines
13 KiB
Rust
use codex_arg0::Arg0DispatchPaths;
|
|
use codex_cloud_config::cloud_config_bundle_loader;
|
|
use codex_config::CloudConfigBundleLoader;
|
|
use codex_config::ConfigLayerStack;
|
|
use codex_config::LoaderOverrides;
|
|
use codex_config::ThreadConfigLoader;
|
|
use codex_config::loader::load_config_layers_state;
|
|
use codex_core::config::Config;
|
|
use codex_core::config::ConfigOverrides;
|
|
use codex_exec_server::LOCAL_FS;
|
|
use codex_features::feature_for_key;
|
|
use codex_login::AuthManager;
|
|
use codex_login::default_client::set_default_client_residency_requirement;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_json_to_toml::json_to_toml;
|
|
use std::collections::BTreeMap;
|
|
use std::collections::BTreeSet;
|
|
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use std::sync::RwLock;
|
|
use toml::Value as TomlValue;
|
|
use tracing::instrument;
|
|
use tracing::warn;
|
|
|
|
/// Shared app-server entry point for loading effective Codex configuration.
|
|
#[derive(Clone)]
|
|
pub(crate) struct ConfigManager {
|
|
codex_home: PathBuf,
|
|
cli_overrides: Arc<RwLock<Vec<(String, TomlValue)>>>,
|
|
runtime_feature_enablement: Arc<RwLock<BTreeMap<String, bool>>>,
|
|
loader_overrides: LoaderOverrides,
|
|
strict_config: bool,
|
|
cloud_config_bundle: Arc<RwLock<CloudConfigBundleLoader>>,
|
|
arg0_paths: Arg0DispatchPaths,
|
|
thread_config_loader: Arc<RwLock<Arc<dyn ThreadConfigLoader>>>,
|
|
}
|
|
|
|
impl ConfigManager {
|
|
pub(crate) fn new(
|
|
codex_home: PathBuf,
|
|
cli_overrides: Vec<(String, TomlValue)>,
|
|
loader_overrides: LoaderOverrides,
|
|
strict_config: bool,
|
|
cloud_config_bundle: CloudConfigBundleLoader,
|
|
arg0_paths: Arg0DispatchPaths,
|
|
thread_config_loader: Arc<dyn ThreadConfigLoader>,
|
|
) -> Self {
|
|
Self {
|
|
codex_home,
|
|
cli_overrides: Arc::new(RwLock::new(cli_overrides)),
|
|
runtime_feature_enablement: Arc::new(RwLock::new(BTreeMap::new())),
|
|
loader_overrides,
|
|
strict_config,
|
|
cloud_config_bundle: Arc::new(RwLock::new(cloud_config_bundle)),
|
|
arg0_paths,
|
|
thread_config_loader: Arc::new(RwLock::new(thread_config_loader)),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn codex_home(&self) -> &Path {
|
|
self.codex_home.as_path()
|
|
}
|
|
|
|
pub(crate) fn user_config_path(&self) -> std::io::Result<AbsolutePathBuf> {
|
|
self.loader_overrides.user_config_path(self.codex_home())
|
|
}
|
|
|
|
pub(crate) fn current_cli_overrides(&self) -> Vec<(String, TomlValue)> {
|
|
self.cli_overrides
|
|
.read()
|
|
.map(|guard| guard.clone())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
pub(crate) fn current_cloud_config_bundle(&self) -> CloudConfigBundleLoader {
|
|
self.cloud_config_bundle
|
|
.read()
|
|
.map(|guard| guard.clone())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
pub(crate) fn extend_runtime_feature_enablement<I>(&self, enablement: I) -> Result<(), ()>
|
|
where
|
|
I: IntoIterator<Item = (String, bool)>,
|
|
{
|
|
let mut runtime_feature_enablement =
|
|
self.runtime_feature_enablement.write().map_err(|_| ())?;
|
|
runtime_feature_enablement.extend(enablement);
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn replace_cloud_config_bundle_loader(
|
|
&self,
|
|
auth_manager: Arc<AuthManager>,
|
|
chatgpt_base_url: String,
|
|
) {
|
|
let loader =
|
|
cloud_config_bundle_loader(auth_manager, chatgpt_base_url, self.codex_home.clone());
|
|
if let Ok(mut guard) = self.cloud_config_bundle.write() {
|
|
*guard = loader;
|
|
} else {
|
|
warn!("failed to update cloud config bundle loader");
|
|
}
|
|
}
|
|
|
|
pub(crate) fn replace_thread_config_loader(
|
|
&self,
|
|
thread_config_loader: Arc<dyn ThreadConfigLoader>,
|
|
) {
|
|
if let Ok(mut guard) = self.thread_config_loader.write() {
|
|
*guard = thread_config_loader;
|
|
} else {
|
|
warn!("failed to update thread config loader");
|
|
}
|
|
}
|
|
|
|
fn current_thread_config_loader(&self) -> Arc<dyn ThreadConfigLoader> {
|
|
self.thread_config_loader
|
|
.read()
|
|
.map(|guard| Arc::clone(&*guard))
|
|
.unwrap_or_else(|_| Arc::new(codex_config::NoopThreadConfigLoader))
|
|
}
|
|
|
|
pub(crate) async fn sync_default_client_residency_requirement(&self) {
|
|
match self.load_latest_config(/*fallback_cwd*/ None).await {
|
|
Ok(config) => {
|
|
set_default_client_residency_requirement(config.enforce_residency.value());
|
|
}
|
|
Err(err) => warn!(
|
|
error = %err,
|
|
"failed to sync default client residency requirement after auth refresh"
|
|
),
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn load_latest_config(
|
|
&self,
|
|
fallback_cwd: Option<PathBuf>,
|
|
) -> std::io::Result<Config> {
|
|
self.load_with_cli_overrides(
|
|
&self.current_cli_overrides(),
|
|
/*request_overrides*/ None,
|
|
ConfigOverrides::default(),
|
|
fallback_cwd,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub(crate) async fn load_latest_config_for_thread(
|
|
&self,
|
|
thread_config: &Config,
|
|
) -> std::io::Result<Config> {
|
|
let refreshed_config = self
|
|
.load_latest_config(Some(thread_config.cwd.to_path_buf()))
|
|
.await?;
|
|
let mut config = thread_config
|
|
.rebuild_preserving_session_layers(&refreshed_config)
|
|
.await?;
|
|
self.apply_runtime_feature_enablement(&mut config);
|
|
self.apply_arg0_paths(&mut config);
|
|
Ok(config)
|
|
}
|
|
|
|
pub(crate) async fn load_default_config(&self) -> std::io::Result<Config> {
|
|
let mut config = Config::load_default_with_cli_overrides_for_codex_home(
|
|
self.codex_home.clone(),
|
|
self.current_cli_overrides(),
|
|
)
|
|
.await?;
|
|
if self.loader_overrides.user_config_path.is_some()
|
|
|| self.loader_overrides.user_config_profile.is_some()
|
|
{
|
|
let user_config_path = self.loader_overrides.user_config_path(self.codex_home())?;
|
|
config.config_layer_stack = config.config_layer_stack.with_user_config_profile(
|
|
&user_config_path,
|
|
self.loader_overrides.user_config_profile.as_ref(),
|
|
TomlValue::Table(toml::map::Map::new()),
|
|
);
|
|
}
|
|
self.apply_runtime_feature_enablement(&mut config);
|
|
self.apply_arg0_paths(&mut config);
|
|
Ok(config)
|
|
}
|
|
|
|
pub(crate) async fn load_with_overrides(
|
|
&self,
|
|
request_overrides: Option<HashMap<String, serde_json::Value>>,
|
|
typesafe_overrides: ConfigOverrides,
|
|
) -> std::io::Result<Config> {
|
|
self.load_with_cli_overrides(
|
|
&self.current_cli_overrides(),
|
|
request_overrides,
|
|
typesafe_overrides,
|
|
/*fallback_cwd*/ None,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub(crate) async fn load_for_cwd(
|
|
&self,
|
|
request_overrides: Option<HashMap<String, serde_json::Value>>,
|
|
typesafe_overrides: ConfigOverrides,
|
|
cwd: Option<PathBuf>,
|
|
) -> std::io::Result<Config> {
|
|
self.load_with_cli_overrides(
|
|
&self.current_cli_overrides(),
|
|
request_overrides,
|
|
typesafe_overrides,
|
|
cwd,
|
|
)
|
|
.await
|
|
}
|
|
|
|
#[instrument(level = "trace", skip_all)]
|
|
pub(crate) async fn load_with_cli_overrides(
|
|
&self,
|
|
cli_overrides: &[(String, TomlValue)],
|
|
request_overrides: Option<HashMap<String, serde_json::Value>>,
|
|
mut typesafe_overrides: ConfigOverrides,
|
|
fallback_cwd: Option<PathBuf>,
|
|
) -> std::io::Result<Config> {
|
|
let mut request_overrides = request_overrides.unwrap_or_default();
|
|
if let Some(value) = request_overrides.remove("bypass_hook_trust") {
|
|
typesafe_overrides.bypass_hook_trust = Some(value.as_bool().ok_or_else(|| {
|
|
std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
"`bypass_hook_trust` override must be a boolean",
|
|
)
|
|
})?);
|
|
}
|
|
let merged_cli_overrides = cli_overrides
|
|
.iter()
|
|
.cloned()
|
|
.chain(
|
|
request_overrides
|
|
.into_iter()
|
|
.map(|(key, value)| (key, json_to_toml(value))),
|
|
)
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut config = codex_core::config::ConfigBuilder::default()
|
|
.codex_home(self.codex_home.clone())
|
|
.cli_overrides(merged_cli_overrides)
|
|
.loader_overrides(self.loader_overrides.clone())
|
|
.strict_config(self.strict_config)
|
|
.harness_overrides(typesafe_overrides)
|
|
.fallback_cwd(fallback_cwd)
|
|
.cloud_config_bundle(self.current_cloud_config_bundle())
|
|
.thread_config_loader(self.current_thread_config_loader())
|
|
.build()
|
|
.await?;
|
|
self.apply_runtime_feature_enablement(&mut config);
|
|
self.apply_arg0_paths(&mut config);
|
|
Ok(config)
|
|
}
|
|
|
|
pub(crate) async fn load_config_layers_for_cwd(
|
|
&self,
|
|
cwd: AbsolutePathBuf,
|
|
) -> std::io::Result<ConfigLayerStack> {
|
|
self.load_config_layers(Some(cwd)).await
|
|
}
|
|
|
|
pub(crate) async fn load_config_layers(
|
|
&self,
|
|
cwd: Option<AbsolutePathBuf>,
|
|
) -> std::io::Result<ConfigLayerStack> {
|
|
let thread_config_loader = self.current_thread_config_loader();
|
|
load_config_layers_state(
|
|
LOCAL_FS.as_ref(),
|
|
&self.codex_home,
|
|
cwd,
|
|
&self.current_cli_overrides(),
|
|
codex_config::ConfigLoadOptions {
|
|
loader_overrides: self.loader_overrides.clone(),
|
|
strict_config: self.strict_config,
|
|
cloud_config_bundle: self.current_cloud_config_bundle(),
|
|
},
|
|
thread_config_loader.as_ref(),
|
|
)
|
|
.await
|
|
}
|
|
|
|
fn apply_runtime_feature_enablement(&self, config: &mut Config) {
|
|
apply_runtime_feature_enablement(config, &self.current_runtime_feature_enablement());
|
|
}
|
|
|
|
fn current_runtime_feature_enablement(&self) -> BTreeMap<String, bool> {
|
|
self.runtime_feature_enablement
|
|
.read()
|
|
.map(|guard| guard.clone())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
fn apply_arg0_paths(&self, config: &mut Config) {
|
|
config.codex_self_exe = self.arg0_paths.codex_self_exe.clone();
|
|
config.codex_linux_sandbox_exe = self.arg0_paths.codex_linux_sandbox_exe.clone();
|
|
config.main_execve_wrapper_exe = self.arg0_paths.main_execve_wrapper_exe.clone();
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn new_for_tests(
|
|
codex_home: PathBuf,
|
|
cli_overrides: Vec<(String, TomlValue)>,
|
|
loader_overrides: LoaderOverrides,
|
|
cloud_config_bundle: CloudConfigBundleLoader,
|
|
) -> Self {
|
|
Self::new(
|
|
codex_home,
|
|
cli_overrides,
|
|
loader_overrides,
|
|
/*strict_config*/ false,
|
|
cloud_config_bundle,
|
|
Arg0DispatchPaths::default(),
|
|
Arc::new(codex_config::NoopThreadConfigLoader),
|
|
)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn without_managed_config_for_tests(codex_home: PathBuf) -> Self {
|
|
Self::new_for_tests(
|
|
codex_home,
|
|
Vec::new(),
|
|
LoaderOverrides::without_managed_config_for_tests(),
|
|
CloudConfigBundleLoader::default(),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn protected_feature_keys(config_layer_stack: &ConfigLayerStack) -> BTreeSet<String> {
|
|
let mut protected_features = config_layer_stack
|
|
.effective_config()
|
|
.get("features")
|
|
.and_then(toml::Value::as_table)
|
|
.map(|features| features.keys().cloned().collect::<BTreeSet<_>>())
|
|
.unwrap_or_default();
|
|
|
|
if let Some(feature_requirements) = config_layer_stack
|
|
.requirements_toml()
|
|
.feature_requirements
|
|
.as_ref()
|
|
{
|
|
protected_features.extend(feature_requirements.entries.keys().cloned());
|
|
}
|
|
|
|
protected_features
|
|
}
|
|
|
|
pub(crate) fn apply_runtime_feature_enablement(
|
|
config: &mut Config,
|
|
runtime_feature_enablement: &BTreeMap<String, bool>,
|
|
) {
|
|
let protected_features = protected_feature_keys(&config.config_layer_stack);
|
|
for (name, enabled) in runtime_feature_enablement {
|
|
if protected_features.contains(name) {
|
|
continue;
|
|
}
|
|
let Some(feature) = feature_for_key(name) else {
|
|
continue;
|
|
};
|
|
if let Err(err) = config.features.set_enabled(feature, *enabled) {
|
|
warn!(
|
|
feature = name,
|
|
error = %err,
|
|
"failed to apply runtime feature enablement"
|
|
);
|
|
}
|
|
}
|
|
}
|