Honor managed plugin disablement for executor capabilities (#40640)

## Why

Selected executor plugin roots could still expose capabilities when managed
requirements disabled the `plugins` feature.

## What changed

- Suppress MCP servers, skills, apps, and connectors from selected executor
  plugins when `plugins` is disabled, while preserving the selected-root
  identity used to filter those capabilities.
- Apply the policy consistently to direct selected-root discovery and batched
  executor capability discovery.

## Testing

- Add contributor-level and app-server coverage that verifies disabled plugin
  capabilities are absent and their MCP servers never start.

GitOrigin-RevId: e30bd8a936c4b0e083908b89a65f4d02e01747de
This commit is contained in:
hefuc-oai authored and copyberry committed 2026-08-25 16:47:13 +00:00
1 parent e44bd9b64d
commit 5ce04789fb
6 files changed
+162 -1

No files matched your search

@@ -39,6 +39,7 @@ use pretty_assertions::assert_eq;
use pretty_assertions::assert_ne;
use serde_json::json;
use tempfile::TempDir;
use test_case::test_case;
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;
use tokio::process::Child;
@@ -63,6 +64,93 @@ const MCP_SERVER_NAME: &str = "executor_probe";
const MCP_CALL_ID: &str = "selected-executor-mcp-call";
const CONNECTOR_ID: &str = "calendar";
#[test_case(false; "direct selected root discovery")]
#[test_case(true; "batched executor capability discovery")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn managed_plugins_requirement_disables_selected_executor_plugin_capabilities(
executor_capability_discovery: bool,
) -> Result<()> {
let responses_server = responses::start_mock_server().await;
let (apps_url, apps_server_handle) = start_apps_server_with_delays(
vec![AppInfo {
id: CONNECTOR_ID.to_string(),
name: "Calendar".to_string(),
description: None,
logo_url: None,
logo_url_dark: None,
icon_assets: None,
icon_dark_assets: None,
distribution_channel: None,
branding: None,
app_metadata: None,
labels: None,
install_url: None,
is_accessible: false,
is_enabled: true,
plugin_display_names: Vec::new(),
}],
vec![connector_tool(CONNECTOR_ID, "Calendar")?],
Duration::ZERO,
Duration::ZERO,
)
.await?;
let fixture = selected_capability_fixture(&responses_server.uri(), &apps_url)?;
let config_path = fixture.codex_home.path().join("config.toml");
let config = std::fs::read_to_string(&config_path)?.replace(
"executor_capability_discovery = true",
&format!("executor_capability_discovery = {executor_capability_discovery}\nplugins = true"),
);
std::fs::write(config_path, config)?;
std::fs::write(
fixture.codex_home.path().join("requirements.toml"),
"[features]\nplugins = false\n",
)?;
let response_mock = responses::mount_sse_once(
&responses_server,
responses::sse(vec![
responses::ev_response_created("disabled-selected-plugin"),
responses::ev_assistant_message("disabled-selected-plugin-message", "Done"),
responses::ev_completed("disabled-selected-plugin"),
]),
)
.await;
let mut app_server = TestAppServer::builder()
.with_codex_home(fixture.codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(READ_TIMEOUT, app_server.initialize()).await??;
let thread_id = start_thread(
&mut app_server,
fixture.selected_root,
fixture.environment_cwd.clone(),
)
.await?;
let mut exec_server =
spawn_exec_server(fixture.codex_home.path(), &fixture.exec_server_url).await?;
add_environment(&mut app_server, &fixture.exec_server_url).await?;
run_turn(
&mut app_server,
&thread_id,
"Inspect the disabled selected plugin capabilities",
fixture.environment_cwd,
)
.await?;
assert!(
!fixture.pid_file.exists(),
"the disabled selected plugin MCP server must never start"
);
assert_selected_capabilities_absent(&response_mock.single_request());
exec_server.kill().await?;
apps_server_handle.abort();
let _ = apps_server_handle.await;
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn selected_capability_stack_tracks_environment_availability_and_resume() -> Result<()> {
let responses_server = responses::start_mock_server().await;