mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
## Why `#16193` moved the pure `tool_search` and `tool_suggest` spec builders into `codex-tools`, but `codex-core` still owned the shared discoverable-tool model that those builders and the `tool_suggest` runtime both depend on. This change continues the migration by moving that reusable model boundary out of `codex-core` as well, so the discovery/suggestion stack uses one shared set of types and `core/src/tools` no longer needs its own `discoverable.rs` module. ## What changed - Moved `DiscoverableTool`, `DiscoverablePluginInfo`, and `filter_tool_suggest_discoverable_tools_for_client()` into `codex-rs/tools/src/tool_discovery.rs` alongside the extracted discovery/suggestion spec builders. - Added `codex-app-server-protocol` as a `codex-tools` dependency so the shared discoverable-tool model can own the connector-side `AppInfo` variant directly. - Updated `core/src/tools/handlers/tool_suggest.rs`, `core/src/tools/spec.rs`, `core/src/tools/router.rs`, `core/src/connectors.rs`, and `core/src/codex.rs` to consume the shared `codex-tools` model instead of the old core-local declarations. - Changed `core/src/plugins/discoverable.rs` to return `DiscoverablePluginInfo` directly, moved the pure client-filter coverage into `tool_discovery_tests.rs`, and deleted the old `core/src/tools/discoverable.rs` module. - Updated `codex-rs/tools/README.md` so the crate boundary documents that `codex-tools` now owns the discoverable-tool models in addition to the discovery/suggestion spec builders. ## Test plan - `cargo test -p codex-tools` - `CARGO_TARGET_DIR=/tmp/codex-core-discoverable-model cargo test -p codex-core --lib tools::handlers::tool_suggest::` - `CARGO_TARGET_DIR=/tmp/codex-core-discoverable-model cargo test -p codex-core --lib tools::spec::` - `CARGO_TARGET_DIR=/tmp/codex-core-discoverable-model cargo test -p codex-core --lib plugins::discoverable::` - `just bazel-lock-check` - `just argument-comment-lint` ## References - #16193 - #16154 - #15923 - #15928 - #15944 - #15953 - #16031 - #16047 - #16129 - #16132 - #16138 - #16141
99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
use anyhow::Context;
|
|
use std::collections::HashSet;
|
|
use tracing::warn;
|
|
|
|
use super::OPENAI_CURATED_MARKETPLACE_NAME;
|
|
use super::PluginCapabilitySummary;
|
|
use super::PluginReadRequest;
|
|
use super::PluginsManager;
|
|
use crate::config::Config;
|
|
use crate::config::types::ToolSuggestDiscoverableType;
|
|
use codex_features::Feature;
|
|
use codex_tools::DiscoverablePluginInfo;
|
|
|
|
const TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST: &[&str] = &[
|
|
"github@openai-curated",
|
|
"notion@openai-curated",
|
|
"slack@openai-curated",
|
|
"gmail@openai-curated",
|
|
"google-calendar@openai-curated",
|
|
"google-drive@openai-curated",
|
|
"linear@openai-curated",
|
|
"figma@openai-curated",
|
|
];
|
|
|
|
pub(crate) fn list_tool_suggest_discoverable_plugins(
|
|
config: &Config,
|
|
) -> anyhow::Result<Vec<DiscoverablePluginInfo>> {
|
|
if !config.features.enabled(Feature::Plugins) {
|
|
return Ok(Vec::new());
|
|
}
|
|
|
|
let plugins_manager = PluginsManager::new(config.codex_home.clone());
|
|
let configured_plugin_ids = config
|
|
.tool_suggest
|
|
.discoverables
|
|
.iter()
|
|
.filter(|discoverable| discoverable.kind == ToolSuggestDiscoverableType::Plugin)
|
|
.map(|discoverable| discoverable.id.as_str())
|
|
.collect::<HashSet<_>>();
|
|
let marketplaces = plugins_manager
|
|
.list_marketplaces_for_config(config, &[])
|
|
.context("failed to list plugin marketplaces for tool suggestions")?
|
|
.marketplaces;
|
|
let Some(curated_marketplace) = marketplaces
|
|
.into_iter()
|
|
.find(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME)
|
|
else {
|
|
return Ok(Vec::new());
|
|
};
|
|
|
|
let mut discoverable_plugins = Vec::<DiscoverablePluginInfo>::new();
|
|
for plugin in curated_marketplace.plugins {
|
|
if plugin.installed
|
|
|| (!TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST.contains(&plugin.id.as_str())
|
|
&& !configured_plugin_ids.contains(plugin.id.as_str()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
let plugin_id = plugin.id.clone();
|
|
let plugin_name = plugin.name.clone();
|
|
|
|
match plugins_manager.read_plugin_for_config(
|
|
config,
|
|
&PluginReadRequest {
|
|
plugin_name,
|
|
marketplace_path: curated_marketplace.path.clone(),
|
|
},
|
|
) {
|
|
Ok(plugin) => {
|
|
let plugin: PluginCapabilitySummary = plugin.plugin.into();
|
|
discoverable_plugins.push(DiscoverablePluginInfo {
|
|
id: plugin.config_name,
|
|
name: plugin.display_name,
|
|
description: plugin.description,
|
|
has_skills: plugin.has_skills,
|
|
mcp_server_names: plugin.mcp_server_names,
|
|
app_connector_ids: plugin
|
|
.app_connector_ids
|
|
.into_iter()
|
|
.map(|connector_id| connector_id.0)
|
|
.collect(),
|
|
});
|
|
}
|
|
Err(err) => warn!("failed to load discoverable plugin suggestion {plugin_id}: {err:#}"),
|
|
}
|
|
}
|
|
discoverable_plugins.sort_by(|left, right| {
|
|
left.name
|
|
.cmp(&right.name)
|
|
.then_with(|| left.id.cmp(&right.id))
|
|
});
|
|
Ok(discoverable_plugins)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "discoverable_tests.rs"]
|
|
mod tests;
|