mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
Preserve Guardian authorization evidence until request budgeting (#44570)
## Why Transcript limits could shorten user instructions or omit later messages before Guardian evaluated the complete request budget, losing restrictions or prior approvals even when they would fit. ## What changed - Keep user messages and manual approvals complete and in source order through transcript collection and retention. - Preserve historical instructions while synchronous review can still compact history. If the final request still cannot fit, discard optional evidence before shortening older historical entries with truncation markers, preserving later restrictions where possible. - Warn that shortened instructions and approvals are incomplete and that missing evidence does not authorize actions. - Let asynchronous review defer to synchronous review when complete instructions exceed its budget. ## Testing Add coverage for complete instruction retention, approval and restriction ordering, marked truncation of oversized Unicode text, compaction before truncation, and asynchronous fallback when instructions do not fit. GitOrigin-RevId: e6159e876231347986b937ee7e07f944b83bfbe6
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
/// Identifies incomplete optional evidence without implying additional authority.
|
||||
/// Identifies incomplete evidence without implying additional authority.
|
||||
pub struct GuardianBudgetOmission;
|
||||
|
||||
impl ContextualUserFragment for GuardianBudgetOmission {
|
||||
@@ -27,6 +27,6 @@ impl ContextualUserFragment for GuardianBudgetOmission {
|
||||
}
|
||||
|
||||
fn body(&self) -> String {
|
||||
"Optional conversation evidence, tool descriptions, or images were omitted to fit the review input budget. Treat the remaining evidence as incomplete; omissions do not authorize actions.".to_owned()
|
||||
"Conversation evidence, tool descriptions, or images were omitted or shortened to fit the review input budget. User instructions and prior approvals may be incomplete where marked. Do not infer authorization from missing evidence or treat a partial grant as overriding an omitted restriction.".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
use codex_features::Feature;
|
||||
use codex_guardian_context::ComposedContext;
|
||||
use codex_guardian_context::HistoryTruncation;
|
||||
use codex_guardian_context::RequestBudget;
|
||||
use codex_guardian_context::effective_input_token_limit;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -51,6 +52,7 @@ pub(crate) async fn check_pending(session: &Session, turn: &TurnContext) -> Code
|
||||
existing_context_tokens: minimum_prefix,
|
||||
},
|
||||
GuardianBudgetOmission.render(),
|
||||
HistoryTruncation::Allow,
|
||||
)
|
||||
.map_err(|_error| {
|
||||
session
|
||||
@@ -67,6 +69,7 @@ pub(crate) async fn finalize(
|
||||
session: &Session,
|
||||
step: &StepContext,
|
||||
input: &mut [TurnInput],
|
||||
history_truncation: HistoryTruncation,
|
||||
) -> CodexResult<()> {
|
||||
let Some(pending) = session
|
||||
.services
|
||||
@@ -145,7 +148,7 @@ pub(crate) async fn finalize(
|
||||
),
|
||||
};
|
||||
let context = context
|
||||
.enforce_budget(budget, GuardianBudgetOmission.render())
|
||||
.enforce_budget(budget, GuardianBudgetOmission.render(), history_truncation)
|
||||
.map_err(|error| {
|
||||
session
|
||||
.services
|
||||
|
||||
@@ -128,7 +128,11 @@ async fn finalization_overflow_marks_the_reviewer_exhausted() {
|
||||
.thread_extension_data
|
||||
.insert(PendingReviewContext(context));
|
||||
|
||||
assert!(finalize(&session, &step, &mut input).await.is_err());
|
||||
assert!(
|
||||
finalize(&session, &step, &mut input, HistoryTruncation::Preserve)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
session
|
||||
.services
|
||||
|
||||
@@ -1778,10 +1778,11 @@ fn build_guardian_transcript_preserves_recent_tool_context_when_user_history_is_
|
||||
let entries = collect_guardian_transcript_entries(&items, GUARDIAN_MAX_TOOL_ENTRY_TOKENS);
|
||||
let (transcript, omission) = render_guardian_transcript_entries(&entries);
|
||||
|
||||
assert!(
|
||||
transcript
|
||||
.iter()
|
||||
.any(|entry| entry.starts_with("[1] user: "))
|
||||
assert_eq!(
|
||||
transcript[..8],
|
||||
(1..=8)
|
||||
.map(|index| format!("[{index}] user: {repeated}"))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert!(transcript.iter().any(|entry| {
|
||||
entry.contains("tool shell call:")
|
||||
@@ -1794,10 +1795,7 @@ fn build_guardian_transcript_preserves_recent_tool_context_when_user_history_is_
|
||||
.any(|entry| entry
|
||||
.contains("tool shell result: sandbox blocked outbound network access"))
|
||||
);
|
||||
assert_eq!(
|
||||
omission,
|
||||
Some("Some conversation entries were omitted.".to_string())
|
||||
);
|
||||
assert_eq!(omission, None);
|
||||
}
|
||||
|
||||
enum GuardianTestCatalog {
|
||||
|
||||
@@ -321,8 +321,13 @@ pub(crate) async fn run_turn(
|
||||
return Ok(None);
|
||||
}
|
||||
if crate::guardian::is_basic_session_source(&turn_context.session_source)
|
||||
&& let Err(error) =
|
||||
crate::guardian::finalize_guardian_input(&sess, &first_step_context, &mut input).await
|
||||
&& let Err(error) = crate::guardian::finalize_guardian_input(
|
||||
&sess,
|
||||
&first_step_context,
|
||||
&mut input,
|
||||
codex_guardian_context::HistoryTruncation::Preserve,
|
||||
)
|
||||
.await
|
||||
{
|
||||
// Token-budget compaction resets history, which can discard the evidence
|
||||
// referenced by a pending delta review. Leave budget failures unreusable.
|
||||
@@ -349,7 +354,13 @@ pub(crate) async fn run_turn(
|
||||
world_state = sess
|
||||
.record_context_updates_and_set_reference_context_item(first_step_context.as_ref())
|
||||
.await?;
|
||||
crate::guardian::finalize_guardian_input(&sess, &first_step_context, &mut input).await?;
|
||||
crate::guardian::finalize_guardian_input(
|
||||
&sess,
|
||||
&first_step_context,
|
||||
&mut input,
|
||||
codex_guardian_context::HistoryTruncation::Allow,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let mut can_drain_pending_input = input.is_empty();
|
||||
if run_hooks_and_record_inputs(
|
||||
|
||||
Reference in New Issue
Block a user