From c2222b447b4e69f81338fd9d8cfacab37135c22e Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 2 Jul 2026 04:00:44 -0400 Subject: [PATCH] Use bundled ConPTY for LocalPtyProvider spawns on Windows The daemon spawn path already passes useConptyDll: true (bundled modern ConPTY); LocalPtyProvider spawns - degraded mode and fresh-local routing - silently ran on the legacy system ConPTY, which the daemon path's own comment blames for full-width TUI scrollback corruption. --- .../local-pty-utils-windows-fallback.test.ts | 4 ++-- src/main/providers/local-pty-utils.ts | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/providers/local-pty-utils-windows-fallback.test.ts b/src/main/providers/local-pty-utils-windows-fallback.test.ts index 48b35cd27c0..0de16d03f17 100644 --- a/src/main/providers/local-pty-utils-windows-fallback.test.ts +++ b/src/main/providers/local-pty-utils-windows-fallback.test.ts @@ -76,13 +76,13 @@ describe('spawnShellWithFallback on Windows', () => { 1, PWSH7, attempts[0].shellArgs, - expect.objectContaining({ cwd: 'C:\\repo' }) + expect.objectContaining({ cwd: 'C:\\repo', useConptyDll: true }) ) expect(ptySpawn).toHaveBeenNthCalledWith( 2, WINDOWS_POWERSHELL, attempts[1].shellArgs, - expect.objectContaining({ cwd: 'C:\\repo' }) + expect.objectContaining({ cwd: 'C:\\repo', useConptyDll: true }) ) }) diff --git a/src/main/providers/local-pty-utils.ts b/src/main/providers/local-pty-utils.ts index 0ad6effc9c9..c8eb87c249d 100644 --- a/src/main/providers/local-pty-utils.ts +++ b/src/main/providers/local-pty-utils.ts @@ -160,6 +160,14 @@ export type ShellSpawnResult = { * executables with per-shell args, so when the primary fails we retry with the * next safe shell instead of leaving the user with no terminal. */ +// Why: match the daemon spawn path (pty-subprocess.ts) — the bundled ConPTY +// has the modern wrap-marker behavior xterm expects; legacy system ConPTY can +// corrupt full-width TUI rows in scrollback. Without this, degraded-mode and +// fresh-local spawns silently behave differently from daemon terminals. +function windowsConptyDllOptions(): { useConptyDll: true } | Record { + return process.platform === 'win32' ? { useConptyDll: true } : {} +} + function spawnWindowsFallbackChain( params: ShellSpawnParams, primaryError: string @@ -174,7 +182,8 @@ function spawnWindowsFallbackChain( cols, rows, cwd: attempt.effectiveCwd, - env + env, + ...windowsConptyDllOptions() }) console.warn( `[pty] Primary shell "${params.shellPath}" failed (${primaryError}), fell back to "${attempt.shellPath}"` @@ -217,7 +226,14 @@ export function spawnShellWithFallback(params: ShellSpawnParams): ShellSpawnResu if (!primaryError) { try { return { - process: ptySpawn(shellPath, shellArgs, { name: termName, cols, rows, cwd, env }), + process: ptySpawn(shellPath, shellArgs, { + name: termName, + cols, + rows, + cwd, + env, + ...windowsConptyDllOptions() + }), shellPath } } catch (err) {