From 547e9daebdbd6860148dab393c8f321eb05f78a3 Mon Sep 17 00:00:00 2001 From: Chirag Tripathi Date: Mon, 15 Jun 2026 15:49:15 +0000 Subject: [PATCH 1/3] fix(cursor): seed default cursor with enabled=false to prevent overlay leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default cursor was seeded with enabled=true in three places: - CursorRegistry::new() (platform-macos/src/cursor/state.rs) - overlay::init() (platform-macos/src/cursor/overlay.rs) - RenderStateCore::new() hardcoded visible=true (cursor-overlay/src/render_state.rs) This caused every anonymous (non-session) tool call to render a cursor overlay that was never cleaned up — the "default" key is explicitly guarded against removal. The documented contract is "without a session, actions run cursor-less," but the hardcoded visible=true bypassed the config entirely. Fixes: 1. state.rs: seed default with enabled=false 2. overlay.rs: seed default render state with enabled=false 3. render_state.rs: derive visible from cfg.enabled instead of hardcoding true Without a declared session, the overlay window still exists (AppKit NSWindow lifecycle) but the default cursor draws nothing — paint_cursor early-returns on !visible, and animate_cursor_to skips on !cfg.enabled. Session cursors work normally via set_agent_cursor_enabled / start_session. Closes #1777 --- .../crates/cursor-overlay/src/render_state.rs | 3 ++- .../platform-macos/src/cursor/overlay.rs | 5 +++- .../crates/platform-macos/src/cursor/state.rs | 25 +++++++++++++------ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs b/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs index 34a07183a2..ee7cb6d2f3 100644 --- a/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs +++ b/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs @@ -96,6 +96,7 @@ impl RenderStateCore { let palette = cfg.palette(); let motion = cfg.motion.clone(); let shape = cfg.shape.clone(); + let visible = cfg.enabled; Self { cfg, palette, @@ -111,7 +112,7 @@ impl RenderStateCore { spring_tgt: None, click_t: None, pressed: false, - visible: true, + visible, // was hardcoded true — respect config.enabled (#1777) idle_secs: 0.0, idle_alpha: 1.0, pinned_wid: None, diff --git a/libs/cua-driver/rust/crates/platform-macos/src/cursor/overlay.rs b/libs/cua-driver/rust/crates/platform-macos/src/cursor/overlay.rs index edd4d65d45..a341c572d7 100644 --- a/libs/cua-driver/rust/crates/platform-macos/src/cursor/overlay.rs +++ b/libs/cua-driver/rust/crates/platform-macos/src/cursor/overlay.rs @@ -156,7 +156,10 @@ pub fn init(cfg: CursorConfig) { *CMD_RX_CELL.lock().unwrap() = Some(rx); *ARRIVAL_TX.lock().unwrap() = Some(HashMap::new()); let mut cursors = IndexMap::new(); - cursors.insert("default".to_owned(), RenderState::new(cfg.clone())); + // Default cursor starts disabled — see state.rs:new() and #1777. + let mut default_cfg = cfg.clone(); + default_cfg.enabled = false; + cursors.insert("default".to_owned(), RenderState::new(default_cfg)); *RENDER.lock().unwrap() = Some(RenderMap { cursors, win_w: 0.0, diff --git a/libs/cua-driver/rust/crates/platform-macos/src/cursor/state.rs b/libs/cua-driver/rust/crates/platform-macos/src/cursor/state.rs index 931673484f..eff1b3eb4d 100644 --- a/libs/cua-driver/rust/crates/platform-macos/src/cursor/state.rs +++ b/libs/cua-driver/rust/crates/platform-macos/src/cursor/state.rs @@ -59,8 +59,17 @@ pub struct CursorRegistry { impl CursorRegistry { pub fn new() -> Self { let mut map = HashMap::new(); + let default_cfg = CursorConfig { + // Default cursor starts DISABLED. The anonymous / non-session + // path must never render a cursor — the documented contract is + // "without a session, actions run cursor-less." A session's + // first enable/start_session call activates its own cursor. + // See #1777. + enabled: false, + ..CursorConfig::default() + }; let default = CursorState { - config: CursorConfig::default(), + config: default_cfg, position: None, }; map.insert("default".into(), default); @@ -203,15 +212,17 @@ mod tests { } #[test] - fn default_cursor_unaffected_by_guard() { - // "default" is seeded and is never tombstoned — its mutators still work. + fn default_cursor_starts_disabled() { + // Default cursor is seeded disabled — anonymous calls must be + // cursor-less per the documented contract. See #1777. let reg = CursorRegistry::new(); assert!(!cua_driver_core::session::is_session_ended("default")); - reg.set_enabled("default", false); - reg.update_position("default", 1.0, 2.0); let s = reg.get("default").expect("default cursor always present"); - assert!(!s.config.enabled); - assert_eq!(s.position.as_ref().map(|p| (p.x, p.y)), Some((1.0, 2.0))); + assert!(!s.config.enabled, "default cursor must start disabled"); + // But mutators still work — a session that enables it should succeed. + reg.set_enabled("default", true); + let s2 = reg.get("default").unwrap(); + assert!(s2.config.enabled); } #[test] From ada00cb58f98653d9a111220b0c2685c6ccf2782 Mon Sep 17 00:00:00 2001 From: Chirag Tripathi Date: Mon, 15 Jun 2026 17:03:16 +0000 Subject: [PATCH 2/3] fix(cursor): update get_config test for default enabled=false --- .../rust/crates/platform-macos/src/tools/cursor_tools.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/cua-driver/rust/crates/platform-macos/src/tools/cursor_tools.rs b/libs/cua-driver/rust/crates/platform-macos/src/tools/cursor_tools.rs index c927f0144a..e16c60a2af 100644 --- a/libs/cua-driver/rust/crates/platform-macos/src/tools/cursor_tools.rs +++ b/libs/cua-driver/rust/crates/platform-macos/src/tools/cursor_tools.rs @@ -603,8 +603,9 @@ mod tests { }; assert!(read_for(&json!({ "session": "sessA" }))); assert!(!read_for(&json!({ "session": "sessB" }))); - // Anonymous caller (no session) falls back to the seeded default (on). - assert!(read_for(&json!({}))); + // Anonymous caller (no session) falls back to the seeded default, + // which starts disabled per #1777. + assert!(!read_for(&json!({}))); } #[test] From 6b96958ef87d43cfea5d7a38e56bc5b632387824 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 15 Jun 2026 17:32:34 +0000 Subject: [PATCH 3/3] fix(cursor): keep cfg.enabled in sync with visible in SetEnabled (#1900) --- libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs b/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs index ee7cb6d2f3..ef208c3ffa 100644 --- a/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs +++ b/libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs @@ -467,6 +467,7 @@ impl RenderStateCore { } OverlayCommand::SetEnabled(v) => { self.visible = v; + self.cfg.enabled = v; // keep cfg.enabled in sync with visible (#1900) true } OverlayCommand::SetMotion(m) => {