Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/content/docs/cua-driver/reference/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ The canonical, always-current source is the
release body is auto-generated per release from the commits touching
`libs/cua-driver/rust`, including SHA256 checksums and install instructions.

## 0.5.6 (unreleased)

- **Windows: fix idle CPU burn and orphaned `mcp` processes from the cursor
overlay** (#1808). The agent-cursor overlay render timer ran at ~125 Hz
unconditionally and re-composited a full virtual-screen pixmap (plus an
RGBA→BGRA copy and `UpdateLayeredWindow` blit) on every tick — so an idle
`cua-driver mcp` with no automation pinned 60–85% of a CPU core. The render
loop is now event-driven: it only composites and blits while a cursor is
actually animating or fading, and drops to a slow heartbeat (waking instantly
on the next command) once every cursor is static. The in-process `mcp` (stdio)
child now force-exits when the client disconnects (stdin EOF / pipe closed)
instead of leaving the overlay loop running as an orphan that accumulates CPU.
Use `--no-overlay` to disable the agent cursor entirely for headless runs.

## 0.5.4 (2026-06-15)

- **macOS: fix 0x0 screenshots on macOS 15+ — the release bundle now ships the
Expand Down
17 changes: 15 additions & 2 deletions libs/cua-driver/rust/crates/cua-driver/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,21 @@ async fn async_main() -> anyhow::Result<()> {
let registry = Arc::new(build_registry(cursor_cfg));
registry.init_self_weak();
maybe_init_pip();
cua_driver_core::server::run(registry).await?;
Ok(())
let result = cua_driver_core::server::run(registry).await;
if let Err(e) = &result {
tracing::error!("MCP server error: {e}");
}

// The stdio MCP server loop has ended — the client disconnected (stdin
// EOF) or a fatal I/O error occurred. The cursor overlay runs on its own
// detached thread with an independent Win32 message loop (and we raised the
// multimedia timer resolution via `timeBeginPeriod`), so simply returning
// is not guaranteed to tear it down promptly: that thread is never joined
// and would otherwise keep its render loop alive as an orphan, accumulating
// CPU after the client is gone (issue #1808). Force a clean process exit so
// the overlay thread dies with us the moment the transport closes — mirrors
// the macOS `std::process::exit(0)` after `server::run`.
std::process::exit(if result.is_ok() { 0 } else { 1 });
}

// ── Registry builder (non-macOS) ──────────────────────────────────────────
Expand Down
Loading
Loading