mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Classify contextual fragments with content kinds (#40180)
## What changed - Require each `ContextualUserFragment` to provide a stable `<feature>.<name>` `ContentItemKind`. - Add `AnnotatedContent` and `RenderedFragment` so rendered text, its role, and its classification can travel together to API boundaries. - Derive extension-owned world-state classifications from the extension ID and keep the skills catalog classification with its fragment implementation. ## Testing - Verify that an extension-owned world-state section renders with an `<extension-id>.instructions` content kind. GitOrigin-RevId: e46b74a0bb41e0b6112667c9d36bc9e7f2714451
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_utils_string::truncate_middle_with_token_budget;
|
||||
|
||||
use crate::ContextualUserFragment;
|
||||
@@ -23,6 +24,10 @@ impl ContextualUserFragment for AdditionalContextUserFragment {
|
||||
"user"
|
||||
}
|
||||
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("generic.user_additional_context".to_string())
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
Self::type_markers()
|
||||
}
|
||||
@@ -69,6 +74,10 @@ impl ContextualUserFragment for AdditionalContextDeveloperFragment {
|
||||
"developer"
|
||||
}
|
||||
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("generic.developer_additional_context".to_string())
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
Self::type_markers()
|
||||
}
|
||||
|
||||
36
codex-rs/context-fragments/src/annotated_content.rs
Normal file
36
codex-rs/context-fragments/src/annotated_content.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
/// Model-visible content paired with its harness-owned classification.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AnnotatedContent {
|
||||
content: ContentItem,
|
||||
kind: ContentItemKind,
|
||||
}
|
||||
|
||||
impl AnnotatedContent {
|
||||
/// Creates content and its classification together.
|
||||
pub fn new(content: ContentItem, kind: ContentItemKind) -> Self {
|
||||
Self { content, kind }
|
||||
}
|
||||
|
||||
/// Creates model-visible input text and its classification together.
|
||||
pub fn input_text(text: impl Into<String>, kind: ContentItemKind) -> Self {
|
||||
Self::new(ContentItem::InputText { text: text.into() }, kind)
|
||||
}
|
||||
|
||||
/// Returns the model-visible content.
|
||||
pub fn content(&self) -> &ContentItem {
|
||||
&self.content
|
||||
}
|
||||
|
||||
/// Returns the classification associated with the content.
|
||||
pub fn kind(&self) -> &ContentItemKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
/// Separates the content from its classification at an API boundary.
|
||||
pub fn into_parts(self) -> (ContentItem, ContentItemKind) {
|
||||
(self.content, self.kind)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,33 @@
|
||||
use crate::AnnotatedContent;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
|
||||
/// A rendered contextual fragment and the role that owns its annotated content.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RenderedFragment {
|
||||
role: &'static str,
|
||||
content: AnnotatedContent,
|
||||
}
|
||||
|
||||
impl RenderedFragment {
|
||||
/// Returns the response role associated with this fragment.
|
||||
pub fn role(&self) -> &'static str {
|
||||
self.role
|
||||
}
|
||||
|
||||
/// Returns this fragment's model-visible content and classification.
|
||||
pub fn annotated_content(&self) -> &AnnotatedContent {
|
||||
&self.content
|
||||
}
|
||||
|
||||
/// Separates the role and annotated content at an API boundary.
|
||||
pub fn into_parts(self) -> (&'static str, AnnotatedContent) {
|
||||
(self.role, self.content)
|
||||
}
|
||||
}
|
||||
|
||||
/// Context payload that is injected as a message fragment.
|
||||
///
|
||||
/// Implementations own the response role and provide the exact fragment body.
|
||||
@@ -14,6 +40,9 @@ use codex_protocol::models::ResponseItem;
|
||||
pub trait ContextualUserFragment {
|
||||
fn role(&self) -> &'static str;
|
||||
|
||||
/// Returns a stable `<feature>.<name>` classification, using `generic` for shared fragments.
|
||||
fn content_kind(&self) -> ContentItemKind;
|
||||
|
||||
/// Whether this fragment must be recorded as its own response item.
|
||||
fn requires_separate_message(&self) -> bool {
|
||||
false
|
||||
@@ -45,6 +74,14 @@ pub trait ContextualUserFragment {
|
||||
format!("{start_marker}{body}{end_marker}")
|
||||
}
|
||||
|
||||
/// Renders the role, model-visible content, and classification together.
|
||||
fn render_fragment(&self) -> RenderedFragment {
|
||||
RenderedFragment {
|
||||
role: self.role(),
|
||||
content: AnnotatedContent::input_text(self.render(), self.content_kind()),
|
||||
}
|
||||
}
|
||||
|
||||
fn into(self) -> ResponseItem
|
||||
where
|
||||
Self: Sized,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
mod additional_context;
|
||||
mod annotated_content;
|
||||
mod fragment;
|
||||
|
||||
pub use additional_context::AdditionalContextDeveloperFragment;
|
||||
pub use additional_context::AdditionalContextUserFragment;
|
||||
pub use annotated_content::AnnotatedContent;
|
||||
pub use fragment::ContextualUserFragment;
|
||||
pub use fragment::RenderedFragment;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
pub(crate) const APPROVED_COMMAND_PREFIX_SAVED_MESSAGE_PREFIX: &str =
|
||||
"Approved command prefix saved:";
|
||||
@@ -17,6 +18,10 @@ impl ApprovedCommandPrefixSaved {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for ApprovedCommandPrefixSaved {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("permissions.approved_command_prefix_saved".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@ use codex_protocol::protocol::APPS_INSTRUCTIONS_CLOSE_TAG;
|
||||
use codex_protocol::protocol::APPS_INSTRUCTIONS_OPEN_TAG;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct AppsInstructions;
|
||||
|
||||
impl ContextualUserFragment for AppsInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("apps.instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -2,11 +2,16 @@ use codex_protocol::protocol::PLUGINS_INSTRUCTIONS_CLOSE_TAG;
|
||||
use codex_protocol::protocol::PLUGINS_INSTRUCTIONS_OPEN_TAG;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct AvailablePluginsInstructions;
|
||||
|
||||
impl ContextualUserFragment for AvailablePluginsInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("plugins.usage_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
pub(crate) struct CurrentTimeReminder {
|
||||
current_time: DateTime<Utc>,
|
||||
@@ -20,6 +21,10 @@ impl CurrentTimeReminder {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for CurrentTimeReminder {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("current_time.reminder".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -2,10 +2,15 @@ use codex_protocol::protocol::ENVIRONMENTS_INSTRUCTIONS_CLOSE_TAG;
|
||||
use codex_protocol::protocol::ENVIRONMENTS_INSTRUCTIONS_OPEN_TAG;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
pub(crate) struct EnvironmentsInstructions;
|
||||
|
||||
impl ContextualUserFragment for EnvironmentsInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("environments.instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct GuardianFollowupReviewReminder;
|
||||
|
||||
impl ContextualUserFragment for GuardianFollowupReviewReminder {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("guardian.followup_review_reminder".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct GuardianNodeReplPolicy;
|
||||
|
||||
impl ContextualUserFragment for GuardianNodeReplPolicy {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("guardian.node_repl_policy".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use serde_json::json;
|
||||
use super::ContextualUserFragment;
|
||||
use crate::codex_thread::GuardianAuthorizationVersion;
|
||||
use crate::guardian::guardian_truncate_text;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
const MAX_RETAINED_REVIEWS: usize = 8;
|
||||
// Including markers, each rendered fragment stays below 1,000 approximate tokens.
|
||||
@@ -108,6 +109,10 @@ pub struct GuardianReviewEvidenceFragment {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for GuardianReviewEvidenceFragment {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("guardian.review_evidence".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct HookAdditionalContext {
|
||||
@@ -12,6 +13,10 @@ impl HookAdditionalContext {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for HookAdditionalContext {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("hooks.additional_context".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ImageResizeNoticeSource {
|
||||
@@ -32,6 +33,10 @@ impl ImageResizeNotice {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for ImageResizeNotice {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("images.resize_notice".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use codex_protocol::AgentPath;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct InterAgentCompletionMessage {
|
||||
@@ -20,6 +21,10 @@ impl InterAgentCompletionMessage {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for InterAgentCompletionMessage {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("multi_agent.inter_agent_completion_message".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"assistant"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use codex_protocol::AgentPath;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum InterAgentMessageType {
|
||||
@@ -42,6 +43,10 @@ impl InterAgentMessage {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for InterAgentMessage {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("multi_agent.inter_agent_message".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"assistant"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Hidden user-context fragment for extension-owned model steering.
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
@@ -76,6 +77,10 @@ impl InternalModelContextFragment {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for InternalModelContextFragment {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("generic.internal_model_context".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
// This warning is not produced anymore but fragment definition is used to filter messaged from old sessions
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct LegacyApplyPatchExecCommandWarning;
|
||||
|
||||
impl ContextualUserFragment for LegacyApplyPatchExecCommandWarning {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("apply_patch.legacy_exec_command_warning".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
// This warning is not produced anymore but fragment definition is used to filter messaged from old sessions
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct LegacyModelMismatchWarning;
|
||||
|
||||
impl ContextualUserFragment for LegacyModelMismatchWarning {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("model_switch.legacy_mismatch_warning".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
// This warning is not produced anymore but fragment definition is used to filter messaged from old sessions
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct LegacyUnifiedExecProcessLimitWarning;
|
||||
|
||||
impl ContextualUserFragment for LegacyUnifiedExecProcessLimitWarning {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("unified_exec.legacy_process_limit_warning".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct ModelSwitchInstructions {
|
||||
@@ -14,6 +15,10 @@ impl ModelSwitchInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for ModelSwitchInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("model_switch.instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::MULTI_AGENT_MODE_CLOSE_TAG;
|
||||
use codex_protocol::protocol::MULTI_AGENT_MODE_OPEN_TAG;
|
||||
|
||||
@@ -25,6 +26,10 @@ impl MultiAgentModeInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for MultiAgentModeInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("multi_agent.mode_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct MultiAgentRoleInstructions {
|
||||
@@ -23,6 +24,10 @@ impl MultiAgentRoleInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for MultiAgentRoleInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("multi_agent.role_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
/// Configured multi-agent instructions emitted as a standalone developer message.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -15,6 +16,10 @@ impl MultiAgentUsageHint {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for MultiAgentUsageHint {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("multi_agent.usage_hint".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::approvals::NetworkPolicyAmendment;
|
||||
use codex_protocol::approvals::NetworkPolicyRuleAction;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct NetworkRuleSaved {
|
||||
@@ -18,6 +19,10 @@ impl NetworkRuleSaved {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for NetworkRuleSaved {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("network_proxy.rule_saved".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::sync::PoisonError;
|
||||
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_protocol::user_input::UserInput::Image;
|
||||
use codex_protocol::user_input::UserInput::Text;
|
||||
@@ -358,6 +359,10 @@ impl NodeReplReviewEvidenceFragment {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for NodeReplReviewEvidenceFragment {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("guardian.node_repl_review_evidence".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct PersonalitySpecInstructions {
|
||||
@@ -12,6 +13,10 @@ impl PersonalitySpecInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for PersonalitySpecInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("personality.spec_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct PluginInstructions {
|
||||
@@ -12,6 +13,10 @@ impl PluginInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for PluginInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("plugins.instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
const MAX_REALTIME_DELEGATION_FIELD_BYTES: usize = 4 * 1024;
|
||||
const TRUNCATION_MARKER: &str = "…";
|
||||
@@ -31,6 +32,10 @@ impl<'a> RealtimeDelegation<'a> {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for RealtimeDelegation<'_> {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("realtime_conversation.delegation".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_prompts::END_INSTRUCTIONS;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::REALTIME_CONVERSATION_CLOSE_TAG;
|
||||
use codex_protocol::protocol::REALTIME_CONVERSATION_OPEN_TAG;
|
||||
|
||||
@@ -21,6 +22,10 @@ impl RealtimeEndInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for RealtimeEndInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("realtime_conversation.end_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_prompts::START_INSTRUCTIONS;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::REALTIME_CONVERSATION_CLOSE_TAG;
|
||||
use codex_protocol::protocol::REALTIME_CONVERSATION_OPEN_TAG;
|
||||
|
||||
@@ -7,6 +8,10 @@ use codex_protocol::protocol::REALTIME_CONVERSATION_OPEN_TAG;
|
||||
pub(crate) struct RealtimeStartInstructions;
|
||||
|
||||
impl ContextualUserFragment for RealtimeStartInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("realtime_conversation.start_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::REALTIME_CONVERSATION_CLOSE_TAG;
|
||||
use codex_protocol::protocol::REALTIME_CONVERSATION_OPEN_TAG;
|
||||
|
||||
@@ -16,6 +17,10 @@ impl RealtimeStartWithInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for RealtimeStartWithInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("realtime_conversation.custom_start_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_tools::DiscoverableTool;
|
||||
|
||||
const RECOMMENDED_PLUGINS_INTRO: &str =
|
||||
@@ -26,6 +27,10 @@ impl RecommendedPluginsInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for RecommendedPluginsInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("plugins.recommendations".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct RolloutBudgetContext {
|
||||
@@ -6,6 +7,10 @@ pub(crate) struct RolloutBudgetContext {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for RolloutBudgetContext {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("rollout_budget.remaining_tokens".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::AgentStatus;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
@@ -18,6 +19,10 @@ impl SubagentNotification {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for SubagentNotification {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("multi_agent.subagent_notification".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::ContextualUserFragment;
|
||||
use super::world_state::PreviousSectionState;
|
||||
use super::world_state::WorldStateSection;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::CONTEXT_WINDOW_CLOSE_TAG;
|
||||
use codex_protocol::protocol::CONTEXT_WINDOW_GUIDANCE_CLOSE_TAG;
|
||||
use codex_protocol::protocol::CONTEXT_WINDOW_GUIDANCE_OPEN_TAG;
|
||||
@@ -36,6 +37,10 @@ impl TokenBudgetContext {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for TokenBudgetContext {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("token_budget.context_window".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
@@ -101,6 +106,10 @@ impl ContextWindowGuidance {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for ContextWindowGuidance {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("token_budget.context_window_guidance".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
@@ -139,6 +148,10 @@ impl TokenBudgetRemainingContext {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for TokenBudgetRemainingContext {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("token_budget.remaining_tokens".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
@@ -175,6 +188,10 @@ impl TokenBudgetReminder {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for TokenBudgetReminder {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("token_budget.reminder".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
@@ -206,6 +223,10 @@ impl AutoCompactFallbackPrompt {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for AutoCompactFallbackPrompt {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("compaction.auto_fallback_prompt".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct TurnAborted {
|
||||
@@ -17,6 +18,10 @@ impl TurnAborted {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for TurnAborted {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("generic.turn_aborted".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct UserInstructions {
|
||||
@@ -7,6 +8,10 @@ pub(crate) struct UserInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for UserInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("agents_md.instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct UserShellCommand {
|
||||
@@ -27,6 +28,10 @@ impl UserShellCommand {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for UserShellCommand {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("shell.user_command".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use super::WorldStateSection;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::openai_models::CollaborationModeMessages;
|
||||
use codex_protocol::protocol::COLLABORATION_MODE_CLOSE_TAG;
|
||||
use codex_protocol::protocol::COLLABORATION_MODE_OPEN_TAG;
|
||||
@@ -125,6 +126,10 @@ struct CollaborationModeInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for CollaborationModeInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("collaboration_mode.instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::context::environment_context::NetworkContext;
|
||||
use crate::context::environment_context::push_xml_escaped_text;
|
||||
use crate::environment_selection::TurnEnvironmentSnapshot;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -154,6 +155,10 @@ impl WorldStateSection for EnvironmentsState {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for EnvironmentsState {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("environments.environment_context".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
@@ -188,6 +193,10 @@ enum EnvironmentUpdate {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for RenderedEnvironments {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("environments.environment_context".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use super::WorldStateHash;
|
||||
use super::WorldStateSection;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use codex_config::Sourced;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_utils_string::approx_bytes_for_tokens;
|
||||
use codex_utils_string::approx_tokens_from_byte_count;
|
||||
use serde::Deserialize;
|
||||
@@ -21,6 +22,10 @@ pub(crate) struct ManagedDeveloperInstructions {
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for ManagedDeveloperInstructions {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("managed_config.developer_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ use codex_extension_api::PreviousWorldStateSection;
|
||||
use codex_extension_api::RenderedWorldStateFragment;
|
||||
use codex_extension_api::WorldStateSectionContribution;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use indexmap::IndexMap;
|
||||
use serde::Deserialize;
|
||||
@@ -167,25 +168,35 @@ impl ErasedWorldStateSection for ExtensionWorldStateSection {
|
||||
PreviousSectionState::Unknown => PreviousWorldStateSection::Unknown,
|
||||
PreviousSectionState::Known(previous) => PreviousWorldStateSection::Known(previous),
|
||||
};
|
||||
self.0
|
||||
.render_diff(previous)
|
||||
.map(|fragment| Box::new(WorldStateContextFragment(fragment)) as _)
|
||||
self.0.render_diff(previous).map(|fragment| {
|
||||
Box::new(WorldStateContextFragment {
|
||||
fragment,
|
||||
content_kind: ContentItemKind(format!("{}.instructions", self.0.id())),
|
||||
}) as _
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct WorldStateContextFragment(RenderedWorldStateFragment);
|
||||
struct WorldStateContextFragment {
|
||||
fragment: RenderedWorldStateFragment,
|
||||
content_kind: ContentItemKind,
|
||||
}
|
||||
|
||||
impl ContextualUserFragment for WorldStateContextFragment {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
self.content_kind.clone()
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
self.0.role()
|
||||
self.fragment.role()
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
self.0.markers()
|
||||
self.fragment.markers()
|
||||
}
|
||||
|
||||
fn body(&self) -> String {
|
||||
self.0.body().to_string()
|
||||
self.fragment.body().to_string()
|
||||
}
|
||||
|
||||
fn type_markers() -> (&'static str, &'static str) {
|
||||
|
||||
@@ -4,6 +4,7 @@ use super::WorldStateSection;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use crate::context::environment_context::push_xml_escaped_text;
|
||||
use codex_extension_api::RenderedWorldStateFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::TOOLS_CLOSE_TAG;
|
||||
use codex_protocol::protocol::TOOLS_OPEN_TAG;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -97,9 +98,14 @@ impl WorldStateSection for ToolsState {
|
||||
)
|
||||
}
|
||||
};
|
||||
Some(Box::new(WorldStateContextFragment(
|
||||
RenderedWorldStateFragment::new("developer", (TOOLS_OPEN_TAG, TOOLS_CLOSE_TAG), body),
|
||||
)))
|
||||
Some(Box::new(WorldStateContextFragment {
|
||||
fragment: RenderedWorldStateFragment::new(
|
||||
"developer",
|
||||
(TOOLS_OPEN_TAG, TOOLS_CLOSE_TAG),
|
||||
body,
|
||||
),
|
||||
content_kind: ContentItemKind("tools.deferred_namespaces".to_string()),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::*;
|
||||
use codex_context_fragments::AnnotatedContent;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -36,6 +38,10 @@ impl WorldStateSection for TestSection {
|
||||
struct TestFragment(String);
|
||||
|
||||
impl ContextualUserFragment for TestFragment {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("generic.test".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"user"
|
||||
}
|
||||
@@ -157,6 +163,38 @@ fn extension_owned_section_uses_its_snapshot_and_renderer() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_owned_section_uses_its_stable_id_as_content_kind_feature() {
|
||||
let mut world_state = WorldState::default();
|
||||
world_state.add_extension_section(WorldStateSectionContribution::new(
|
||||
"extension_test",
|
||||
json!({"value": "after"}),
|
||||
|_| {
|
||||
Some(RenderedWorldStateFragment::new(
|
||||
"developer",
|
||||
("<extension_test>", "</extension_test>"),
|
||||
"after",
|
||||
))
|
||||
},
|
||||
));
|
||||
|
||||
let rendered = world_state.render_diff(&WorldStateSnapshot::default());
|
||||
|
||||
assert_eq!(
|
||||
rendered
|
||||
.into_iter()
|
||||
.map(|fragment| fragment.render_fragment().into_parts())
|
||||
.collect::<Vec<_>>(),
|
||||
vec![(
|
||||
"developer",
|
||||
AnnotatedContent::input_text(
|
||||
"<extension_test>after</extension_test>",
|
||||
ContentItemKind("extension_test.instructions".to_string()),
|
||||
),
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_retained_fragment_is_rendered_again() {
|
||||
let mut world_state = WorldState::default();
|
||||
|
||||
@@ -9,7 +9,6 @@ use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||
use codex_extension_api::ConfigContributor;
|
||||
use codex_extension_api::ContentItemKind;
|
||||
use codex_extension_api::ContextContributor;
|
||||
use codex_extension_api::ContextualUserFragment;
|
||||
use codex_extension_api::ExtensionData;
|
||||
@@ -238,10 +237,7 @@ where
|
||||
rendered
|
||||
.fragment
|
||||
.map(|fragment| {
|
||||
PromptFragment::developer_capability(
|
||||
fragment.render(),
|
||||
ContentItemKind("skills.catalog".to_string()),
|
||||
)
|
||||
PromptFragment::developer_capability(fragment.render(), fragment.content_kind())
|
||||
})
|
||||
.into_iter()
|
||||
.collect()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use codex_extension_api::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::protocol::SKILLS_INSTRUCTIONS_CLOSE_TAG;
|
||||
use codex_protocol::protocol::SKILLS_INSTRUCTIONS_OPEN_TAG;
|
||||
|
||||
@@ -40,6 +41,10 @@ impl ContextualUserFragment for AvailableSkillsInstructions {
|
||||
"developer"
|
||||
}
|
||||
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("skills.catalog".to_string())
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
Self::type_markers()
|
||||
}
|
||||
@@ -73,6 +78,10 @@ impl ContextualUserFragment for SkillInstructions {
|
||||
"user"
|
||||
}
|
||||
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("skills.selected_skill_instructions".to_string())
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
Self::type_markers()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use codex_context_fragments::ContextualUserFragment;
|
||||
use codex_execpolicy::Policy;
|
||||
use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::format_allow_prefixes;
|
||||
use codex_protocol::openai_models::ApprovalMessages;
|
||||
@@ -177,6 +178,10 @@ impl ContextualUserFragment for PermissionsInstructions {
|
||||
"developer"
|
||||
}
|
||||
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("generic.permissions_instructions".to_string())
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
Self::type_markers()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user