mirror of
https://github.com/openai/codex.git
synced 2026-11-10 00:42:33 +00:00
Cache executor metadata from initialization (#40343)
## What changed - Include `environmentInfo` in the exec-server `initialize` response and seed the Rust client's metadata cache from it. - Fall back to one lazy `environment/info` request when connecting to older servers that omit initialization metadata, then share the cached result across client clones and session recovery. - Keep app-server `environment/info` requests uncached so they continue to probe the executor. ## Testing - Cover initialization metadata, the legacy-server fallback, shared caching, accepted WebSocket environments, and repeated uncached app-server probes. GitOrigin-RevId: 380132d9db183998d73e8c6b0954e4d70c7e8406
This commit is contained in:
1 parent
068c49f075
commit
2df6705423
14 files changed
+249
-13
No files matched your search
@@ -11,18 +11,91 @@ use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use futures::SinkExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use tempfile::TempDir;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::time::timeout;
|
||||
use tokio_tungstenite::accept_async;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
|
||||
use super::exec_server_test_support::accept_exec_server_environment;
|
||||
use super::exec_server_test_support::read_exec_server_json;
|
||||
|
||||
const RPC_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const INVALID_REQUEST_ERROR_CODE: i64 = -32600;
|
||||
const INTERNAL_ERROR_CODE: i64 = -32603;
|
||||
|
||||
#[tokio::test]
|
||||
async fn environment_info_probes_executor_even_when_metadata_is_cached() -> Result<()> {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
||||
let exec_server_url = format!("ws://{}", listener.local_addr()?);
|
||||
let exec_server = tokio::spawn(async move {
|
||||
let (stream, _) = listener.accept().await?;
|
||||
let mut websocket = accept_async(stream).await?;
|
||||
let initialize = read_exec_server_json(&mut websocket).await?;
|
||||
assert_eq!(initialize["method"], "initialize");
|
||||
let response = json!({
|
||||
"id": initialize["id"],
|
||||
"result": {
|
||||
"sessionId": "test-session",
|
||||
"environmentInfo": {"shell": {"name": "bash", "path": "/bin/bash"}},
|
||||
},
|
||||
});
|
||||
websocket
|
||||
.send(Message::Text(response.to_string().into()))
|
||||
.await?;
|
||||
let initialized = read_exec_server_json(&mut websocket).await?;
|
||||
assert_eq!(initialized["method"], "initialized");
|
||||
|
||||
for _ in 0..2 {
|
||||
let request = read_exec_server_json(&mut websocket).await?;
|
||||
assert_eq!(request["method"], "environment/info");
|
||||
let response = json!({
|
||||
"id": request["id"],
|
||||
"error": {"code": INTERNAL_ERROR_CODE, "message": "executor unavailable"},
|
||||
});
|
||||
websocket
|
||||
.send(Message::Text(response.to_string().into()))
|
||||
.await?;
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
});
|
||||
let mut app_server = TestAppServer::builder().without_auto_env().build().await?;
|
||||
app_server.initialize().await?;
|
||||
add_environment(
|
||||
&mut app_server,
|
||||
&exec_server_url,
|
||||
/*connect_timeout_ms*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
for _ in 0..2 {
|
||||
let request_id = app_server
|
||||
.send_raw_request(
|
||||
"environment/info",
|
||||
Some(json!({"environmentId": "remote-a"})),
|
||||
)
|
||||
.await?;
|
||||
let error = timeout(
|
||||
RPC_TIMEOUT,
|
||||
app_server.read_stream_until_error_message(RequestId::Integer(request_id)),
|
||||
)
|
||||
.await??;
|
||||
assert_eq!(error, JSONRPCError {
|
||||
id: RequestId::Integer(request_id),
|
||||
error: JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: "failed to get info for environment `remote-a`: exec-server rejected request (-32603): executor unavailable".to_string(),
|
||||
data: None,
|
||||
},
|
||||
});
|
||||
}
|
||||
timeout(RPC_TIMEOUT, exec_server).await???;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn environment_info_returns_remote_environment_info() -> Result<()> {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
||||
|
||||
@@ -332,6 +332,11 @@ async fn legacy_executor_skips_required_browser_and_keeps_host_owned_mcp() -> Re
|
||||
let mut message = message?;
|
||||
if let Message::Text(text) = &message {
|
||||
let mut response: Value = serde_json::from_str(text)?;
|
||||
if let Some(result) = response.get_mut("result").and_then(Value::as_object_mut)
|
||||
{
|
||||
result.remove("environmentInfo");
|
||||
message = Message::Text(response.to_string().into());
|
||||
}
|
||||
if let Some(capabilities) = response
|
||||
.pointer_mut("/result/capabilities")
|
||||
.and_then(Value::as_object_mut)
|
||||
|
||||
Reference in new issue
Block a user