diff --git a/codex-rs/core/src/unified_exec/process_manager.rs b/codex-rs/core/src/unified_exec/process_manager.rs index 73afca2da8..9db43f630e 100644 --- a/codex-rs/core/src/unified_exec/process_manager.rs +++ b/codex-rs/core/src/unified_exec/process_manager.rs @@ -382,6 +382,20 @@ impl UnifiedExecProcessManager { (Arc::new(process), deferred_network_approval) } Err(err) => { + let message = err.to_string(); + emit_failed_exec_end_for_unified_exec( + Arc::clone(&context.session), + Arc::clone(&context.turn), + context.call_id.clone(), + request.command.clone(), + cwd, + Some(request.process_id.to_string()), + Arc::new(tokio::sync::Mutex::new(HeadTailBuffer::default())), + String::new(), + message, + Duration::ZERO, + ) + .await; self.release_process_id(request.process_id).await; return Err(err); } diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index bcc93d7d09..77a71942ac 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -745,11 +745,16 @@ impl TestCodex { }) .await?; - let turn_id = wait_for_event_match(&self.codex, |event| match event { - EventMsg::TurnStarted(event) => Some(event.turn_id.clone()), - _ => None, - }) - .await; + let turn_id = match wait_for_event_with_timeout( + &self.codex, + |event| matches!(event, EventMsg::TurnStarted(_)), + SUBMIT_TURN_COMPLETE_TIMEOUT, + ) + .await + { + EventMsg::TurnStarted(event) => event.turn_id, + _ => unreachable!("predicate only matches TurnStarted"), + }; wait_for_event_with_timeout( &self.codex, |event| match event { diff --git a/codex-rs/core/tests/suite/realtime_conversation.rs b/codex-rs/core/tests/suite/realtime_conversation.rs index ff273a77c4..c4da422096 100644 --- a/codex-rs/core/tests/suite/realtime_conversation.rs +++ b/codex-rs/core/tests/suite/realtime_conversation.rs @@ -662,7 +662,7 @@ async fn conversation_webrtc_close_while_sideband_connecting_drops_pending_join( let realtime_server = start_websocket_server_with_headers(vec![WebSocketConnectionConfig { requests: vec![vec![]], response_headers: Vec::new(), - accept_delay: Some(Duration::from_millis(500)), + accept_delay: Some(Duration::from_secs(5)), close_after_requests: false, }]) .await; diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index c9ad846493..0949e9fb76 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -794,6 +794,8 @@ async fn unified_exec_network_denial_emits_failed_background_end_event() -> Resu let call_id = "uexec-network-denied"; let args = json!({ "cmd": "python3 -c \"import os, socket, time, urllib.parse; time.sleep(0.3); proxy = urllib.parse.urlparse(os.environ['HTTP_PROXY']); sock = socket.create_connection((proxy.hostname, proxy.port), timeout=2); sock.sendall(b'GET http://codex-network-denied.invalid/ HTTP/1.1\\r\\nHost: codex-network-denied.invalid\\r\\n\\r\\n'); sock.recv(1024); time.sleep(5)\"", + "shell": "/bin/sh", + "login": false, "yield_time_ms": 50, }); let response_mock = @@ -837,6 +839,8 @@ async fn unified_exec_short_lived_network_denial_emits_failed_end_event() -> Res let call_id = "uexec-short-network-denied"; let args = json!({ "cmd": "python3 -c \"import os, socket, urllib.parse; proxy = urllib.parse.urlparse(os.environ['HTTP_PROXY']); sock = socket.create_connection((proxy.hostname, proxy.port), timeout=2); sock.sendall(b'GET http://codex-short-network-denied.invalid/ HTTP/1.1\\r\\nHost: codex-short-network-denied.invalid\\r\\n\\r\\n'); sock.recv(1024)\"", + "shell": "/bin/sh", + "login": false, "yield_time_ms": 1000, }); let response_mock =