diff --git a/codex-rs/core/src/tools/code_mode/backend.rs b/codex-rs/core/src/tools/code_mode/backend.rs new file mode 100644 index 00000000000..e08f88ad096 --- /dev/null +++ b/codex-rs/core/src/tools/code_mode/backend.rs @@ -0,0 +1,98 @@ +use std::collections::HashMap; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; + +use codex_code_mode::CodeModeTurnHost; +use codex_code_mode::ExecuteRequest; +use codex_code_mode::RuntimeResponse; +use codex_code_mode::WaitOutcome; +use codex_code_mode::WaitRequest; +use serde_json::Value as JsonValue; + +/// Host-facing execution boundary for code-mode runtimes. +/// +/// Implementations own how JavaScript execution is reached, while callers keep +/// the model-facing host behavior in `codex-core`. The initial implementation is +/// in-process; later implementations can forward the same requests to a child +/// process without changing the call sites that manage turns. +pub(super) trait CodeModeBackend: Send + Sync { + fn allocate_cell_id(&self) -> String; + + fn execute( + &self, + request: ExecuteRequest, + ) -> Pin> + Send + '_>>; + + fn wait( + &self, + request: WaitRequest, + ) -> Pin> + Send + '_>>; + + fn start_turn_worker(&self, host: Arc) -> CodeModeTurnWorker; +} + +/// Opaque turn-scoped worker guard returned by code-mode backends. +/// +/// The host only needs to keep this guard alive until the turn ends. Hiding the +/// concrete type keeps the public host seam independent of whether the backend +/// is local or process-backed. +pub(crate) struct CodeModeTurnWorker { + _inner: Box, +} + +impl CodeModeTurnWorker { + fn new(inner: impl TurnWorkerHandle + 'static) -> Self { + Self { + _inner: Box::new(inner), + } + } +} + +trait TurnWorkerHandle: Send {} + +impl TurnWorkerHandle for T where T: Send {} + +pub(super) struct InProcessCodeModeBackend { + inner: codex_code_mode::CodeModeService, +} + +impl InProcessCodeModeBackend { + pub(super) fn new() -> Self { + Self { + inner: codex_code_mode::CodeModeService::new(), + } + } +} + +impl Default for InProcessCodeModeBackend { + fn default() -> Self { + Self::new() + } +} + +impl CodeModeBackend for InProcessCodeModeBackend { + fn allocate_cell_id(&self) -> String { + self.inner.allocate_cell_id() + } + + fn execute( + &self, + request: ExecuteRequest, + ) -> Pin> + Send + '_>> { + Box::pin(self.inner.execute(request)) + } + + fn wait( + &self, + request: WaitRequest, + ) -> Pin> + Send + '_>> { + Box::pin(self.inner.wait(request)) + } + + fn start_turn_worker(&self, host: Arc) -> CodeModeTurnWorker { + CodeModeTurnWorker::new(self.inner.start_turn_worker(host)) + } +} + +pub(super) type StoredValues = HashMap; diff --git a/codex-rs/core/src/tools/code_mode/mod.rs b/codex-rs/core/src/tools/code_mode/mod.rs index 77cbd72b086..2efdb0f572a 100644 --- a/codex-rs/core/src/tools/code_mode/mod.rs +++ b/codex-rs/core/src/tools/code_mode/mod.rs @@ -1,3 +1,4 @@ +mod backend; mod execute_handler; pub(crate) mod execute_spec; mod response_adapter; @@ -8,6 +9,10 @@ use std::collections::HashSet; use std::sync::Arc; use std::time::Duration; +use backend::CodeModeBackend; +use backend::CodeModeTurnWorker; +use backend::InProcessCodeModeBackend; +use backend::StoredValues; use codex_code_mode::CodeModeNestedToolCall; use codex_code_mode::CodeModeTurnHost; use codex_code_mode::RuntimeResponse; @@ -15,6 +20,7 @@ use codex_protocol::models::FunctionCallOutputContentItem; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseInputItem; use serde_json::Value as JsonValue; +use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; use crate::function_tool::FunctionCallError; @@ -59,43 +65,42 @@ pub(crate) struct ExecContext { } pub(crate) struct CodeModeService { - inner: codex_code_mode::CodeModeService, + backend: Arc, + stored_values: Mutex, } impl CodeModeService { pub(crate) fn new() -> Self { Self { - inner: codex_code_mode::CodeModeService::new(), + backend: Arc::new(InProcessCodeModeBackend::new()), + stored_values: Mutex::new(StoredValues::new()), } } - pub(crate) async fn stored_values(&self) -> std::collections::HashMap { - self.inner.stored_values().await + pub(crate) async fn stored_values(&self) -> StoredValues { + self.stored_values.lock().await.clone() } - pub(crate) async fn replace_stored_values( - &self, - values: std::collections::HashMap, - ) { - self.inner.replace_stored_values(values).await; + pub(crate) async fn replace_stored_values(&self, values: StoredValues) { + *self.stored_values.lock().await = values; } pub(crate) fn allocate_cell_id(&self) -> String { - self.inner.allocate_cell_id() + self.backend.allocate_cell_id() } pub(crate) async fn execute( &self, request: codex_code_mode::ExecuteRequest, ) -> Result { - self.inner.execute(request).await + self.backend.execute(request).await } pub(crate) async fn wait( &self, request: codex_code_mode::WaitRequest, ) -> Result { - self.inner.wait(request).await + self.backend.wait(request).await } pub(crate) async fn start_turn_worker( @@ -104,7 +109,7 @@ impl CodeModeService { turn: &Arc, router: Arc, tracker: SharedTurnDiffTracker, - ) -> Option { + ) -> Option { if !turn.features.enabled(Feature::CodeMode) { return None; } @@ -116,7 +121,7 @@ impl CodeModeService { let tool_runtime = ToolCallRuntime::new(router, Arc::clone(session), Arc::clone(turn), tracker); let host = Arc::new(CoreTurnHost { exec, tool_runtime }); - Some(self.inner.start_turn_worker(host)) + Some(self.backend.start_turn_worker(host)) } }