From 5a80f62ec2dc73631c281c3f97d885d3c203ca85 Mon Sep 17 00:00:00 2001 From: Taksh Date: Fri, 24 Jul 2026 14:27:38 +0300 Subject: [PATCH 1/3] fix(desktop): isolate managed Codex sessions under nest CODEX_HOME Co-authored-by: Cursor --- desktop/src-tauri/src/managed_agents/nest.rs | 53 +++++++++++++++++++ .../src/managed_agents/nest/tests.rs | 29 ++++++++++ .../src-tauri/src/managed_agents/runtime.rs | 8 +++ 3 files changed, 90 insertions(+) diff --git a/desktop/src-tauri/src/managed_agents/nest.rs b/desktop/src-tauri/src/managed_agents/nest.rs index e13ab2baad..3d4a260a1c 100644 --- a/desktop/src-tauri/src/managed_agents/nest.rs +++ b/desktop/src-tauri/src/managed_agents/nest.rs @@ -401,6 +401,59 @@ pub fn ensure_cli_symlink(_exe_parent: &Path, _is_dev: bool) -> Result<(), Strin Ok(()) } +/// Buzz-owned Codex state directory (`$NEST/.codex`). +/// +/// Codex defaults to `~/.codex` for sessions and history. Managed agents must +/// not write there — that mixes Buzz sessions into the user's personal Codex +/// sidebar / ChatGPT Remote list (#2660). +pub fn buzz_codex_home() -> Option { + nest_dir().map(|root| root.join(".codex")) +} + +/// Ensure a Buzz-owned `CODEX_HOME` exists and is seeded with auth/config from +/// the user's personal `~/.codex` when missing. +/// +/// Copies (does not symlink) `auth.json` and `config.toml` so Buzz can own +/// session/history files while still inheriting an existing login. Returns the +/// directory path suitable for the `CODEX_HOME` env var. +pub fn prepare_isolated_codex_home() -> Option { + let home = buzz_codex_home()?; + fs::create_dir_all(&home).ok()?; + seed_codex_home_file(&home, "auth.json"); + seed_codex_home_file(&home, "config.toml"); + Some(home) +} + +fn seed_codex_home_file(codex_home: &Path, name: &str) { + let dest = codex_home.join(name); + if dest.exists() { + return; + } + let Some(user_codex) = dirs::home_dir().map(|h| h.join(".codex")) else { + return; + }; + let src = user_codex.join(name); + if src.is_file() { + let _ = fs::copy(&src, &dest); + } +} + +/// Test helper: seed logic against explicit nest/user Codex paths. +#[cfg(test)] +pub(crate) fn seed_codex_home_from(user_codex: &Path, nest_codex: &Path) { + let _ = fs::create_dir_all(nest_codex); + for name in ["auth.json", "config.toml"] { + let dest = nest_codex.join(name); + if dest.exists() { + continue; + } + let src = user_codex.join(name); + if src.is_file() { + let _ = fs::copy(&src, &dest); + } + } +} + /// Read a version number from a file. Returns 0 if the file doesn't exist or can't be parsed. fn read_version_file(path: &Path) -> u32 { fs::read_to_string(path) diff --git a/desktop/src-tauri/src/managed_agents/nest/tests.rs b/desktop/src-tauri/src/managed_agents/nest/tests.rs index a959381603..e3b2430c1b 100644 --- a/desktop/src-tauri/src/managed_agents/nest/tests.rs +++ b/desktop/src-tauri/src/managed_agents/nest/tests.rs @@ -943,3 +943,32 @@ fn test_path_is_dev_nest_root_returns_false() { "root path must not be identified as dev nest" ); } + +#[test] +fn seed_codex_home_copies_auth_and_config_once() { + let tmp = tempfile::tempdir().unwrap(); + let user = tmp.path().join("user-codex"); + let nest = tmp.path().join("nest-codex"); + fs::create_dir_all(&user).unwrap(); + fs::write(user.join("auth.json"), r#"{"tokens":1}"#).unwrap(); + fs::write(user.join("config.toml"), "model = \"o3\"\n").unwrap(); + + seed_codex_home_from(&user, &nest); + assert_eq!( + fs::read_to_string(nest.join("auth.json")).unwrap(), + r#"{"tokens":1}"# + ); + assert_eq!( + fs::read_to_string(nest.join("config.toml")).unwrap(), + "model = \"o3\"\n" + ); + + // Second seed must not overwrite nest-local edits. + fs::write(nest.join("auth.json"), r#"{"tokens":"buzz"}"#).unwrap(); + fs::write(user.join("auth.json"), r#"{"tokens":"user"}"#).unwrap(); + seed_codex_home_from(&user, &nest); + assert_eq!( + fs::read_to_string(nest.join("auth.json")).unwrap(), + r#"{"tokens":"buzz"}"# + ); +} diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index 6687cbbcf2..7dee9e0bb7 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -1736,6 +1736,14 @@ pub fn spawn_agent_child( if runtime_meta.is_some_and(|r| r.mcp_hooks) { command.env("MCP_HOOK_SERVERS", "*"); } + // Isolate Buzz-managed Codex sessions from the user's personal ~/.codex + // (sidebar / ChatGPT Remote). Auth + config are seeded from ~/.codex when + // the nest copy is missing; session/history stay under the nest (#2660). + if runtime_meta.is_some_and(|r| r.id == "codex") { + if let Some(codex_home) = super::prepare_isolated_codex_home() { + command.env("CODEX_HOME", codex_home); + } + } // ── Readiness check: set setup-payload if agent is not ready ───────────── // From 4038231986ed6f3816ae46adfeba8d7f4ad067d2 Mon Sep 17 00:00:00 2001 From: Taksh Date: Fri, 24 Jul 2026 14:27:38 +0300 Subject: [PATCH 2/3] fix(desktop): make the active community obvious in the sidebar Co-authored-by: Cursor --- .../features/communities/ui/CommunitySwitcher.tsx | 15 ++++++++++++--- desktop/src/features/sidebar/ui/CommunityRail.tsx | 11 ++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/desktop/src/features/communities/ui/CommunitySwitcher.tsx b/desktop/src/features/communities/ui/CommunitySwitcher.tsx index 020a2e8838..26583ef38c 100644 --- a/desktop/src/features/communities/ui/CommunitySwitcher.tsx +++ b/desktop/src/features/communities/ui/CommunitySwitcher.tsx @@ -227,11 +227,17 @@ export function CommunitySwitcher({
{communities.map((community) => (