mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
simplify dependency
This commit is contained in:
1
codex-rs/Cargo.lock
generated
1
codex-rs/Cargo.lock
generated
@@ -3402,7 +3402,6 @@ dependencies = [
|
||||
"codex-extension-api",
|
||||
"codex-tools",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -12,14 +12,17 @@ use codex_extension_api::ExtensionRegistryBuilder;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::error::CodexErr;
|
||||
|
||||
pub(crate) fn thread_extensions<S>(guardian_agent_spawner: S) -> Arc<ExtensionRegistry<Config>>
|
||||
pub(crate) fn thread_extensions<S>(
|
||||
guardian_agent_spawner: S,
|
||||
plugin_install_list_tool_enabled: bool,
|
||||
) -> Arc<ExtensionRegistry<Config>>
|
||||
where
|
||||
S: AgentSpawner<StartThreadOptions, Spawned = NewThread, Error = CodexErr> + 'static,
|
||||
{
|
||||
let mut builder = ExtensionRegistryBuilder::<Config>::new();
|
||||
codex_guardian::install(&mut builder, guardian_agent_spawner);
|
||||
codex_memories_extension::install(&mut builder);
|
||||
codex_plugins_extension::install(&mut builder);
|
||||
codex_plugins_extension::install(&mut builder, plugin_install_list_tool_enabled);
|
||||
Arc::new(builder.build())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::config_manager::ConfigManager;
|
||||
use codex_core::CodexThread;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::McpServerRefreshConfig;
|
||||
use codex_protocol::protocol::Op;
|
||||
@@ -186,7 +187,10 @@ mod tests {
|
||||
auth_manager,
|
||||
SessionSource::Exec,
|
||||
Arc::new(EnvironmentManager::default_for_tests()),
|
||||
thread_extensions(guardian_agent_spawner(thread_manager.clone())),
|
||||
thread_extensions(
|
||||
guardian_agent_spawner(thread_manager.clone()),
|
||||
good_config.features.enabled(Feature::PluginInstallListTool),
|
||||
),
|
||||
/*analytics_events_client*/ None,
|
||||
thread_store,
|
||||
Some(state_db.clone()),
|
||||
|
||||
@@ -68,6 +68,7 @@ use codex_chatgpt::workspace_settings;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_features::Feature;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::auth::ExternalAuth;
|
||||
@@ -307,7 +308,10 @@ impl MessageProcessor {
|
||||
auth_manager.clone(),
|
||||
session_source,
|
||||
environment_manager,
|
||||
thread_extensions(guardian_agent_spawner(thread_manager.clone())),
|
||||
thread_extensions(
|
||||
guardian_agent_spawner(thread_manager.clone()),
|
||||
config.features.enabled(Feature::PluginInstallListTool),
|
||||
),
|
||||
Some(analytics_events_client.clone()),
|
||||
Arc::clone(&thread_store),
|
||||
state_db.clone(),
|
||||
|
||||
@@ -40,7 +40,6 @@ use crate::tools::handlers::request_plugin_install_spec::create_request_plugin_i
|
||||
use crate::tools::registry::CoreToolRuntime;
|
||||
use crate::tools::registry::ToolExecutor;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RequestPluginInstallHandler {
|
||||
discoverable_tools: Vec<RequestPluginInstallEntry>,
|
||||
plugin_install_list_tool: bool,
|
||||
|
||||
@@ -85,6 +85,7 @@ struct ToolRegistryBuildParams<'a> {
|
||||
deferred_mcp_tools: Option<&'a [ToolInfo]>,
|
||||
discoverable_tools: Option<&'a [DiscoverableTool]>,
|
||||
extension_tool_executors: &'a [Arc<dyn ToolExecutor<ExtensionToolCall>>],
|
||||
list_installable_plugins_registered: bool,
|
||||
dynamic_tools: &'a [DynamicToolSpec],
|
||||
default_agent_type_description: &'a str,
|
||||
wait_agent_timeouts: WaitAgentTimeoutOptions,
|
||||
@@ -106,6 +107,9 @@ fn build_tool_specs_and_registry(
|
||||
extension_tool_executors,
|
||||
dynamic_tools,
|
||||
} = params;
|
||||
let list_installable_plugins_registered = extension_tool_executors.iter().any(|executor| {
|
||||
executor.tool_name() == ToolName::plain(LIST_INSTALLABLE_PLUGINS_TOOL_NAME)
|
||||
});
|
||||
let default_agent_type_description =
|
||||
crate::agent::role::spawn_tool_spec::build(&std::collections::BTreeMap::new());
|
||||
let mut executors = collect_tool_executors(
|
||||
@@ -115,6 +119,7 @@ fn build_tool_specs_and_registry(
|
||||
deferred_mcp_tools: deferred_mcp_tools.as_deref(),
|
||||
discoverable_tools: discoverable_tools.as_deref(),
|
||||
extension_tool_executors: &extension_tool_executors,
|
||||
list_installable_plugins_registered,
|
||||
dynamic_tools,
|
||||
default_agent_type_description: &default_agent_type_description,
|
||||
wait_agent_timeouts: wait_agent_timeout_options(config),
|
||||
@@ -419,7 +424,7 @@ fn collect_tool_executors(
|
||||
{
|
||||
executors.push(Arc::new(RequestPluginInstallHandler::new(
|
||||
discoverable_tools,
|
||||
config.plugin_install_list_tool,
|
||||
params.list_installable_plugins_registered,
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -614,11 +619,6 @@ fn append_extension_tool_executors(
|
||||
|
||||
for executor in executors.iter().cloned() {
|
||||
let tool_name = executor.tool_name();
|
||||
if tool_name == ToolName::plain(LIST_INSTALLABLE_PLUGINS_TOOL_NAME)
|
||||
&& !config.plugin_install_list_tool
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if !reserved_tool_names.insert(tool_name.clone()) {
|
||||
warn!("Skipping extension tool `{tool_name}`: tool already registered");
|
||||
continue;
|
||||
|
||||
@@ -1970,7 +1970,10 @@ fn request_plugin_install_is_not_registered_without_feature_flag() {
|
||||
"Google Calendar",
|
||||
"Plan events and schedules.",
|
||||
)]),
|
||||
/*extension_tool_executors*/ &[],
|
||||
&[extension_tool_executor(
|
||||
LIST_INSTALLABLE_PLUGINS_TOOL_NAME,
|
||||
"List installable plugins.",
|
||||
)],
|
||||
&[],
|
||||
);
|
||||
|
||||
@@ -2011,7 +2014,10 @@ fn request_plugin_install_can_be_registered_without_search_tool() {
|
||||
"Google Calendar",
|
||||
"Plan events and schedules.",
|
||||
)]),
|
||||
/*extension_tool_executors*/ &[],
|
||||
&[extension_tool_executor(
|
||||
LIST_INSTALLABLE_PLUGINS_TOOL_NAME,
|
||||
"List installable plugins.",
|
||||
)],
|
||||
&[],
|
||||
);
|
||||
|
||||
@@ -2039,14 +2045,13 @@ fn request_plugin_install_can_be_registered_without_search_tool() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_plugin_install_description_lists_discoverable_tools() {
|
||||
fn request_plugin_install_description_lists_discoverable_tools_without_list_tool() {
|
||||
let model_info = search_capable_model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
features.enable(Feature::Plugins);
|
||||
features.enable(Feature::ToolSearch);
|
||||
features.enable(Feature::ToolSuggest);
|
||||
features.disable(Feature::PluginInstallListTool);
|
||||
let available_models = Vec::new();
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
@@ -2509,6 +2514,9 @@ fn build_specs_with_inputs_for_test(
|
||||
deferred_mcp_tools: deferred_mcp_tools.as_deref(),
|
||||
discoverable_tools: discoverable_tools.as_deref(),
|
||||
extension_tool_executors,
|
||||
list_installable_plugins_registered: extension_tool_executors.iter().any(|executor| {
|
||||
executor.tool_name() == ToolName::plain(LIST_INSTALLABLE_PLUGINS_TOOL_NAME)
|
||||
}),
|
||||
dynamic_tools,
|
||||
default_agent_type_description: DEFAULT_AGENT_TYPE_DESCRIPTION,
|
||||
wait_agent_timeouts: wait_agent_timeout_options(),
|
||||
|
||||
@@ -477,7 +477,7 @@ impl TestCodexBuilder {
|
||||
let installation_id = resolve_installation_id(&config.codex_home).await?;
|
||||
let extensions = if self.plugins_extension_enabled {
|
||||
let mut builder = ExtensionRegistryBuilder::new();
|
||||
codex_plugins_extension::install(&mut builder);
|
||||
codex_plugins_extension::install(&mut builder, true);
|
||||
Arc::new(builder.build())
|
||||
} else {
|
||||
empty_extension_registry()
|
||||
|
||||
@@ -16,7 +16,6 @@ workspace = true
|
||||
async-trait = { workspace = true }
|
||||
codex-extension-api = { workspace = true }
|
||||
codex-tools = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -17,66 +17,34 @@ use codex_extension_api::ToolSpec;
|
||||
use codex_tools::JsonSchema;
|
||||
use codex_tools::LIST_INSTALLABLE_PLUGINS_TOOL_NAME;
|
||||
use codex_tools::RequestPluginInstallEntry;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct PluginsExtension;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait InstallablePluginsProvider: Send + Sync {
|
||||
async fn list_installable_plugins(&self) -> Result<Vec<RequestPluginInstallEntry>, String>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstallablePluginsProviderHandle {
|
||||
provider: Arc<dyn InstallablePluginsProvider>,
|
||||
}
|
||||
|
||||
pub type InstallablePluginsFuture =
|
||||
Pin<Box<dyn Future<Output = Result<Vec<RequestPluginInstallEntry>, String>> + Send>>;
|
||||
|
||||
struct FnInstallablePluginsProvider<F>
|
||||
where
|
||||
F: Fn() -> InstallablePluginsFuture + Send + Sync,
|
||||
{
|
||||
list_installable_plugins: F,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<F> InstallablePluginsProvider for FnInstallablePluginsProvider<F>
|
||||
where
|
||||
F: Fn() -> InstallablePluginsFuture + Send + Sync,
|
||||
{
|
||||
async fn list_installable_plugins(&self) -> Result<Vec<RequestPluginInstallEntry>, String> {
|
||||
(self.list_installable_plugins)().await
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct InstallablePluginsProviderHandle {
|
||||
list_installable_plugins: Arc<dyn Fn() -> InstallablePluginsFuture + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl InstallablePluginsProviderHandle {
|
||||
pub fn new(provider: Arc<dyn InstallablePluginsProvider>) -> Self {
|
||||
Self { provider }
|
||||
}
|
||||
|
||||
pub fn from_fn<F>(list_installable_plugins: F) -> Self
|
||||
where
|
||||
F: Fn() -> InstallablePluginsFuture + Send + Sync + 'static,
|
||||
{
|
||||
Self::new(Arc::new(FnInstallablePluginsProvider {
|
||||
list_installable_plugins,
|
||||
}))
|
||||
Self {
|
||||
list_installable_plugins: Arc::new(list_installable_plugins),
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_installable_plugins(&self) -> Result<Vec<RequestPluginInstallEntry>, String> {
|
||||
self.provider.list_installable_plugins().await
|
||||
(self.list_installable_plugins)().await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
|
||||
struct ListInstallablePluginsResponse {
|
||||
entries: Vec<RequestPluginInstallEntry>,
|
||||
}
|
||||
|
||||
impl ToolContributor for PluginsExtension {
|
||||
fn tools(
|
||||
&self,
|
||||
@@ -128,15 +96,18 @@ impl ToolExecutor<ToolCall> for ListInstallablePluginsTool {
|
||||
.then_with(|| left.id.cmp(&right.id))
|
||||
});
|
||||
|
||||
Ok(Box::new(JsonToolOutput::new(json!(
|
||||
ListInstallablePluginsResponse { entries }
|
||||
))))
|
||||
Ok(Box::new(JsonToolOutput::new(json!({ "entries": entries }))))
|
||||
}
|
||||
}
|
||||
|
||||
/// Installs plugins extension contributors into the supplied extension registry.
|
||||
pub fn install<C>(registry: &mut ExtensionRegistryBuilder<C>) {
|
||||
registry.tool_contributor(Arc::new(PluginsExtension));
|
||||
pub fn install<C>(
|
||||
registry: &mut ExtensionRegistryBuilder<C>,
|
||||
list_installable_plugins_enabled: bool,
|
||||
) {
|
||||
if list_installable_plugins_enabled {
|
||||
registry.tool_contributor(Arc::new(PluginsExtension));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -145,18 +116,6 @@ mod tests {
|
||||
use codex_tools::DiscoverableToolType;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct StaticInstallablePluginsProvider {
|
||||
entries: Vec<RequestPluginInstallEntry>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InstallablePluginsProvider for StaticInstallablePluginsProvider {
|
||||
async fn list_installable_plugins(&self) -> Result<Vec<RequestPluginInstallEntry>, String> {
|
||||
Ok(self.entries.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tools_are_not_contributed_without_provider() {
|
||||
let extension = PluginsExtension;
|
||||
@@ -175,19 +134,19 @@ mod tests {
|
||||
async fn list_tool_returns_provider_installable_entries() {
|
||||
let extension = PluginsExtension;
|
||||
let session_store = ExtensionData::new("session");
|
||||
session_store.insert(InstallablePluginsProviderHandle::new(Arc::new(
|
||||
StaticInstallablePluginsProvider {
|
||||
entries: vec![RequestPluginInstallEntry {
|
||||
id: "sample@openai-curated".to_string(),
|
||||
name: "Sample Plugin".to_string(),
|
||||
description: Some("Adds sample capabilities.".to_string()),
|
||||
tool_type: DiscoverableToolType::Plugin,
|
||||
has_skills: true,
|
||||
mcp_server_names: vec!["sample-docs".to_string()],
|
||||
app_connector_ids: vec!["connector_sample".to_string()],
|
||||
}],
|
||||
},
|
||||
)));
|
||||
let entries = vec![RequestPluginInstallEntry {
|
||||
id: "sample@openai-curated".to_string(),
|
||||
name: "Sample Plugin".to_string(),
|
||||
description: Some("Adds sample capabilities.".to_string()),
|
||||
tool_type: DiscoverableToolType::Plugin,
|
||||
has_skills: true,
|
||||
mcp_server_names: vec!["sample-docs".to_string()],
|
||||
app_connector_ids: vec!["connector_sample".to_string()],
|
||||
}];
|
||||
session_store.insert(InstallablePluginsProviderHandle::from_fn(move || {
|
||||
let entries = entries.clone();
|
||||
Box::pin(async move { Ok(entries) })
|
||||
}));
|
||||
|
||||
let tools = extension.tools(&session_store, &ExtensionData::new("thread"));
|
||||
assert_eq!(tools.len(), 1);
|
||||
|
||||
@@ -109,7 +109,6 @@ pub struct ToolsConfig {
|
||||
pub search_tool: bool,
|
||||
pub namespace_tools: bool,
|
||||
pub tool_suggest: bool,
|
||||
pub plugin_install_list_tool: bool,
|
||||
pub exec_permission_approvals_enabled: bool,
|
||||
pub request_permissions_tool_enabled: bool,
|
||||
pub code_mode_enabled: bool,
|
||||
@@ -188,8 +187,6 @@ impl ToolsConfig {
|
||||
let include_tool_suggest = features.enabled(Feature::ToolSuggest)
|
||||
&& features.enabled(Feature::Apps)
|
||||
&& features.enabled(Feature::Plugins);
|
||||
let include_plugin_install_list_tool =
|
||||
include_tool_suggest && features.enabled(Feature::PluginInstallListTool);
|
||||
let include_original_image_detail = can_request_original_image_detail(model_info);
|
||||
// API-key auth bypasses Codex backend entitlement/tool normalization, so
|
||||
// callers must confirm ChatGPT auth before exposing the built-in tool.
|
||||
@@ -252,7 +249,6 @@ impl ToolsConfig {
|
||||
search_tool: include_search_tool,
|
||||
namespace_tools: true,
|
||||
tool_suggest: include_tool_suggest,
|
||||
plugin_install_list_tool: include_plugin_install_list_tool,
|
||||
exec_permission_approvals_enabled,
|
||||
request_permissions_tool_enabled,
|
||||
code_mode_enabled: include_code_mode,
|
||||
|
||||
Reference in New Issue
Block a user