fix(core): remove shell outputChunks buffer caching to prevent memory bloat and sanitize prompt input (#23751)

This commit is contained in:
Spencer
2026-03-26 17:16:07 -04:00
committed by GitHub
parent 30397816da
commit d25ce0e143
4 changed files with 37 additions and 39 deletions

View File

@@ -120,7 +120,8 @@ interface ActiveChildProcess {
state: {
output: string;
truncated: boolean;
outputChunks: Buffer[];
sniffChunks: Buffer[];
binaryBytesReceived: number;
};
}
@@ -493,7 +494,8 @@ export class ShellExecutionService {
const state = {
output: '',
truncated: false,
outputChunks: [] as Buffer[],
sniffChunks: [] as Buffer[],
binaryBytesReceived: 0,
};
if (child.pid) {
@@ -563,14 +565,19 @@ export class ShellExecutionService {
}
}
state.outputChunks.push(data);
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
state.sniffChunks.push(data);
} else if (!isStreamingRawContent) {
state.binaryBytesReceived += data.length;
}
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
const sniffBuffer = Buffer.concat(state.outputChunks.slice(0, 20));
const sniffBuffer = Buffer.concat(state.sniffChunks.slice(0, 20));
sniffedBytes = sniffBuffer.length;
if (isBinary(sniffBuffer)) {
isStreamingRawContent = false;
state.binaryBytesReceived = sniffBuffer.length;
const event: ShellOutputEvent = { type: 'binary_detected' };
onOutputEvent(event);
if (child.pid) {
@@ -610,10 +617,7 @@ export class ShellExecutionService {
}
}
} else {
const totalBytes = state.outputChunks.reduce(
(sum, chunk) => sum + chunk.length,
0,
);
const totalBytes = state.binaryBytesReceived;
const event: ShellOutputEvent = {
type: 'binary_progress',
bytesReceived: totalBytes,
@@ -629,7 +633,7 @@ export class ShellExecutionService {
code: number | null,
signal: NodeJS.Signals | null,
) => {
const { finalBuffer } = cleanup();
cleanup();
let combinedOutput = state.output;
if (state.truncated) {
@@ -644,7 +648,7 @@ export class ShellExecutionService {
const exitSignal = signal ? os.constants.signals[signal] : null;
const resultPayload: ShellExecutionResult = {
rawOutput: finalBuffer,
rawOutput: Buffer.from(''),
output: finalStrippedOutput,
exitCode,
signal: exitSignal,
@@ -733,8 +737,7 @@ export class ShellExecutionService {
}
}
const finalBuffer = Buffer.concat(state.outputChunks);
return { finalBuffer };
return;
}
return { pid: child.pid, result };
@@ -864,7 +867,8 @@ export class ShellExecutionService {
let processingChain = Promise.resolve();
let decoder: TextDecoder | null = null;
let output: string | AnsiOutput | null = null;
const outputChunks: Buffer[] = [];
const sniffChunks: Buffer[] = [];
let binaryBytesReceived = 0;
const error: Error | null = null;
let exited = false;
@@ -995,14 +999,19 @@ export class ShellExecutionService {
}
}
outputChunks.push(data);
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
sniffChunks.push(data);
} else if (!isStreamingRawContent) {
binaryBytesReceived += data.length;
}
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
const sniffBuffer = Buffer.concat(outputChunks.slice(0, 20));
const sniffBuffer = Buffer.concat(sniffChunks.slice(0, 20));
sniffedBytes = sniffBuffer.length;
if (isBinary(sniffBuffer)) {
isStreamingRawContent = false;
binaryBytesReceived = sniffBuffer.length;
const event: ShellOutputEvent = { type: 'binary_detected' };
onOutputEvent(event);
ExecutionLifecycleService.emitEvent(ptyPid, event);
@@ -1027,10 +1036,7 @@ export class ShellExecutionService {
resolveChunk();
});
} else {
const totalBytes = outputChunks.reduce(
(sum, chunk) => sum + chunk.length,
0,
);
const totalBytes = binaryBytesReceived;
const event: ShellOutputEvent = {
type: 'binary_progress',
bytesReceived: totalBytes,
@@ -1076,7 +1082,7 @@ export class ShellExecutionService {
});
ExecutionLifecycleService.completeWithResult(ptyPid, {
rawOutput: Buffer.concat(outputChunks),
rawOutput: Buffer.from(''),
output: getFullBufferText(headlessTerminal),
exitCode,
signal: signal ?? null,