mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Preserve HTTP fallback for delegated sessions (#38681)
## Why Responses WebSocket fallback is session-scoped. A delegated session created after its parent switched to HTTP could otherwise make another WebSocket connection attempt. ## What changed Disable WebSocket support for a delegated session when the parent session has already fallen back to HTTP. ## Testing Add a guardian review regression test that verifies only the parent's initial WebSocket attempt occurs and the guardian request uses the HTTP response path. GitOrigin-RevId: 1345f8fe739f55975aef432045348eae5a9278d6
This commit is contained in:
@@ -66,6 +66,10 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
));
|
||||
}
|
||||
config.permissions.approval_policy = Constrained::allow_only(AskForApproval::Never);
|
||||
config.model_provider.supports_websockets &= parent_session
|
||||
.services
|
||||
.model_client
|
||||
.responses_websocket_enabled();
|
||||
|
||||
let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
let (tx_ops, rx_ops) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
|
||||
@@ -72,6 +72,11 @@ use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use tempfile::TempDir;
|
||||
use test_case::test_case;
|
||||
use wiremock::Mock;
|
||||
use wiremock::ResponseTemplate;
|
||||
use wiremock::http::Method;
|
||||
use wiremock::matchers::method;
|
||||
use wiremock::matchers::path_regex;
|
||||
|
||||
const CURRENT_TIME_AT: i64 = 1_781_717_655;
|
||||
|
||||
@@ -112,6 +117,78 @@ impl TimeProvider for RecordingTimeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn guardian_session_inherits_parent_http_fallback() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
skip_if_wine_exec!(
|
||||
Ok(()),
|
||||
"Guardian approval actions require host-native paths"
|
||||
);
|
||||
|
||||
let server = start_mock_server().await;
|
||||
Mock::given(method("GET"))
|
||||
.and(path_regex(".*/responses$"))
|
||||
.respond_with(ResponseTemplate::new(426))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let responses = mount_sse_sequence(
|
||||
&server,
|
||||
vec![
|
||||
sse(vec![
|
||||
ev_function_call(
|
||||
"call",
|
||||
"exec_command",
|
||||
r#"{"cmd":"true","sandbox_permissions":"require_escalated","justification":"test"}"#,
|
||||
),
|
||||
ev_completed("parent-tool"),
|
||||
]),
|
||||
sse(vec![
|
||||
ev_assistant_message("guardian", r#"{"outcome":"deny"}"#),
|
||||
ev_completed("guardian-review"),
|
||||
]),
|
||||
sse(vec![ev_completed("parent-complete")]),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut builder = test_codex().with_config(|config| {
|
||||
config.model_provider.supports_websockets = true;
|
||||
config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
|
||||
config.approvals_reviewer = ApprovalsReviewer::User;
|
||||
});
|
||||
let test = builder.build_with_auto_env(&server).await?;
|
||||
|
||||
test.codex
|
||||
.start_or_steer_turn(
|
||||
TurnInputRequest::user_input(vec![UserInput::Text {
|
||||
text: "run a command".into(),
|
||||
text_elements: Vec::new(),
|
||||
}])
|
||||
.with_thread_settings(ThreadSettingsOverrides {
|
||||
approvals_reviewer: Some(ApprovalsReviewer::AutoReview),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
wait_for_event(&test.codex, |event| {
|
||||
matches!(event, EventMsg::TurnComplete(_))
|
||||
})
|
||||
.await;
|
||||
|
||||
let requests = server.received_requests().await.unwrap_or_default();
|
||||
let websocket_attempts = requests
|
||||
.iter()
|
||||
.filter(|request| request.method == Method::GET)
|
||||
.count();
|
||||
assert_eq!(websocket_attempts, 1);
|
||||
assert!(responses.requests().iter().any(|request| {
|
||||
request.body_json()["client_metadata"]["x-openai-subagent"].as_str() == Some("guardian")
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[test_case(CodexAuth::from_api_key("test-api-key"), "gpt-5.6-luna"; "api_key_uses_luna_with_responses_lite")]
|
||||
#[test_case(CodexAuth::create_dummy_chatgpt_auth_for_testing(), "codex-auto-review"; "chatgpt_uses_codex_auto_review")]
|
||||
|
||||
Reference in New Issue
Block a user