Hydrate recommended plugin metadata on selection (#39143)

## What changed

- Fetch recommendations from the Codex-specific `/ps/plugins/suggested/codex` endpoint and parse its compact response shape.
- Fetch the selected plugin's details before presenting an install request, using them to verify availability and populate connector metadata.
- Skip install elicitation when the selected recommendation is no longer available, and return a retryable response when its metadata cannot be verified.

## Testing

- Cover the new recommendation route and response shape.
- Cover metadata hydration for available plugins and rejection of unavailable plugins.

GitOrigin-RevId: 2b0e2d70572aae2b7cd8e458b42e9dd14be3dbaf
This commit is contained in:
Matthew Zeng
2026-08-18 05:42:39 +00:00
committed by copyberry
parent f47f77ada6
commit de7bbb0481
9 changed files with 386 additions and 201 deletions
@@ -177,6 +177,31 @@ impl RequestPluginInstallHandler {
"{argument_name} must match one of {source}"
))
})?;
let tool = if self.presentation == ToolSuggestPresentation::RecommendationContext {
let plugin_id = tool.id().to_string();
let auth = session.services.auth_manager.auth().await;
let plugins_config = turn.config.plugins_config_input();
match codex_core_plugins::hydrate_selected_recommended_plugin_install_metadata(
&plugins_config,
auth.as_ref(),
tool,
)
.await
{
Ok(Some(tool)) => tool,
Ok(None) => return Err(recommended_plugins_no_longer_available()),
Err(err) => {
warn!(
plugin_id,
error = %err,
"failed to hydrate selected recommended plugin install metadata"
);
return Err(recommended_plugin_metadata_retryable());
}
}
} else {
tool
};
let tool_type = tool.tool_type();
let suggestion_id = format!("request_plugin_install_{call_id}");
@@ -286,6 +311,18 @@ impl RequestPluginInstallHandler {
}
}
fn recommended_plugins_no_longer_available() -> FunctionCallError {
FunctionCallError::RespondToModel(
"The recommended plugins for this turn are no longer available.".to_string(),
)
}
fn recommended_plugin_metadata_retryable() -> FunctionCallError {
FunctionCallError::RespondToModel(
"The selected recommended plugin could not be verified right now. Retry request_plugin_install with the same plugin_id.".to_string(),
)
}
impl CoreToolRuntime for RequestPluginInstallHandler {}
async fn maybe_persist_disabled_install_request(