mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
## Why Large nested-tool callbacks can occupy a WebSocket and delay unrelated session operations on the same code-mode connection. ## What changed - Negotiate the optional `dual-websocket-v1` capability and pair a second, token-scoped WebSocket with the control connection. - Route nested-tool callbacks and their results over the bulk socket while keeping session operations, notifications, and execution responses on the control socket. Reject messages sent on the wrong lane. - Preserve the single-connection transport when the capability is unavailable, and bound pairing, queued callbacks, and deferred cross-socket messages. - Defer callbacks that arrive before their execution-started response, and return delegate errors without disconnecting the connection. ## Testing Add protocol, transport, driver, and WebSocket integration coverage for capability negotiation, lane routing, pairing failures, out-of-order messages, and progress during large concurrent tool results. GitOrigin-RevId: fa4504653e7cbf3c4ec930ae57aa0a41345bad66
79 lines
2.3 KiB
Rust
79 lines
2.3 KiB
Rust
use std::net::SocketAddr;
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::BulkConnectionRegistry;
|
|
use super::ListenTransport;
|
|
use super::MAX_PENDING_BULK_CONNECTIONS;
|
|
use super::parse_listen_url;
|
|
|
|
#[test]
|
|
fn bulk_connection_registration_cleans_up_when_dropped() {
|
|
let registry = BulkConnectionRegistry::default();
|
|
let registration = registry.reserve().expect("bulk connection registration");
|
|
let token = registration.token();
|
|
|
|
drop(registration);
|
|
|
|
assert!(registry.remove(token).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn bulk_connection_registrations_are_bounded_and_released() {
|
|
let registry = BulkConnectionRegistry::default();
|
|
let mut registrations = (0..MAX_PENDING_BULK_CONNECTIONS)
|
|
.map(|_| registry.reserve().expect("bulk connection registration"))
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(registry.reserve().is_none());
|
|
drop(registrations.pop());
|
|
assert!(registry.reserve().is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn parse_listen_url_accepts_stdio_transports() {
|
|
assert_eq!(
|
|
parse_listen_url("stdio").expect("stdio listen URL should parse"),
|
|
ListenTransport::Stdio
|
|
);
|
|
assert_eq!(
|
|
parse_listen_url("stdio://").expect("stdio URL should parse"),
|
|
ListenTransport::Stdio
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_listen_url_accepts_websocket_addresses() {
|
|
assert_eq!(
|
|
parse_listen_url("ws://127.0.0.1:0").expect("websocket listen URL should parse"),
|
|
ListenTransport::WebSocket(
|
|
"127.0.0.1:0"
|
|
.parse::<SocketAddr>()
|
|
.expect("valid socket address")
|
|
)
|
|
);
|
|
assert_eq!(
|
|
parse_listen_url("ws://[::1]:9000").expect("IPv6 websocket listen URL should parse"),
|
|
ListenTransport::WebSocket(
|
|
"[::1]:9000"
|
|
.parse::<SocketAddr>()
|
|
.expect("valid IPv6 socket address")
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_listen_url_rejects_invalid_transports() {
|
|
let invalid_address = parse_listen_url("ws://localhost:9000")
|
|
.expect_err("websocket listener requires an IP address");
|
|
assert!(
|
|
invalid_address
|
|
.to_string()
|
|
.contains("expected `ws://IP:PORT`")
|
|
);
|
|
|
|
let unsupported =
|
|
parse_listen_url("http://127.0.0.1:9000").expect_err("HTTP is not a listen transport");
|
|
assert!(unsupported.to_string().contains("unsupported --listen URL"));
|
|
}
|