Move plugin and skill warmup into session startup (#23535)

## Why

Plugin and skill loading is useful as warmup and early validation, but
session startup does not need to wait for that work before it can
continue building the session. Keeping it on the serial startup path
adds avoidable latency to every fresh thread start.

We still want invalid skill configurations to show up quickly, and we
want the warmup to exercise the same plugin and skill manager caches
that the normal turn path uses.

## What changed

- moved plugin and skill warmup into the session startup async path
instead of eagerly awaiting it on the serial setup path
- kept the warmup using the session's resolved filesystem/environment
context so skill loading still sees the right roots
- preserved early skill-load error logging so broken skill
configurations still surface during startup
- left the per-turn plugin and skill loading path unchanged, so turns
still use the normal cached managers

## Testing

- Not run locally; relying on CI for validation.
This commit is contained in:
Ahmed Ibrahim
2026-05-19 20:05:52 -07:00
committed by GitHub
parent c3faea0b09
commit 532b9c83ae
2 changed files with 55 additions and 16 deletions

View File

@@ -480,20 +480,6 @@ impl Codex {
} = args;
let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
let (tx_event, rx_event) = async_channel::unbounded();
let fs = environment_selections.primary_filesystem();
let plugins_input = config.plugins_config_input();
let plugin_outcome = plugins_manager.plugins_for_config(&plugins_input).await;
let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots();
let skills_input = skills_load_input_from_config(&config, effective_skill_roots);
let loaded_skills = skills_manager.skills_for_config(&skills_input, fs).await;
for err in &loaded_skills.errors {
error!(
"failed to load skill {}: {}",
err.path.display(),
err.message
);
}
if let SessionSource::SubAgent(SubAgentSource::ThreadSpawn { depth, .. }) = session_source
&& depth >= config.agent_max_depth

View File

@@ -1,6 +1,7 @@
use super::input_queue::InputQueue;
use super::*;
use crate::goals::GoalRuntimeState;
use crate::skills::SkillError;
use crate::state::ActiveTurn;
use codex_protocol::SessionId;
use codex_protocol::config_types::ServiceTier;
@@ -405,6 +406,29 @@ pub(crate) struct AppServerClientMetadata {
pub(crate) client_version: Option<String>,
}
async fn warm_plugins_and_skills_for_session_init(
config: Arc<Config>,
environment_manager: Arc<EnvironmentManager>,
plugins_manager: Arc<PluginsManager>,
skills_manager: Arc<SkillsManager>,
environments: Vec<TurnEnvironmentSelection>,
) -> Vec<SkillError> {
let fs = crate::environment_selection::resolve_environment_selections(
environment_manager.as_ref(),
&environments,
)
.ok()
.and_then(|resolved| resolved.primary_filesystem());
let plugins_input = config.plugins_config_input();
let plugin_outcome = plugins_manager.plugins_for_config(&plugins_input).await;
let effective_skill_roots = plugin_outcome.effective_plugin_skill_roots();
let skills_input = skills_load_input_from_config(config.as_ref(), effective_skill_roots);
skills_manager
.skills_for_config(&skills_input, fs)
.await
.errors
}
impl Session {
/// Returns the concrete identity for this thread.
pub(crate) fn thread_id(&self) -> ThreadId {
@@ -578,9 +602,38 @@ impl Session {
otel.name = "session_init.auth_mcp",
));
let plugin_and_skill_warmup_fut = warm_plugins_and_skills_for_session_init(
Arc::clone(&config),
Arc::clone(&environment_manager),
Arc::clone(&plugins_manager),
Arc::clone(&skills_manager),
session_configuration.environments.clone(),
)
.instrument(info_span!(
"session_init.plugin_skill_warmup",
otel.name = "session_init.plugin_skill_warmup",
));
// Join all independent futures.
let (thread_persistence_result, state_db_ctx, (auth, mcp_servers, auth_statuses)) =
tokio::join!(thread_persistence_fut, state_db_fut, auth_and_mcp_fut);
let (
thread_persistence_result,
state_db_ctx,
(auth, mcp_servers, auth_statuses),
plugin_skill_errors,
) = tokio::join!(
thread_persistence_fut,
state_db_fut,
auth_and_mcp_fut,
plugin_and_skill_warmup_fut
);
for err in &plugin_skill_errors {
error!(
"failed to load skill {}: {}",
err.path.display(),
err.message
);
}
let mut live_thread_init =
LiveThreadInitGuard::new(thread_persistence_result.map_err(|e| {