diff --git a/src/ai-checker-base.ts b/src/ai-checker-base.ts index 23a6bbfc..64d50a87 100644 --- a/src/ai-checker-base.ts +++ b/src/ai-checker-base.ts @@ -28,8 +28,7 @@ import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { EventEmitter } from 'node:events'; -import { getAugmentedPath } from './utils/claude-cli-resolver.js'; -import { ANSI_ESCAPE_PATTERN_SIMPLE } from './utils/index.js'; +import { getAugmentedPath, ANSI_ESCAPE_PATTERN_SIMPLE } from './utils/index.js'; import { AI_CHECK_MAX_BACKOFF_MS } from './config/ai-defaults.js'; // ========== Security Validation ========== diff --git a/src/config/buffer-limits.ts b/src/config/buffer-limits.ts index 035d1945..921e5f79 100644 --- a/src/config/buffer-limits.ts +++ b/src/config/buffer-limits.ts @@ -56,11 +56,6 @@ export const TRIM_TEXT_TO = 768 * 1024; // 768KB */ export const MAX_MESSAGES = 1000; -/** - * Number of messages to keep when trimming (80% of max). - */ -export const TRIM_MESSAGES_TO = 800; - // ============================================================================ // Line Buffer Limits // ============================================================================ @@ -85,3 +80,14 @@ export const MAX_RESPAWN_BUFFER_SIZE = 1 * 1024 * 1024; // 1MB * Size to trim respawn buffer to when max is exceeded. */ export const TRIM_RESPAWN_BUFFER_TO = 512 * 1024; // 512KB + +// ============================================================================ +// File Peek Limits +// ============================================================================ + +/** + * Maximum bytes to read when peeking at the beginning of a file. + * Used with `createReadStream({ end })` (inclusive) to read the first 8KB, + * which is enough to extract metadata from the first few JSONL lines. + */ +export const FILE_PEEK_BYTES = 8 * 1024 - 1; // 8KB (inclusive end offset) diff --git a/src/config/terminal-limits.ts b/src/config/terminal-limits.ts index 2df2dc7b..8d8f563c 100644 --- a/src/config/terminal-limits.ts +++ b/src/config/terminal-limits.ts @@ -11,11 +11,5 @@ /** Max input length per API request (bytes) */ export const MAX_INPUT_LENGTH = 64 * 1024; -/** Max terminal columns for resize requests */ -export const MAX_TERMINAL_COLS = 500; - -/** Max terminal rows for resize requests */ -export const MAX_TERMINAL_ROWS = 200; - /** Max session name length (chars) */ export const MAX_SESSION_NAME_LENGTH = 128; diff --git a/src/plan-orchestrator.ts b/src/plan-orchestrator.ts index 902d4623..1135458f 100644 --- a/src/plan-orchestrator.ts +++ b/src/plan-orchestrator.ts @@ -521,7 +521,7 @@ export class PlanOrchestrator { } finally { // Always clean up session and progress interval — centralizing here // prevents the race where cancel() and catch both try to manage the set - await session.stop().catch(() => {}); + await session.stop().catch(() => {}); // Ignore - session cleanup is best-effort in finally block this.runningSessions.delete(session); clearInterval(progressInterval); } @@ -651,7 +651,7 @@ export class PlanOrchestrator { } finally { // Always clean up session and progress interval — centralizing here // prevents the race where cancel() and catch both try to manage the set - await session.stop().catch(() => {}); + await session.stop().catch(() => {}); // Ignore - session cleanup is best-effort in finally block this.runningSessions.delete(session); clearInterval(progressInterval); } diff --git a/src/respawn-controller.ts b/src/respawn-controller.ts index 55ecbd7b..b3b33e03 100644 --- a/src/respawn-controller.ts +++ b/src/respawn-controller.ts @@ -49,8 +49,7 @@ import { Session } from './session.js'; import { AiIdleChecker, type AiCheckResult, type AiCheckState } from './ai-idle-checker.js'; import { AiPlanChecker, type AiPlanCheckResult } from './ai-plan-checker.js'; import type { TeamWatcher } from './team-watcher.js'; -import { BufferAccumulator } from './utils/buffer-accumulator.js'; -import { ANSI_ESCAPE_PATTERN_SIMPLE, assertNever, CleanupManager } from './utils/index.js'; +import { BufferAccumulator, ANSI_ESCAPE_PATTERN_SIMPLE, assertNever, CleanupManager } from './utils/index.js'; import { MAX_RESPAWN_BUFFER_SIZE, TRIM_RESPAWN_BUFFER_TO as RESPAWN_BUFFER_TRIM_SIZE } from './config/buffer-limits.js'; import { isCompletionMessage, diff --git a/src/session-cli-builder.ts b/src/session-cli-builder.ts index 94a75a85..efc58c7f 100644 --- a/src/session-cli-builder.ts +++ b/src/session-cli-builder.ts @@ -9,7 +9,7 @@ */ import type { ClaudeMode } from './types.js'; -import { getAugmentedPath } from './utils/claude-cli-resolver.js'; +import { getAugmentedPath } from './utils/index.js'; /** * Build Claude CLI permission flags based on the configured mode. diff --git a/src/session.ts b/src/session.ts index 57cb6e7a..1296fe5f 100644 --- a/src/session.ts +++ b/src/session.ts @@ -48,8 +48,8 @@ import type { TerminalMultiplexer, MuxSession } from './mux-interface.js'; import { TaskTracker, type BackgroundTask } from './task-tracker.js'; import { RalphTracker } from './ralph-tracker.js'; import { BashToolParser } from './bash-tool-parser.js'; -import { BufferAccumulator } from './utils/buffer-accumulator.js'; import { + BufferAccumulator, ANSI_ESCAPE_PATTERN_FULL, TOKEN_PATTERN, SPINNER_PATTERN, diff --git a/src/subagent-watcher.ts b/src/subagent-watcher.ts index 2a55e97d..6079c23c 100644 --- a/src/subagent-watcher.ts +++ b/src/subagent-watcher.ts @@ -17,7 +17,7 @@ * Tracks per-agent: status, token counts, model, description, tool call count, liveness (PID). * * @dependencies config/map-limits (MAX_TRACKED_AGENTS, PENDING_TOOL_CALL_TTL_MS), - * utils (CleanupManager, KeyedDebouncer) + * config/buffer-limits (FILE_PEEK_BYTES), utils (CleanupManager, KeyedDebouncer) * @consumedby web/server (SSE broadcast), session (subagent-session correlation) * @emits subagent:discovered, subagent:updated, subagent:tool_call, subagent:tool_result, * subagent:progress, subagent:message, subagent:completed @@ -35,6 +35,7 @@ import { execFile } from 'node:child_process'; import { readFile, readdir, stat as statAsync } from 'node:fs/promises'; import { PENDING_TOOL_CALL_TTL_MS, MAX_PENDING_TOOL_CALLS, MAX_TRACKED_AGENTS } from './config/map-limits.js'; import { STALE_DATA_MAX_AGE_MS } from './config/server-timing.js'; +import { FILE_PEEK_BYTES } from './config/buffer-limits.js'; import { CleanupManager, KeyedDebouncer } from './utils/index.js'; // ========== Types ========== @@ -1009,7 +1010,7 @@ export class SubagentWatcher extends EventEmitter { private async extractDescriptionFromFile(filePath: string): Promise { try { // Only read the first 8KB — more than enough for 5 JSONL lines - const stream = createReadStream(filePath, { end: 8191 }); + const stream = createReadStream(filePath, { end: FILE_PEEK_BYTES }); const rl = createInterface({ input: stream }); return await new Promise((resolve) => { @@ -1141,10 +1142,10 @@ export class SubagentWatcher extends EventEmitter { if (this.fileAgentContext.has(filePath)) { // Known file — handle content change - this.handleFileChange(filePath).catch(() => {}); + this.handleFileChange(filePath).catch(() => {}); // Ignore - errors logged internally, don't crash watcher callback } else { // New file — register it - this.registerAgentFile(filePath, projectHash, sessionId).catch(() => {}); + this.registerAgentFile(filePath, projectHash, sessionId).catch(() => {}); // Ignore - errors logged internally, don't crash watcher callback } }); }); diff --git a/src/team-watcher.ts b/src/team-watcher.ts index a6f353ac..1822c0e6 100644 --- a/src/team-watcher.ts +++ b/src/team-watcher.ts @@ -61,7 +61,7 @@ export class TeamWatcher extends EventEmitter { persistent: false, }); - const teamsHandler = () => this.pollAsync().catch(() => {}); + const teamsHandler = () => this.pollAsync().catch(() => {}); // Ignore - poll errors are non-fatal, next poll will retry this.teamsWatcher.on('add', teamsHandler); this.teamsWatcher.on('change', teamsHandler); this.teamsWatcher.on('unlink', teamsHandler); @@ -82,8 +82,8 @@ export class TeamWatcher extends EventEmitter { persistent: false, }); - this.tasksWatcher.on('add', () => this.pollTasks().catch(() => {})); - this.tasksWatcher.on('change', () => this.pollTasks().catch(() => {})); + this.tasksWatcher.on('add', () => this.pollTasks().catch(() => {})); // Ignore - poll errors are non-fatal, next poll will retry + this.tasksWatcher.on('change', () => this.pollTasks().catch(() => {})); // Ignore - poll errors are non-fatal, next poll will retry this.tasksWatcher.on('error', (err) => { console.warn('[TeamWatcher] chokidar tasks watcher error:', err); }); @@ -95,11 +95,11 @@ export class TeamWatcher extends EventEmitter { stop(): void { // Close chokidar watchers if (this.teamsWatcher) { - this.teamsWatcher.close().catch(() => {}); + this.teamsWatcher.close().catch(() => {}); // Ignore - watcher cleanup is best-effort during shutdown this.teamsWatcher = null; } if (this.tasksWatcher) { - this.tasksWatcher.close().catch(() => {}); + this.tasksWatcher.close().catch(() => {}); // Ignore - watcher cleanup is best-effort during shutdown this.tasksWatcher = null; } if (this.pollTimer) { diff --git a/src/tmux-manager.ts b/src/tmux-manager.ts index 34c90eee..5d68f802 100644 --- a/src/tmux-manager.ts +++ b/src/tmux-manager.ts @@ -40,8 +40,7 @@ import { type SessionMode, type OpenCodeConfig, } from './types.js'; -import { wrapWithNice } from './utils/nice-wrapper.js'; -import { SAFE_PATH_PATTERN } from './utils/regex-patterns.js'; +import { wrapWithNice, SAFE_PATH_PATTERN, findClaudeDir, resolveOpenCodeDir } from './utils/index.js'; import type { TerminalMultiplexer, MuxSession, @@ -50,11 +49,6 @@ import type { RespawnPaneOptions, } from './mux-interface.js'; -// Claude CLI PATH resolution — shared utility -import { findClaudeDir } from './utils/claude-cli-resolver.js'; -// OpenCode CLI PATH resolution -import { resolveOpenCodeDir } from './utils/opencode-cli-resolver.js'; - // ============================================================================ // Timing Constants // ============================================================================ diff --git a/src/web/routes/ralph-routes.ts b/src/web/routes/ralph-routes.ts index b1029c8a..51d332b9 100644 --- a/src/web/routes/ralph-routes.ts +++ b/src/web/routes/ralph-routes.ts @@ -505,7 +505,7 @@ export function registerRalphRoutes( settings.lastUsedCase = caseName; const dir = dirname(SETTINGS_PATH); if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); - fs.writeFile(SETTINGS_PATH, JSON.stringify(settings, null, 2)).catch(() => {}); + fs.writeFile(SETTINGS_PATH, JSON.stringify(settings, null, 2)).catch(() => {}); // Ignore - persisting lastUsedCase is non-critical } catch { /* non-critical */ } diff --git a/src/web/schemas.ts b/src/web/schemas.ts index 2110d00b..827faf50 100644 --- a/src/web/schemas.ts +++ b/src/web/schemas.ts @@ -8,7 +8,7 @@ */ import { z } from 'zod'; -import { SAFE_PATH_PATTERN } from '../utils/regex-patterns.js'; +import { SAFE_PATH_PATTERN } from '../utils/index.js'; // ========== Path Validation ==========