Files
codex/codex-rs/core/src/context/apps_instructions.rs
T
goodman-oai 83a23c0936 Support model catalog Apps usage instructions (#61213)
## What changed

- Add the disabled-by-default `catalog_apps_instructions` feature to use the selected model's `model_messages.apps.usage_instructions`, subject to existing Apps availability and model gates.
- Preserve valid catalog text literally, allow an empty string to clear guidance, and fall back to bundled instructions for missing, whitespace-only, or oversized values. Overrides are limited to 800 UTF-8 bytes.
- Reconcile guidance with the latest retained Apps fragment, emitting replacement or removal notices when needed and avoiding repeated updates across resumes. Preserve compatibility with bundled guidance and legacy checkpoints.
- Recognize catalog Apps fragments as contextual developer messages in event mapping and rollout rollback.

## Testing

Add resolver, world-state, client, and scenario coverage for literal text and byte limits, feature and availability gates, model switches, clearing guidance, resumes, and legacy history recovery.

GitOrigin-RevId: 6240d407ada8f22fa101753ff3ddb338632ae283
2026-11-13 00:29:24 +00:00

77 lines
2.2 KiB
Rust

//! Renders bundled Apps guidance and catalog updates with the same developer provenance.
//! Older clients recognize only the bundled markers when recovering from retained history.
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
use codex_protocol::protocol::APPS_INSTRUCTIONS_CLOSE_TAG;
use codex_protocol::protocol::APPS_INSTRUCTIONS_OPEN_TAG;
use codex_protocol::protocol::CATALOG_APPS_INSTRUCTIONS_CLOSE_TAG;
use codex_protocol::protocol::CATALOG_APPS_INSTRUCTIONS_OPEN_TAG;
use super::ContextualUserFragment;
use codex_protocol::models::ContentItem;
use codex_protocol::models::ContentItemKind;
use codex_protocol::models::input_text;
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct AppsInstructions(pub(crate) String);
impl Default for AppsInstructions {
fn default() -> Self {
Self(
codex_prompts::ResolvedModelMessages::bundled()
.apps_usage_instructions(CODEX_APPS_MCP_SERVER_NAME)
.usage_instructions,
)
}
}
impl ContextualUserFragment for AppsInstructions {
fn content_kind(&self) -> ContentItemKind {
ContentItemKind("apps.instructions".to_string())
}
fn role(&self) -> &'static str {
"developer"
}
fn markers(&self) -> (&'static str, &'static str) {
Self::type_markers()
}
fn type_markers() -> (&'static str, &'static str) {
(APPS_INSTRUCTIONS_OPEN_TAG, APPS_INSTRUCTIONS_CLOSE_TAG)
}
fn body(&self) -> ContentItem {
input_text(self.0.clone())
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct CatalogAppsInstructions(pub(crate) String);
impl ContextualUserFragment for CatalogAppsInstructions {
fn content_kind(&self) -> ContentItemKind {
ContentItemKind("apps.instructions".to_string())
}
fn role(&self) -> &'static str {
"developer"
}
fn markers(&self) -> (&'static str, &'static str) {
Self::type_markers()
}
fn type_markers() -> (&'static str, &'static str) {
(
CATALOG_APPS_INSTRUCTIONS_OPEN_TAG,
CATALOG_APPS_INSTRUCTIONS_CLOSE_TAG,
)
}
fn body(&self) -> ContentItem {
input_text(self.0.clone())
}
}