From 72fa74fbc9c4b72d513304bfa0eda427d2402ed9 Mon Sep 17 00:00:00 2001 From: jif Date: Thu, 13 Aug 2026 11:50:47 +0000 Subject: [PATCH] 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 --- .../app-server-exports-stable.json.zst | Bin 133059 -> 133187 bytes .../src/protocol/thread_history.rs | 1 + .../src/protocol/thread_history_projection.rs | 1 + .../thread_history_projection_tests.rs | 6 ++ codex-rs/core/src/agent/control/spawn.rs | 4 +- codex-rs/core/src/agent/control_tests.rs | 1 + .../src/session/rollout_reconstruction.rs | 3 + .../session/rollout_reconstruction_tests.rs | 27 +++++++++ codex-rs/core/src/session/tests.rs | 2 + codex-rs/ext/extension-api/src/lib.rs | 1 + .../src/sessions/append.rs | 5 +- .../src/sessions/append_tests.rs | 9 +++ codex-rs/history/src/lib.rs | 3 + codex-rs/history/src/rollout_payload.rs | 10 ++++ codex-rs/history/src/tests.rs | 6 +- codex-rs/memories/write/src/phase1.rs | 6 ++ codex-rs/protocol/src/lib.rs | 1 + codex-rs/protocol/src/security_risk.rs | 13 +++++ codex-rs/rollout/src/list.rs | 4 +- codex-rs/rollout/src/metadata.rs | 2 + codex-rs/rollout/src/model_context.rs | 1 + codex-rs/rollout/src/persistence_metrics.rs | 1 + codex-rs/rollout/src/policy.rs | 1 + codex-rs/rollout/src/recorder.rs | 1 + codex-rs/rollout/src/recorder_tests.rs | 52 ++++++++++++++++++ codex-rs/rollout/src/search.rs | 1 + codex-rs/state/src/extract.rs | 2 + codex-rs/state/src/runtime/threads.rs | 1 + .../local/rollout_migration/canonicalizer.rs | 1 + .../local/rollout_migration/rollback_plan.rs | 1 + .../rollout_migration/rollback_replay.rs | 1 + .../thread-store/src/thread_metadata_sync.rs | 1 + 32 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 codex-rs/protocol/src/security_risk.rs diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst index a92dff8820fe594a38e2cb31e248c9e0479d05e4..24b643f17821103580fead14f4def1dc63159bf3 100644 GIT binary patch delta 4946 zcmV-Y6RqsSj|jt%2!ON!@AM9u0QTHgARu)fAh1g2?w9@a0T_Qy089W&02qnmLeapE zq~ck0ux8hwu~a^vz(yf-7h_obYbndpHhV?I>t|`Yb!+YHM`@Kko9TA>MyJ?&*1PM- zU`X@iHDmD~B+C;$s(U5QpX~uJzWPG3UYV2;n;(BN{n#dV7GPcUZY*sU=9fd&drBjA zds>45Rd`L~+Gl?$T4(Zx3@@PVbouV>er8CPG27+WE`i%jmy_+=v;xk;mhI(7#%)$* zO;e`PuQ~jmA2%=;#=MtDu9EB}k!3C9hXemxzW8>`=4Ccq@8&A9;m6(V-~m$p$M{_PmpJsWjQ5c_bo-gk$nL4D|scP+){J5B;uKbpvYAKzp$F9*ehQzTyocYaw> z5OQviFD6ZU-4NS8LR<=DEPa_L%*dwCMzKXnn*j_T94gOT6P`lK|7~uDUf*b3oI`Tz z&|m`eWgl4`sBX6zD<0|Es4yCT2I$#kxI=IF1QU=ZUtVKS5j3t*n(YygBeM;LLrhe$K2974Z{sfySNV>_G7%orD7jddN3a~_$+K5J3 ztvX6mIM(Jc<+vypMIJY3#I)&Ljr^)^V&Vtoc$*TZn*z?YYgs`?SV6NMO|>UM&I8#r4+L%X&AAveq%C29&IRH(*+m5Y&w1N(P@(0c0Rd~b1qE%$o(X}K$ zekxC*(0RH2Ks+HgyXlz3XBN@V?3esEiY88bG^cwax(Jk=By{BqydkSlPsWVol2} zOk3?TYh2SV@1FQ-u6xW7)wCI|@I7gb#P#xZ4p*Q5lyW%TrxMb*9&TMvmTE=L;~~`c z+`EWz*-!%_@*o=Mx`9#gCTX`b^gg3{g74FRnD`RZb=k9f^GM}4&;Q-hW)avwjo1xF zw-_yRr7MZx;0|1?GbBnlU}65~0CHo%`%}COE;N7ngO>ALE}22z(;`Z->%LVXWhUe2aP81k}^Hk6w84;15s6m5& zJ?8n{&lBy#P08eQM+f3Rt?SpwCM}+H6~tDOe4PF&h~iPZe+|S6qLY-t95vUOQqG9V zW~Cr*lnoC+J+*EuEbGWTXqXg@xhifxWnJk?%qjVm+00y4lYcA3L{;-$0-;ER_RjW2l+mHyeQ6t4EIr9X;oCCy>u;&EZ7;d4z*cBNDYJ>%Sf z+4S91hgn*4;thj9^0lsV^`_ys!z6Sq3lO;?iTIkvF3a(#n&PETjRcc_NM%J3I0PsC z^}w?rOS{9K3zXx!pQ)8aBc9d;pUHvqR?yq;S~ zshAO|m!AjX1*F14E_k+(@SC(}0#H)Hxk~D4xyc*gtWj|xXCupNhLhaX5P&-&>#4Ua zs#$Q5cDlW!>Mh|8qET0Wl+&oi2?CHK%M8&U=lkF(&oI{orkoYSEy;-guQ)79Bg^Gk zJB~W}G{aIJsHDau!*~w|T7~KH>7GtqBk=H81|4ekjgSZq2uVbv=Lk(>DvaSmUMJ3X z9HBJ=5{4qWhY#_agK#00FI3!CA?Og|!u}l{tTo0Ik}@POAEFh1^LmrQ|x|tZsbO`Aif-y#qyXPfg&wXSH_J91i<)I4ocv!Dw@R^rAF`Yz8?#~6Iz$_RfuK2&(#8S) zV#v_qxXDQl;VKwFY#8mX!%DV7{cmN!JQ<>*;6!EDpYvK{QiijvoO;+4x$i$2%6UP> zD#E=v%~y6^ka?+dI2i{hVM~CU#4h#Su2Z6ALm&jDLkdxUL+2;#dY;^mNDR+U`$@X# zi_6w118+k3DBE*;tQpo7bD6;ULnFDa{h0Bh3MUF2ixUY@wP6hSb^mpKbspU09hVjJ$JC$2635g6zx6$M9 zPE`^aO3VHuB{GaygMt~P*K3IknTXilxPA7#nubX1qPP*eGUZZu|JBkit->SS*un*i z`!R}b?K16gGc!XRu2nu*)qK{3OZ9$%xqHzt)SCc*kt`MCCquU34B_Av#<; zTrHrqDxiX&Dzut%5*+AN*Eaj3LHglD4h{wC%d`++SEnMc&a)k*ny(n}!O_&GC1*Dq zD11e@kk9FJOl`P5{)6x!+?HCwu;q!?P@l&M=XuNp%YE&=FJ8&^I0~)^*7FzU!J1Ga zx+R-`1ksIQ>?B77TUeaGUHRzP1?IJPD}SwtIsAGaONys)fbKyCGQzPOMlJDw6)6~T zz&<+3+lYa9WnK-}EZmU7Yd$2n#J$7=V)=MGn7f`Wt{U^A005bGyOTdEO(OSXIy-M6 zii0Pq%JPAd4{*I1{sv|i^(yxbE7qs;9qP7!Xj|1pG)hPJPB=#jKQQs4Smep^u;#(; z>lGAM{%3j2s10l)Q%sK4*EtQ>r=zQWCgJYwYeF8 zz_Wl#n2dW4Q!;PaZC_N}GP?>&eCin-CcZerY|b?Mbn5fYVy;2FfpSx=#v~?~u7p1w zlPV-g8YvFN`yjy@w{%=6*|^<*PiBC4 z>ALp7#^qiJ-W}Fn<)B##_joJa-Fl!;o7z-dLW^O+-4(2y+cu3XH`e7SY5fs)KZ&bc z&;d*42&CqWkPZl47X`A1a*|!8k#3qM#IjRFb_9<1L{VVp0v`PM%dSX4we`pCagRs zz)=;@#C<4*8dUEZJ#;eNUL2U!lxi9npLx4YXcIx-bgfPkpJA`W8Ref|8x^)#;M+q2 zj0xCX3V8|wZiWV7p_syuk#wF~s6>}jGTw>agKs_v@dF|BvV9;mBcnV&5rj~PQ4hHX z)Fawi>;cWU`-O~qPxMfK@6oeH>H!+5hnLi45PPf;sjT5<_foYXK+A}UhC)5WjLA*1 zQk6Ahow3zQ`)6)h?JOCX=sVcRgl%k8=z{xQ2^;vzBSvd77H(A~Ik{%Dk1kmG=_j!W z6Mke%T6~c0>fCsv4rvQFXTEBDbrPI(n#Uu%dFkAB4P+V2*lf)^7Ga<)B{k>X35V|HV3n`D})5vgUMuh3|%H~ zeF}e|?M#6zA1u!nRdTMt@e*dIa_UgwRLIUVR-@9C*W{a7s9Dr^&;C5;?xl>y4oujM zRfeUAo}$@@jjHp1l<*fI$JQz2Q*m5D!*)7rU)Ob4t}u{_13KW^EmFJG9for~$XkSE z>8RVE88Oe`k*BBL_ldn%%od`OZKe%>xguY#QSv?`_Xh$)jRB$x zw?_xrVN)}Iz1`t`7aZIkO8ZlCt8k~nK{jL{JSgeZiKj~v-4nJhNPaQ zii4RJPH&L97*}uhl28A+O8p9guZ1q7RRfqtT*fkgxKuK2e2ST4GPwD~`qi{ux(VyG zHzI_YD(#3_Dr!2O)Ih^bQ-|?P8GHc9irQ|w^+gDbfP1DhjT1R#`0YgL^lXLn9L+j_ zNh-2#V6}RiGducq0<9*?^R)*X`?eDE)WTPwlBGi%Nhg~3t*D`x3Knp9$Z(j|fO??w zO@oGi)2{}}%GmPC8zdF8OR;>BaFd8wxwDZ{YZ5%m86J}yYf{E3l1&Bavf%K9Fu+3R z-d<%`+Rza2KuMn`K=kBc)4|HmF%N@`+`(QI&}t?w)@S9IPuY?&zyhJ;2t@F~84i2D zf3ucYwWGshew>oSRav4-a)XP|K%-;0g;rdDI8iy;NJvCWs* z9V*{Rpg6tc>iB}hl8UQ%mR=8GI9C?PaWfA{K-~&mxYOLG4mT}k5H|=u?9>GP+M-c^ zfdAU#!3RGzH(01Qr~FeGA1^+(HNIu&RePI@LDbRC^pF|6$I=h z+5z-qJ>jRU>4AGll8e@qL~%UfSVH91caPZ*O|#zZ2!`KUpQJNHgjoR6z z787RRhp?rxeJo5VwAh!IY9TbC{xGK?CXNX-sG*FS2bZ2AW$guzk{;|EeOah|QAGv^ z!jwql-gSafIq>%Uo)A?3mDPhu@}0LCrWNf8S+GB3bo*?$h!H%)!D_CTD88xb QqSX9@D=1lH^pXKmC!*bNBLDyZ delta 4797 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 {} } } diff --git a/codex-rs/app-server-protocol/src/protocol/thread_history_projection.rs b/codex-rs/app-server-protocol/src/protocol/thread_history_projection.rs index 27b1033cc0..d742fd0b22 100644 --- a/codex-rs/app-server-protocol/src/protocol/thread_history_projection.rs +++ b/codex-rs/app-server-protocol/src/protocol/thread_history_projection.rs @@ -82,6 +82,7 @@ pub fn project_rollout_line(line: &RolloutLine) -> ThreadHistoryChangeSet { | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => ThreadHistoryChangeSet::default(), } } diff --git a/codex-rs/app-server-protocol/src/protocol/thread_history_projection_tests.rs b/codex-rs/app-server-protocol/src/protocol/thread_history_projection_tests.rs index 7dcbb26c01..d240e3a7ef 100644 --- a/codex-rs/app-server-protocol/src/protocol/thread_history_projection_tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/thread_history_projection_tests.rs @@ -10,6 +10,7 @@ use codex_protocol::protocol::TurnAbortReason; use codex_protocol::protocol::TurnAbortedEvent; use codex_protocol::protocol::TurnCompleteEvent; use codex_protocol::protocol::TurnStartedEvent; +use codex_protocol::security_risk::SecurityRiskScore; use codex_protocol::user_input::UserInput; use codex_rollout::CompactedItem; use codex_rollout::RolloutItem; @@ -200,9 +201,14 @@ fn ignores_legacy_abort_without_turn_id_and_context_only_records() { previous_window_id: None, window_id: None, })); + let security_risk = project(RolloutItem::SecurityRiskScore(SecurityRiskScore { + category: "action_risk".to_string(), + score: 0.92, + })); assert!(aborted.is_empty()); assert!(compacted.is_empty()); + assert!(security_risk.is_empty()); } #[test] diff --git a/codex-rs/core/src/agent/control/spawn.rs b/codex-rs/core/src/agent/control/spawn.rs index 9683c4eaf9..9029352329 100644 --- a/codex-rs/core/src/agent/control/spawn.rs +++ b/codex-rs/core/src/agent/control/spawn.rs @@ -71,7 +71,8 @@ fn keep_forked_rollout_item(item: &RolloutItem, preserve_reference_context_item: | ResponseItem::Other => false, }, RolloutItem::InterAgentCommunication(_) - | RolloutItem::InterAgentCommunicationMetadata { .. } => false, + | RolloutItem::InterAgentCommunicationMetadata { .. } + | RolloutItem::SecurityRiskScore(_) => false, // Full-history forks preserve the cached prompt prefix and can keep diffing // from the parent's durable baseline. Truncated forks drop part of that prompt, // so they must rebuild context on their first child turn. @@ -794,6 +795,7 @@ impl AgentControl { | RolloutItem::InterAgentCommunication(_) | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::WorldState(_) => true, + RolloutItem::SecurityRiskScore(_) => false, } }); // Full forks reuse the parent's reference context instead of rebuilding it. If that diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index 3ae17b49ad..59cd408205 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -250,6 +250,7 @@ async fn persisted_originator(thread: &CodexThread) -> String { | RolloutItem::EventMsg(_) | RolloutItem::Compacted(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::TurnContext(_) => None, }) .expect("session metadata should be persisted") diff --git a/codex-rs/core/src/session/rollout_reconstruction.rs b/codex-rs/core/src/session/rollout_reconstruction.rs index f7f0e8d2fe..dae13c97fb 100644 --- a/codex-rs/core/src/session/rollout_reconstruction.rs +++ b/codex-rs/core/src/session/rollout_reconstruction.rs @@ -282,6 +282,7 @@ impl Session { } RolloutItem::EventMsg(_) | RolloutItem::SessionMeta(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::InterAgentCommunicationMetadata { .. } => {} } @@ -371,6 +372,7 @@ impl Session { RolloutItem::EventMsg(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::SessionMeta(_) => {} } } @@ -409,6 +411,7 @@ impl Session { | RolloutItem::InterAgentCommunication(_) | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::TurnContext(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => { unreachable!("only world-state replay items are collected") } diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index 5b6614154a..7e2a01f926 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -16,6 +16,7 @@ use codex_protocol::protocol::SessionContextWindow; use codex_protocol::protocol::SessionMeta; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::WorldStateItem; +use codex_protocol::security_risk::SecurityRiskScore; use pretty_assertions::assert_eq; use serde_json::json; use std::path::PathBuf; @@ -146,6 +147,32 @@ async fn record_initial_history_reconstructs_typed_inter_agent_message() { ); } +#[tokio::test] +async fn record_initial_history_ignores_security_risk_scores() { + let (session, _turn_context) = make_session_and_context().await; + let user_item = user_message("visible user input"); + let security_risk = SecurityRiskScore { + category: "credential_access".to_string(), + score: 0.92, + }; + + session + .record_initial_history(InitialHistory::Resumed(ResumedHistory { + conversation_id: ThreadId::default(), + history: Arc::new(vec![ + RolloutItem::ResponseItem(ResponseItemEnvelope::new(user_item.clone())), + RolloutItem::SecurityRiskScore(security_risk), + ]), + rollout_path: Some(PathBuf::from("/tmp/resume.jsonl")), + })) + .await; + + assert_eq!( + raw_history_items(&session.state.lock().await.clone_history()), + vec![user_item] + ); +} + #[tokio::test] async fn record_initial_history_restores_world_state_baseline() { let (session, turn_context) = make_session_and_context().await; diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index d9c89d7ed9..098434ca72 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -3256,6 +3256,7 @@ async fn start_new_context_window_assigns_and_persists_item_ids() { | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }); assert_eq!( @@ -3322,6 +3323,7 @@ async fn record_initial_history_assigns_and_persists_id_for_forked_response_item | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }); let persisted_item = persisted_item.expect("forked response item should be persisted"); diff --git a/codex-rs/ext/extension-api/src/lib.rs b/codex-rs/ext/extension-api/src/lib.rs index 2f21380b0f..36417e51bd 100644 --- a/codex-rs/ext/extension-api/src/lib.rs +++ b/codex-rs/ext/extension-api/src/lib.rs @@ -15,6 +15,7 @@ pub use capabilities::ResponseItemInjectionFuture; pub use capabilities::ResponseItemInjector; pub use codex_context_fragments::ContextualUserFragment; pub use codex_protocol::models::ResponseItem; +pub use codex_protocol::security_risk::SecurityRiskScore; pub use codex_tools::ConversationHistory; pub use codex_tools::ExtensionTurnItem; pub use codex_tools::FunctionCallError; diff --git a/codex-rs/external-agent-migration/src/sessions/append.rs b/codex-rs/external-agent-migration/src/sessions/append.rs index a9efc6f5f4..e08f0607a8 100644 --- a/codex-rs/external-agent-migration/src/sessions/append.rs +++ b/codex-rs/external-agent-migration/src/sessions/append.rs @@ -241,6 +241,7 @@ fn source_model_items(items: &[RolloutItem]) -> Option>> | 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> { 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(_), diff --git a/codex-rs/external-agent-migration/src/sessions/append_tests.rs b/codex-rs/external-agent-migration/src/sessions/append_tests.rs index f86a9af882..2a64e1a2cc 100644 --- a/codex-rs/external-agent-migration/src/sessions/append_tests.rs +++ b/codex-rs/external-agent-migration/src/sessions/append_tests.rs @@ -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), diff --git a/codex-rs/history/src/lib.rs b/codex-rs/history/src/lib.rs index 26788c95d0..f95a9d994e 100644 --- a/codex-rs/history/src/lib.rs +++ b/codex-rs/history/src/lib.rs @@ -22,6 +22,7 @@ use codex_protocol::protocol::ThreadHistoryMode; use codex_protocol::protocol::ThreadSource; use codex_protocol::protocol::TurnContextItem; use codex_protocol::protocol::WorldStateItem; +use codex_protocol::security_risk::SecurityRiskScore; use schemars::JsonSchema; use serde::Deserialize; use serde::Deserializer; @@ -98,6 +99,7 @@ pub enum RolloutItem { Compacted(CompactedItem), TurnContext(TurnContextItem), WorldState(WorldStateItem), + SecurityRiskScore(SecurityRiskScore), EventMsg(EventMsg), } @@ -410,6 +412,7 @@ fn multi_agent_version_from_items( | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::Compacted(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }) }) diff --git a/codex-rs/history/src/rollout_payload.rs b/codex-rs/history/src/rollout_payload.rs index 018b370a5e..6d40c3d881 100644 --- a/codex-rs/history/src/rollout_payload.rs +++ b/codex-rs/history/src/rollout_payload.rs @@ -7,6 +7,7 @@ use super::InterAgentCommunication; use super::ResponseItem; use super::ResponseItemEnvelope; use super::RolloutItem; +use super::SecurityRiskScore; use super::SessionMetaLine; use super::TurnContextItem; use super::WorldStateItem; @@ -41,6 +42,9 @@ pub(super) enum RolloutItemWire<'a> { WorldState { payload: Cow<'a, WorldStateItem>, }, + SecurityRiskScore { + payload: Cow<'a, SecurityRiskScore>, + }, EventMsg { payload: Cow<'a, EventMsg>, }, @@ -75,6 +79,9 @@ impl<'a> From<&'a RolloutItem> for RolloutItemWire<'a> { RolloutItem::WorldState(payload) => Self::WorldState { payload: Cow::Borrowed(payload), }, + RolloutItem::SecurityRiskScore(payload) => Self::SecurityRiskScore { + payload: Cow::Borrowed(payload), + }, RolloutItem::EventMsg(payload) => Self::EventMsg { payload: Cow::Borrowed(payload), }, @@ -103,6 +110,9 @@ impl From> for RolloutItem { RolloutItemWire::Compacted { payload } => Self::Compacted(payload.into_owned()), RolloutItemWire::TurnContext { payload } => Self::TurnContext(payload.into_owned()), RolloutItemWire::WorldState { payload } => Self::WorldState(payload.into_owned()), + RolloutItemWire::SecurityRiskScore { payload } => { + Self::SecurityRiskScore(payload.into_owned()) + } RolloutItemWire::EventMsg { payload } => Self::EventMsg(payload.into_owned()), } } diff --git a/codex-rs/history/src/tests.rs b/codex-rs/history/src/tests.rs index b968d6ea7c..056bad02f9 100644 --- a/codex-rs/history/src/tests.rs +++ b/codex-rs/history/src/tests.rs @@ -352,6 +352,10 @@ fn rollout_item_variants_preserve_existing_payload_shapes() -> Result<()> { "type": "world_state", "payload": { "full": true, "state": { "cwd": "/tmp" } }, }), + json!({ + "type": "security_risk_score", + "payload": { "category": "action_risk", "score": 0.92 }, + }), json!({ "type": "event_msg", "payload": { "type": "warning", "message": "heads up" }, @@ -370,7 +374,7 @@ fn rollout_item_variants_preserve_existing_payload_shapes() -> Result<()> { fn rollout_item_schema_matches_tagged_payload_and_sibling_metadata() -> Result<()> { let schema = serde_json::to_value(schemars::schema_for!(RolloutItem))?; let variants = schema["oneOf"].as_array().expect("rollout variants"); - assert_eq!(variants.len(), 8); + assert_eq!(variants.len(), 9); for variant in variants { let required = variant["required"].as_array().expect("required fields"); diff --git a/codex-rs/memories/write/src/phase1.rs b/codex-rs/memories/write/src/phase1.rs index f655da3ac2..f7caefbeda 100644 --- a/codex-rs/memories/write/src/phase1.rs +++ b/codex-rs/memories/write/src/phase1.rs @@ -417,6 +417,7 @@ mod job { | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }) .collect::>(); @@ -672,6 +673,7 @@ mod tests { use super::*; use codex_protocol::AgentPath; use codex_protocol::protocol::InterAgentCommunication; + use codex_protocol::security_risk::SecurityRiskScore; use pretty_assertions::assert_eq; #[test] @@ -722,6 +724,10 @@ mod tests { let serialized = job::serialize_filtered_rollout_response_items(&[ RolloutItem::ResponseItem(mixed_contextual_message.into()), RolloutItem::ResponseItem(skill_message.into()), + RolloutItem::SecurityRiskScore(SecurityRiskScore { + category: "action_risk".to_string(), + score: 0.92, + }), RolloutItem::ResponseItem(subagent_message.clone().into()), ]) .expect("serialize"); diff --git a/codex-rs/protocol/src/lib.rs b/codex-rs/protocol/src/lib.rs index fe2680f43f..b8f4bc65f1 100644 --- a/codex-rs/protocol/src/lib.rs +++ b/codex-rs/protocol/src/lib.rs @@ -35,6 +35,7 @@ pub mod protocol; pub mod request_permissions; pub mod request_user_input; pub mod review_format; +pub mod security_risk; pub mod shell_environment; pub mod turn_input; pub mod user_input; diff --git a/codex-rs/protocol/src/security_risk.rs b/codex-rs/protocol/src/security_risk.rs new file mode 100644 index 0000000000..73565eff82 --- /dev/null +++ b/codex-rs/protocol/src/security_risk.rs @@ -0,0 +1,13 @@ +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; + +/// A durable, thread-owned security risk classifier score. +/// +/// These records belong to rollout history only and must not enter model-visible +/// conversation context or user-visible thread item projections. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct SecurityRiskScore { + pub category: String, + pub score: f64, +} diff --git a/codex-rs/rollout/src/list.rs b/codex-rs/rollout/src/list.rs index fa4dc67106..b849490dce 100644 --- a/codex-rs/rollout/src/list.rs +++ b/codex-rs/rollout/src/list.rs @@ -1170,7 +1170,7 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result { // Not included in `head`; skip. } - RolloutItem::WorldState(_) => { + RolloutItem::WorldState(_) | RolloutItem::SecurityRiskScore(_) => { // Not included in `head`; skip. } RolloutItem::Compacted(_) => { @@ -1243,6 +1243,7 @@ pub async fn read_head_for_summary(path: &Path) -> io::Result {} } } @@ -1295,6 +1296,7 @@ pub async fn read_session_meta_line(path: &Path) -> io::Result | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => {} } } diff --git a/codex-rs/rollout/src/metadata.rs b/codex-rs/rollout/src/metadata.rs index 471efb251d..15b4344a3c 100644 --- a/codex-rs/rollout/src/metadata.rs +++ b/codex-rs/rollout/src/metadata.rs @@ -75,6 +75,7 @@ pub fn builder_from_items( | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }) && let Some(builder) = builder_from_session_meta(session_meta, rollout_path) { @@ -143,6 +144,7 @@ pub async fn extract_metadata_from_rollout( | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }), parse_errors, diff --git a/codex-rs/rollout/src/model_context.rs b/codex-rs/rollout/src/model_context.rs index 7976e84ca8..06f245b39b 100644 --- a/codex-rs/rollout/src/model_context.rs +++ b/codex-rs/rollout/src/model_context.rs @@ -153,6 +153,7 @@ impl ModelContextScan { RolloutItem::EventMsg(_) | RolloutItem::SessionMeta(_) | RolloutItem::InterAgentCommunicationMetadata { .. } + | RolloutItem::SecurityRiskScore(_) | RolloutItem::WorldState(_) => {} } diff --git a/codex-rs/rollout/src/persistence_metrics.rs b/codex-rs/rollout/src/persistence_metrics.rs index 79f1fd789d..d345c1ae53 100644 --- a/codex-rs/rollout/src/persistence_metrics.rs +++ b/codex-rs/rollout/src/persistence_metrics.rs @@ -236,6 +236,7 @@ fn rollout_item_type(item: &RolloutItem) -> String { RolloutItem::Compacted(_) => "compacted".to_string(), RolloutItem::TurnContext(_) => "turn_context".to_string(), RolloutItem::WorldState(_) => "world_state".to_string(), + RolloutItem::SecurityRiskScore(_) => "security_risk_score".to_string(), RolloutItem::EventMsg(EventMsg::ItemCompleted(event)) => { format!("event.item_completed.{}", turn_item_type(&event.item)) } diff --git a/codex-rs/rollout/src/policy.rs b/codex-rs/rollout/src/policy.rs index 00a9b0f8e0..372c0afaf5 100644 --- a/codex-rs/rollout/src/policy.rs +++ b/codex-rs/rollout/src/policy.rs @@ -16,6 +16,7 @@ pub fn is_persisted_rollout_item(item: &RolloutItem, history_mode: ThreadHistory RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::SessionMeta(_) => true, } } diff --git a/codex-rs/rollout/src/recorder.rs b/codex-rs/rollout/src/recorder.rs index 11ce48b5bf..93a3c294da 100644 --- a/codex-rs/rollout/src/recorder.rs +++ b/codex-rs/rollout/src/recorder.rs @@ -2067,6 +2067,7 @@ async fn resume_candidate_matches_cwd( | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::Compacted(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }) { diff --git a/codex-rs/rollout/src/recorder_tests.rs b/codex-rs/rollout/src/recorder_tests.rs index b24e617a86..3c64aa6179 100644 --- a/codex-rs/rollout/src/recorder_tests.rs +++ b/codex-rs/rollout/src/recorder_tests.rs @@ -20,6 +20,7 @@ use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::ThreadHistoryMode; use codex_protocol::protocol::TurnContextItem; use codex_protocol::protocol::UserMessageEvent; +use codex_protocol::security_risk::SecurityRiskScore; use codex_utils_absolute_path::test_support::PathExt; use pretty_assertions::assert_eq; use std::fs; @@ -431,6 +432,57 @@ async fn load_rollout_items_preserves_legacy_guardian_assessment_lines() -> std: Ok(()) } +#[tokio::test] +async fn load_rollout_items_preserves_security_risk_scores() -> std::io::Result<()> { + let home = TempDir::new().expect("temp dir"); + let rollout_path = home.path().join("rollout.jsonl"); + let thread_id = ThreadId::new(); + let security_risk = SecurityRiskScore { + category: "action_risk".to_string(), + score: 0.76, + }; + let security_risk_item = RolloutItem::SecurityRiskScore(security_risk.clone()); + for history_mode in [ThreadHistoryMode::Legacy, ThreadHistoryMode::Paginated] { + assert!(crate::is_persisted_rollout_item( + &security_risk_item, + history_mode + )); + } + + let mut file = File::create(&rollout_path)?; + for (ordinal, item) in [ + paginated_session_meta_item(thread_id, home.path()), + security_risk_item, + ] + .into_iter() + .enumerate() + { + let line = RolloutLine { + timestamp: "2026-07-09T00:00:00Z".to_string(), + ordinal: Some(ordinal as u64), + item, + }; + writeln!( + file, + "{}", + serde_json::to_string(&line).map_err(std::io::Error::other)? + )?; + } + + let (items, loaded_thread_id, parse_errors) = + RolloutRecorder::load_rollout_items(&rollout_path).await?; + + assert_eq!(loaded_thread_id, Some(thread_id)); + assert_eq!(parse_errors, 0); + assert_eq!(items.len(), 2); + let RolloutItem::SecurityRiskScore(persisted_security_risk) = &items[1] else { + panic!("expected security risk score rollout item"); + }; + assert_eq!(persisted_security_risk, &security_risk); + + Ok(()) +} + #[tokio::test] async fn load_rollout_items_filters_legacy_ghost_snapshots_from_compaction_history() -> std::io::Result<()> { diff --git a/codex-rs/rollout/src/search.rs b/codex-rs/rollout/src/search.rs index 6f0a30b1fd..72f3e54606 100644 --- a/codex-rs/rollout/src/search.rs +++ b/codex-rs/rollout/src/search.rs @@ -292,6 +292,7 @@ fn conversation_text_from_item(item: &RolloutItem) -> Option { | RolloutItem::InterAgentCommunication(_) | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::Compacted(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::WorldState(_) => None, } } diff --git a/codex-rs/state/src/extract.rs b/codex-rs/state/src/extract.rs index 68642283a4..f8a2abf499 100644 --- a/codex-rs/state/src/extract.rs +++ b/codex-rs/state/src/extract.rs @@ -26,6 +26,7 @@ pub fn apply_rollout_item( | RolloutItem::InterAgentCommunicationMetadata { .. } => {} RolloutItem::Compacted(_) => {} RolloutItem::WorldState(_) => {} + RolloutItem::SecurityRiskScore(_) => {} } if metadata.model_provider.is_empty() { metadata.model_provider = default_provider.to_string(); @@ -52,6 +53,7 @@ pub fn rollout_item_affects_thread_metadata(item: &RolloutItem) -> bool { | RolloutItem::InterAgentCommunication(_) | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::Compacted(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::WorldState(_) => false, } } diff --git a/codex-rs/state/src/runtime/threads.rs b/codex-rs/state/src/runtime/threads.rs index dc9a1c154a..31d414a50a 100644 --- a/codex-rs/state/src/runtime/threads.rs +++ b/codex-rs/state/src/runtime/threads.rs @@ -1293,6 +1293,7 @@ pub(super) fn extract_memory_mode(items: &[RolloutItem]) -> Option { | RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::EventMsg(_) => None, }) } diff --git a/codex-rs/thread-store/src/local/rollout_migration/canonicalizer.rs b/codex-rs/thread-store/src/local/rollout_migration/canonicalizer.rs index 6e2e8ca0ef..703d666ff8 100644 --- a/codex-rs/thread-store/src/local/rollout_migration/canonicalizer.rs +++ b/codex-rs/thread-store/src/local/rollout_migration/canonicalizer.rs @@ -292,6 +292,7 @@ impl LegacyRolloutCanonicalizer { } item @ (RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::TurnContext(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::WorldState(_)) => { self.write_item(writer, ×tamp, item).await?; } diff --git a/codex-rs/thread-store/src/local/rollout_migration/rollback_plan.rs b/codex-rs/thread-store/src/local/rollout_migration/rollback_plan.rs index efe492584e..ceee507db8 100644 --- a/codex-rs/thread-store/src/local/rollout_migration/rollback_plan.rs +++ b/codex-rs/thread-store/src/local/rollout_migration/rollback_plan.rs @@ -220,6 +220,7 @@ impl RollbackPlanner { } } RolloutItem::WorldState(_) => {} + RolloutItem::SecurityRiskScore(_) => self.record_boundaries[index] = None, } Ok(()) diff --git a/codex-rs/thread-store/src/local/rollout_migration/rollback_replay.rs b/codex-rs/thread-store/src/local/rollout_migration/rollback_replay.rs index b5f4ade103..744866eeb8 100644 --- a/codex-rs/thread-store/src/local/rollout_migration/rollback_replay.rs +++ b/codex-rs/thread-store/src/local/rollout_migration/rollback_replay.rs @@ -75,6 +75,7 @@ impl ModelReplayPlanner { | RolloutItem::ResponseItem(_) | RolloutItem::EventMsg(_) | RolloutItem::InterAgentCommunicationMetadata { .. } + | RolloutItem::SecurityRiskScore(_) | RolloutItem::WorldState(_) => return, }; self.records.push(record); diff --git a/codex-rs/thread-store/src/thread_metadata_sync.rs b/codex-rs/thread-store/src/thread_metadata_sync.rs index e5a2d34036..81dca9b000 100644 --- a/codex-rs/thread-store/src/thread_metadata_sync.rs +++ b/codex-rs/thread-store/src/thread_metadata_sync.rs @@ -304,6 +304,7 @@ impl ThreadMetadataSync { | RolloutItem::InterAgentCommunication(_) | RolloutItem::InterAgentCommunicationMetadata { .. } | RolloutItem::Compacted(_) + | RolloutItem::SecurityRiskScore(_) | RolloutItem::WorldState(_) => {} } }