mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Speed up TUI startup by reusing plugin discovery (#26469)
## Summary TUI startup loads related plugin data from `hooks/list`, session MCP initialization, and plugin skill warmup. These paths repeated filesystem discovery and emitted the same plugin warnings, while `hooks/list` and account/model bootstrap ran serially. This change: - Reuses one immutable plugin load outcome across startup consumers. - Keys the cache only on plugin-relevant configuration. - Single-flights concurrent plugin loads and prevents invalidated loads from repopulating the cache. - Runs hook discovery and account/model bootstrap concurrently. - Preserves configuration-migration ordering, hook review behavior, and accurate startup telemetry. In 10 alternating release-build launches in the Ruff repository with the existing `~/.codex` configuration, median time to the first editable composer decreased from 833ms to 504ms. The branch was faster in 9 of 10 pairs, with a paired median improvement of 312ms.
This commit is contained in:
1 parent
345cf6e8d0
commit
055c7a7c53
9 files changed
+303
-37
No files matched your search
@@ -1300,6 +1300,97 @@ async fn load_plugins_returns_empty_when_feature_disabled() {
|
||||
assert_eq!(outcome, PluginLoadOutcome::default());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_cache_ignores_unrelated_session_overrides() {
|
||||
let codex_home = TempDir::new().unwrap();
|
||||
let plugin_root = codex_home
|
||||
.path()
|
||||
.join("plugins/cache")
|
||||
.join("test/sample/local");
|
||||
write_plugin(
|
||||
codex_home.path().join("plugins/cache/test").as_path(),
|
||||
"sample/local",
|
||||
"sample",
|
||||
);
|
||||
write_file(
|
||||
&plugin_root.join(".mcp.json"),
|
||||
r#"{
|
||||
"mcpServers": {
|
||||
"sample": {
|
||||
"url": "https://sample.example/mcp"
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let user_file = codex_home.path().join(CONFIG_TOML_FILE).abs();
|
||||
let user_config: toml::Value = toml::from_str(&plugin_config_toml(
|
||||
/*enabled*/ true, /*plugins_feature_enabled*/ true,
|
||||
))
|
||||
.expect("user config should parse");
|
||||
let stack = |session_config: &str| {
|
||||
ConfigLayerStack::new(
|
||||
vec![
|
||||
ConfigLayerEntry::new(
|
||||
ConfigLayerSource::User {
|
||||
file: user_file.clone(),
|
||||
profile: None,
|
||||
},
|
||||
user_config.clone(),
|
||||
),
|
||||
ConfigLayerEntry::new(
|
||||
ConfigLayerSource::SessionFlags,
|
||||
toml::from_str(session_config).expect("session config should parse"),
|
||||
),
|
||||
],
|
||||
ConfigRequirements::default(),
|
||||
ConfigRequirementsToml::default(),
|
||||
)
|
||||
.expect("config layer stack should build")
|
||||
};
|
||||
let config = |session_config| {
|
||||
PluginsConfigInput::new(
|
||||
stack(session_config),
|
||||
/*plugins_enabled*/ true,
|
||||
/*remote_plugin_enabled*/ false,
|
||||
"https://chatgpt.com".to_string(),
|
||||
)
|
||||
};
|
||||
let manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
let first = manager
|
||||
.plugins_for_config(&config(r#"model = "first""#))
|
||||
.await;
|
||||
std::fs::remove_file(plugin_root.join(".mcp.json")).unwrap();
|
||||
let second = manager
|
||||
.plugins_for_config(&config(r#"model = "second""#))
|
||||
.await;
|
||||
|
||||
assert_eq!(second, first);
|
||||
assert_eq!(second.plugins()[0].mcp_servers.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_cache_invalidation_rejects_stale_load_completion() {
|
||||
let codex_home = TempDir::new().unwrap();
|
||||
let manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
let cache_key = PluginLoadCacheKey {
|
||||
configured_plugins: HashMap::new(),
|
||||
skill_config_rules: SkillConfigRules::default(),
|
||||
remote_plugin_enabled: false,
|
||||
};
|
||||
let stale_generation = manager.enabled_outcome_cache_generation();
|
||||
|
||||
manager.clear_enabled_outcome_cache();
|
||||
manager.cache_enabled_outcome_if_current(
|
||||
stale_generation,
|
||||
cache_key.clone(),
|
||||
PluginLoadOutcome::default(),
|
||||
);
|
||||
|
||||
assert_eq!(manager.cached_enabled_outcome(&cache_key), None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_plugins_rejects_invalid_plugin_keys() {
|
||||
let codex_home = TempDir::new().unwrap();
|
||||
|
||||
Reference in new issue
Block a user