mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 21:32:56 -07:00
Checkpoint of shell optimization
fix(cli): Write shell command output to a file and limit memory buffered in UI Fixes. Checkpoint. fix(core, cli): await outputStream.end() to prevent race conditions This commit fixes a critical race condition where was called synchronously without being awaited. This led to potential file truncation or EBUSY errors on Windows when attempting to manipulate the file immediately after the call. Additionally, this change removes fixed wait times (`setTimeout`) that were previously used in test files as a band-aid. fix(core): stream processed xterm output to file to remove spurious escape codes test(core): update shell regression tests to use file_data events
This commit is contained in:
@@ -615,6 +615,48 @@ ${head}
|
||||
${tail}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves tool output from a source path to a temporary file for later retrieval.
|
||||
*/
|
||||
export async function moveToolOutputToFile(
|
||||
sourcePath: string,
|
||||
toolName: string,
|
||||
id: string | number, // Accept string (callId) or number (truncationId)
|
||||
projectTempDir: string,
|
||||
sessionId?: string,
|
||||
): Promise<{ outputFile: string }> {
|
||||
const safeToolName = sanitizeFilenamePart(toolName).toLowerCase();
|
||||
const safeId = sanitizeFilenamePart(id.toString()).toLowerCase();
|
||||
const fileName = safeId.startsWith(safeToolName)
|
||||
? `${safeId}.txt`
|
||||
: `${safeToolName}_${safeId}.txt`;
|
||||
|
||||
let toolOutputDir = path.join(projectTempDir, TOOL_OUTPUTS_DIR);
|
||||
if (sessionId) {
|
||||
const safeSessionId = sanitizeFilenamePart(sessionId);
|
||||
toolOutputDir = path.join(toolOutputDir, `session-${safeSessionId}`);
|
||||
}
|
||||
const outputFile = path.join(toolOutputDir, fileName);
|
||||
|
||||
await fsPromises.mkdir(toolOutputDir, { recursive: true });
|
||||
|
||||
try {
|
||||
// Attempt rename (efficient if on the same filesystem)
|
||||
await fsPromises.rename(sourcePath, outputFile);
|
||||
} catch (error: unknown) {
|
||||
// If rename fails (e.g. cross-filesystem), copy and then delete
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
if ((error as { code?: string }).code === 'EXDEV') {
|
||||
await fsPromises.copyFile(sourcePath, outputFile);
|
||||
await fsPromises.unlink(sourcePath);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return { outputFile };
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves tool output to a temporary file for later retrieval.
|
||||
*/
|
||||
|
||||
@@ -22,12 +22,13 @@ function createFunctionResponsePart(
|
||||
callId: string,
|
||||
toolName: string,
|
||||
output: string,
|
||||
outputFile?: string,
|
||||
): Part {
|
||||
return {
|
||||
functionResponse: {
|
||||
id: callId,
|
||||
name: toolName,
|
||||
response: { output },
|
||||
response: { output, outputFile },
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -50,9 +51,12 @@ export function convertToFunctionResponse(
|
||||
llmContent: PartListUnion,
|
||||
model: string,
|
||||
config?: Config,
|
||||
outputFile?: string,
|
||||
): Part[] {
|
||||
if (typeof llmContent === 'string') {
|
||||
return [createFunctionResponsePart(callId, toolName, llmContent)];
|
||||
return [
|
||||
createFunctionResponsePart(callId, toolName, llmContent, outputFile),
|
||||
];
|
||||
}
|
||||
|
||||
const parts = toParts(llmContent);
|
||||
@@ -94,7 +98,10 @@ export function convertToFunctionResponse(
|
||||
functionResponse: {
|
||||
id: callId,
|
||||
name: toolName,
|
||||
response: textParts.length > 0 ? { output: textParts.join('\n') } : {},
|
||||
response: {
|
||||
...(textParts.length > 0 ? { output: textParts.join('\n') } : {}),
|
||||
outputFile,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user