Reapply "Add app-server transport layer with websocket support" (#11370)

Reapply "Add app-server transport layer with websocket support" with
additional fixes from https://github.com/openai/codex/pull/11313/changes
to avoid deadlocking.

This reverts commit 47356ff83c.

## Summary

To avoid deadlocking when queues are full, we maintain separate tokio
tasks dedicated to incoming vs outgoing event handling
- split the app-server main loop into two tasks in
`run_main_with_transport`
   - inbound handling (`transport_event_rx`)
   - outbound handling (`outgoing_rx` + `thread_created_rx`)
- separate incoming and outgoing websocket tasks

## Validation

Integration tests, testing thoroughly e2e in codex app w/ >10 concurrent
requests

<img width="1365" height="979" alt="Screenshot 2026-02-10 at 2 54 22 PM"
src="https://github.com/user-attachments/assets/47ca2c13-f322-4e5c-bedd-25859cbdc45f"
/>

---------

Co-authored-by: jif-oai <jif@openai.com>
This commit is contained in:
Max Johnson
2026-02-11 10:13:39 -08:00
committed by GitHub
parent 577a416f9a
commit 7053aa5457
19 changed files with 1940 additions and 388 deletions

View File

@@ -371,25 +371,6 @@ async fn review_does_not_emit_agent_message_on_structured_output() {
_ => false,
})
.await;
// On slower CI hosts, the final AgentMessage can arrive immediately after
// TurnComplete. Drain a brief tail window to make ordering nondeterminism
// harmless while still enforcing "exactly one final AgentMessage".
while let Ok(Ok(event)) =
tokio::time::timeout(std::time::Duration::from_millis(200), codex.next_event()).await
{
match event.msg {
EventMsg::AgentMessage(_) => agent_messages += 1,
EventMsg::EnteredReviewMode(_) => saw_entered = true,
EventMsg::ExitedReviewMode(_) => saw_exited = true,
EventMsg::AgentMessageContentDelta(_) => {
panic!("unexpected AgentMessageContentDelta surfaced during review")
}
EventMsg::AgentMessageDelta(_) => {
panic!("unexpected AgentMessageDelta surfaced during review")
}
_ => {}
}
}
assert_eq!(1, agent_messages, "expected exactly one AgentMessage event");
assert!(saw_entered && saw_exited, "missing review lifecycle events");