Repository navigation
Make closing winapp's Sandbox window end the Sandbox, after Windows asks - #1047
Conversation
winapp started its Sandbox with `wsb start`, which has no window and ends only with `wsb stop`, then showed it with `wsb connect`, a viewer. Closing that viewer left the Sandbox running out of sight: one found on a dev machine had been holding about 3.9 GB for three days. Opening Windows Sandbox from Start then failed with "Only one running instance". winapp now opens a new Sandbox the way Start does, by launching the Windows Sandbox client (the alias beside wsb.exe) with no arguments. That window owns the Sandbox: closing it shows Windows' own "all of its content will be discarded" confirmation and then ends the Sandbox. Cancel keeps it. No `wsb connect` is added on top, so there is exactly one window. `wsb share`, `wsb exec` and `wsb list` work against it unchanged, and winapp already used the default configuration. Windows assigns the instance ID, so winapp finds it with `wsb list`. It claims the Sandbox as Created only when the window it launched reached a session; otherwise, as when someone opens one from Start at the same moment, it uses the Sandbox as adopted. Pending-start records written by winapp 0.7.1 are still recovered. Verified live: cold start (one window, focus returned, piped output closed), warm reuse, close then Cancel (kept and reused), close then Ok (ended, next command starts fresh), and winapp killed mid-start (the Sandbox survives and the next command uses it). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
If the window winapp opened took longer than 30 s to show a session, the new Sandbox was used as adopted, and a guest still signing in reported no login session, so winapp ran `wsb connect` and put a second window on screen. Closing that one would leave the Sandbox running. A Sandbox the command just opened always has a window attached, either winapp's or the one Start opened in the same moment, so the session probe and `wsb connect` are now skipped whenever this command opened one, not only when it could prove the window was its own. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Build Metrics ReportValidation passed. All required build and validation jobs succeeded. Binary Sizes
.NET Test Results (TRX reports)Other suites are reflected in the overall validation status above. ✅ 8315 passed, 37 skipped out of 8352 tests in 1128.9s (-101.8s vs. baseline) Test Coverage✅ 86.7% line coverage, 81.3% branch coverage · ✅ no change vs. baseline CLI Startup Time61ms median (x64, Try This BuildInstalls the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing. & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) 1047Switching between builds often?Put the tool on your PATH once: & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) -AddToPathThen this build is just: winapp-pr 1047Run Updated 2026-10-09 19:09:57 UTC · commit |
There was a problem hiding this comment.
🟡 Changes recommended
A concurrent headless wsb start can be adopted without connecting a client, and the documentation overstates close-to-end behavior.
3 open findings
What changed in this PR
Updates Windows Sandbox startup so its owning window controls Sandbox lifetime and closing it triggers Windows’ confirmation.
Changes:
- Launches Windows Sandbox directly instead of using headless
wsb start. - Tracks the owning client window and safely adopts ambiguous instances.
- Updates tests and user guidance for the new lifecycle.
| File | Description |
|---|---|
WsbHResult.cs |
Removes start-specific HRESULT constants. |
WindowsSandboxWindowController.cs |
Recognizes directly launched clients. |
WindowsSandboxLifecycle.cs |
Reworks creation and ownership reconciliation. |
WindowsSandboxHostProbe.cs |
Exposes execution-alias validation internally. |
WindowsSandboxCli.cs |
Launches the Sandbox client directly. |
WindowsSandboxBackend.cs |
Uses the owning window during bootstrap. |
SandboxConnectAttempt.cs |
Documents direct-client ownership. |
IWindowsSandboxCli.cs |
Replaces headless start with client launch. |
TargetState.cs |
Documents legacy pending-start records. |
WindowsSandboxWindowControllerTests.cs |
Tests direct-launch window matching. |
WindowsSandboxLifecycleTests.cs |
Tests launch, adoption, and recovery behavior. |
WindowsSandboxCliTests.cs |
Tests argument-free client launch. |
WindowsSandboxCliClassificationTests.cs |
Removes obsolete start tests. |
SandboxUxRegressionTests.cs |
Verifies one-window bootstrap behavior. |
SandboxLiveAdoptionTests.cs |
Retains live headless-adoption coverage. |
SandboxAdoptionTests.cs |
Tests unidentified-window adoption. |
winapp-sandbox/SKILL.md |
Updates Sandbox lifetime guidance. |
sandbox-execution.md |
Documents close confirmation and termination. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
If a headless `wsb start` won the Sandbox singleton in the same moment winapp opened its own window, winapp used that headless Sandbox but never connected a window to it, so the agent waited three minutes and failed. The agent's own "no input desktop" after that wait now connects one window, as it already does for a closed window. A window winapp opened but could not identify still gets the full wait to sign in. The docs and skill now say that closing the window ends a Sandbox winapp started, and that one started with `wsb start` keeps running. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Measured live: a headless `wsb start` in the same moment as winapp's launch wins the singleton, and winapp's window shows "Only one running instance of Windows Sandbox is allowed". winapp then assumed a window was attached, waited out the agent's three-minute deadline, and failed after about six minutes. The fallback added in the previous commit never fired, because the agent's dispatch failure on a headless guest is not observed in time. The window controller now reports when the window winapp launched shows that error page, without waiting out its 30 s timeout. winapp then treats the Sandbox like any other it adopts: the session probe finds nobody signed in and winapp connects one window. The same race now succeeds in about 20 s. The previous commit's fallback is reverted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
If someone opens Windows Sandbox from Start at the same moment as winapp's launch and wins, winapp's window shows the same "Only one running instance" page. The winner has its own window, still signing in, so probing the guest reported no login session and winapp connected a second window. winapp now treats the Sandbox as windowless only when its window shows that page and no other Sandbox window is open. Measured live: racing winapp with the Start entry, Start won and winapp used that Sandbox without a second window (exit 0, 21 s). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>


