Initialize execution environments with the final HTTP policy (#34995)

## Why

The TUI must inspect the default execution environment before loading its final
configuration. Initializing the environment manager at that point can give
startup services the bootstrap HTTP policy instead of the effective policy after
managed requirements are applied.

## What changed

- Split environment discovery from manager construction so callers can inspect
  the default environment without starting remote connections.
- Build the environment manager after final configuration loading and pass its
  resolved `HttpClientFactory` through all construction paths.
- Add shared test support for managers that use the legacy default HTTP policy.

## Testing

- Cover connection-free environment discovery and explicit HTTP policy
  propagation.
- Verify TUI startup services use the final managed `respect_system_proxy` value.

GitOrigin-RevId: 928fa31e6b4bcfbe1a121cade2f351427fdfa0f4
This commit is contained in:
Celia Chen
2026-07-23 19:24:41 +00:00
committed by copyberry
parent c769a05340
commit 265cd2e100
31 changed files with 582 additions and 131 deletions
@@ -2,12 +2,12 @@ use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use codex_exec_server::EnvironmentManager;
use codex_exec_server::EnvironmentReadyInfo;
use codex_exec_server::ExecServerError;
use codex_exec_server::NoiseChannelPublicKey;
use codex_exec_server::NoiseRendezvousConnectBundle;
use codex_exec_server::NoiseRendezvousConnectProvider;
use codex_exec_server_test_support::environment_manager_without_environments;
use codex_protocol::capabilities::CapabilityRootLocation;
use codex_protocol::capabilities::SelectedCapabilityRoot;
use codex_utils_path_uri::PathUri;
@@ -56,7 +56,7 @@ fn ready_info(root_id: &str, environment_id: &str) -> anyhow::Result<Environment
#[tokio::test]
async fn deferred_environment_waits_before_connecting() -> anyhow::Result<()> {
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let provider = Arc::new(FailingNoiseConnectProvider::default());
let registration =
manager.register_deferred_noise_environment("tools".to_string(), provider.clone())?;
@@ -85,7 +85,7 @@ async fn deferred_environment_waits_before_connecting() -> anyhow::Result<()> {
#[tokio::test]
async fn failure_and_dropped_registration_are_terminal() -> anyhow::Result<()> {
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let failed_provider = Arc::new(FailingNoiseConnectProvider::default());
let failed = manager
.register_deferred_noise_environment("failed".to_string(), failed_provider.clone())?;
@@ -118,7 +118,7 @@ async fn failure_and_dropped_registration_are_terminal() -> anyhow::Result<()> {
#[tokio::test]
async fn invalid_ready_info_is_terminal() -> anyhow::Result<()> {
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let provider = Arc::new(FailingNoiseConnectProvider::default());
let registration =
manager.register_deferred_noise_environment("tools".to_string(), provider.clone())?;
@@ -141,7 +141,7 @@ async fn invalid_ready_info_is_terminal() -> anyhow::Result<()> {
#[tokio::test]
async fn late_completion_is_isolated_from_replacement() -> anyhow::Result<()> {
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let old_provider = Arc::new(FailingNoiseConnectProvider::default());
let old_registration =
manager.register_deferred_noise_environment("tools".to_string(), old_provider.clone())?;
@@ -183,7 +183,7 @@ async fn late_completion_is_isolated_from_replacement() -> anyhow::Result<()> {
#[tokio::test]
async fn eager_noise_environment_connects_without_registration() -> anyhow::Result<()> {
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let provider = Arc::new(FailingNoiseConnectProvider::default());
manager.upsert_noise_environment("tools".to_string(), provider.clone())?;
let environment = manager.get_environment("tools").expect("environment");
+2 -2
View File
@@ -16,7 +16,6 @@ use base64::Engine as _;
use base64::engine::general_purpose::STANDARD;
use codex_api::AuthProvider;
use codex_exec_server::EnvironmentConnectionState;
use codex_exec_server::EnvironmentManager;
use codex_exec_server::EnvironmentReadyInfo;
use codex_exec_server::ExecParams;
use codex_exec_server::ExecResponse;
@@ -31,6 +30,7 @@ use codex_exec_server::NoiseRendezvousConnectBundle;
use codex_exec_server::NoiseRendezvousConnectProvider;
use codex_exec_server::ProcessId;
use codex_exec_server::RemoteEnvironmentConfig;
use codex_exec_server_test_support::environment_manager_without_environments;
use codex_protocol::capabilities::CapabilityRootLocation;
use codex_protocol::capabilities::SelectedCapabilityRoot;
use codex_utils_path_uri::PathUri;
@@ -156,7 +156,7 @@ async fn deferred_noise_environment_connects_and_reconnects_with_fresh_bundle()
executor_public_key: registered_executor_public_key(&registry).await?,
calls: AtomicUsize::new(0),
});
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let registration = manager
.register_deferred_noise_environment(ENVIRONMENT_ID.to_string(), provider.clone())?;
let environment = manager
@@ -5,7 +5,7 @@ mod common;
use std::collections::HashMap;
use std::sync::Arc;
use codex_exec_server::EnvironmentManager;
use codex_exec_server_test_support::environment_manager_without_environments;
use codex_protocol::capabilities::CapabilityRootLocation;
use codex_protocol::capabilities::SelectedCapabilityRoot;
use codex_utils_path_uri::PathUri;
@@ -15,7 +15,7 @@ use pretty_assertions::assert_eq;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn selected_capability_roots_use_captured_handle_after_replacement() -> anyhow::Result<()> {
let mut executor = exec_server().await?;
let manager = EnvironmentManager::without_environments();
let manager = environment_manager_without_environments();
let selected_root = SelectedCapabilityRoot {
id: "demo@1".to_string(),
location: CapabilityRootLocation::Environment {
@@ -0,0 +1,7 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "support",
crate_name = "codex_exec_server_test_support",
crate_srcs = glob(["*.rs"]),
)
@@ -0,0 +1,17 @@
[package]
name = "codex-exec-server-test-support"
version.workspace = true
edition.workspace = true
license.workspace = true
[lib]
path = "lib.rs"
test = false
doctest = false
[lints]
workspace = true
[dependencies]
codex-exec-server = { workspace = true }
codex-http-client = { workspace = true }
+10
View File
@@ -0,0 +1,10 @@
use codex_exec_server::EnvironmentManager;
use codex_http_client::HttpClientFactory;
use codex_http_client::OutboundProxyPolicy;
/// Builds a manager without environments using the legacy outbound HTTP policy.
pub fn environment_manager_without_environments() -> EnvironmentManager {
EnvironmentManager::without_environments(HttpClientFactory::new(
OutboundProxyPolicy::ReqwestDefault,
))
}