mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
Expose hosted plugin extensions in app-server summaries (#47263)
## What changed - Request installed-plugin metadata with `includeExtensions=true` and carry optional `extensions` through the cache into `PluginSummary`. - Add typed declarations for global, settings, thread, and file entrypoints, quick actions, settings tools, icons, and search mention providers. Update JSON schemas and TypeScript and Python bindings. - Accept supported snake_case aliases and emit camelCase fields. Discard incompatible extension declarations without invalidating the installed-plugin inventory. ## Testing Add an integration test covering extension metadata preservation, field normalization, and unknown entrypoint and quick-action types across live and cached `plugin/installed` reads. Update pagination assertions to require `includeExtensions=true`. GitOrigin-RevId: b73b0d13c151a30dc1fb9cda70b5cd6d7e3e2441
This commit is contained in:
@@ -24,6 +24,7 @@ mod model;
|
||||
mod notification;
|
||||
mod permissions;
|
||||
mod plugin;
|
||||
mod plugin_extensions;
|
||||
mod plugin_search;
|
||||
mod process;
|
||||
mod project;
|
||||
@@ -63,6 +64,7 @@ pub use model::*;
|
||||
pub use notification::*;
|
||||
pub use permissions::*;
|
||||
pub use plugin::*;
|
||||
pub use plugin_extensions::*;
|
||||
pub use plugin_search::*;
|
||||
pub use process::*;
|
||||
pub use project::*;
|
||||
|
||||
@@ -696,6 +696,8 @@ pub enum PluginDisabledReason {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginSummary {
|
||||
#[serde(default)]
|
||||
pub extensions: Option<super::PluginExtensions>,
|
||||
pub id: String,
|
||||
/// Backend remote plugin identifier when available.
|
||||
pub remote_plugin_id: Option<String>,
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
//! Hosted plugin extension declarations carried by installed-plugin discovery.
|
||||
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginExtensions {
|
||||
#[serde(default)]
|
||||
pub entrypoints: Option<Vec<PluginEntrypoint>>,
|
||||
#[serde(default, alias = "settings_entrypoints")]
|
||||
pub settings_entrypoints: Vec<PluginEntrypoint>,
|
||||
#[serde(default)]
|
||||
pub settings: Vec<PluginSettings>,
|
||||
#[serde(default, alias = "thread_entrypoints")]
|
||||
pub thread_entrypoints: Vec<PluginEntrypoint>,
|
||||
#[serde(default, alias = "file_handlers")]
|
||||
pub file_handlers: Vec<PluginEntrypoint>,
|
||||
#[serde(default, alias = "search_mention_providers")]
|
||||
pub search_mention_providers: Vec<PluginSearchProvider>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
#[ts(tag = "type", rename_all = "camelCase", export_to = "v2/")]
|
||||
pub enum PluginEntrypoint {
|
||||
Global {
|
||||
#[serde(flatten)]
|
||||
presentation: PluginEntrypointPresentation,
|
||||
#[serde(default, rename = "quickAction", alias = "quick_action")]
|
||||
#[ts(rename = "quickAction")]
|
||||
quick_action: Option<PluginQuickAction>,
|
||||
},
|
||||
Settings {
|
||||
#[serde(flatten)]
|
||||
presentation: PluginEntrypointPresentation,
|
||||
#[serde(default, rename = "searchTerms", alias = "search_terms")]
|
||||
#[ts(rename = "searchTerms")]
|
||||
search_terms: Vec<String>,
|
||||
},
|
||||
Thread {
|
||||
#[serde(flatten)]
|
||||
presentation: PluginEntrypointPresentation,
|
||||
},
|
||||
File {
|
||||
#[serde(flatten)]
|
||||
presentation: PluginEntrypointPresentation,
|
||||
extensions: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginEntrypointPresentation {
|
||||
#[serde(alias = "app_id")]
|
||||
pub app_id: String,
|
||||
#[serde(alias = "tool_name")]
|
||||
pub tool_name: String,
|
||||
pub title: String,
|
||||
#[serde(alias = "resource_uri")]
|
||||
pub resource_uri: String,
|
||||
pub icons: Vec<PluginIcon>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginIcon {
|
||||
pub src: String,
|
||||
pub mime_type: Option<String>,
|
||||
pub sizes: Option<Vec<String>>,
|
||||
pub theme: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginQuickAction {
|
||||
pub title: String,
|
||||
pub icons: Vec<PluginIcon>,
|
||||
pub target: PluginQuickActionTarget,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
#[ts(tag = "type", rename_all = "camelCase", export_to = "v2/")]
|
||||
pub enum PluginQuickActionTarget {
|
||||
Tool {
|
||||
name: String,
|
||||
arguments: Option<serde_json::Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginSettings {
|
||||
#[serde(alias = "app_id")]
|
||||
pub app_id: String,
|
||||
#[serde(alias = "read_tool_name")]
|
||||
pub read_tool_name: String,
|
||||
#[serde(alias = "update_tool_name")]
|
||||
pub update_tool_name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginSearchProvider {
|
||||
#[serde(alias = "app_id")]
|
||||
pub app_id: String,
|
||||
#[serde(alias = "tool_name")]
|
||||
pub tool_name: String,
|
||||
#[serde(alias = "link_id")]
|
||||
pub link_id: String,
|
||||
pub title: String,
|
||||
pub call: Option<PluginSearchProviderCall>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginSearchProviderCall {
|
||||
pub name: String,
|
||||
pub arguments: serde_json::Value,
|
||||
#[serde(rename = "_meta")]
|
||||
#[ts(rename = "_meta")]
|
||||
pub meta: serde_json::Value,
|
||||
}
|
||||
@@ -4470,6 +4470,7 @@ fn plugin_share_list_response_serializes_share_items() {
|
||||
serde_json::to_value(PluginShareListResponse {
|
||||
data: vec![PluginShareListItem {
|
||||
plugin: PluginSummary {
|
||||
extensions: None,
|
||||
id: "gmail@openai-curated-remote".to_string(),
|
||||
remote_plugin_id: Some(
|
||||
"plugins~Plugin_00000000000000000000000000000000".to_string(),
|
||||
@@ -4499,6 +4500,7 @@ fn plugin_share_list_response_serializes_share_items() {
|
||||
json!({
|
||||
"data": [{
|
||||
"plugin": {
|
||||
"extensions": null,
|
||||
"id": "gmail@openai-curated-remote",
|
||||
"remotePluginId": "plugins~Plugin_00000000000000000000000000000000",
|
||||
"version": null,
|
||||
@@ -4551,6 +4553,7 @@ fn plugin_summary_defaults_missing_availability_to_available() {
|
||||
#[test]
|
||||
fn plugin_summary_round_trips_plan_eligibility_metadata() {
|
||||
let value = json!({
|
||||
"extensions": null,
|
||||
"id": "gmail@openai-curated-remote",
|
||||
"remotePluginId": "plugins~Plugin_00000000000000000000000000000000",
|
||||
"version": null,
|
||||
|
||||
Reference in New Issue
Block a user