mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Preserve remote plugin default prompts (#25887)
## Summary - Read `default_prompts` from remote plugin release metadata. - Prefer the plural prompt list over legacy `default_prompt`. - Fall back to `default_prompt` as a single-item list for backward compatibility. ## Testing - `just test -p codex-core-plugins` - `just test -p codex-app-server`
This commit is contained in:
committed by
GitHub
Unverified
parent
10b408080a
commit
aeac226d16
@@ -1754,6 +1754,8 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
"interface": {
|
||||
"short_description": "Plan and track work",
|
||||
"capabilities": ["Read", "Write"],
|
||||
"default_prompt": "Use the legacy Linear prompt",
|
||||
"default_prompts": ["Create a Linear issue", "Review my Linear projects"],
|
||||
"logo_url": "https://example.com/linear.png",
|
||||
"screenshot_urls": ["https://example.com/linear-shot.png"]
|
||||
},
|
||||
@@ -1893,6 +1895,16 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
.and_then(|interface| interface.display_name.as_deref()),
|
||||
Some("Linear")
|
||||
);
|
||||
assert_eq!(
|
||||
remote_marketplace.plugins[0]
|
||||
.interface
|
||||
.as_ref()
|
||||
.and_then(|interface| interface.default_prompt.clone()),
|
||||
Some(vec![
|
||||
"Create a Linear issue".to_string(),
|
||||
"Review my Linear projects".to_string(),
|
||||
])
|
||||
);
|
||||
assert_eq!(
|
||||
remote_marketplace.plugins[0].keywords,
|
||||
vec![
|
||||
@@ -1907,6 +1919,10 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
let cached_catalog: serde_json::Value =
|
||||
serde_json::from_slice(&std::fs::read(&cache_files[0])?)?;
|
||||
assert_eq!(cached_catalog["schema_version"], serde_json::json!(1));
|
||||
assert_eq!(
|
||||
cached_catalog["plugins"][0]["release"]["interface"]["default_prompts"],
|
||||
serde_json::json!(["Create a Linear issue", "Review my Linear projects"])
|
||||
);
|
||||
let cached_plugin_ids = cached_catalog["plugins"]
|
||||
.as_array()
|
||||
.expect("cached plugins should be an array")
|
||||
|
||||
@@ -159,7 +159,9 @@ plugins = true
|
||||
"keywords": [],
|
||||
"interface": {
|
||||
"short_description": "Plan and track work",
|
||||
"capabilities": []
|
||||
"capabilities": [],
|
||||
"default_prompt": "Use the legacy Linear prompt",
|
||||
"default_prompts": []
|
||||
},
|
||||
"skills": []
|
||||
}
|
||||
@@ -217,6 +219,15 @@ plugins = true
|
||||
assert_eq!(response.plugin.summary.name, "linear");
|
||||
assert_eq!(response.plugin.summary.source, PluginSource::Remote);
|
||||
assert_eq!(response.plugin.summary.share_context, None);
|
||||
assert_eq!(
|
||||
response
|
||||
.plugin
|
||||
.summary
|
||||
.interface
|
||||
.as_ref()
|
||||
.and_then(|interface| interface.default_prompt.clone()),
|
||||
Some(vec!["Use the legacy Linear prompt".to_string()])
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -404,6 +415,8 @@ async fn plugin_read_reads_remote_plugin_details_when_remote_plugin_enabled() ->
|
||||
"interface": {
|
||||
"short_description": "Plan and track work",
|
||||
"capabilities": ["Read", "Write"],
|
||||
"default_prompt": "Use the legacy Linear prompt",
|
||||
"default_prompts": ["Create a Linear issue", "Review my Linear projects"],
|
||||
"logo_url": "https://example.com/linear.png",
|
||||
"screenshot_urls": ["https://example.com/linear-shot.png"]
|
||||
},
|
||||
@@ -518,6 +531,18 @@ async fn plugin_read_reads_remote_plugin_details_when_remote_plugin_enabled() ->
|
||||
"project management".to_string()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
response
|
||||
.plugin
|
||||
.summary
|
||||
.interface
|
||||
.as_ref()
|
||||
.and_then(|interface| interface.default_prompt.clone()),
|
||||
Some(vec![
|
||||
"Create a Linear issue".to_string(),
|
||||
"Review my Linear projects".to_string(),
|
||||
])
|
||||
);
|
||||
assert_eq!(response.plugin.skills.len(), 1);
|
||||
assert_eq!(response.plugin.skills[0].name, "plan-work");
|
||||
assert_eq!(response.plugin.skills[0].path, None);
|
||||
|
||||
@@ -66,6 +66,7 @@ pub const REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_DISPLAY_NAME: &st
|
||||
const OPENAI_CURATED_REMOTE_COLLECTION_KEY: &str = "vertical";
|
||||
const REMOTE_PLUGIN_CATALOG_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const REMOTE_PLUGIN_LIST_PAGE_LIMIT: u32 = 200;
|
||||
const MAX_REMOTE_DEFAULT_PROMPT_COUNT: usize = 3;
|
||||
const MAX_REMOTE_DEFAULT_PROMPT_LEN: usize = 128;
|
||||
const INVALID_REQUEST_ERROR_CODE: i64 = -32600;
|
||||
const REMOTE_INSTALLED_MARKETPLACE_DISPLAY_ORDER: [(&str, &str); 5] = [
|
||||
@@ -392,6 +393,7 @@ struct RemotePluginReleaseInterfaceResponse {
|
||||
terms_of_service_url: Option<String>,
|
||||
brand_color: Option<String>,
|
||||
default_prompt: Option<String>,
|
||||
default_prompts: Option<Vec<String>>,
|
||||
composer_icon_url: Option<String>,
|
||||
logo_url: Option<String>,
|
||||
#[serde(default)]
|
||||
@@ -1193,9 +1195,16 @@ fn remote_plugin_interface_to_info(plugin: &RemotePluginDirectoryItem) -> Option
|
||||
let interface = &plugin.release.interface;
|
||||
let display_name = non_empty_string(Some(&plugin.release.display_name));
|
||||
let default_prompt = interface
|
||||
.default_prompt
|
||||
.as_ref()
|
||||
.and_then(|prompt| normalize_remote_default_prompt(prompt));
|
||||
.default_prompts
|
||||
.as_deref()
|
||||
.and_then(normalize_remote_default_prompts)
|
||||
.or_else(|| {
|
||||
interface
|
||||
.default_prompt
|
||||
.as_deref()
|
||||
.and_then(normalize_remote_default_prompt)
|
||||
.map(|prompt| vec![prompt])
|
||||
});
|
||||
let result = PluginInterface {
|
||||
display_name,
|
||||
short_description: interface.short_description.clone(),
|
||||
@@ -1279,12 +1288,21 @@ fn non_empty_string(value: Option<&str>) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
fn normalize_remote_default_prompt(prompt: &str) -> Option<Vec<String>> {
|
||||
fn normalize_remote_default_prompts(prompts: &[String]) -> Option<Vec<String>> {
|
||||
let prompts = prompts
|
||||
.iter()
|
||||
.filter_map(|prompt| normalize_remote_default_prompt(prompt))
|
||||
.take(MAX_REMOTE_DEFAULT_PROMPT_COUNT)
|
||||
.collect::<Vec<_>>();
|
||||
(!prompts.is_empty()).then_some(prompts)
|
||||
}
|
||||
|
||||
fn normalize_remote_default_prompt(prompt: &str) -> Option<String> {
|
||||
let prompt = prompt.trim();
|
||||
if prompt.is_empty() || prompt.chars().count() > MAX_REMOTE_DEFAULT_PROMPT_LEN {
|
||||
return None;
|
||||
}
|
||||
Some(vec![prompt.to_string()])
|
||||
Some(prompt.to_string())
|
||||
}
|
||||
|
||||
async fn fetch_directory_plugins_for_scope(
|
||||
|
||||
Reference in New Issue
Block a user