Represent Guardian policy as a contextual fragment (#40420)

## What changed

- Add a `GuardianPolicy` contextual fragment that renders as a standalone
  developer message with the `guardian.policy` content kind.
- Use the fragment when rebuilding Guardian context and verify that its content
  kind remains attached after history compaction.

GitOrigin-RevId: f7028fb29a48ad2453437f3d3a4405fcab7a2fd7
This commit is contained in:
pakrym-oai authored and copyberry committed 2026-08-24 15:42:16 +00:00
1 parent a25e986323
commit 60821c5043
4 files changed
+62 -11

No files matched your search

@@ -0,0 +1,42 @@
use super::ContextualUserFragment;
use codex_protocol::models::ContentItemKind;
/// The isolated developer policy provided to a Guardian reviewer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct GuardianPolicy {
policy: String,
}
impl GuardianPolicy {
pub(crate) fn new(policy: impl Into<String>) -> Self {
Self {
policy: policy.into(),
}
}
}
impl ContextualUserFragment for GuardianPolicy {
fn content_kind(&self) -> ContentItemKind {
ContentItemKind("guardian.policy".to_string())
}
fn role(&self) -> &'static str {
"developer"
}
fn requires_separate_message(&self) -> bool {
true
}
fn markers(&self) -> (&'static str, &'static str) {
Self::type_markers()
}
fn type_markers() -> (&'static str, &'static str) {
("", "")
}
fn body(&self) -> String {
self.policy.clone()
}
}
+2
View File
@@ -13,6 +13,7 @@ mod environments_instructions;
mod guardian_approved_action; mod guardian_approved_action;
mod guardian_followup_review_reminder; mod guardian_followup_review_reminder;
mod guardian_node_repl_policy; mod guardian_node_repl_policy;
mod guardian_policy;
mod guardian_review_evidence; mod guardian_review_evidence;
mod hook_additional_context; mod hook_additional_context;
mod image_resize_notice; mod image_resize_notice;
@@ -62,6 +63,7 @@ pub(crate) use environments_instructions::EnvironmentsInstructions;
pub(crate) use guardian_approved_action::GuardianApprovedAction; pub(crate) use guardian_approved_action::GuardianApprovedAction;
pub(crate) use guardian_followup_review_reminder::GuardianFollowupReviewReminder; pub(crate) use guardian_followup_review_reminder::GuardianFollowupReviewReminder;
pub(crate) use guardian_node_repl_policy::GuardianNodeReplPolicy; pub(crate) use guardian_node_repl_policy::GuardianNodeReplPolicy;
pub(crate) use guardian_policy::GuardianPolicy;
pub use guardian_review_evidence::GuardianReviewEvidence; pub use guardian_review_evidence::GuardianReviewEvidence;
pub use guardian_review_evidence::GuardianReviewEvidenceFragment; pub use guardian_review_evidence::GuardianReviewEvidenceFragment;
pub(crate) use hook_additional_context::HookAdditionalContext; pub(crate) use hook_additional_context::HookAdditionalContext;
+2 -8
View File
@@ -22,6 +22,7 @@ use crate::config::ManagedFeatures;
use crate::config::resolve_tool_suggest_config_from_layer_stack; use crate::config::resolve_tool_suggest_config_from_layer_stack;
use crate::context::ContextualUserFragment; use crate::context::ContextualUserFragment;
use crate::context::DeveloperInstructions; use crate::context::DeveloperInstructions;
use crate::context::GuardianPolicy;
use crate::context::ManagedDeveloperInstructions; use crate::context::ManagedDeveloperInstructions;
use crate::context::ModelSwitchInstructions; use crate::context::ModelSwitchInstructions;
use crate::context::MultiAgentRoleInstructions; use crate::context::MultiAgentRoleInstructions;
@@ -57,7 +58,6 @@ use codex_analytics::SubAgentThreadStartedInput;
use codex_analytics::TurnCodexErrorFact; use codex_analytics::TurnCodexErrorFact;
use codex_async_utils::OrCancelExt; use codex_async_utils::OrCancelExt;
use codex_connectors::connector_runtime_context_key; use codex_connectors::connector_runtime_context_key;
use codex_context_fragments::AnnotatedContent;
use codex_context_fragments::RenderedFragment; use codex_context_fragments::RenderedFragment;
use codex_exec_server::Environment; use codex_exec_server::Environment;
use codex_exec_server::EnvironmentManager; use codex_exec_server::EnvironmentManager;
@@ -3719,13 +3719,7 @@ impl Session {
&& !developer_instructions.is_empty() && !developer_instructions.is_empty()
&& let Some(guardian_developer_message) = && let Some(guardian_developer_message) =
crate::context_manager::updates::build_rendered_message(vec![ crate::context_manager::updates::build_rendered_message(vec![
RenderedFragment::new( GuardianPolicy::new(developer_instructions).render_fragment(),
"developer",
AnnotatedContent::input_text(
developer_instructions,
ContentItemKind("guardian.policy".to_string()),
),
),
]) ])
{ {
items.push(guardian_developer_message); items.push(guardian_developer_message);
@@ -23,6 +23,7 @@ use codex_model_provider::create_model_provider;
use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile; use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::ContentItem; use codex_protocol::models::ContentItem;
use codex_protocol::models::ContentItemKind;
use codex_protocol::models::NetworkPermissions; use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem; use codex_protocol::models::ResponseItem;
@@ -640,7 +641,13 @@ async fn process_compacted_history_preserves_separate_guardian_developer_message
.iter() .iter()
.filter_map(|item| match item { .filter_map(|item| match item {
ResponseItem::Message { role, content, .. } if role == "developer" => { ResponseItem::Message { role, content, .. } if role == "developer" => {
crate::content_items_to_text(content) crate::content_items_to_text(content).map(|text| {
(
text,
item.executed_tool_call_metadata()
.and_then(|metadata| metadata.content_item_kinds.clone()),
)
})
} }
_ => None, _ => None,
}) })
@@ -649,10 +656,16 @@ async fn process_compacted_history_preserves_separate_guardian_developer_message
assert!( assert!(
!developer_messages !developer_messages
.iter() .iter()
.any(|message| message.contains("stale developer message")) .any(|(message, _)| message.contains("stale developer message"))
); );
assert!(developer_messages.len() >= 2); assert!(developer_messages.len() >= 2);
assert_eq!(developer_messages.last(), Some(&guardian_policy)); assert_eq!(
developer_messages.last(),
Some(&(
guardian_policy,
Some(vec![ContentItemKind("guardian.policy".to_string())]),
))
);
} }
#[tokio::test] #[tokio::test]