Keep agent- and script-opened workspaces and panes in the background - #15281
Conversation
`cmux cloud workspace new <vm>` selected the new workspace while the person was typing elsewhere. The vm/cloud and surface open commands defaulted focus to true on both sides: the CLI never sent a focus value, and the socket handlers read a missing `focus` as true while running in `v2VmCall`'s detached task, where no socket focus policy frame applies. CLI: a new `defaultFocusForUserOpen()` decides the default for every open command. It focuses only for an interactive run (a terminal on stdin and stdout, no coding agent environment; `AI_AGENT` joins the agent key list). `CMUX_FOCUS_NEW=1|0` overrides it, and `--focus` / `--no-focus` always win. vm/cloud new, base open/reset, fork, restore, shell/attach, ssh, tui, open, workspace new/open and agent, `surface open`/`new-terminal`, `cmux open` and `cmux ssh`/`ssh-tmux` take the flags and always send an explicit focus. `vm agent` now opens its pane beside the caller instead of in whichever workspace is selected. The app sets `CMUX_FOCUS_NEW=1` when it launches the CLI for a click (Machines panel, Base, ssh:// links), so those still focus. Server: vm.* and surface.* open handlers default `focus` to false. Creating a local workspace for a group honors focus, so a background open no longer switches the window or workspace. A pane opened without focus gets the unread dot, which clears when the person selects the workspace, clicks the pane or types in it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 2 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository: manaflow-ai/cmux/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (33)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
All contributors have signed the CLA ✍️ ✅ |
|
Dogfood build of cmux DEV pr-15281-59edd395.app The link opens this exact commit in the cmux dev menu bar app. The build starts on each push and the page waits until it is ready; a newer push replaces it. It signs in against production, so Cloud or backend changes still need a tagged build with a development backend. |
CI failure attributionCI passes on Written by |
- docs/cli-contract.md: vm agent and vm open help probes and table rows match the new focus flags; add a Focus on open row and an ssh help probe. - vm dev and vm layout apply --open take --focus/--no-focus and send focus explicitly (default: interactive only). - One focus flag grammar (--focus, --focus true|false, --focus=<bool>, --no-focus) for vm workspace new/open, vm agent, cmux ssh and ssh-tmux. The agent alias router reads a value after --focus only when it is exactly true/false/1/0/yes/no. - vm agent retries once without the caller workspace when that stale context is rejected; the server validates it before creating anything. - amp, grok, hermes-agent (and the other launch kinds) count as coding agent environments for the focus default. - Update cmux-cloud-vm skill references and add router focus tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The warning budget flagged `case "--no-focus", "--focus", _ where ...`: the `where` binds only the last pattern, which was intended but reads as if it guarded all three. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…request's Dogfooding `CLAUDECODE=1 cmux surface new-terminal --machine local` on a PR build showed focus still jumping to the new terminal. The local provider first makes the pane with focus: true in the selected workspace and only then projects it to its destination with the requested focus, so the staging step stole focus before the request's focus: false applied. Also update the top-level `cmux surface` usage lines to the new flags. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Dogfood round 2, on a fleet mini running the CI build of 59edd39 (the fix that makes the local
The focus ring stays on the original pane. "Terminal 2" opened next to it, and the sidebar row shows the unread badge. |
|
Merge receipt for |
1028a08 test: isolate background workspace git probe fixture (manaflow-ai#15388) 41ad40d fix: keep the terminal area when the window is too narrow for the side panels (manaflow-ai#15369) 2890f0b Roll the Base create back when the owner network resolve fails (manaflow-ai#15358) 7b0a15f Keep agent- and script-opened workspaces and panes in the background (manaflow-ai#15281) 4f14fa3 ci: move CLI regressions to CLI product tests and rebalance the seven app-host shards (manaflow-ai#15177) 906926a ci: dogfood builds are opt-in with the dev-build label (manaflow-ai#15380) 2f6716c PR media: classify app changes by CI's build inputs; a reuse error is no refusal (manaflow-ai#15386) bc28bc4 Release the Base generation when a create is refused for credits (manaflow-ai#15343) 6760c93 iOS: Add Computer never disturbs the active Mac (manaflow-ai#15102) 0f2d3d3 Show Claude sessions that stop on an API error instead of leaving them Running (manaflow-ai#15232) 20ef7c9 Keep the main window floor on the animating setFrame path (manaflow-ai#15368) b4f5dc5 ci: move UI runs pinned to Blacksmith macOS 26 onto owned Macs (manaflow-ai#15383) e02c385 PR media: compile once when CI's build cannot load, and say why a tour skipped (manaflow-ai#15378) ebd1f4f fix(iroh-v2): commit delivery accounting only after the frame is sent (manaflow-ai#15344) # Conflicts: # .github/workflows/ci-guards.yml # .github/workflows/ci-macos.yml # .github/workflows/ci.yml # .github/workflows/pr-media.yml # .github/workflows/test-e2e.yml



Agent-opened workspaces and panes stole focus mid-typing.
cmux cloud workspace new <vm>had no way to stay in the background and selected the new workspace immediately. cmux's socket policy (skills/cmux-socket-policy) already says CLI commands must not steal focus without explicit intent. Thevm/cloud/surfacefamily broke that policy in three ways:v2VmCall's detachedTask, where the thread-local focus allowance is empty.SurfacePaneFactorytreats an empty allowance as allowed.surface.project,surface.new_terminal,vm.terminal_open,vm.terminal_new,vm.workspace_newandvm.workspace_openall defaultedfocusto true.SurfacePaneFactory.createLocalWorkspacealways selected the new workspace and switched the active window, even withfocus: false. That is the path behindvm workspace openandvm workspace new --reuse.The rule now
A UI-opening CLI command takes focus only when a person ran it interactively. A coding agent or script gets a background open plus an unread dot.
--focus,--focus true|false,--focus=<bool>and--no-focusparse the same way on every command, includingcmux agent <kind>andcmux ssh. Incmux agent, a value after--focuscounts only if it is exactly true/false/1/0/yes/no.CMUX_FOCUS_NEW=1|0decides.isCodingAgentEnvironment:CLAUDECODE,CODEX_THREAD_ID,OPENCODE,CMUX_AGENT_LAUNCH_KIND, and so on, plusAI_AGENT, which is new.focusparam. These CLI paths always send one. Server defaults for the six handlers above are nowfalsefor any other socket client.createLocalWorkspace/addWorkspaceInPreferredMainWindowtakeselect:, so a background workspace no longer switches window or selection.ssh://links) setCMUX_FOCUS_NEW=1, so those clicks focus as before.vm agentlands beside the caller. It now passes the caller's workspace and surface, so its terminal splits next to the agent instead of into whatever workspace is selected. If that context is stale (the tab moved), it retries once against the selected workspace.Commands covered
The audit covered every CLI path that opens UI:
vm/cloud workspace new,workspace open--focus/--no-focusvm new,base open,base reset,fork,restore,shell/attach,ssh,tui,open,dev,layout apply --openvm agent(without--no-open)surface open,surface new-terminalcmux ssh,ssh-tmux--no-focusnew-workspace,new-split,new-pane,new-surface,markdown open,browser open,vm desktop, port openssplit-window-d(tmux semantics)Left as is on purpose:
project openandssh-session-attach, since both are explicit user flows.Follow-ups (design, for the team)
Where agent-opened panes land is a feature call: a right-side background split, the right-sidebar dock, or a new workspace. It is not part of this fix. Today a background open keeps each command's current placement and adds the unread dot.
Related, not touched here: #15160 (pane drag), #14894 (focus pulse), #15229 (sidebar text weight). Companion sidebar model fix: #15260.
Tests
docs/cli-contract.mdhelp probes are updated for the new flags, andcmux ssh --helphas a new probe.CLICoderouterCommandTestscoverscmux agent --focusparsing.SurfaceSocketCommandTests: the default is background, explicittrueis honored, and a background open marks the pane unread.tests/test_cli_vm_open_device_target.py: the background default,--focus,--no-focusandCMUX_FOCUS_NEW.CLI tests that relied on the old focus default now pass
--focusor expect background.Fleet dogfood evidence follows in a comment.
🤖 Generated with Claude Code