fix: properly serialize initial AnsiOutput for background processes

This commit is contained in:
Spencer
2026-04-09 13:53:52 +00:00
parent 886025f6b9
commit 1755678cf9
2 changed files with 38 additions and 3 deletions
+31 -2
View File
@@ -37,7 +37,10 @@ import {
type ShellOutputEvent, type ShellOutputEvent,
} from '../services/shellExecutionService.js'; } from '../services/shellExecutionService.js';
import { formatBytes } from '../utils/formatters.js'; import { formatBytes } from '../utils/formatters.js';
import type { AnsiOutput } from '../utils/terminalSerializer.js'; import {
serializeAnsiOutputToText,
type AnsiOutput,
} from '../utils/terminalSerializer.js';
import { import {
getCommandRoots, getCommandRoots,
initializeShellParsers, initializeShellParsers,
@@ -534,7 +537,28 @@ export class ShellToolInvocation extends BaseToolInvocation<
break; break;
case 'data': case 'data':
if (isBinaryStream) break; if (isBinaryStream) break;
if (typeof event.chunk === 'string') {
if (typeof cumulativeOutput === 'string') {
// Accumulate string chunks, capped at 16MB (same as ShellExecutionService)
const MAX_BUFFER = 16 * 1024 * 1024;
if (
cumulativeOutput.length + event.chunk.length >
MAX_BUFFER
) {
cumulativeOutput = (cumulativeOutput + event.chunk).slice(
-MAX_BUFFER,
);
} else {
cumulativeOutput += event.chunk;
}
} else {
// In case of mode switch (unlikely)
cumulativeOutput = event.chunk; cumulativeOutput = event.chunk;
}
} else {
// Snapshots (AnsiOutput) always replace
cumulativeOutput = event.chunk;
}
if (updateOutput && !this.params.is_background) { if (updateOutput && !this.params.is_background) {
updateOutput(cumulativeOutput); updateOutput(cumulativeOutput);
lastUpdateTime = Date.now(); lastUpdateTime = Date.now();
@@ -630,9 +654,14 @@ export class ShellToolInvocation extends BaseToolInvocation<
await new Promise((resolve) => setTimeout(resolve, delay)); await new Promise((resolve) => setTimeout(resolve, delay));
if (!completed) { if (!completed) {
const initialOutputText =
typeof cumulativeOutput === 'string'
? cumulativeOutput
: serializeAnsiOutputToText(cumulativeOutput);
// Return early with initial output if still running // Return early with initial output if still running
return { return {
llmContent: `Command is running in background. PID: ${pid}. Initial output:\n${cumulativeOutput}`, llmContent: `Command is running in background. PID: ${pid}. Initial output:\n${initialOutputText}`,
returnDisplay: `Background process started with PID ${pid}.`, returnDisplay: `Background process started with PID ${pid}.`,
}; };
} }
@@ -250,6 +250,12 @@ export function serializeTerminalToObject(
return result; return result;
} }
export function serializeAnsiOutputToText(output: AnsiOutput): string {
return output
.map((line) => line.map((segment) => segment.text).join(''))
.join('\n');
}
// ANSI color palette from https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit // ANSI color palette from https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
const ANSI_COLORS = [ const ANSI_COLORS = [
'#000000', '#000000',