From a82db604a4995a3f5bba65c346fba98f3c1b3e4c Mon Sep 17 00:00:00 2001 From: Matt Leaverton Date: Thu, 2 Apr 2026 14:00:42 -0500 Subject: [PATCH 1/2] fix: raise default scrollback sizes for coding CLI sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default client scrollback: 5000 → 10000 lines Default server buffer: 64KB → 512KB (max 4MB) Default ReplayRing: 256KB → 1MB Chars-per-line estimate: 200 → 300 Co-Authored-By: Claude Opus 4.6 (1M context) --- server/terminal-registry.ts | 6 +++--- server/terminal-stream/replay-ring.ts | 2 +- shared/settings.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/terminal-registry.ts b/server/terminal-registry.ts index fe4980534..581542cfb 100644 --- a/server/terminal-registry.ts +++ b/server/terminal-registry.ts @@ -25,10 +25,10 @@ import { getOpencodeEnvOverrides, resolveOpencodeLaunchModel } from './opencode- import { generateMcpInjection, cleanupMcpConfig } from './mcp/config-writer.js' const MAX_WS_BUFFERED_AMOUNT = Number(process.env.MAX_WS_BUFFERED_AMOUNT || 2 * 1024 * 1024) -const DEFAULT_MAX_SCROLLBACK_CHARS = Number(process.env.MAX_SCROLLBACK_CHARS || 64 * 1024) +const DEFAULT_MAX_SCROLLBACK_CHARS = Number(process.env.MAX_SCROLLBACK_CHARS || 512 * 1024) const MIN_SCROLLBACK_CHARS = 64 * 1024 -const MAX_SCROLLBACK_CHARS = 2 * 1024 * 1024 -const APPROX_CHARS_PER_LINE = 200 +const MAX_SCROLLBACK_CHARS = 4 * 1024 * 1024 +const APPROX_CHARS_PER_LINE = 300 const MAX_TERMINALS = Number(process.env.MAX_TERMINALS || 50) const DEFAULT_MAX_PENDING_SNAPSHOT_CHARS = 512 * 1024 const OUTPUT_FLUSH_MS = Number(process.env.OUTPUT_FLUSH_MS || process.env.MOBILE_OUTPUT_FLUSH_MS || 40) diff --git a/server/terminal-stream/replay-ring.ts b/server/terminal-stream/replay-ring.ts index 505d03db6..ffc6635b7 100644 --- a/server/terminal-stream/replay-ring.ts +++ b/server/terminal-stream/replay-ring.ts @@ -6,7 +6,7 @@ export type ReplayFrame = { at: number } -export const DEFAULT_TERMINAL_REPLAY_RING_MAX_BYTES = 256 * 1024 +export const DEFAULT_TERMINAL_REPLAY_RING_MAX_BYTES = 1024 * 1024 function resolveMaxBytes(explicitMaxBytes?: number): number { if (typeof explicitMaxBytes === 'number' && Number.isFinite(explicitMaxBytes) && explicitMaxBytes > 0) { diff --git a/shared/settings.ts b/shared/settings.ts index 40dfd917e..cb231fc27 100644 --- a/shared/settings.ts +++ b/shared/settings.ts @@ -624,7 +624,7 @@ export function createDefaultServerSettings(options: SettingsDefaultsOptions = { autoKillIdleMinutes: 180, }, terminal: { - scrollback: 5000, + scrollback: 10000, }, panes: { defaultNewPane: 'ask', From 53ea01d3819d83217c38ff23f485eb3ca887f60d Mon Sep 17 00:00:00 2001 From: Matt Leaverton Date: Thu, 2 Apr 2026 14:01:39 -0500 Subject: [PATCH 2/2] fix: proxy-router sends 502 response on WebSocket ECONNREFUSED Previously the WebSocket proxy silently destroyed the socket when the target port was unreachable, causing the client to retry endlessly and thrash the UI. Now sends a proper HTTP 502 response before closing so the client can handle the error cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) --- server/proxy-router.ts | 3 +++ test/unit/server/production-edge-cases.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/proxy-router.ts b/server/proxy-router.ts index 578c42248..a1c4d1234 100644 --- a/server/proxy-router.ts +++ b/server/proxy-router.ts @@ -206,6 +206,9 @@ export function attachProxyUpgradeHandler(server: http.Server): void { proxySocket.on('error', (err) => { log.warn({ err, targetPort, path: targetPath }, 'WebSocket proxy connection failed') + if (socket.writable) { + socket.write('HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\n\r\nExtension server on port ' + targetPort + ' is unavailable\r\n') + } socket.destroy() }) diff --git a/test/unit/server/production-edge-cases.test.ts b/test/unit/server/production-edge-cases.test.ts index 929ff26fb..7e118d92b 100644 --- a/test/unit/server/production-edge-cases.test.ts +++ b/test/unit/server/production-edge-cases.test.ts @@ -317,9 +317,9 @@ describe('TerminalRegistry Production Edge Cases', () => { emitData(largeChunk) } - // Buffer should be capped at DEFAULT_MAX_SCROLLBACK_CHARS (64KB default) + // Buffer should be capped at DEFAULT_MAX_SCROLLBACK_CHARS (512KB default) const snapshot = record.buffer.snapshot() - expect(snapshot.length).toBeLessThanOrEqual(64 * 1024) + expect(snapshot.length).toBeLessThanOrEqual(512 * 1024) }) it('idle monitor timer is created and runs', () => {