diff --git a/src/ClaudeDo.Ui/Services/PtyTerminalSession.cs b/src/ClaudeDo.Ui/Services/PtyTerminalSession.cs index ee8a8bf9..3c7e5ffb 100644 --- a/src/ClaudeDo.Ui/Services/PtyTerminalSession.cs +++ b/src/ClaudeDo.Ui/Services/PtyTerminalSession.cs @@ -192,12 +192,24 @@ public sealed class PtyTerminalSession : IDisposable return (Task)s_sendToPty.Invoke(view, new object[] { data, CancellationToken.None })!; } + /// Kills the child and closes its pseudo console; falls back to a plain kill if the private seam is gone. public void Kill() { - try { _control?.Kill(); } + try + { + // TerminalControl.Kill() leaves the pseudo console open (one orphaned conhost.exe per tile); + // only the private CleanupProcess disposes it, and BeginReparent suppressed its detach-time call. + if (_control is { } control && s_terminalViewField?.GetValue(control) is TerminalView view && s_cleanupProcess is not null) + s_cleanupProcess.Invoke(view, null); + else + _control?.Kill(); + } catch (Exception) { /* already exited */ } } + private static readonly MethodInfo? s_cleanupProcess = + typeof(TerminalView).GetMethod("CleanupProcess", BindingFlags.Instance | BindingFlags.NonPublic); + public void Dispose() { if (_disposed) return;