Report the exec-server release version in environment info (#42270)

## Why

Clients need the executor's release version to make version-based compatibility decisions.

## What changed

- Add `executorVersion` to `EnvironmentInfo` and return it from both `initialize` and `environment/info`.
- Resolve the package release version at process startup so the advertised value remains stable for the server's lifetime.
- Use `0.0.0` when the version is unknown or omitted by a legacy executor.

## Testing

- Cover packaged and source-build initialization, startup caching, protocol serialization, and client-side metadata caching.

GitOrigin-RevId: a0c7f0b701a8e7e50345cd5fb1b5c5d2d38ff3b1
This commit is contained in:
felixxia-oai
2026-09-02 12:07:32 +00:00
committed by copyberry
parent a0dcfe2ada
commit 8d32abcd01
15 changed files with 131 additions and 28 deletions
+21 -1
View File
@@ -19,6 +19,7 @@ use std::time::Instant;
use anyhow::Context;
use anyhow::Result;
use codex_exec_server::EnvironmentInfo;
use codex_exec_server::ExecParams;
use codex_exec_server::ExecServerClient;
use codex_exec_server::NoiseChannelIdentity;
@@ -32,6 +33,7 @@ use futures::SinkExt;
use futures::StreamExt;
use predicates::prelude::PredicateBooleanExt;
use predicates::str::contains;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncReadExt;
@@ -175,7 +177,15 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr
"#
),
)?;
let mut command = tokio::process::Command::new(codex_utils_cargo_bin::cargo_bin("codex")?);
let package = TempDir::new()?;
let bin_dir = package.path().join("bin");
std::fs::create_dir(&bin_dir)?;
let executable = bin_dir.join(format!("codex{}", std::env::consts::EXE_SUFFIX));
std::fs::copy(codex_utils_cargo_bin::cargo_bin("codex")?, &executable)?;
let manifest = package.path().join("codex-package.json");
std::fs::write(&manifest, r#"{"version":"1.2.3-alpha.4"}"#)?;
let mut command = tokio::process::Command::new(executable);
command
.env("CODEX_HOME", codex_home.path())
.env("CODEX_API_KEY", "test-api-key")
@@ -205,6 +215,8 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr
.ok_or_else(|| anyhow::anyhow!("remote exec-server stdin was not piped"))?;
let environment_websocket = accept_parent_lifetime_websocket(&listener, TEST_TIMEOUT).await?;
// Remote startup must capture the version before registration, not on the first initialize.
std::fs::write(&manifest, r#"{"version":"9.9.9"}"#)?;
let executor_public_key = registered_parent_lifetime_executor_public_key(&registry).await?;
let harness_args = NoiseRendezvousConnectArgs {
bundle: NoiseRendezvousConnectBundle {
@@ -232,6 +244,14 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr
.await
.context("remote harness did not connect")???;
let expected_info = EnvironmentInfo {
executor_version: "1.2.3-alpha.4".to_string(),
..EnvironmentInfo::local()
};
assert_eq!(client.environment_info().await?, expected_info);
std::fs::remove_file(&manifest)?;
assert_eq!(client.force_environment_info().await?, expected_info);
#[cfg(windows)]
let argv = vec![
"cmd.exe",