Description
Problem. winapp started its Windows Sandbox with
wsb start. That Sandbox has no window, and onlywsb stopends it. winapp then showed it withwsb connect, which only views it. So closing the Sandbox window left the Sandbox running out of sight. One left on a dev machine this way had about 3.9 GB of memory committed three days later. Opening Windows Sandbox from Start then failed with "Only one running instance of Windows Sandbox is allowed".Change. winapp now opens a new Sandbox the same way Start does: it launches the Windows Sandbox app, with no arguments. That window owns the Sandbox. Closing it shows Windows' own confirmation, "Are you sure you want to close Windows Sandbox? Once Windows Sandbox is closed all of its content will be discarded and permanently lost.":
--on sandboxcommand starts a fresh one.There is still exactly one Sandbox window. winapp still moves it behind your window.
winapp records the Sandbox as one it created only when the window it launched reaches a session. In any other case it uses the Sandbox as adopted, meaning started by something else, which winapp never ends. One example is opening Windows Sandbox from Start at the same moment.
Usage Example
Related Issue
Replaces #978, which ended the Sandbox from a hidden background process with no confirmation.
Type of Change
Checklist
plugins/winapp/skills/(if CLI commands/workflows changed)docs/sandbox-execution.mdand thewinapp-sandboxskill now say that closing the window of a Sandbox winapp started asks for confirmation and then ends it. No commands or options change, sodocs/usage.mddoes not.Screenshots / Demo
Measured on a real Windows Sandbox (Windows build 28000, Arm64, Windows Sandbox 0.8.107.0), with a NativeAOT build of this branch:
winapp target exec sandbox -- cmd /c echo hellowith nothing runningwinapp ui list-windows --on sandbox,winapp ui screenshot --on sandbox -w <hwnd>wsb listempty; the next command started a fresh Sandbox (35 s)wsb start0.7 s after winapp began a cold start, so the headless Sandbox wonBefore this change, closing the window showed no prompt and
wsb liststill listed the Sandbox.Additional Notes
wsb start, for example by another tool or by winapp 0.7.1 or earlier, still keeps running after its window is closed. The docs point those users towsb stop --id <id>. If winapp 0.7.1 crashed mid-start and left a pending-start record, newer winapp still recovers it.