From e9a55e5299bc741a085fea0af7aefb252502a996 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Tue, 17 Mar 2026 15:25:06 -0700 Subject: [PATCH] feat: [US-034] - Add reverse mutual exclusivity check in DesktopRuntime Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sandbox-agent/src/desktop_errors.rs | 9 +++++++++ .../sandbox-agent/src/desktop_runtime.rs | 19 ++++++++++++++++++- server/packages/sandbox-agent/src/router.rs | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/server/packages/sandbox-agent/src/desktop_errors.rs b/server/packages/sandbox-agent/src/desktop_errors.rs index ce57e9e..a57ec2a 100644 --- a/server/packages/sandbox-agent/src/desktop_errors.rs +++ b/server/packages/sandbox-agent/src/desktop_errors.rs @@ -62,6 +62,15 @@ impl DesktopProblem { ) } + pub fn browser_conflict() -> Self { + Self::new( + 409, + "Browser Conflict", + "desktop/browser-conflict", + "The browser runtime is currently active. Browser and desktop modes are mutually exclusive.", + ) + } + pub fn runtime_starting(message: impl Into) -> Self { Self::new( 409, diff --git a/server/packages/sandbox-agent/src/desktop_runtime.rs b/server/packages/sandbox-agent/src/desktop_runtime.rs index 29a84dc..ac9df32 100644 --- a/server/packages/sandbox-agent/src/desktop_runtime.rs +++ b/server/packages/sandbox-agent/src/desktop_runtime.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::fs::{self, OpenOptions}; use std::path::{Path, PathBuf}; use std::process::{Output, Stdio}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::time::Duration; use tokio::process::{Child, Command}; @@ -50,6 +50,7 @@ pub struct DesktopRuntime { recording_manager: DesktopRecordingManager, streaming_manager: DesktopStreamingManager, inner: Arc>, + browser_runtime: Arc>>, } #[derive(Debug, Clone)] @@ -179,9 +180,17 @@ impl DesktopRuntime { recording_fps: None, })), config, + browser_runtime: Arc::new(OnceLock::new()), } } + pub fn set_browser_runtime( + &self, + browser_runtime: Arc, + ) { + let _ = self.browser_runtime.set(browser_runtime); + } + pub async fn status(&self) -> DesktopStatusResponse { let mut state = self.inner.lock().await; self.refresh_status_locked(&mut state).await; @@ -204,6 +213,14 @@ impl DesktopRuntime { &self, request: DesktopStartRequest, ) -> Result { + // Check mutual exclusivity with browser runtime + if let Some(browser_rt) = self.browser_runtime.get() { + let browser_status = browser_rt.status().await; + if browser_status.state == crate::browser_types::BrowserState::Active { + return Err(DesktopProblem::browser_conflict()); + } + } + let mut state = self.inner.lock().await; if !self.platform_supported() { diff --git a/server/packages/sandbox-agent/src/router.rs b/server/packages/sandbox-agent/src/router.rs index 5272e6d..4920be3 100644 --- a/server/packages/sandbox-agent/src/router.rs +++ b/server/packages/sandbox-agent/src/router.rs @@ -125,6 +125,7 @@ impl AppState { process_runtime.clone(), desktop_runtime.clone(), )); + desktop_runtime.set_browser_runtime(browser_runtime.clone()); Self { auth, agent_manager,