Persist verified user answers in Guardian thread context (#42579)

## Why

Guardian authorization reviews need host-verified `request_user_input` answers to
remain aligned with thread history across compaction, resume, and rollback.

## What changed

- Record verified question-and-answer pairs in retained thread context when
  `GuardianThreadContext` is enabled, and source both local and delegated Guardian
  review evidence from that context.
- Preserve complete answer records within the evidence budget. Mark authorization
  context incomplete when records are unavailable or oversized, and defer cached
  fast approvals in that case.
- Keep the existing runtime-only answer path for threads that do not enable the
  thread-owned context.

## Testing

- Cover answer retention through compaction and resume, removal after rollback,
  source-call eviction, oversized answers, delegated review evidence, and legacy
  behavior.

GitOrigin-RevId: 01f6f4541515434a0d00fdde318ff59f2f8bd2eb
This commit is contained in:
felixxia-oai authored and copyberry committed 2026-09-03 16:41:11 +00:00
1 parent 7a7c188682
commit 1d74c3ba1e
22 files changed
+960 -397

No files matched your search

+1
View File
@@ -230,6 +230,7 @@ mod mcp_refresh;
mod mcp_runtime;
pub(crate) mod multi_agents;
mod realtime_history;
mod retained_context;
mod review;
mod rollout_budget;
mod rollout_reconstruction;
@@ -0,0 +1,26 @@
//! Records host-verified facts at the same persistence boundary as context checkpoints.
use codex_history::RetainedContextEvent;
use codex_history::RolloutItem;
use super::Session;
use super::thread_settings;
impl Session {
pub(crate) async fn record_retained_context(&self, mut event: RetainedContextEvent) {
event.bound();
// Share the checkpoint persistence lock so a fact cannot land on the wrong side
// of the checkpoint/suffix boundary. Ephemeral threads use the same live state.
let _guard = thread_settings::acquire_persistence_lock(self).await;
if self
.state
.lock()
.await
.history
.record_retained_context(&event)
{
self.persist_rollout_items(&[RolloutItem::RetainedContext(event)])
.await;
}
}
}
+4
View File
@@ -837,6 +837,10 @@ impl Session {
thread_id.to_string(),
thread_extension_init,
);
// Select the answer path once, before extensions or tool handlers observe the thread.
thread_extension_data.insert(crate::context::GuardianReviewEvidence::from_features(
&config.features,
));
// Kick off independent async setup tasks in parallel to reduce startup latency.
//
// - initialize thread persistence with new or resumed session info