Files
NetMesh/infrastructure/ai/harness/terminalCompression.ts

101 lines
3.3 KiB
TypeScript
Raw Normal View History

import { compressVerboseText, truncateTextWithHeadAndTail } from '../requestPayloadCompression';
import type { ToolOutputStore } from './toolOutputStore';
import { redactSecretsForModel } from './modelSecretRedaction';
export const MAX_LIVE_TERMINAL_STDOUT_CHARS = 24_000;
export const MAX_LIVE_TERMINAL_STDERR_CHARS = 12_000;
export interface TerminalExecuteResult {
stdout: string;
stderr: string;
exitCode: number | null;
command?: string;
sessionId?: string;
}
export interface TerminalOutputHandle {
kind: 'terminal-output';
sessionId: string;
command?: string;
totalStdoutChars: number;
totalStderrChars: number;
handleId?: string;
restartPersistenceAvailable?: boolean;
}
export interface FitTerminalExecuteResultOptions {
chatSessionId?: string;
toolOutputStore?: ToolOutputStore;
}
export function fitTerminalExecuteResultForModel(
result: TerminalExecuteResult,
options?: FitTerminalExecuteResultOptions,
): TerminalExecuteResult {
const stdout = truncateTextWithHeadAndTail(
redactSecretsForModel(compressVerboseText(result.stdout)),
MAX_LIVE_TERMINAL_STDOUT_CHARS,
);
const stderr = truncateTextWithHeadAndTail(
redactSecretsForModel(compressVerboseText(result.stderr)),
MAX_LIVE_TERMINAL_STDERR_CHARS,
);
const fitted: TerminalExecuteResult = {
...result,
command: result.command ? redactSecretsForModel(result.command) : result.command,
stdout,
stderr,
};
if (
stdout.length < result.stdout.length
|| stderr.length < result.stderr.length
) {
const fullContent = [
result.command ? `command: ${result.command}` : '',
result.stdout ? `stdout:\n${result.stdout}` : '',
result.stderr ? `stderr:\n${result.stderr}` : '',
].filter(Boolean).join('\n\n');
let handleId: string | undefined;
if (options?.toolOutputStore && options.chatSessionId && fullContent) {
handleId = options.toolOutputStore.store({
chatSessionId: options.chatSessionId,
capabilityId: 'terminal.execute',
sessionId: result.sessionId,
content: fullContent,
}).id;
}
const handle: TerminalOutputHandle = {
kind: 'terminal-output',
sessionId: result.sessionId ?? 'unknown',
command: result.command ? redactSecretsForModel(result.command) : result.command,
totalStdoutChars: result.stdout.length,
totalStderrChars: result.stderr.length,
handleId,
restartPersistenceAvailable: false,
};
fitted.stdout = appendOutputHandleNotice(stdout, handle, 'stdout');
if (result.stderr) {
fitted.stderr = appendOutputHandleNotice(stderr, handle, 'stderr');
}
}
return fitted;
}
function appendOutputHandleNotice(
truncated: string,
handle: TerminalOutputHandle,
stream: 'stdout' | 'stderr',
): string {
const totalChars = stream === 'stdout' ? handle.totalStdoutChars : handle.totalStderrChars;
const handleSuffix = handle.handleId ? ` handleId=${handle.handleId}` : '';
const restartSuffix = handle.handleId && handle.restartPersistenceAvailable === false
? ' restartPersistence=unavailable (read before closing the app)'
: '';
return `${truncated}\n\n[output handle: session=${handle.sessionId}${handle.command ? ` command=${handle.command}` : ''} ${stream}=${totalChars} chars truncated for model context${handleSuffix}${restartSuffix}]`;
}