Tie remote exec servers to their parent stdin (#35843)

## What changed

- Add `--exit-on-stdin-close` and the `CODEX_EXEC_SERVER_EXIT_ON_STDIN_CLOSE` environment variable as opt-in controls for remote exec servers.
- Gracefully drain active sessions and processes when the parent closes stdin, then flush telemetry before exiting.
- Remove the parent-lifetime environment variable from child process environments.

## Testing

- Cover parent disconnects after signal-listener failures.
- Exercise remote shutdown end to end, including child termination and final telemetry metrics.
- Verify that explicitly disabling the environment variable preserves local exec-server behavior.

GitOrigin-RevId: 63063bc097b54684c370bd545cd32d17c4e55d90
This commit is contained in:
Alexi Christakis authored and copyberry committed 2026-07-28 23:27:46 +00:00
1 parent 9f4c20aadc
commit 166658a34a
9 files changed
+475 -27

No files matched your search

+41 -12
View File
@@ -587,6 +587,14 @@ struct ExecServerCommand {
/// Use Agent Identity auth from CODEX_ACCESS_TOKEN for remote registration.
#[arg(long = "use-agent-identity-auth", requires = "remote")]
use_agent_identity_auth: bool,
/// Exit when the parent-owned standard-input pipe closes.
#[arg(
long = "exit-on-stdin-close",
env = codex_exec_server::CODEX_EXEC_SERVER_EXIT_ON_STDIN_CLOSE_ENV_VAR,
requires_if("true", "remote")
)]
exit_on_stdin_close: bool,
}
#[derive(Debug, clap::Subcommand)]
@@ -1724,9 +1732,26 @@ async fn run_exec_server_command(
remote_config.name = name;
}
let remote_config = remote_config.with_telemetry(telemetry);
exec_server_telemetry::run_until_shutdown(async move {
codex_exec_server::run_remote_environment(remote_config, runtime_paths).await
})
let parent_lifetime = if cmd.exit_on_stdin_close {
exec_server_telemetry::ParentLifetime::StdinPipe
} else {
exec_server_telemetry::ParentLifetime::Independent
};
let (shutdown_sender, shutdown_receiver) = tokio::sync::oneshot::channel();
exec_server_telemetry::run_until_shutdown(
async move {
codex_exec_server::run_remote_environment_until_shutdown(
remote_config,
runtime_paths,
async move {
let _ = shutdown_receiver.await;
},
)
.await
},
parent_lifetime,
exec_server_telemetry::ShutdownBehavior::Graceful(shutdown_sender),
)
.await?;
Ok(())
} else {
@@ -1748,15 +1773,19 @@ async fn run_exec_server_command(
let listen_url = cmd
.listen
.unwrap_or_else(|| codex_exec_server::DEFAULT_LISTEN_URL.to_string());
exec_server_telemetry::run_until_shutdown(async move {
codex_exec_server::run_main_with_telemetry(
&listen_url,
runtime_paths,
telemetry,
http_client_factory,
)
.await
})
exec_server_telemetry::run_until_shutdown(
async move {
codex_exec_server::run_main_with_telemetry(
&listen_url,
runtime_paths,
telemetry,
http_client_factory,
)
.await
},
exec_server_telemetry::ParentLifetime::Independent,
exec_server_telemetry::ShutdownBehavior::Immediate,
)
.await
.map_err(anyhow::Error::from_boxed)
}