From cf8416290d122763040e6ea2142c4312bba3b2ed Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sat, 21 Mar 2026 19:39:56 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20crash=20handling=20=E2=80=94=20save=20se?= =?UTF-8?q?ssion,=20kill=20agent,=20distinct=20exit=20codes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hardened shutdown/crash behavior: - Browser disconnect exits with code 2 (distinct from crash code 1) - emergencyCleanup kills agent subprocess and saves session state - Clean shutdown saves session before exit (chat history persists) - Clear user message on browser disconnect: "Run $B connect to reconnect" Co-Authored-By: Claude Opus 4.6 (1M context) --- browse/src/browser-manager.ts | 7 ++++--- browse/src/server.ts | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/browse/src/browser-manager.ts b/browse/src/browser-manager.ts index a8659701..d7eb6426 100644 --- a/browse/src/browser-manager.ts +++ b/browse/src/browser-manager.ts @@ -242,12 +242,13 @@ export class BrowserManager { await this.newTab(); } - // Browser disconnect handler + // Browser disconnect handler — exit code 2 distinguishes from crashes (1) if (this.browser) { this.browser.on('disconnected', () => { if (this.intentionalDisconnect) return; - console.error('[browse] Real browser disconnected.'); - process.exit(1); + console.error('[browse] Real browser disconnected (user closed or crashed).'); + console.error('[browse] Run `$B connect` to reconnect.'); + process.exit(2); }); } diff --git a/browse/src/server.ts b/browse/src/server.ts index 8463d83b..f4d2b56b 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -601,6 +601,7 @@ async function shutdown() { console.log('[browse] Shutting down...'); killAgent(); messageQueue = []; + saveSession(); // Persist chat history before exit if (agentHealthInterval) clearInterval(agentHealthInterval); clearInterval(flushInterval); clearInterval(idleCheckInterval); @@ -624,10 +625,15 @@ async function shutdown() { process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); -// Emergency cleanup for crashes (OOM, uncaught exceptions) +// Emergency cleanup for crashes (OOM, uncaught exceptions, browser disconnect) function emergencyCleanup() { if (isShuttingDown) return; isShuttingDown = true; + // Kill agent subprocess if running + try { killAgent(); } catch {} + // Save session state so chat history persists across crashes + try { saveSession(); } catch {} + // Clean Chromium profile locks const profileDir = path.join(process.env.HOME || '/tmp', '.gstack', 'chromium-profile'); for (const lockFile of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) { try { fs.unlinkSync(path.join(profileDir, lockFile)); } catch {}