fix: shorten tool call IDs and fix duplicate tool name in truncated output filenames (#18600)

This commit is contained in:
Sandy Tao
2026-02-09 09:09:17 -08:00
committed by GitHub
parent da66c7c0d1
commit 01906a9205
5 changed files with 30 additions and 7 deletions

View File

@@ -168,7 +168,7 @@ describe('Turn', () => {
}),
);
expect(event2.value.callId).toEqual(
expect.stringMatching(/^tool2-\d{13}-\w{10,}$/),
expect.stringMatching(/^tool2_\d{13}_\d+$/),
);
expect(turn.pendingToolCalls[1]).toEqual(event2.value);
expect(turn.getDebugResponses().length).toBe(1);

View File

@@ -233,6 +233,8 @@ export type ServerGeminiStreamEvent =
// A turn manages the agentic loop turn within the server context.
export class Turn {
private callCounter = 0;
readonly pendingToolCalls: ToolCallRequestInfo[] = [];
private debugResponses: GenerateContentResponse[] = [];
private pendingCitations = new Set<string>();
@@ -398,11 +400,9 @@ export class Turn {
fnCall: FunctionCall,
traceId?: string,
): ServerGeminiStreamEvent | null {
const callId =
fnCall.id ??
`${fnCall.name}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
const name = fnCall.name || 'undefined_tool_name';
const args = fnCall.args || {};
const callId = fnCall.id ?? `${name}_${Date.now()}_${this.callCounter++}`;
const toolCallRequest: ToolCallRequestInfo = {
callId,

View File

@@ -180,6 +180,7 @@ describe('ToolExecutor', () => {
it('should truncate large shell output', async () => {
// 1. Setup Config for Truncation
vi.spyOn(config, 'getTruncateToolOutputThreshold').mockReturnValue(10);
vi.spyOn(config.storage, 'getProjectTempDir').mockReturnValue('/tmp');
const mockTool = new MockTool({ name: SHELL_TOOL_NAME });
const invocation = mockTool.build({});

View File

@@ -1110,7 +1110,7 @@ describe('fileUtils', () => {
it('should save content to a file with safe name', async () => {
const content = 'some content';
const toolName = 'shell';
const id = '123';
const id = 'shell_123';
const result = await saveTruncatedToolOutput(
content,
@@ -1154,6 +1154,26 @@ describe('fileUtils', () => {
expect(result.outputFile).toBe(expectedOutputFile);
});
it('should not duplicate tool name when id already starts with it', async () => {
const content = 'content';
const toolName = 'run_shell_command';
const id = 'run_shell_command_1707400000000_0';
const result = await saveTruncatedToolOutput(
content,
toolName,
id,
tempRootDir,
);
const expectedOutputFile = path.join(
tempRootDir,
'tool-outputs',
'run_shell_command_1707400000000_0.txt',
);
expect(result.outputFile).toBe(expectedOutputFile);
});
it('should sanitize id in filename', async () => {
const content = 'content';
const toolName = 'shell';
@@ -1178,7 +1198,7 @@ describe('fileUtils', () => {
it('should sanitize sessionId in filename/path', async () => {
const content = 'content';
const toolName = 'shell';
const id = '1';
const id = 'shell_1';
const sessionId = '../../etc/passwd';
const result = await saveTruncatedToolOutput(

View File

@@ -617,7 +617,9 @@ export async function saveTruncatedToolOutput(
): Promise<{ outputFile: string }> {
const safeToolName = sanitizeFilenamePart(toolName).toLowerCase();
const safeId = sanitizeFilenamePart(id.toString()).toLowerCase();
const fileName = `${safeToolName}_${safeId}.txt`;
const fileName = safeId.startsWith(safeToolName)
? `${safeId}.txt`
: `${safeToolName}_${safeId}.txt`;
let toolOutputDir = path.join(projectTempDir, TOOL_OUTPUTS_DIR);
if (sessionId) {