mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
Merge pull request #138 from aakhter/cod-80-raise-terminal-defaults
Raise terminal history/scrollback/buffer defaults (50k→100k, 2MB→32MB)
This commit is contained in:
@@ -6,10 +6,9 @@
|
||||
* it easy to tune memory usage.
|
||||
*
|
||||
* Memory Budget Rationale (for 20 concurrent sessions):
|
||||
* - Terminal buffer: 2MB max × 20 = 40MB worst case
|
||||
* - Terminal buffer: 32MB max × 20 = 640MB worst case
|
||||
* - Text output: 1MB max × 20 = 20MB worst case
|
||||
* - Messages: ~1KB each × 1000 × 20 = 20MB worst case
|
||||
* - Total buffer overhead: ~80MB (acceptable for a long-running server)
|
||||
*
|
||||
* @module config/buffer-limits
|
||||
*/
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
/**
|
||||
* Defaults, bounds, and resolution for terminal history retention.
|
||||
*
|
||||
* Centralizes the terminal scrollback, tmux history-limit, and server PTY buffer
|
||||
* byte caps that were previously scattered as hardcoded literals. Each value is
|
||||
* overridable (env var or the settings object) and clamped to a sane range via
|
||||
* resolveTerminalHistoryConfig(). Defaults intentionally match the prior
|
||||
* hardcoded values, so introducing this module is behavior-neutral.
|
||||
* Raised defaults (the ones actually wired):
|
||||
* - tmux history-limit: 50,000 -> 100,000 lines (applied at session spawn)
|
||||
* - server PTY buffer cap: 2MB max / 1.5MB trim -> 32MB / 24MB (via buffer-limits.ts)
|
||||
* Browser xterm scrollback is a separate hardcoded DEFAULT_SCROLLBACK (50,000) in
|
||||
* src/web/public/constants.js and deliberately stays at 50k — 100k xterm lines per tab
|
||||
* is a mobile-memory hazard — so DEFAULT_TERMINAL_SCROLLBACK_LINES stays 50,000 to match.
|
||||
* The terminalScrollbackLines/terminalBufferMaxBytes/terminalBufferTrimBytes settings keys
|
||||
* remain schema-validated but inert (a follow-up wires them); only tmuxHistoryLimit is live.
|
||||
* All values remain env- and settings-overridable and bounds-clamped via
|
||||
* resolveTerminalHistoryConfig().
|
||||
*/
|
||||
|
||||
export const DEFAULT_TERMINAL_SCROLLBACK_LINES = 50_000;
|
||||
export const DEFAULT_TMUX_HISTORY_LIMIT = 50_000;
|
||||
export const DEFAULT_TMUX_HISTORY_LIMIT = 100_000;
|
||||
export const DEFAULT_TERMINAL_BUFFER_MAX_BYTES =
|
||||
parseInt(process.env.CODEMAN_MAX_TERMINAL_BUFFER || '', 10) || 2 * 1024 * 1024;
|
||||
export const DEFAULT_TERMINAL_BUFFER_TRIM_BYTES =
|
||||
parseInt(process.env.CODEMAN_TRIM_TERMINAL_TO || '', 10) || 1.5 * 1024 * 1024;
|
||||
parseInt(process.env.CODEMAN_MAX_TERMINAL_BUFFER || '', 10) || 32 * 1024 * 1024;
|
||||
// Trim must stay below the max: BufferAccumulator.trim() keeps the last trimSize chars, so a
|
||||
// trim >= max never shrinks the buffer — every append then re-joins the whole string (O(n²))
|
||||
// and memory overshoots the operator's cap (e.g. CODEMAN_MAX_TERMINAL_BUFFER=2097152 with no
|
||||
// trim env would leave the 24MB trim default in force). Clamp to 75% of the resolved max,
|
||||
// preserving the 24MB/32MB default ratio as trim hysteresis.
|
||||
export const DEFAULT_TERMINAL_BUFFER_TRIM_BYTES = Math.min(
|
||||
parseInt(process.env.CODEMAN_TRIM_TERMINAL_TO || '', 10) || 24 * 1024 * 1024,
|
||||
Math.floor(DEFAULT_TERMINAL_BUFFER_MAX_BYTES * 0.75)
|
||||
);
|
||||
|
||||
export const MIN_TERMINAL_SCROLLBACK_LINES = 1_000;
|
||||
export const MAX_TERMINAL_SCROLLBACK_LINES = 1_000_000;
|
||||
|
||||
+2
-1
@@ -1202,7 +1202,8 @@ export class TmuxManager extends EventEmitter implements TerminalMultiplexer {
|
||||
/* Already set globally as fallback */
|
||||
}),
|
||||
// Raise tmux scrollback from its 2000-line default so re-attach preserves
|
||||
// more context. Matches the xterm-side default in constants.js.
|
||||
// more context. Intentionally exceeds the xterm-side DEFAULT_SCROLLBACK (50k
|
||||
// in constants.js), which stays lower to protect browser/mobile memory.
|
||||
execAsync(`${this.tmux()} set-option -t "${muxName}" history-limit ${historyLimit}`, {
|
||||
timeout: EXEC_TIMEOUT_MS,
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import {
|
||||
resolveTerminalHistoryConfig,
|
||||
DEFAULT_TERMINAL_SCROLLBACK_LINES,
|
||||
@@ -22,11 +22,34 @@ describe('resolveTerminalHistoryConfig', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('defaults match the prior hardcoded values (behavior-neutral)', () => {
|
||||
expect(DEFAULT_TMUX_HISTORY_LIMIT).toBe(50_000);
|
||||
it('defaults raise tmux history to 100k and buffer caps to 32MB/24MB; browser scrollback stays 50k', () => {
|
||||
expect(DEFAULT_TMUX_HISTORY_LIMIT).toBe(100_000);
|
||||
// Matches the hardcoded browser-side DEFAULT_SCROLLBACK in src/web/public/constants.js —
|
||||
// deliberately NOT raised (100k xterm lines per tab is a mobile-memory hazard).
|
||||
expect(DEFAULT_TERMINAL_SCROLLBACK_LINES).toBe(50_000);
|
||||
expect(DEFAULT_TERMINAL_BUFFER_MAX_BYTES).toBe(2 * 1024 * 1024);
|
||||
expect(DEFAULT_TERMINAL_BUFFER_TRIM_BYTES).toBe(1.5 * 1024 * 1024);
|
||||
expect(DEFAULT_TERMINAL_BUFFER_MAX_BYTES).toBe(32 * 1024 * 1024);
|
||||
expect(DEFAULT_TERMINAL_BUFFER_TRIM_BYTES).toBe(24 * 1024 * 1024);
|
||||
});
|
||||
|
||||
it('clamps the env-derived trim default below an env-lowered max (CODEMAN_MAX_TERMINAL_BUFFER only)', async () => {
|
||||
const originalMax = process.env.CODEMAN_MAX_TERMINAL_BUFFER;
|
||||
const originalTrim = process.env.CODEMAN_TRIM_TERMINAL_TO;
|
||||
vi.resetModules();
|
||||
process.env.CODEMAN_MAX_TERMINAL_BUFFER = String(2 * 1024 * 1024);
|
||||
delete process.env.CODEMAN_TRIM_TERMINAL_TO;
|
||||
try {
|
||||
const mod = await import('../src/config/terminal-history.js');
|
||||
expect(mod.DEFAULT_TERMINAL_BUFFER_MAX_BYTES).toBe(2 * 1024 * 1024);
|
||||
// trim >= max would make BufferAccumulator.trim() a no-op (unbounded growth + O(n²) appends).
|
||||
expect(mod.DEFAULT_TERMINAL_BUFFER_TRIM_BYTES).toBeLessThan(mod.DEFAULT_TERMINAL_BUFFER_MAX_BYTES);
|
||||
expect(mod.DEFAULT_TERMINAL_BUFFER_TRIM_BYTES).toBe(Math.floor(2 * 1024 * 1024 * 0.75));
|
||||
} finally {
|
||||
if (originalMax === undefined) delete process.env.CODEMAN_MAX_TERMINAL_BUFFER;
|
||||
else process.env.CODEMAN_MAX_TERMINAL_BUFFER = originalMax;
|
||||
if (originalTrim === undefined) delete process.env.CODEMAN_TRIM_TERMINAL_TO;
|
||||
else process.env.CODEMAN_TRIM_TERMINAL_TO = originalTrim;
|
||||
vi.resetModules();
|
||||
}
|
||||
});
|
||||
|
||||
it('passes valid in-range values through unchanged', () => {
|
||||
@@ -80,9 +103,9 @@ describe('resolveTerminalHistoryConfig', () => {
|
||||
});
|
||||
|
||||
it('caps the default trim to a lowered max', () => {
|
||||
// Default trim (1.5MB) exceeds a 1MB max → trim must fall to the max.
|
||||
const cfg = resolveTerminalHistoryConfig({ terminalBufferMaxBytes: 1 * 1024 * 1024 });
|
||||
expect(cfg.terminalBufferTrimBytes).toBe(1 * 1024 * 1024);
|
||||
// Default trim (24MB) exceeds a 2MB max → trim must fall to the max.
|
||||
const cfg = resolveTerminalHistoryConfig({ terminalBufferMaxBytes: 2 * 1024 * 1024 });
|
||||
expect(cfg.terminalBufferTrimBytes).toBe(2 * 1024 * 1024);
|
||||
});
|
||||
|
||||
it('truncates fractional inputs to integers', () => {
|
||||
|
||||
Reference in New Issue
Block a user