mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
Preserve Guardian stream-disconnect classification
This commit is contained in:
@@ -910,6 +910,7 @@ fn should_retry_guardian_review(outcome: &GuardianReviewOutcome) -> bool {
|
||||
#[cfg(test)]
|
||||
mod review_tests {
|
||||
use super::*;
|
||||
use codex_protocol::error::CodexErr;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
@@ -1014,6 +1015,23 @@ mod review_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guardian_review_retries_codex_stream_disconnect_errors() {
|
||||
let error_event = CodexErr::Stream(
|
||||
"WebSocket protocol error: Connection reset without closing handshake".to_string(),
|
||||
None,
|
||||
)
|
||||
.to_error_event(Some("Error running remote compact task".to_string()));
|
||||
let outcome = GuardianReviewOutcome::Error(GuardianReviewError::session_with_error_info(
|
||||
anyhow::anyhow!(error_event.message),
|
||||
error_event
|
||||
.codex_error_info
|
||||
.expect("stream disconnect should preserve structured error info"),
|
||||
));
|
||||
|
||||
assert!(should_retry_guardian_review(&outcome));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn guardian_review_retry_wait_honors_cancellation() {
|
||||
let cancel_token = CancellationToken::new();
|
||||
|
||||
@@ -1066,6 +1066,7 @@ async fn interrupt_and_drain_turn(codex: &Codex, expected_turn_id: &str) -> anyh
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_protocol::error::CodexErr;
|
||||
use codex_protocol::protocol::AgentStatus;
|
||||
use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::Submission;
|
||||
@@ -1652,6 +1653,59 @@ mod tests {
|
||||
assert!(capture_token_usage);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wait_for_guardian_review_preserves_remote_compact_stream_disconnect() {
|
||||
let (review_session, tx_event, _rx_sub) = test_review_session().await;
|
||||
let stream_error = CodexErr::Stream(
|
||||
"WebSocket protocol error: Connection reset without closing handshake".to_string(),
|
||||
None,
|
||||
);
|
||||
tx_event
|
||||
.send(Event {
|
||||
id: "current-turn".to_string(),
|
||||
msg: EventMsg::Error(
|
||||
stream_error
|
||||
.to_error_event(Some("Error running remote compact task".to_string())),
|
||||
),
|
||||
})
|
||||
.await
|
||||
.expect("queue guardian error");
|
||||
tx_event
|
||||
.send(turn_complete_event(
|
||||
"current-turn",
|
||||
/*last_agent_message*/ None,
|
||||
Some(42),
|
||||
))
|
||||
.await
|
||||
.expect("queue current turn completion");
|
||||
|
||||
let mut analytics_result = GuardianReviewAnalyticsResult::without_session();
|
||||
let (outcome, keep_review_session, capture_token_usage) = wait_for_guardian_review(
|
||||
&review_session,
|
||||
"current-turn",
|
||||
tokio::time::Instant::now() + Duration::from_secs(1),
|
||||
/*external_cancel*/ None,
|
||||
&mut analytics_result,
|
||||
)
|
||||
.await;
|
||||
|
||||
let GuardianReviewSessionOutcome::SessionFailed { error, error_info } = outcome else {
|
||||
panic!("expected structured session failure");
|
||||
};
|
||||
assert_eq!(
|
||||
error.to_string(),
|
||||
"Error running remote compact task: stream disconnected before completion: WebSocket protocol error: Connection reset without closing handshake"
|
||||
);
|
||||
assert_eq!(
|
||||
error_info,
|
||||
Some(CodexErrorInfo::ResponseStreamDisconnected {
|
||||
http_status_code: None,
|
||||
})
|
||||
);
|
||||
assert!(keep_review_session);
|
||||
assert!(capture_token_usage);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wait_for_guardian_review_ignores_prior_turn_aborts() {
|
||||
let (review_session, tx_event, _rx_sub) = test_review_session().await;
|
||||
|
||||
@@ -228,6 +228,9 @@ impl CodexErr {
|
||||
CodexErr::RetryLimit(_) => CodexErrorInfo::ResponseTooManyFailedAttempts {
|
||||
http_status_code: self.http_status_code_value(),
|
||||
},
|
||||
CodexErr::Stream(..) => CodexErrorInfo::ResponseStreamDisconnected {
|
||||
http_status_code: self.http_status_code_value(),
|
||||
},
|
||||
CodexErr::ConnectionFailed(_) => CodexErrorInfo::HttpConnectionFailed {
|
||||
http_status_code: self.http_status_code_value(),
|
||||
},
|
||||
|
||||
@@ -190,6 +190,27 @@ fn to_error_event_handles_response_stream_failed() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_error_event_handles_response_stream_disconnected() {
|
||||
let err = CodexErr::Stream(
|
||||
"WebSocket protocol error: Connection reset without closing handshake".to_string(),
|
||||
None,
|
||||
);
|
||||
|
||||
let event = err.to_error_event(Some("Error running remote compact task".to_string()));
|
||||
|
||||
assert_eq!(
|
||||
event.message,
|
||||
"Error running remote compact task: stream disconnected before completion: WebSocket protocol error: Connection reset without closing handshake"
|
||||
);
|
||||
assert_eq!(
|
||||
event.codex_error_info,
|
||||
Some(CodexErrorInfo::ResponseStreamDisconnected {
|
||||
http_status_code: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_denied_reports_exit_code_when_no_output_available() {
|
||||
let output = ExecToolCallOutput {
|
||||
|
||||
Reference in New Issue
Block a user