Allow plugin guidance to drive app installs

This commit is contained in:
Alex Daley
2026-05-29 20:37:44 -04:00
parent 3378621cc0
commit 364d340d0a
2 changed files with 10 additions and 5 deletions

View File

@@ -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.",
);

View File

@@ -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"));
}