Persist security risk scores in rollout history (#38363)

## What changed

- Add a `SecurityRiskScore` rollout item containing a category and numeric score.
- Persist the item in both thread history modes while excluding it from model context, user-visible thread history, search text, forks, and reconstructed conversation history.
- Re-export the score type from the extension API.

## Testing

- Cover serialization, persistence and loading, thread history projection, session reconstruction, append planning, and memory filtering.

GitOrigin-RevId: 1926fe366aeaa75052708a6da589f45a38eefb52
This commit is contained in:
jif authored and copyberry committed 2026-08-13 12:04:29 +00:00
1 parent 9ed0047a61
commit 72fa74fbc9
32 files changed
+165 -4

No files matched your search

@@ -241,6 +241,7 @@ fn source_model_items(items: &[RolloutItem]) -> Option<Vec<SourceModelItem<'_>>>
| RolloutItem::InterAgentCommunication(_)
| RolloutItem::Compacted(_)
| RolloutItem::TurnContext(_)
| RolloutItem::SecurityRiskScore(_)
| RolloutItem::WorldState(_) => return None,
RolloutItem::EventMsg(_) => {}
}
@@ -252,7 +253,9 @@ fn history_model_items(items: &[RolloutItem]) -> Option<Vec<&ResponseItem>> {
let mut model_items = Vec::new();
for item in items {
match item {
RolloutItem::SessionMeta(_) | RolloutItem::InterAgentCommunicationMetadata { .. } => {}
RolloutItem::SessionMeta(_)
| RolloutItem::InterAgentCommunicationMetadata { .. }
| RolloutItem::SecurityRiskScore(_) => {}
RolloutItem::ResponseItem(response_item) => model_items.push(&response_item.item),
RolloutItem::EventMsg(
EventMsg::ContextCompacted(_) | EventMsg::ThreadRolledBack(_),
@@ -7,6 +7,7 @@ use codex_protocol::models::ContentItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::ContextCompactedEvent;
use codex_protocol::protocol::ThreadRolledBackEvent;
use codex_protocol::security_risk::SecurityRiskScore;
use pretty_assertions::assert_eq;
#[test]
@@ -62,9 +63,17 @@ fn requires_a_strict_nonempty_model_prefix() {
event.started_at = Some(9_999);
}
}
let security_risk = RolloutItem::SecurityRiskScore(SecurityRiskScore {
category: "action_risk".to_string(),
score: 0.92,
});
metadata_changed.push(security_risk.clone());
assert!(model_transcripts_match(&history, &metadata_changed));
assert!(!model_transcripts_match(&source, &history));
assert!(plan_append(&source, &metadata_changed).is_some());
let mut source_with_security_risk = source.clone();
source_with_security_risk.push(security_risk);
assert!(plan_append(&source_with_security_risk, &metadata_changed).is_none());
for event in [
EventMsg::ContextCompacted(ContextCompactedEvent),