From 3378621cc04485cf388d4d3c77c4549e402c30f2 Mon Sep 17 00:00:00 2001 From: Alex Daley Date: Fri, 29 May 2026 20:33:15 -0400 Subject: [PATCH 1/2] Expose plugin app IDs in invocation guidance --- codex-rs/core/src/plugins/render.rs | 12 ++++++++++++ codex-rs/core/src/plugins/render_tests.rs | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/codex-rs/core/src/plugins/render.rs b/codex-rs/core/src/plugins/render.rs index fc197dbbea4..6cb7f77b824 100644 --- a/codex-rs/core/src/plugins/render.rs +++ b/codex-rs/core/src/plugins/render.rs @@ -48,6 +48,18 @@ pub(crate) fn render_explicit_plugin_instructions( )); } + if !plugin.app_connector_ids.is_empty() { + lines.push(format!( + "- App IDs declared by this plugin, including templates: {}.", + plugin + .app_connector_ids + .iter() + .map(|connector_id| format!("`{}`", connector_id.0)) + .collect::>() + .join(", ") + )); + } + if lines.len() == 1 { return None; } diff --git a/codex-rs/core/src/plugins/render_tests.rs b/codex-rs/core/src/plugins/render_tests.rs index a0ec5312090..35c475905b3 100644 --- a/codex-rs/core/src/plugins/render_tests.rs +++ b/codex-rs/core/src/plugins/render_tests.rs @@ -1,4 +1,5 @@ use super::*; +use codex_plugin::AppConnectorId; use pretty_assertions::assert_eq; #[test] @@ -21,3 +22,25 @@ fn render_plugins_section_includes_descriptions_and_skill_naming_guidance() { assert_eq!(rendered, expected); } + +#[test] +fn render_explicit_plugin_instructions_include_declared_app_ids() { + let rendered = render_explicit_plugin_instructions( + &PluginCapabilitySummary { + display_name: "sample".to_string(), + app_connector_ids: vec![ + AppConnectorId("connector_calendar".to_string()), + AppConnectorId("templated_apps_Databricks".to_string()), + ], + ..PluginCapabilitySummary::default() + }, + &[], + &[], + ) + .expect("plugin app IDs should render"); + + assert_eq!( + rendered, + "Capabilities from the `sample` plugin:\n- App IDs declared by this plugin, including templates: `connector_calendar`, `templated_apps_Databricks`.\nUse these plugin-associated capabilities to help solve the task." + ); +} From 364d340d0a673d52802b8e2eb2ff9568799cbf81 Mon Sep 17 00:00:00 2001 From: Alex Daley Date: Fri, 29 May 2026 20:37:44 -0400 Subject: [PATCH 2/2] Allow plugin guidance to drive app installs --- .../src/tools/handlers/request_plugin_install_spec.rs | 8 +++++--- codex-rs/core/src/tools/spec_plan_tests.rs | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/codex-rs/core/src/tools/handlers/request_plugin_install_spec.rs b/codex-rs/core/src/tools/handlers/request_plugin_install_spec.rs index ed143283e74..6b10fbce3d6 100644 --- a/codex-rs/core/src/tools/handlers/request_plugin_install_spec.rs +++ b/codex-rs/core/src/tools/handlers/request_plugin_install_spec.rs @@ -32,7 +32,7 @@ pub(crate) fn create_request_plugin_install_tool() -> ToolSpec { ]); let description = format!( - "# Request plugin/connector install\n\nUse this tool only after `{LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME}` returns a plugin or connector that exactly matches the user's explicit request.\n\nDo not use it for adjacent capabilities, broad recommendations, or tools that merely seem useful. Pass the returned `tool_type` through directly, and pass the returned `id` as `tool_id`.\n\nIMPORTANT: DO NOT call this tool in parallel with other tools." + "# Request plugin/connector install\n\nUse this tool only when either:\n- `{LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME}` returns a plugin or connector that exactly matches the user's explicit request.\n- An active plugin's invocation guidance provides an exact concrete app ID for a connector required by the user's explicit request.\n\nDo not use it for adjacent capabilities, broad recommendations, or tools that merely seem useful. When using a list result, pass the returned `tool_type` through directly and pass the returned `id` as `tool_id`. When using an active plugin's invocation guidance, use `connector` as `tool_type` and pass the concrete app ID as `tool_id`. Do not pass template IDs such as `templated_apps_*`.\n\nIMPORTANT: DO NOT call this tool in parallel with other tools." ); ToolSpec::Function(ResponsesApiTool { @@ -65,8 +65,10 @@ mod tests { fn create_request_plugin_install_tool_uses_expected_wire_shape() { let expected_description = concat!( "# Request plugin/connector install\n\n", - "Use this tool only after `list_available_plugins_to_install` returns a plugin or connector that exactly matches the user's explicit request.\n\n", - "Do not use it for adjacent capabilities, broad recommendations, or tools that merely seem useful. Pass the returned `tool_type` through directly, and pass the returned `id` as `tool_id`.\n\n", + "Use this tool only when either:\n", + "- `list_available_plugins_to_install` returns a plugin or connector that exactly matches the user's explicit request.\n", + "- An active plugin's invocation guidance provides an exact concrete app ID for a connector required by the user's explicit request.\n\n", + "Do not use it for adjacent capabilities, broad recommendations, or tools that merely seem useful. When using a list result, pass the returned `tool_type` through directly and pass the returned `id` as `tool_id`. When using an active plugin's invocation guidance, use `connector` as `tool_type` and pass the concrete app ID as `tool_id`. Do not pass template IDs such as `templated_apps_*`.\n\n", "IMPORTANT: DO NOT call this tool in parallel with other tools.", ); diff --git a/codex-rs/core/src/tools/spec_plan_tests.rs b/codex-rs/core/src/tools/spec_plan_tests.rs index e059b5f4af2..992a26326ab 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -652,7 +652,7 @@ async fn install_suggestion_tools_stay_visible_without_tool_search() { } #[tokio::test] -async fn request_plugin_install_description_defers_inventory_to_list_tool() { +async fn request_plugin_install_description_accepts_list_or_active_plugin_guidance() { let plan = probe_with( |turn| { set_features( @@ -686,7 +686,10 @@ async fn request_plugin_install_description_defers_inventory_to_list_tool() { panic!("expected request_plugin_install function spec"); }; assert!(request_description.contains( - "Use this tool only after `list_available_plugins_to_install` returns a plugin or connector that exactly matches the user's explicit request." + "- `list_available_plugins_to_install` returns a plugin or connector that exactly matches the user's explicit request." + )); + assert!(request_description.contains( + "- An active plugin's invocation guidance provides an exact concrete app ID for a connector required by the user's explicit request." )); assert!(!request_description.contains("github")); }