mirror of
https://github.com/openai/codex.git
synced 2026-11-03 19:53:25 +00:00
Apply stable environment identity across MCP projection
This commit is contained in:
@@ -17,7 +17,7 @@ use crate::ResolvedExecutorPlugin;
|
||||
|
||||
const DEFAULT_MCP_CONFIG_FILE: &str = ".mcp.json";
|
||||
|
||||
/// MCP and connector declarations read from one exact executor binding.
|
||||
/// MCP and connector declarations read from one stable executor capability root.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExecutorPluginRuntime {
|
||||
plugin: ResolvedPlugin,
|
||||
@@ -26,10 +26,8 @@ pub struct ExecutorPluginRuntime {
|
||||
}
|
||||
|
||||
impl ExecutorPluginRuntime {
|
||||
/// Reads both runtime declaration files through the root's pinned filesystem.
|
||||
///
|
||||
/// `Ok(None)` is intentionally not cacheable: the plugin manifest may appear
|
||||
/// once a deferred executor finishes starting.
|
||||
/// Reads both runtime declaration files through the root's current ready filesystem.
|
||||
/// `Ok(None)` means the stable root is not a plugin and may be cached by the caller.
|
||||
pub async fn project(root: &ResolvedSelectedCapabilityRoot) -> anyhow::Result<Option<Self>> {
|
||||
let Some(plugin) = ExecutorPluginProvider::resolve_pinned(root).await? else {
|
||||
return Ok(None);
|
||||
|
||||
@@ -116,7 +116,7 @@ impl Session {
|
||||
.environment_manager()
|
||||
.resolve_selected_capability_roots(
|
||||
selected_roots,
|
||||
&environments.captured_environments(),
|
||||
&environments.captured_environment_availability(),
|
||||
)
|
||||
.await;
|
||||
let bindings = selected_bindings(selected_roots, &resolved_roots);
|
||||
|
||||
@@ -9,7 +9,8 @@ use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
///
|
||||
/// Selected environment identity and contents are stable. Plugin manifests, MCP declarations, and
|
||||
/// app declarations are therefore cached by the complete selected root for the session lifetime.
|
||||
/// Missing or failed projections are not cached, so a deferred environment can recover later.
|
||||
/// Successful projections and stable non-plugin roots are cached. Failed reads are not cached, so
|
||||
/// a transient executor error can recover later.
|
||||
///
|
||||
/// A live runtime is a separate cache: it is reusable only for the same ordered selected roots and
|
||||
/// the same process-local environment handles. The caller additionally compares the effective MCP
|
||||
@@ -37,7 +38,7 @@ struct CachedSelectedRuntime {
|
||||
|
||||
struct CachedExecutorPluginProjection {
|
||||
selected_root: SelectedCapabilityRoot,
|
||||
plugin: ExecutorPluginRuntime,
|
||||
plugin: Option<ExecutorPluginRuntime>,
|
||||
}
|
||||
|
||||
impl SelectedMcpRuntimeCache {
|
||||
@@ -73,26 +74,28 @@ impl SelectedMcpRuntimeCache {
|
||||
let mut plugins = Vec::new();
|
||||
for (selection_order, root) in bindings {
|
||||
let selected_root = root.selected_root();
|
||||
if let Some(plugin) = self
|
||||
if let Some(cached) = self
|
||||
.plugin_projections
|
||||
.iter()
|
||||
.find(|cached| &cached.selected_root == selected_root)
|
||||
.map(|cached| cached.plugin.clone())
|
||||
{
|
||||
plugins.push((*selection_order, plugin));
|
||||
if let Some(plugin) = &cached.plugin {
|
||||
plugins.push((*selection_order, plugin.clone()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
match ExecutorPluginRuntime::project(root).await {
|
||||
Ok(Some(plugin)) => {
|
||||
Ok(plugin) => {
|
||||
self.plugin_projections
|
||||
.push(CachedExecutorPluginProjection {
|
||||
selected_root: selected_root.clone(),
|
||||
plugin: plugin.clone(),
|
||||
});
|
||||
plugins.push((*selection_order, plugin));
|
||||
if let Some(plugin) = plugin {
|
||||
plugins.push((*selection_order, plugin));
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
selected_root = selected_root.id,
|
||||
|
||||
@@ -557,10 +557,14 @@ impl Session {
|
||||
config.current_time_reminder.as_ref(),
|
||||
external_time_provider,
|
||||
)?;
|
||||
let selected_capability_roots = if selected_capability_roots.is_empty() {
|
||||
initial_history.get_selected_capability_roots()
|
||||
} else {
|
||||
let selected_capability_roots = if !selected_capability_roots.is_empty() {
|
||||
selected_capability_roots
|
||||
} else if let Some(selected_capability_roots) =
|
||||
thread_extension_init.get::<Vec<SelectedCapabilityRoot>>()
|
||||
{
|
||||
selected_capability_roots.as_ref().clone()
|
||||
} else {
|
||||
initial_history.get_selected_capability_roots()
|
||||
};
|
||||
let thread_extension_data = codex_extension_api::ExtensionData::new_with_init(
|
||||
thread_id.to_string(),
|
||||
|
||||
Reference in New Issue
Block a user