@@ -38,6 +38,12 @@ const GUARDIAN_REJECTION_INSTRUCTIONS: &str = concat!(
3838 "Otherwise, stop and request user input." ,
3939) ;
4040
41+ const GUARDIAN_TIMEOUT_INSTRUCTIONS : & str = concat ! (
42+ "The automatic approval review did not finish before its deadline. " ,
43+ "Do not assume the action is unsafe based on the timeout alone. " ,
44+ "You may retry once with a narrower or simpler request, or ask the user for guidance or explicit approval." ,
45+ ) ;
46+
4147pub ( crate ) fn new_guardian_review_id ( ) -> String {
4248 uuid:: Uuid :: new_v4 ( ) . to_string ( )
4349}
@@ -63,6 +69,10 @@ pub(crate) async fn guardian_rejection_message(session: &Session, review_id: &st
6369 }
6470}
6571
72+ pub ( crate ) fn guardian_timeout_message ( ) -> String {
73+ GUARDIAN_TIMEOUT_INSTRUCTIONS . to_string ( )
74+ }
75+
6676#[ derive( Debug ) ]
6777pub ( super ) enum GuardianReviewOutcome {
6878 Completed ( anyhow:: Result < GuardianAssessment > ) ,
@@ -97,8 +107,9 @@ pub(crate) fn is_guardian_reviewer_source(
97107 )
98108}
99109
100- /// This function always fails closed: any timeout, review-session failure, or
101- /// parse failure is treated as a high-risk denial.
110+ /// This function always fails closed: timeouts, review-session failures, and
111+ /// parse failures all block execution, but timeouts are still surfaced to the
112+ /// caller as distinct from explicit guardian denials.
102113async fn run_guardian_review (
103114 session : Arc < Session > ,
104115 turn : Arc < TurnContext > ,
@@ -170,14 +181,36 @@ async fn run_guardian_review(
170181 outcome : GuardianAssessmentOutcome :: Deny ,
171182 rationale : format ! ( "Automatic approval review failed: {err}" ) ,
172183 } ,
173- GuardianReviewOutcome :: TimedOut => GuardianAssessment {
174- risk_level : GuardianRiskLevel :: High ,
175- user_authorization : GuardianUserAuthorization :: Unknown ,
176- outcome : GuardianAssessmentOutcome :: Deny ,
177- rationale :
184+ GuardianReviewOutcome :: TimedOut => {
185+ let rationale =
178186 "Automatic approval review timed out while evaluating the requested approval."
179- . to_string ( ) ,
180- } ,
187+ . to_string ( ) ;
188+ session
189+ . send_event (
190+ turn. as_ref ( ) ,
191+ EventMsg :: Warning ( WarningEvent {
192+ message : rationale. clone ( ) ,
193+ } ) ,
194+ )
195+ . await ;
196+ session
197+ . send_event (
198+ turn. as_ref ( ) ,
199+ EventMsg :: GuardianAssessment ( GuardianAssessmentEvent {
200+ id : review_id,
201+ target_item_id,
202+ turn_id : assessment_turn_id,
203+ status : GuardianAssessmentStatus :: TimedOut ,
204+ risk_level : None ,
205+ user_authorization : None ,
206+ rationale : Some ( rationale) ,
207+ decision_source : Some ( GuardianAssessmentDecisionSource :: Agent ) ,
208+ action : terminal_action,
209+ } ) ,
210+ )
211+ . await ;
212+ return ReviewDecision :: TimedOut ;
213+ }
181214 GuardianReviewOutcome :: Aborted => {
182215 session
183216 . send_event (
0 commit comments