mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Run code mode exclusively through the standalone host (#36217)
## What changed - Move the V8 implementation into a dedicated `codex-code-mode-runtime` crate used by `codex-code-mode-host`, removing the embedded runtime fallback from the Codex process. - Resolve the host executable from the active installation layout and check its availability before selecting tools. - Fall back to direct tools with a one-time warning when optional code mode is unavailable. Keep `code_mode_only` and `disable_in_process_fallback` configurations fail-closed. ## Testing - Cover host discovery for standalone and package layouts, including missing hosts and symlinks. - Verify direct-tool fallback, one-time warnings, and fail-closed code-mode-only behavior. GitOrigin-RevId: 5aa3c6f1db148b2231fc24089a2ee0e2b00dbddb
This commit is contained in:
committed by
copyberry
parent
acd540f158
commit
97576b1794
@@ -1,4 +1,3 @@
|
||||
use std::ffi::OsString;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -20,6 +19,7 @@ use codex_code_mode_protocol::WaitRequest;
|
||||
use codex_code_mode_protocol::host::SessionId;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::OutboundProxyPolicy;
|
||||
use codex_install_context::InstallContext;
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::sync::watch;
|
||||
|
||||
@@ -31,87 +31,74 @@ use crate::NoopCodeModeSessionDelegate;
|
||||
|
||||
mod connection;
|
||||
|
||||
const CODE_MODE_HOST_PATH_ENV: &str = "CODEX_CODE_MODE_HOST_PATH";
|
||||
|
||||
type ShutdownResultReceiver = watch::Receiver<Option<Result<(), String>>>;
|
||||
|
||||
/// Creates code-mode sessions backed by one lazily spawned process host.
|
||||
pub struct ProcessOwnedCodeModeSessionProvider {
|
||||
state: StdMutex<ProviderState>,
|
||||
allow_in_process_fallback: bool,
|
||||
host: Arc<OwnedCodeModeHost>,
|
||||
}
|
||||
|
||||
/// Rejects code-mode sessions when the standalone host is disabled.
|
||||
#[derive(Default)]
|
||||
pub struct DisabledCodeModeSessionProvider;
|
||||
|
||||
/// Creates code-mode sessions backed by one shared remote WebSocket connection.
|
||||
pub struct WebSocketCodeModeSessionProvider {
|
||||
host: Arc<OwnedCodeModeHost>,
|
||||
}
|
||||
|
||||
enum ProviderState {
|
||||
OwnedProcess(Arc<OwnedCodeModeHost>),
|
||||
InProcess,
|
||||
}
|
||||
|
||||
impl ProcessOwnedCodeModeSessionProvider {
|
||||
pub fn with_host_program(host_program: PathBuf) -> Self {
|
||||
Self {
|
||||
state: StdMutex::new(ProviderState::OwnedProcess(Arc::new(
|
||||
OwnedCodeModeHost::new(host_program),
|
||||
))),
|
||||
allow_in_process_fallback: true,
|
||||
host: Arc::new(OwnedCodeModeHost::new(host_program)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn without_in_process_fallback(mut self) -> Self {
|
||||
self.allow_in_process_fallback = false;
|
||||
self
|
||||
}
|
||||
|
||||
fn process_host(&self) -> Option<Arc<OwnedCodeModeHost>> {
|
||||
match &*self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
{
|
||||
ProviderState::OwnedProcess(process_host) => Some(Arc::clone(process_host)),
|
||||
ProviderState::InProcess => None,
|
||||
}
|
||||
fn process_host(&self) -> Arc<OwnedCodeModeHost> {
|
||||
Arc::clone(&self.host)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProcessOwnedCodeModeSessionProvider {
|
||||
fn default() -> Self {
|
||||
Self::with_host_program(default_host_program())
|
||||
Self::with_host_program(InstallContext::current().code_mode_host_program())
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeModeSessionProvider for ProcessOwnedCodeModeSessionProvider {
|
||||
fn availability(&self) -> Result<(), String> {
|
||||
let HostEndpoint::Process(host_program) = &self.host.endpoint else {
|
||||
unreachable!("a process-owned provider always has a process endpoint");
|
||||
};
|
||||
if host_program.is_file() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ConnectionError::Spawn {
|
||||
host_program: host_program.clone(),
|
||||
error: io::Error::new(io::ErrorKind::NotFound, "host executable was not found"),
|
||||
}
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_session<'a>(
|
||||
&'a self,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(process_host) = self.process_host() else {
|
||||
let session: Arc<dyn CodeModeSession> =
|
||||
Arc::new(crate::InProcessCodeModeSession::with_delegate(delegate));
|
||||
return Ok(session);
|
||||
};
|
||||
Box::pin(create_host_session(delegate, self.process_host()))
|
||||
}
|
||||
}
|
||||
|
||||
match process_host.connection().await {
|
||||
Ok(_) => {}
|
||||
Err(error) if error.host_program_not_found() && self.allow_in_process_fallback => {
|
||||
*self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) =
|
||||
ProviderState::InProcess;
|
||||
let session: Arc<dyn CodeModeSession> =
|
||||
Arc::new(crate::InProcessCodeModeSession::with_delegate(delegate));
|
||||
return Ok(session);
|
||||
}
|
||||
Err(error) => return Err(error.to_string()),
|
||||
}
|
||||
create_host_session(delegate, process_host).await
|
||||
})
|
||||
impl CodeModeSessionProvider for DisabledCodeModeSessionProvider {
|
||||
fn availability(&self) -> Result<(), String> {
|
||||
Err("code-mode host is disabled".to_string())
|
||||
}
|
||||
|
||||
fn create_session<'a>(
|
||||
&'a self,
|
||||
_delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(async { Err("code-mode host is disabled".to_string()) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +260,9 @@ impl ProcessOwnedCodeModeSession {
|
||||
pub fn new() -> Self {
|
||||
Self::with_host(
|
||||
Arc::new(NoopCodeModeSessionDelegate),
|
||||
Arc::new(OwnedCodeModeHost::new(default_host_program())),
|
||||
Arc::new(OwnedCodeModeHost::new(
|
||||
InstallContext::current().code_mode_host_program(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -569,33 +558,6 @@ impl CodeModeSession for ProcessOwnedCodeModeSession {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_host_program() -> PathBuf {
|
||||
resolve_host_program(
|
||||
std::env::var_os(CODE_MODE_HOST_PATH_ENV),
|
||||
std::env::current_exe(),
|
||||
)
|
||||
}
|
||||
|
||||
fn resolve_host_program(
|
||||
override_path: Option<OsString>,
|
||||
current_exe: io::Result<PathBuf>,
|
||||
) -> PathBuf {
|
||||
if let Some(path) = override_path {
|
||||
return PathBuf::from(path);
|
||||
}
|
||||
let executable_name = if cfg!(windows) {
|
||||
"codex-code-mode-host.exe"
|
||||
} else {
|
||||
"codex-code-mode-host"
|
||||
};
|
||||
if let Ok(current_exe) = current_exe
|
||||
&& let Some(parent) = current_exe.parent()
|
||||
{
|
||||
return parent.join(executable_name);
|
||||
}
|
||||
PathBuf::from(executable_name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "remote_session_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
Reference in New Issue
Block a user