Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions codex-rs/core/src/context/available_plugins_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,17 @@ impl ContextualUserFragment for AvailablePluginsInstructions {
fn body(&self) -> String {
let mut lines = vec![
"## Plugins".to_string(),
"A plugin is a local bundle of skills, MCP servers, and apps. Below is the list of plugins that are enabled and available in this session.".to_string(),
"### Available plugins".to_string(),
"A plugin is a local bundle of skills, MCP servers, and apps.".to_string(),
];

lines.extend(
self.plugins
.iter()
.map(|plugin| match plugin.description.as_deref() {
Some(description) => format!("- `{}`: {description}", plugin.display_name),
None => format!("- `{}`", plugin.display_name),
}),
);

lines.push("### How to use plugins".to_string());
lines.push(
r###"- Discovery: The list above is the plugins available in this session.
- Skill naming: If a plugin contributes skills, those skill entries are prefixed with `plugin_name:` in the Skills list.
r###"- Skill naming: If a plugin contributes skills, those skill entries are prefixed with `plugin_name:` in the Skills list.
- MCP naming: Plugin-provided MCP tools keep standard MCP identifiers such as `mcp__server__tool`; use tool provenance to tell which plugin they come from.
- Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn.
- Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task.
- Preference: When a relevant plugin is available, prefer using capabilities associated with that plugin over standalone capabilities that provide similar functionality.
- Missing/blocked: If the user requests a plugin that is not listed above, or the plugin does not have relevant callable capabilities for the task, say so briefly and continue with the best fallback."###
- Relevance: Determine what a plugin can help with from explicit user mention or from the plugin-associated skills, MCP tools, and apps exposed elsewhere in this turn.
- Missing/blocked: If the user requests a plugin that does not have relevant callable capabilities for the task, say so briefly and continue with the best fallback."###
.to_string(),
);

Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/plugins/render_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn render_plugins_section_returns_none_for_empty_plugins() {
}

#[test]
fn render_plugins_section_includes_descriptions_and_skill_naming_guidance() {
fn render_plugins_section_keeps_plugin_usage_guidance_without_listing_plugins() {
let rendered = render_plugins_section(&[PluginCapabilitySummary {
config_name: "sample@test".to_string(),
display_name: "sample".to_string(),
Expand All @@ -17,7 +17,7 @@ fn render_plugins_section_includes_descriptions_and_skill_naming_guidance() {
}])
.expect("plugin section should render");

let expected = "<plugins_instructions>\n## Plugins\nA plugin is a local bundle of skills, MCP servers, and apps. Below is the list of plugins that are enabled and available in this session.\n### Available plugins\n- `sample`: inspect sample data\n### How to use plugins\n- Discovery: The list above is the plugins available in this session.\n- Skill naming: If a plugin contributes skills, those skill entries are prefixed with `plugin_name:` in the Skills list.\n- Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn.\n- Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task.\n- Preference: When a relevant plugin is available, prefer using capabilities associated with that plugin over standalone capabilities that provide similar functionality.\n- Missing/blocked: If the user requests a plugin that is not listed above, or the plugin does not have relevant callable capabilities for the task, say so briefly and continue with the best fallback.\n</plugins_instructions>";
let expected = "<plugins_instructions>\n## Plugins\nA plugin is a local bundle of skills, MCP servers, and apps.\n### How to use plugins\n- Skill naming: If a plugin contributes skills, those skill entries are prefixed with `plugin_name:` in the Skills list.\n- MCP naming: Plugin-provided MCP tools keep standard MCP identifiers such as `mcp__server__tool`; use tool provenance to tell which plugin they come from.\n- Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn.\n- Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task.\n- Relevance: Determine what a plugin can help with from explicit user mention or from the plugin-associated skills, MCP tools, and apps exposed elsewhere in this turn.\n- Missing/blocked: If the user requests a plugin that does not have relevant callable capabilities for the task, say so briefly and continue with the best fallback.\n</plugins_instructions>";

assert_eq!(rendered, expected);
}
8 changes: 2 additions & 6 deletions codex-rs/core/tests/suite/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,8 @@ async fn capability_sections_render_in_developer_message_in_order() -> Result<()
"expected Apps -> Skills -> Plugins order: {developer_messages:?}"
);
assert!(
developer_text.contains("`sample`"),
"expected enabled plugin name in developer message: {developer_messages:?}"
);
assert!(
developer_text.contains("`sample`: inspect sample data"),
"expected plugin description in developer message: {developer_messages:?}"
!developer_text.contains("`sample`: inspect sample data"),
"did not expect plugin description in developer message: {developer_messages:?}"
);
assert!(
developer_text.contains("skill entries are prefixed with `plugin_name:`"),
Expand Down
Loading