mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-25 04:24:51 -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:
@@ -661,4 +661,82 @@ describe('ToolOutputMaskingService', () => {
|
||||
)['output'],
|
||||
).toContain(MASKING_INDICATOR_TAG);
|
||||
});
|
||||
|
||||
it('should use existing outputFile if available in the tool response', async () => {
|
||||
// Setup: Create a large history to trigger masking
|
||||
const largeContent = 'a'.repeat(60000);
|
||||
const existingOutputFile = path.join(testTempDir, 'truly_full_output.txt');
|
||||
await fs.promises.writeFile(existingOutputFile, 'truly full content');
|
||||
|
||||
const history: Content[] = [
|
||||
{
|
||||
role: 'user',
|
||||
parts: [{ text: 'Old turn' }],
|
||||
},
|
||||
{
|
||||
role: 'model',
|
||||
parts: [
|
||||
{
|
||||
functionResponse: {
|
||||
name: 'shell',
|
||||
id: 'call-1',
|
||||
response: {
|
||||
output: largeContent,
|
||||
outputFile: existingOutputFile,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// Protection buffer
|
||||
{
|
||||
role: 'user',
|
||||
parts: [
|
||||
{
|
||||
functionResponse: {
|
||||
name: 'padding',
|
||||
response: { output: 'B'.repeat(60000) },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
parts: [{ text: 'Newest turn' }],
|
||||
},
|
||||
];
|
||||
|
||||
mockedEstimateTokenCountSync.mockImplementation((parts: Part[]) => {
|
||||
const resp = parts[0].functionResponse?.response as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const content = (resp?.['output'] as string) ?? JSON.stringify(resp);
|
||||
if (content.includes(`<${MASKING_INDICATOR_TAG}`)) return 100;
|
||||
|
||||
const name = parts[0].functionResponse?.name;
|
||||
if (name === 'shell') return 60000;
|
||||
if (name === 'padding') return 60000;
|
||||
return 10;
|
||||
});
|
||||
|
||||
// Trigger masking
|
||||
const result = await service.mask(history, mockConfig);
|
||||
|
||||
expect(result.maskedCount).toBe(2);
|
||||
const maskedPart = result.newHistory[1].parts![0];
|
||||
const maskedResponse = maskedPart.functionResponse?.response as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const maskedOutput = maskedResponse['output'] as string;
|
||||
|
||||
// Verify the masked snippet points to the existing file
|
||||
expect(maskedOutput).toContain(
|
||||
`Full output available at: ${existingOutputFile}`,
|
||||
);
|
||||
|
||||
// Verify the path in maskedOutput is exactly the one we provided
|
||||
expect(maskedOutput).toContain(existingOutputFile);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user