Summary
When a workspace is created via cmux ssh user@host, closing the last SSH surface in that workspace does not close the workspace. Instead, the workspace is demoted to a local one and a fresh local shell surface is automatically spawned in its place. I'd like an option to opt out of this behavior so that the workspace (and, if it's the only workspace, the window) is closed when the last SSH surface exits.
Current behavior
Starting from a workspace created by cmux ssh user@host and exiting the only SSH surface (e.g. typing exit or Ctrl+D in the remote shell):
- Ghostty reports child-exit and
TabManager.closePanelAfterChildExited is invoked (Sources/TabManager.swift:4601).
- The branch guarded by
keepsRemoteWorkspaceOpen = tab.panels.count <= 1 && tab.shouldDemoteWorkspaceAfterChildExit(surfaceId:) is taken (Sources/TabManager.swift:4604-4623). The comment on that branch explicitly states: "Exiting the last SSH surface should demote the workspace back to a local one."
shouldDemoteWorkspaceAfterChildExit returns true for any isRemoteWorkspace (Sources/Workspace.swift:8280-8282).
- Close is routed through
closeRuntimeSurface → Workspace.closePanel(force: true) → bonsplitController.closeTab(...).
- In the close-tab delegate (
Sources/Workspace.swift:12127), clearRemoteConfigurationIfWorkspaceBecameLocal() clears the remote configuration once panels.isEmpty, and then createReplacementTerminalPanel() is called (Sources/Workspace.swift:12218-12234), creating a new local shell terminal that becomes the focused surface.
Result: the workspace stays open, now connected to nothing remote, with a freshly spawned local shell the user did not ask for.
Desired behavior
Provide a user-facing option (Settings toggle + settings.json key, mirroring the existing keepWorkspaceOpenWhenClosingLastSurface design) such that when it is enabled:
- If the last surface of a
cmux ssh-created (remote) workspace exits, the workspace is closed outright.
- If it is the last workspace in the window, the window is closed (i.e. fall through to
closeMainWindowContainingTabId / closeWorkspace(tab), as the non-remote branch already does at Sources/TabManager.swift:4627-4640).
- No replacement local shell surface is spawned.
The existing keepWorkspaceOpenWhenClosingLastSurface preference (LastSurfaceCloseShortcutSettings, Sources/TabManager.swift:63) only affects the Cmd+W shortcut path (shouldCloseWorkspaceOnLastSurfaceShortcut / closePanelByShortcut at Sources/TabManager.swift:4472-4513) and does not influence the child-exit / SSH-session-end demote path, so it cannot be reused to solve this.
Proposed implementation sketch
- Add a new preference, e.g.
closeRemoteWorkspaceOnLastSSHSurface (default: false to preserve current behavior).
- In
closePanelAfterChildExited, consult the preference before taking the keepsRemoteWorkspaceOpen branch; when enabled, skip the demote path and fall through to the existing "last panel → closeWorkspace(tab) / closeMainWindowContainingTabId" branch.
- Expose it in Settings and the
settings.json schema, alongside keepWorkspaceOpenWhenClosingLastSurface.
- Add regression tests analogous to the existing
WorkspaceRemoteConnectionTests (e.g. testRemoteTerminalSessionEndClosesWorkspaceWhenOptIn).
Motivation
cmux ssh user@host is an explicit "open this remote host as a workspace" action. When the SSH session ends intentionally (exit / Ctrl+D) or unintentionally (network drop), the current demotion-plus-auto-local-shell behavior is surprising:
- The workspace lingers with a local shell that has no relationship to the remote target it was opened for.
- Users who treat the workspace as scoped to the SSH target have to manually close the workspace every time.
- It conflicts with how other terminal apps handle
ssh sessions (the session ending closes the tab/window).
Making this opt-in preserves the current default for users who rely on the demote-to-local flow (e.g. the PRs #2104 and #2697 scenarios) while giving users who prefer "SSH session ends → workspace closes" a clean way to get that behavior.
Related prior work
All three go in the opposite direction (keep the workspace, fall back to local). This issue asks only for an opt-in alternative, not a change of default.
Summary
When a workspace is created via
cmux ssh user@host, closing the last SSH surface in that workspace does not close the workspace. Instead, the workspace is demoted to a local one and a fresh local shell surface is automatically spawned in its place. I'd like an option to opt out of this behavior so that the workspace (and, if it's the only workspace, the window) is closed when the last SSH surface exits.Current behavior
Starting from a workspace created by
cmux ssh user@hostand exiting the only SSH surface (e.g. typingexitor Ctrl+D in the remote shell):TabManager.closePanelAfterChildExitedis invoked (Sources/TabManager.swift:4601).keepsRemoteWorkspaceOpen = tab.panels.count <= 1 && tab.shouldDemoteWorkspaceAfterChildExit(surfaceId:)is taken (Sources/TabManager.swift:4604-4623). The comment on that branch explicitly states: "Exiting the last SSH surface should demote the workspace back to a local one."shouldDemoteWorkspaceAfterChildExitreturnstruefor anyisRemoteWorkspace(Sources/Workspace.swift:8280-8282).closeRuntimeSurface→Workspace.closePanel(force: true)→bonsplitController.closeTab(...).Sources/Workspace.swift:12127),clearRemoteConfigurationIfWorkspaceBecameLocal()clears the remote configuration oncepanels.isEmpty, and thencreateReplacementTerminalPanel()is called (Sources/Workspace.swift:12218-12234), creating a new local shell terminal that becomes the focused surface.Result: the workspace stays open, now connected to nothing remote, with a freshly spawned local shell the user did not ask for.
Desired behavior
Provide a user-facing option (Settings toggle +
settings.jsonkey, mirroring the existingkeepWorkspaceOpenWhenClosingLastSurfacedesign) such that when it is enabled:cmux ssh-created (remote) workspace exits, the workspace is closed outright.closeMainWindowContainingTabId/closeWorkspace(tab), as the non-remote branch already does atSources/TabManager.swift:4627-4640).The existing
keepWorkspaceOpenWhenClosingLastSurfacepreference (LastSurfaceCloseShortcutSettings,Sources/TabManager.swift:63) only affects the Cmd+W shortcut path (shouldCloseWorkspaceOnLastSurfaceShortcut/closePanelByShortcutatSources/TabManager.swift:4472-4513) and does not influence the child-exit / SSH-session-end demote path, so it cannot be reused to solve this.Proposed implementation sketch
closeRemoteWorkspaceOnLastSSHSurface(default:falseto preserve current behavior).closePanelAfterChildExited, consult the preference before taking thekeepsRemoteWorkspaceOpenbranch; when enabled, skip the demote path and fall through to the existing "last panel →closeWorkspace(tab)/closeMainWindowContainingTabId" branch.settings.jsonschema, alongsidekeepWorkspaceOpenWhenClosingLastSurface.WorkspaceRemoteConnectionTests(e.g.testRemoteTerminalSessionEndClosesWorkspaceWhenOptIn).Motivation
cmux ssh user@hostis an explicit "open this remote host as a workspace" action. When the SSH session ends intentionally (exit/ Ctrl+D) or unintentionally (network drop), the current demotion-plus-auto-local-shell behavior is surprising:sshsessions (the session ending closes the tab/window).Making this opt-in preserves the current default for users who rely on the demote-to-local flow (e.g. the PRs #2104 and #2697 scenarios) while giving users who prefer "SSH session ends → workspace closes" a clean way to get that behavior.
Related prior work
cmux vmdisconnects.All three go in the opposite direction (keep the workspace, fall back to local). This issue asks only for an opt-in alternative, not a change of default.