From 6bb4a555248dc0ebde07b3c7f7ad0e4cf387e5d4 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 24 Apr 2026 00:09:35 +0200 Subject: [PATCH] fix(coding-agent): preserve final TUI frame on quit --- packages/coding-agent/CHANGELOG.md | 1 + .../src/modes/interactive/interactive-mode.ts | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 0cf98e80..35849ba5 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -24,6 +24,7 @@ ### Fixed +- Fixed `/quit` shutdown ordering to stop the TUI before extension UI teardown can repaint, preserving the final rendered frame while still emitting `session_shutdown` before process exit. - Fixed `SettingsManager.inMemory()` initial settings being lost after reloads triggered by SDK resource loading ([#3616](https://github.com/badlogic/pi-mono/issues/3616)) - Fixed `models.json` provider compatibility to accept `compat.supportsLongCacheRetention`, allowing proxies to opt out of long-retention cache fields when needed while long retention is enabled by default when requested ([#3543](https://github.com/badlogic/pi-mono/issues/3543)) - Fixed `--thinking xhigh` for `openai-codex` `gpt-5.5` so it is no longer downgraded to `high`. diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 401c78ca..c75b620d 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -3208,7 +3208,8 @@ export class InteractiveMode { /** * Gracefully shutdown the agent. - * Emits shutdown event to extensions, then exits. + * Stops the TUI before emitting shutdown events so extension UI cleanup cannot + * repaint the final frame while the process is exiting. */ private isShuttingDown = false; @@ -3216,17 +3217,13 @@ export class InteractiveMode { if (this.isShuttingDown) return; this.isShuttingDown = true; this.unregisterSignalHandlers(); - await this.runtimeHost.dispose(); - - // Wait for any pending renders to complete - // requestRender() uses process.nextTick(), so we wait one tick - await new Promise((resolve) => process.nextTick(resolve)); // Drain any in-flight Kitty key release events before stopping. // This prevents escape sequences from leaking to the parent shell over slow SSH. await this.ui.terminal.drainInput(1000); this.stop(); + await this.runtimeHost.dispose(); process.exit(0); }