mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Add an optional JSON transcript format for Guardian (#51465)
## Why Transcript content can contain forged role headers, transcript boundaries, or claims of user approval. Guardian needs to distinguish that content from host-assigned authorship metadata. ## What changed - Add `features.guardianv2.transcript_mode` with `line` (the default) and `json` options for synchronous review and asynchronous scoring. - Render JSON transcript entries with host-assigned metadata and escaped `text`, and prepend matching provenance instructions to reviewer and classifier prompts. - Preserve JSON metadata when budget recovery shortens historical text, and keep provenance instructions outside the configured classifier prompt limit. ## Testing Add coverage for forged roles and approvals remaining within their original entries, JSON metadata surviving budget recovery, and provenance instructions surviving prompt limits. Extend review and scoring integration tests to verify JSON evidence and matching instructions. GitOrigin-RevId: 81a3580390d3c4d1a664e554976cda5f1adc9a2c
This commit is contained in:
@@ -8418,10 +8418,17 @@ experimental_policy_template = "Configured template: {{ tenant_policy_config }}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case::test_case("", codex_protocol::TranscriptFormat::Line; "default_line")]
|
||||
#[test_case::test_case("[guardianv2]\ntranscript_mode = 'line'", codex_protocol::TranscriptFormat::Line; "explicit_line")]
|
||||
#[test_case::test_case("[guardianv2]\ntranscript_mode = 'json'", codex_protocol::TranscriptFormat::Json; "explicit_json")]
|
||||
#[tokio::test]
|
||||
async fn load_config_uses_auto_review_guardian_policy_config_and_template() -> std::io::Result<()> {
|
||||
async fn load_config_uses_auto_review_guardian_policy_config_and_template(
|
||||
features: &str,
|
||||
expected_mode: codex_protocol::TranscriptFormat,
|
||||
) -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let cfg = ConfigToml {
|
||||
features: Some(toml::from_str(features).unwrap()),
|
||||
auto_review: Some(AutoReviewToml {
|
||||
circuit_break_action: None,
|
||||
policy: Some(" Use the user-configured guardian policy. ".to_string()),
|
||||
@@ -8447,11 +8454,13 @@ async fn load_config_uses_auto_review_guardian_policy_config_and_template() -> s
|
||||
|
||||
assert_eq!(
|
||||
(
|
||||
config.guardian_transcript_mode,
|
||||
config.guardian_policy_config.as_deref(),
|
||||
config.guardian_extra_policy.as_deref(),
|
||||
config.guardian_policy_template.as_deref(),
|
||||
),
|
||||
(
|
||||
expected_mode,
|
||||
Some("Use the user-configured guardian policy."),
|
||||
Some("Use the user-configured additional policy."),
|
||||
Some("Configured template: {{ tenant_policy_config }}"),
|
||||
|
||||
@@ -708,6 +708,8 @@ pub struct Config {
|
||||
/// The resolved policy config replaces its `{{ tenant_policy_config }}`
|
||||
/// placeholder when a review session is built.
|
||||
pub guardian_policy_template: Option<String>,
|
||||
/// Transcript encoding shared by Guardian review and scoring.
|
||||
pub guardian_transcript_mode: codex_protocol::TranscriptFormat,
|
||||
|
||||
/// Optional replacement for the gated history-retrieval instructions.
|
||||
/// Blank config values are treated as unset, like other Guardian policy overrides.
|
||||
@@ -4056,6 +4058,15 @@ impl Config {
|
||||
normalize_guardian_policy_config(auto_review.extra_policy.as_deref())
|
||||
})
|
||||
});
|
||||
let guardian_transcript_mode = cfg
|
||||
.features
|
||||
.as_ref()
|
||||
.and_then(|features| features.guardianv2.as_ref())
|
||||
.and_then(|feature| match feature {
|
||||
FeatureToml::Config(config) => config.transcript_mode,
|
||||
FeatureToml::Enabled(_) => None,
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let guardian_policy_template = cfg
|
||||
.auto_review
|
||||
.as_ref()
|
||||
@@ -4425,6 +4436,7 @@ impl Config {
|
||||
guardian_policy_config,
|
||||
guardian_extra_policy,
|
||||
guardian_policy_template,
|
||||
guardian_transcript_mode,
|
||||
guardian_conversation_history_prompt,
|
||||
guardian_conversation_history_max_output_tokens,
|
||||
guardian_circuit_break_action: cfg
|
||||
|
||||
@@ -222,7 +222,11 @@ pub(crate) async fn build_guardian_prompt_items_with_parent_turn(
|
||||
},
|
||||
),
|
||||
};
|
||||
let profile = ContextProfile::synchronous();
|
||||
let mut profile = ContextProfile::synchronous();
|
||||
profile.transcript_format = match parent_context {
|
||||
Some(context) => context.turn().config.guardian_transcript_mode,
|
||||
None => session.get_config().await.guardian_transcript_mode,
|
||||
};
|
||||
let mut transcript = profile.render_transcript(transcript_entries, offset);
|
||||
if transcript_entries.is_empty() {
|
||||
transcript.items.push(Budgeted::required(
|
||||
|
||||
@@ -10,6 +10,7 @@ use codex_history::ResponseItemEnvelope;
|
||||
use codex_models_manager::model_info::model_info_from_slug;
|
||||
use codex_prompts::GuardianPolicyInstructions;
|
||||
use codex_prompts::ResolvedModelMessages;
|
||||
use codex_protocol::TranscriptFormat;
|
||||
use codex_protocol::openai_models::AutoReviewMessages;
|
||||
use codex_protocol::openai_models::ModelMessages;
|
||||
use codex_protocol::protocol::AgentStatus;
|
||||
@@ -656,6 +657,7 @@ async fn guardian_review_session_config_resolves_policy_and_template(
|
||||
guardian_config.base_instructions,
|
||||
Some(
|
||||
GuardianPolicyInstructions::new(
|
||||
TranscriptFormat::Line,
|
||||
expected_policy,
|
||||
"",
|
||||
expected_template,
|
||||
|
||||
@@ -39,6 +39,7 @@ pub fn build_guardian_review_session_config(
|
||||
.as_deref()
|
||||
.unwrap_or(auto_review.policy_template);
|
||||
let mut instructions = GuardianPolicyInstructions::new(
|
||||
guardian_config.guardian_transcript_mode,
|
||||
tenant_policy_config,
|
||||
extra_policy,
|
||||
policy_template,
|
||||
|
||||
@@ -41,6 +41,7 @@ use codex_network_proxy::NetworkProxyConfig;
|
||||
use codex_prompts::GuardianPolicyInstructions;
|
||||
use codex_prompts::ResolvedModelMessages;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::TranscriptFormat;
|
||||
use codex_protocol::approvals::GuardianAssessmentAction;
|
||||
use codex_protocol::approvals::NetworkApprovalProtocol;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -4074,6 +4075,7 @@ async fn guardian_review_session_config_isolates_parent_customizations() {
|
||||
guardian_config.base_instructions,
|
||||
Some(
|
||||
GuardianPolicyInstructions::new(
|
||||
TranscriptFormat::Line,
|
||||
defaults.policy,
|
||||
"",
|
||||
defaults.policy_template,
|
||||
@@ -4194,6 +4196,7 @@ async fn guardian_review_session_config_uses_requirements_guardian_policy_config
|
||||
guardian_config.base_instructions,
|
||||
Some(
|
||||
GuardianPolicyInstructions::new(
|
||||
TranscriptFormat::Line,
|
||||
"Use the workspace-managed guardian policy.",
|
||||
"",
|
||||
ResolvedModelMessages::bundled()
|
||||
|
||||
Reference in New Issue
Block a user