[codex] Update remote connector suggestions (#25172)

## Summary

- Use the session-loaded plugin app IDs as the source of connector
suggestion candidates.
- Remove the redundant plugin reload from
`tool_suggest_connector_ids()`.
- Add regression coverage for connectors declared by a loaded remote
plugin, using the Databricks app case.

## Context

Loaded remote plugins can declare app connector IDs in `.app.json`. The
session-owned `PluginsManager` already loads those plugins and exposes
their effective app IDs.

The connector suggestion path was creating a separate `PluginsManager`
and recomputing plugin app IDs. That new manager does not share the
session manager’s remote installed plugin cache, so app IDs from loaded
remote plugins were missing from connector suggestions.

## Fix

Pass the already-loaded effective app IDs into connector suggestion
generation and use them directly as the plugin-derived connector
candidate set.

Connector candidates are now built from:

- App IDs declared by loaded plugins
- Explicitly configured connector discoverables
- Existing disabled-suggestion filtering

This avoids a second plugin-manager lookup and keeps connector
suggestions aligned with the plugins actually loaded for the turn.

## Behavior

For example, when a plugin is loaded and its `.app.json` declares data
apps, `list_available_plugins_to_install` can now return those data
connectors.

This does not create plugin suggestions from the plugin itself. Plugin
suggestions still come from eligible uninstalled entries in the
marketplace catalog and require existing matching/filtering rules.

## Validation

- `just fmt`
- Added regression coverage for a loaded-plugin connector ID appearing
in discoverable tools
- Attempted `just test -p codex-core`; the command exited unsuccessfully
in the local test environment without useful failure detail captured in
the run output
This commit is contained in:
Eric Ning
2026-05-29 17:57:34 -07:00
committed by GitHub
parent a5a94ee5a7
commit e929bb5c88
2 changed files with 45 additions and 11 deletions

View File

@@ -115,7 +115,7 @@ pub(crate) async fn list_tool_suggest_discoverable_tools_with_auth(
accessible_connectors: &[AppInfo],
loaded_plugin_app_connector_ids: &[String],
) -> anyhow::Result<Vec<DiscoverableTool>> {
let connector_ids = tool_suggest_connector_ids(config).await;
let connector_ids = tool_suggest_connector_ids(config, loaded_plugin_app_connector_ids);
let directory_connectors = codex_connectors::merge::merge_plugin_connectors(
cached_directory_connectors_for_tool_suggest_with_auth(config, auth).await,
connector_ids.iter().cloned(),
@@ -406,15 +406,13 @@ fn write_cached_accessible_connectors(
});
}
async fn tool_suggest_connector_ids(config: &Config) -> HashSet<String> {
let plugins_input = config.plugins_config_input();
let mut connector_ids = PluginsManager::new(config.codex_home.to_path_buf())
.plugins_for_config(&plugins_input)
.await
.capability_summaries()
fn tool_suggest_connector_ids(
config: &Config,
loaded_plugin_app_connector_ids: &[String],
) -> HashSet<String> {
let mut connector_ids = loaded_plugin_app_connector_ids
.iter()
.flat_map(|plugin| plugin.app_connector_ids.iter())
.map(|connector_id| connector_id.0.clone())
.cloned()
.collect::<HashSet<_>>();
connector_ids.extend(
config

View File

@@ -1180,7 +1180,7 @@ discoverables = [
.expect("config should load");
assert_eq!(
tool_suggest_connector_ids(&config).await,
tool_suggest_connector_ids(&config, &[]),
HashSet::from(["connector_2128aebfecb84f64a069897515042a44".to_string()])
);
}
@@ -1209,7 +1209,7 @@ disabled_tools = [
.expect("config should load");
assert_eq!(
tool_suggest_connector_ids(&config).await,
tool_suggest_connector_ids(&config, &[]),
HashSet::from(["connector_gmail".to_string()])
);
}
@@ -1249,3 +1249,39 @@ discoverables = [
))]
);
}
#[tokio::test]
async fn tool_suggest_includes_connectors_from_loaded_plugin_apps() {
let codex_home = tempdir().expect("tempdir should succeed");
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"
[features]
apps = true
"#,
)
.expect("write config");
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.build()
.await
.expect("config should load");
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
let loaded_plugin_app_connector_ids = vec!["asdk_app_databricks_workspace".to_string()];
let discoverable_tools = list_tool_suggest_discoverable_tools_with_auth(
&config,
Some(&auth),
&[],
&loaded_plugin_app_connector_ids,
)
.await
.expect("discoverable tools should load");
assert_eq!(
discoverable_tools,
vec![DiscoverableTool::from(plugin_connector_to_app_info(
"asdk_app_databricks_workspace".to_string(),
))]
);
}