Restore Guardian risk scores across thread lifecycles (#39038)

## Why

Resumed and forked threads need their persisted Guardian risk state available when routing later approval requests.

## What changed

- Initialize a session's `SecurityRiskScore` from the latest score in its rollout history when no score was supplied explicitly.
- Preserve the inherited score when resuming or forking a thread so low-risk actions can skip redundant Guardian reviews.

## Testing

- Add app-server coverage for inheriting the latest Guardian score when resuming and forking threads.

GitOrigin-RevId: 95a6c803f8dae75516d1da9d66a722379a0a7ef2
This commit is contained in:
felixxia-oai authored and copyberry committed 2026-08-17 17:33:30 +00:00
1 parent 1d928cad2f
commit 9dd3d6a13e
2 files changed
+137 -20

No files matched your search

+13
View File
@@ -26,6 +26,7 @@ use codex_protocol::protocol::MultiAgentVersion;
use codex_protocol::protocol::ThreadHistoryMode;
use codex_protocol::protocol::ThreadSource;
use codex_protocol::protocol::TurnEnvironmentSelections;
use codex_protocol::security_risk::SecurityRiskScore;
use codex_skills::SkillError;
use std::sync::OnceLock;
use tokio::sync::Semaphore;
@@ -727,6 +728,18 @@ impl Session {
config.current_time_reminder.as_ref(),
external_time_provider,
)?;
if thread_extension_init.get::<SecurityRiskScore>().is_none()
&& let Some(score) = initial_history
.get_rollout_items()
.iter()
.rev()
.find_map(|item| match item {
RolloutItem::SecurityRiskScore(score) => Some(score),
_ => None,
})
{
thread_extension_init.insert(score.clone());
}
let selected_capability_roots =
match thread_extension_init.get::<Vec<SelectedCapabilityRoot>>() {
Some(roots) => roots.as_ref().clone(),