Problem
On Windows, the desktop app always starts the local backend as a Windows child process. That makes Linux-native WSL workspaces awkward because provider commands, Git, shells, and project paths are evaluated from the Windows runtime instead of the selected WSL distro.
There is broader WSL/remote-backend design discussion in #671, and PR #170 improves WSL-hosted process/file/browser interop from the server side. This issue is the smaller desktop lifecycle piece: let the Windows Electron app keep the UI native while starting its bundled backend inside WSL.
Proposed focused behavior
- Add an opt-in desktop setting for WSL backend mode.
- Let the user choose the default WSL distro or a specific distro.
- When enabled, start the backend with
wsl.exe -- node <linux backend entry> --bootstrap-fd 0.
- Convert the bundled Windows backend entry path through
wslpath before launching.
- Send the bootstrap envelope over stdin because extra file descriptors do not reliably survive the
wsl.exe bridge.
- Do not pass the Windows
t3Home path into the WSL backend; let the Linux process use its own T3 home.
- Convert folder-picker results to WSL paths before sending them to the backend.
- Keep the renderer connection contract unchanged.
Why this should be separate from #170
PR #170 is useful related work for WSL-hosted runtime interop, especially editor/file-manager/browser path handling. It does not start the desktop-managed backend inside WSL. This issue tracks only that missing backend launch/control path.
Verification target
A good fix should keep the normal Windows backend path unchanged, add coverage for WSL distro parsing/path conversion/config persistence, pass typecheck/build, and allow the Windows desktop app to restart its local backend under WSL when the setting changes.
Discovered during implementation
These weren't in the original scope but turned out to be required to make the toggle behave seamlessly. Recording them here for anyone doing similar work in another desktop app:
-
Reconcile renderer state on env-id change. Each backend writes its own environment-id, so the welcome event after a swap reports a different environmentId than before. Without action the renderer keeps the previous backend's slice in environmentStateById, leaving stale threads/projects visible until the next reload. The fix drops the previous env's slice on identity change, disposes the prior EnvironmentConnection, and navigates off any /<oldEnvId>/<threadId> route the user was sitting on.
-
Re-authenticate the desktop session after a swap. Each backend signs sessions with its own key, so the renderer's existing cookie 401s the new backend — including the WS upgrade, which on desktop primary authenticates via session cookie (no wsToken query param). The renderer needs to re-run the desktop bootstrap exchange against the new backend before any reconnect attempts, and the bootstrap retry timeout has to cover cold WSL startup (15s → 60s in practice).
-
Suppress WS connection-status events during the deliberate swap window. Without this, the brief disconnect-then-reconnect cycle around a backend swap surfaces in the connection-status atom and the UI flashes "disconnected" before settling. A scoped suppression around the swap keeps the lifecycle reporting quiet for the duration; once the new session opens, normal lifecycle reporting resumes.
Problem
On Windows, the desktop app always starts the local backend as a Windows child process. That makes Linux-native WSL workspaces awkward because provider commands, Git, shells, and project paths are evaluated from the Windows runtime instead of the selected WSL distro.
There is broader WSL/remote-backend design discussion in #671, and PR #170 improves WSL-hosted process/file/browser interop from the server side. This issue is the smaller desktop lifecycle piece: let the Windows Electron app keep the UI native while starting its bundled backend inside WSL.
Proposed focused behavior
wsl.exe -- node <linux backend entry> --bootstrap-fd 0.wslpathbefore launching.wsl.exebridge.t3Homepath into the WSL backend; let the Linux process use its own T3 home.Why this should be separate from #170
PR #170 is useful related work for WSL-hosted runtime interop, especially editor/file-manager/browser path handling. It does not start the desktop-managed backend inside WSL. This issue tracks only that missing backend launch/control path.
Verification target
A good fix should keep the normal Windows backend path unchanged, add coverage for WSL distro parsing/path conversion/config persistence, pass typecheck/build, and allow the Windows desktop app to restart its local backend under WSL when the setting changes.
Discovered during implementation
These weren't in the original scope but turned out to be required to make the toggle behave seamlessly. Recording them here for anyone doing similar work in another desktop app:
Reconcile renderer state on env-id change. Each backend writes its own
environment-id, so the welcome event after a swap reports a differentenvironmentIdthan before. Without action the renderer keeps the previous backend's slice inenvironmentStateById, leaving stale threads/projects visible until the next reload. The fix drops the previous env's slice on identity change, disposes the priorEnvironmentConnection, and navigates off any/<oldEnvId>/<threadId>route the user was sitting on.Re-authenticate the desktop session after a swap. Each backend signs sessions with its own key, so the renderer's existing cookie 401s the new backend — including the WS upgrade, which on desktop primary authenticates via session cookie (no
wsTokenquery param). The renderer needs to re-run the desktop bootstrap exchange against the new backend before any reconnect attempts, and the bootstrap retry timeout has to cover cold WSL startup (15s → 60s in practice).Suppress WS connection-status events during the deliberate swap window. Without this, the brief disconnect-then-reconnect cycle around a backend swap surfaces in the connection-status atom and the UI flashes "disconnected" before settling. A scoped suppression around the swap keeps the lifecycle reporting quiet for the duration; once the new session opens, normal lifecycle reporting resumes.