Repository navigation
fix: solve #3235 — reduce EDR/security agent CPU from idle terminals #3236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,7 @@ import { SUPERSET_DIR_NAME } from "shared/constants"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MAX_HISTORY_BYTES = 5 * 1024 * 1024; // 5MB per session | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MAX_PENDING_WRITE_BYTES = 256 * 1024; // cap in-memory backlog when disk is slow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DRAIN_TIMEOUT_MS = 1000; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const WRITE_DEBOUNCE_MS = 250; // debounce disk writes to reduce filesystem events | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const HISTORY_DIR_MODE = 0o700; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const HISTORY_FILE_MODE = 0o600; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -135,6 +136,9 @@ export class HistoryWriter { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private pendingWriteBytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private warnedCapReached = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private warnedBackpressureDrop = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private debounceBuffer: string[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private debounceBufferBytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private debounceTimer: ReturnType<typeof setTimeout> | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspaceId: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -249,28 +253,56 @@ export class HistoryWriter { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Respect filesystem backpressure. When disk is slow, stop feeding the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // stream buffer and keep a small in-memory backlog; beyond that we drop. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.isBackpressured || this.pendingWrites.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.pendingWriteBytes + bytes > MAX_PENDING_WRITE_BYTES) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.warnedBackpressureDrop) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.warnedBackpressureDrop = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `[HistoryWriter] Write backlog cap reached for ${this.paneId} (${MAX_PENDING_WRITE_BYTES} bytes); dropping history until drain`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bytesWritten += bytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBuffer.push(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBufferBytes += bytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.debounceTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer = setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.flushDebounceBuffer(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, WRITE_DEBOUNCE_MS); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer.unref(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.streamErrored = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.pendingWrites.push({ data, bytes }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.pendingWriteBytes += bytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bytesWritten += bytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private flushDebounceBuffer(): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.closed || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.streamErrored || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !this.stream || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBuffer.length === 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const combined = this.debounceBuffer.join(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bytes = this.debounceBufferBytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBuffer = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBufferBytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Respect filesystem backpressure. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.isBackpressured || this.pendingWrites.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.pendingWriteBytes + bytes > MAX_PENDING_WRITE_BYTES) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.warnedBackpressureDrop) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.warnedBackpressureDrop = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `[HistoryWriter] Write backlog cap reached for ${this.paneId} (${MAX_PENDING_WRITE_BYTES} bytes); dropping history until drain`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // node-pty produces UTF-8 strings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bytesWritten += bytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ok = this.stream.write(data, "utf8"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.pendingWrites.push({ data: combined, bytes }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.pendingWriteBytes += bytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+289
to
+303
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When the backlog cap is hit, the buffered chunk is dropped but bytesWritten is never rolled back, so history output can be suppressed early even though the data was never written. Adjust bytesWritten when dropping the debounce buffer under backpressure. Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ok = this.stream.write(combined, "utf8"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!ok) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.isBackpressured = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -317,12 +349,22 @@ export class HistoryWriter { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Flush debounce buffer first | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.debounceTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearTimeout(this.debounceTimer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.flushDebounceBuffer(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.flushPendingWrites(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // drain only fires after stream.write() returned false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.isBackpressured) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Promise<void>((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.flushPendingWrites(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Cork and uncork forces a flush | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.stream?.once("drain", resolve); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If nothing to drain, resolve immediately | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.stream?.writableLength === 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.isBackpressured) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolve(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -335,6 +377,13 @@ export class HistoryWriter { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.closed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.debounceTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearTimeout(this.debounceTimer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.flushDebounceBuffer(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.closed = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Best-effort: flush any pending backlog before closing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -408,6 +457,12 @@ export class HistoryWriter { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bytesWritten = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.warnedCapReached = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.warnedBackpressureDrop = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.debounceTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearTimeout(this.debounceTimer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBuffer = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBufferBytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Reset metadata with new start time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.metadata.startedAt = new Date().toISOString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -433,6 +488,12 @@ export class HistoryWriter { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.stream = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.pendingWrites = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.pendingWriteBytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.debounceTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearTimeout(this.debounceTimer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceTimer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBuffer = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.debounceBufferBytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.closed = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Delete the directory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,15 +8,18 @@ import { | |||||||||||
| } from "./port-scanner"; | ||||||||||||
| import type { TerminalSession } from "./types"; | ||||||||||||
|
|
||||||||||||
| // How often to poll for port changes (in ms) | ||||||||||||
| const SCAN_INTERVAL_MS = 2500; | ||||||||||||
| // safety-net sweep; hint scans handle dev-server starts | ||||||||||||
| export const SCAN_INTERVAL_MS = 10_000; | ||||||||||||
|
|
||||||||||||
| // Delay before scanning after a port hint is detected (in ms) | ||||||||||||
| const HINT_SCAN_DELAY_MS = 500; | ||||||||||||
|
|
||||||||||||
| // Ports to ignore (common system ports that are usually not dev servers) | ||||||||||||
| const IGNORED_PORTS = new Set([22, 80, 443, 5432, 3306, 6379, 27017]); | ||||||||||||
|
|
||||||||||||
| // skipped by periodic scan; hint scans still fire immediately | ||||||||||||
| export const IDLE_SESSION_TIMEOUT_MS = 60_000; | ||||||||||||
|
|
||||||||||||
| /** | ||||||||||||
| * Check if terminal output contains hints that a port may have been opened. | ||||||||||||
| * Common patterns from dev servers, test frameworks, etc. | ||||||||||||
|
|
@@ -64,6 +67,8 @@ class PortManager extends EventEmitter { | |||||||||||
| private scanInterval: ReturnType<typeof setInterval> | null = null; | ||||||||||||
| private pendingHintScans = new Map<string, ReturnType<typeof setTimeout>>(); | ||||||||||||
| private isScanning = false; | ||||||||||||
| /** Tracks the last time a port-hint was seen for each pane, used to skip idle sessions */ | ||||||||||||
| private lastHintActivity = new Map<string, number>(); | ||||||||||||
|
|
||||||||||||
| constructor() { | ||||||||||||
| super(); | ||||||||||||
|
|
@@ -84,6 +89,7 @@ class PortManager extends EventEmitter { | |||||||||||
| this.sessions.delete(paneId); | ||||||||||||
| this.removePortsForPane(paneId); | ||||||||||||
| this.clearPendingHintScan(paneId); | ||||||||||||
| this.lastHintActivity.delete(paneId); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /** | ||||||||||||
|
|
@@ -106,10 +112,12 @@ class PortManager extends EventEmitter { | |||||||||||
| this.daemonSessions.delete(paneId); | ||||||||||||
| this.removePortsForPane(paneId); | ||||||||||||
| this.clearPendingHintScan(paneId); | ||||||||||||
| this.lastHintActivity.delete(paneId); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| checkOutputForHint(data: string, paneId: string): void { | ||||||||||||
| if (!containsPortHint(data)) return; | ||||||||||||
| this.lastHintActivity.set(paneId, Date.now()); | ||||||||||||
| this.scheduleHintScan(paneId); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -220,10 +228,21 @@ class PortManager extends EventEmitter { | |||||||||||
| }; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| private isSessionIdle(paneId: string): boolean { | ||||||||||||
| for (const port of this.ports.values()) { | ||||||||||||
| if (port.paneId === paneId) return false; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| const lastHint = this.lastHintActivity.get(paneId); | ||||||||||||
| if (lastHint === undefined) return true; | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Sessions that never emit a port hint are now treated as idle immediately, so the periodic scan will never run for them. That breaks the safety-net sweep and can prevent port detection for servers that don’t print a hint. Prompt for AI agents
Suggested change
|
||||||||||||
| return Date.now() - lastHint > IDLE_SESSION_TIMEOUT_MS; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| private async collectRegularSessionPids(scanState: ScanState): Promise<void> { | ||||||||||||
| const tasks: Promise<void>[] = []; | ||||||||||||
| for (const [paneId, { session, workspaceId }] of this.sessions) { | ||||||||||||
| if (!session.isAlive) continue; | ||||||||||||
| if (this.isSessionIdle(paneId)) continue; | ||||||||||||
| tasks.push( | ||||||||||||
| this.collectPidTree({ | ||||||||||||
| paneId, | ||||||||||||
|
|
@@ -240,6 +259,7 @@ class PortManager extends EventEmitter { | |||||||||||
| const tasks: Promise<void>[] = []; | ||||||||||||
| for (const [paneId, { workspaceId, pid }] of this.daemonSessions) { | ||||||||||||
| if (pid === null) continue; | ||||||||||||
| if (this.isSessionIdle(paneId)) continue; | ||||||||||||
| tasks.push( | ||||||||||||
| this.collectPidTree({ | ||||||||||||
| paneId, | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bytesWrittenover-counted when chunks are dropped under backpressurebytesWrittenis unconditionally incremented inwrite()before data reaches the debounce buffer:But in
flushDebounceBuffer(), when the in-memory backlog is full (pendingWriteBytes + bytes > MAX_PENDING_WRITE_BYTES), the batch is silently dropped with no rollback ofbytesWritten. This inflates the counter, causing theMAX_HISTORY_BYTEScap check inwrite()to fire prematurely — terminal history gets truncated well before the 5 MB limit when disk I/O is slow.Before this PR,
bytesWrittenwas only incremented on paths that actually wrote or queued data. The refactor moved the increment before the drop gate influshDebounceBuffer(), creating a regression.Fix: remove
this.bytesWritten += bytesfromwrite()and add it influshDebounceBuffer()only on the two paths that actually persist data: