[codex] [3/4] Activate endpoint plugin recommendations (#27704)

Summary\n- Await endpoint recommendation selection while constructing
each authenticated turn, removing the first-turn cache race.\n- Snapshot
and filter endpoint candidates once per turn, then use that same set for
the bounded contextual user fragment, tool exposure, and exact install
validation.\n- Keep recommendation selection ephemeral: do not persist
recommendation state in or gate resumed threads on prior context.\n-
Hide the legacy list tool in endpoint mode and preserve legacy discovery
unchanged when the endpoint is disabled or unavailable.\n- Keep remote
plugin and connector app identities out of model-visible context and
attach them only to Codex-owned elicitation metadata.\n\nStack\n- 3/4,
based on #28400.\n- Endpoint client and cache: #28399.\n- Generalized
suggestion presentation: #28400.\n- Install-schema follow-up:
#28403.\n\nValidation\n- \n- \n- \n- \n- Full : 2,649 passed and 88
environment-dependent tests failed because this sandbox cannot write ,
nest Seatbelt, or locate auxiliary test binaries.
This commit is contained in:
Alex Daley
2026-06-16 23:04:07 +00:00
committed by GitHub
parent 587487df9e
commit a34da3b295
18 changed files with 846 additions and 124 deletions
@@ -130,8 +130,8 @@ impl RequestPluginInstallHandler {
ToolSuggestPresentation::ListTool => format!(
"the discoverable tools returned by {LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME}"
),
ToolSuggestPresentation::DeveloperContext => {
"the developer recommendations".to_string()
ToolSuggestPresentation::RecommendationContext => {
"the <recommended_plugins> list".to_string()
}
};
FunctionCallError::RespondToModel(format!(
@@ -39,8 +39,8 @@ pub(crate) fn create_request_plugin_install_tool(
ToolSuggestPresentation::ListTool => 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."
),
ToolSuggestPresentation::DeveloperContext =>
"# Suggest a recommended plugin installation\n\nSuggest installing a plugin from the developer `<recommended_plugins>` list when it would help with the user's current request. Briefly explain why in `suggest_reason`.".to_string(),
ToolSuggestPresentation::RecommendationContext =>
"# Suggest a recommended plugin installation\n\nSuggest installing a plugin from the `<recommended_plugins>` list when it would help with the user's current request. Briefly explain why in `suggest_reason`.".to_string(),
};
ToolSpec::Function(ResponsesApiTool {
@@ -126,15 +126,15 @@ mod tests {
}
#[test]
fn developer_recommendations_change_only_the_description() {
fn recommendation_context_changes_only_the_description() {
let mut expected = create_request_plugin_install_tool(ToolSuggestPresentation::ListTool);
let recommendations =
create_request_plugin_install_tool(ToolSuggestPresentation::DeveloperContext);
create_request_plugin_install_tool(ToolSuggestPresentation::RecommendationContext);
let ToolSpec::Function(expected_function) = &mut expected else {
panic!("expected function tool specs");
};
expected_function.description = "# Suggest a recommended plugin installation\n\nSuggest installing a plugin from the developer `<recommended_plugins>` list when it would help with the user's current request. Briefly explain why in `suggest_reason`.".to_string();
expected_function.description = "# Suggest a recommended plugin installation\n\nSuggest installing a plugin from the `<recommended_plugins>` list when it would help with the user's current request. Briefly explain why in `suggest_reason`.".to_string();
assert_eq!(recommendations, expected);
}
+1 -2
View File
@@ -48,8 +48,7 @@ pub(crate) struct ToolRouterParams<'a> {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ToolSuggestPresentation {
ListTool,
#[allow(dead_code)]
DeveloperContext,
RecommendationContext,
}
#[derive(Clone, Debug)]
+3 -3
View File
@@ -883,7 +883,7 @@ async fn request_plugin_install_requires_all_discovery_features() {
None,
Some(ToolSuggestCandidates {
tools: Vec::new(),
presentation: ToolSuggestPresentation::DeveloperContext,
presentation: ToolSuggestPresentation::RecommendationContext,
}),
] {
let plan = probe_with(
@@ -959,7 +959,7 @@ async fn request_plugin_install_description_refers_to_recommended_plugins_hint()
},
ToolPlanInputs {
tool_suggest_candidates: Some(plugin_candidates(
ToolSuggestPresentation::DeveloperContext,
ToolSuggestPresentation::RecommendationContext,
)),
..ToolPlanInputs::default()
},
@@ -973,7 +973,7 @@ async fn request_plugin_install_description_refers_to_recommended_plugins_hint()
else {
panic!("expected request_plugin_install function spec");
};
assert!(request_description.contains("developer `<recommended_plugins>` list"));
assert!(request_description.contains("the `<recommended_plugins>` list"));
assert!(!request_description.contains("list_available_plugins_to_install"));
assert!(!request_description.contains("github"));
plan.assert_visible_lacks(&["list_available_plugins_to_install"]);