diff --git a/integration-tests/concurrent-runner/export-html-from-chatrecord-jsonl.js b/integration-tests/concurrent-runner/export-html-from-chatrecord-jsonl.js index a589d3fdb..abb893b1c 100644 --- a/integration-tests/concurrent-runner/export-html-from-chatrecord-jsonl.js +++ b/integration-tests/concurrent-runner/export-html-from-chatrecord-jsonl.js @@ -594,12 +594,8 @@ function extractLocations(rawInput, toolCallResult) { // Extract from rawInput - common path field names used by various tools if (rawInput && typeof rawInput === 'object') { - // read_file, write_file use absolute_path - if (typeof rawInput.absolute_path === 'string' && rawInput.absolute_path) { - locations.push({ path: rawInput.absolute_path }); - } - // edit tool uses file_path - else if (typeof rawInput.file_path === 'string' && rawInput.file_path) { + // read_file, write_file, edit tool use file_path + if (typeof rawInput.file_path === 'string' && rawInput.file_path) { locations.push({ path: rawInput.file_path }); } // some tools use just 'path' diff --git a/packages/cli/src/ui/utils/export/collect.ts b/packages/cli/src/ui/utils/export/collect.ts index cd203da95..d929e0b41 100644 --- a/packages/cli/src/ui/utils/export/collect.ts +++ b/packages/cli/src/ui/utils/export/collect.ts @@ -181,10 +181,7 @@ function calculateFileStats(records: ChatRecord[]): FileOperationStats { let filePath: string; if (typeof display.fileName === 'string') { // Prefer args.file_path for full path, fallback to fileName (which may be basename) - filePath = - (args?.['file_path'] as string) || - (args?.['absolute_path'] as string) || - display.fileName; + filePath = (args?.['file_path'] as string) || display.fileName; } else { // Fallback if fileName is not a string filePath = 'unknown'; diff --git a/packages/core/src/core/coreToolScheduler.ts b/packages/core/src/core/coreToolScheduler.ts index 0e5c6898f..097120d08 100644 --- a/packages/core/src/core/coreToolScheduler.ts +++ b/packages/core/src/core/coreToolScheduler.ts @@ -845,14 +845,12 @@ export class CoreToolScheduler { const toolParams = invocation.params as Record; const shellCommand = 'command' in toolParams ? String(toolParams['command']) : undefined; - // Extract file path — tools use 'absolute_path', 'file_path', - // or 'path' (LS / grep / glob). + // Extract file path — tools use 'file_path' or 'path' + // (LS / grep / glob). let invocationFilePath = - typeof toolParams['absolute_path'] === 'string' - ? toolParams['absolute_path'] - : typeof toolParams['file_path'] === 'string' - ? toolParams['file_path'] - : undefined; + typeof toolParams['file_path'] === 'string' + ? toolParams['file_path'] + : undefined; if ( invocationFilePath === undefined && typeof toolParams['path'] === 'string' @@ -1740,11 +1738,9 @@ export class CoreToolScheduler { const shellCommand = 'command' in params ? String(params['command']) : undefined; const filePath = - typeof params['absolute_path'] === 'string' - ? params['absolute_path'] - : typeof params['file_path'] === 'string' - ? params['file_path'] - : undefined; + typeof params['file_path'] === 'string' + ? params['file_path'] + : undefined; let domain: string | undefined; if (typeof params['url'] === 'string') { try { diff --git a/packages/core/src/telemetry/telemetry-utils.test.ts b/packages/core/src/telemetry/telemetry-utils.test.ts index a57586544..0bf34d1bd 100644 --- a/packages/core/src/telemetry/telemetry-utils.test.ts +++ b/packages/core/src/telemetry/telemetry-utils.test.ts @@ -8,14 +8,14 @@ import { describe, it, expect } from 'vitest'; import { getProgrammingLanguage } from './telemetry-utils.js'; describe('getProgrammingLanguage', () => { - it('should return the programming language when file_path is present', () => { + it('should return TypeScript for .ts files', () => { const args = { file_path: 'src/test.ts' }; const language = getProgrammingLanguage(args); expect(language).toBe('TypeScript'); }); - it('should return the programming language when absolute_path is present', () => { - const args = { absolute_path: 'src/test.py' }; + it('should return Python for .py files', () => { + const args = { file_path: 'src/test.py' }; const language = getProgrammingLanguage(args); expect(language).toBe('Python'); }); diff --git a/packages/core/src/telemetry/telemetry-utils.ts b/packages/core/src/telemetry/telemetry-utils.ts index d2a0ffe83..a14c13ac1 100644 --- a/packages/core/src/telemetry/telemetry-utils.ts +++ b/packages/core/src/telemetry/telemetry-utils.ts @@ -9,7 +9,7 @@ import { getLanguageFromFilePath } from '../utils/language-detection.js'; export function getProgrammingLanguage( args: Record, ): string | undefined { - const filePath = args['file_path'] || args['path'] || args['absolute_path']; + const filePath = args['file_path'] || args['path']; if (typeof filePath === 'string') { return getLanguageFromFilePath(filePath); } diff --git a/packages/core/src/tools/read-file.test.ts b/packages/core/src/tools/read-file.test.ts index 64a46fe9e..536b2c0de 100644 --- a/packages/core/src/tools/read-file.test.ts +++ b/packages/core/src/tools/read-file.test.ts @@ -61,7 +61,7 @@ describe('ReadFileTool', () => { describe('build', () => { it('should return an invocation for valid params (absolute path within root)', () => { const params: ReadFileToolParams = { - absolute_path: path.join(tempRootDir, 'test.txt'), + file_path: path.join(tempRootDir, 'test.txt'), }; const result = tool.build(params); expect(typeof result).not.toBe('string'); @@ -69,7 +69,7 @@ describe('ReadFileTool', () => { it('should throw error if file path is relative', () => { const params: ReadFileToolParams = { - absolute_path: 'relative/path.txt', + file_path: 'relative/path.txt', }; expect(() => tool.build(params)).toThrow( 'File path must be absolute, but was relative: relative/path.txt. You must provide an absolute path.', @@ -78,7 +78,7 @@ describe('ReadFileTool', () => { it('should allow path outside root (external path support)', () => { const params: ReadFileToolParams = { - absolute_path: '/outside/root.txt', + file_path: '/outside/root.txt', }; const invocation = tool.build(params); expect(invocation).toBeDefined(); @@ -87,7 +87,7 @@ describe('ReadFileTool', () => { it('should allow access to files in project temp directory', () => { const tempDir = path.join(tempRootDir, '.temp'); const params: ReadFileToolParams = { - absolute_path: path.join(tempDir, 'temp-file.txt'), + file_path: path.join(tempDir, 'temp-file.txt'), }; const result = tool.build(params); expect(typeof result).not.toBe('string'); @@ -95,7 +95,7 @@ describe('ReadFileTool', () => { it('should allow access to files in OS temp directory', () => { const params: ReadFileToolParams = { - absolute_path: path.join(os.tmpdir(), 'pr-review-context.md'), + file_path: path.join(os.tmpdir(), 'pr-review-context.md'), }; const result = tool.build(params); expect(typeof result).not.toBe('string'); @@ -103,7 +103,7 @@ describe('ReadFileTool', () => { it('should allow path completely outside workspace (external path support)', () => { const params: ReadFileToolParams = { - absolute_path: '/completely/outside/path.txt', + file_path: '/completely/outside/path.txt', }; const invocation = tool.build(params); expect(invocation).toBeDefined(); @@ -111,16 +111,16 @@ describe('ReadFileTool', () => { it('should throw error if path is empty', () => { const params: ReadFileToolParams = { - absolute_path: '', + file_path: '', }; expect(() => tool.build(params)).toThrow( - /The 'absolute_path' parameter must be non-empty./, + /The 'file_path' parameter must be non-empty./, ); }); it('should throw error if offset is negative', () => { const params: ReadFileToolParams = { - absolute_path: path.join(tempRootDir, 'test.txt'), + file_path: path.join(tempRootDir, 'test.txt'), offset: -1, }; expect(() => tool.build(params)).toThrow( @@ -130,7 +130,7 @@ describe('ReadFileTool', () => { it('should throw error if limit is zero or negative', () => { const params: ReadFileToolParams = { - absolute_path: path.join(tempRootDir, 'test.txt'), + file_path: path.join(tempRootDir, 'test.txt'), limit: 0, }; expect(() => tool.build(params)).toThrow( @@ -142,7 +142,7 @@ describe('ReadFileTool', () => { describe('getDefaultPermission', () => { it('should return allow for paths within workspace', async () => { const params: ReadFileToolParams = { - absolute_path: path.join(tempRootDir, 'test.txt'), + file_path: path.join(tempRootDir, 'test.txt'), }; const invocation = tool.build(params); const permission = await invocation.getDefaultPermission(); @@ -151,7 +151,7 @@ describe('ReadFileTool', () => { it('should return ask for paths outside workspace', async () => { const params: ReadFileToolParams = { - absolute_path: '/outside/workspace/file.txt', + file_path: '/outside/workspace/file.txt', }; const invocation = tool.build(params); const permission = await invocation.getDefaultPermission(); @@ -161,7 +161,7 @@ describe('ReadFileTool', () => { it('should return allow for paths within temp directory', async () => { const tempDir = path.join(tempRootDir, '.temp'); const params: ReadFileToolParams = { - absolute_path: path.join(tempDir, 'temp-file.txt'), + file_path: path.join(tempDir, 'temp-file.txt'), }; const invocation = tool.build(params); const permission = await invocation.getDefaultPermission(); @@ -173,7 +173,7 @@ describe('ReadFileTool', () => { it('should return relative path without limit/offset', () => { const subDir = path.join(tempRootDir, 'sub', 'dir'); const params: ReadFileToolParams = { - absolute_path: path.join(subDir, 'file.txt'), + file_path: path.join(subDir, 'file.txt'), }; const invocation = tool.build(params); expect(typeof invocation).not.toBe('string'); @@ -187,7 +187,7 @@ describe('ReadFileTool', () => { it('should handle non-normalized file paths correctly', () => { const subDir = path.join(tempRootDir, 'sub', 'dir'); const params: ReadFileToolParams = { - absolute_path: path.join(subDir, '..', 'dir', 'file.txt'), + file_path: path.join(subDir, '..', 'dir', 'file.txt'), }; const invocation = tool.build(params); expect(typeof invocation).not.toBe('string'); @@ -199,7 +199,7 @@ describe('ReadFileTool', () => { }); it('should return . if path is the root directory', () => { - const params: ReadFileToolParams = { absolute_path: tempRootDir }; + const params: ReadFileToolParams = { file_path: tempRootDir }; const invocation = tool.build(params); expect(typeof invocation).not.toBe('string'); expect( @@ -213,7 +213,7 @@ describe('ReadFileTool', () => { describe('execute', () => { it('should return error if file does not exist', async () => { const filePath = path.join(tempRootDir, 'nonexistent.txt'); - const params: ReadFileToolParams = { absolute_path: filePath }; + const params: ReadFileToolParams = { file_path: filePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -235,7 +235,7 @@ describe('ReadFileTool', () => { const filePath = path.join(tempRootDir, 'textfile.txt'); const fileContent = 'This is a test file.'; await fsp.writeFile(filePath, fileContent, 'utf-8'); - const params: ReadFileToolParams = { absolute_path: filePath }; + const params: ReadFileToolParams = { file_path: filePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -250,7 +250,7 @@ describe('ReadFileTool', () => { it('should return error if path is a directory', async () => { const dirPath = path.join(tempRootDir, 'directory'); await fsp.mkdir(dirPath); - const params: ReadFileToolParams = { absolute_path: dirPath }; + const params: ReadFileToolParams = { file_path: dirPath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -273,7 +273,7 @@ describe('ReadFileTool', () => { // 11MB of content exceeds 10MB limit const largeContent = 'x'.repeat(11 * 1024 * 1024); await fsp.writeFile(filePath, largeContent, 'utf-8'); - const params: ReadFileToolParams = { absolute_path: filePath }; + const params: ReadFileToolParams = { file_path: filePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -292,7 +292,7 @@ describe('ReadFileTool', () => { const longLine = 'a'.repeat(2500); // Exceeds MAX_LINE_LENGTH_TEXT_FILE (2000) const fileContent = `Short line\n${longLine}\nAnother short line`; await fsp.writeFile(filePath, fileContent, 'utf-8'); - const params: ReadFileToolParams = { absolute_path: filePath }; + const params: ReadFileToolParams = { file_path: filePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -311,7 +311,7 @@ describe('ReadFileTool', () => { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, ]); await fsp.writeFile(imagePath, pngHeader); - const params: ReadFileToolParams = { absolute_path: imagePath }; + const params: ReadFileToolParams = { file_path: imagePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -333,7 +333,7 @@ describe('ReadFileTool', () => { // Minimal PDF header const pdfHeader = Buffer.from('%PDF-1.4'); await fsp.writeFile(pdfPath, pdfHeader); - const params: ReadFileToolParams = { absolute_path: pdfPath }; + const params: ReadFileToolParams = { file_path: pdfPath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -355,7 +355,7 @@ describe('ReadFileTool', () => { // Binary data with null bytes const binaryData = Buffer.from([0x00, 0xff, 0x00, 0xff]); await fsp.writeFile(binPath, binaryData); - const params: ReadFileToolParams = { absolute_path: binPath }; + const params: ReadFileToolParams = { file_path: binPath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -372,7 +372,7 @@ describe('ReadFileTool', () => { const svgPath = path.join(tempRootDir, 'image.svg'); const svgContent = ''; await fsp.writeFile(svgPath, svgContent, 'utf-8'); - const params: ReadFileToolParams = { absolute_path: svgPath }; + const params: ReadFileToolParams = { file_path: svgPath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -388,7 +388,7 @@ describe('ReadFileTool', () => { // Create SVG content larger than 1MB const largeContent = '' + 'x'.repeat(1024 * 1024 + 1) + ''; await fsp.writeFile(svgPath, largeContent, 'utf-8'); - const params: ReadFileToolParams = { absolute_path: svgPath }; + const params: ReadFileToolParams = { file_path: svgPath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -406,7 +406,7 @@ describe('ReadFileTool', () => { it('should handle empty file', async () => { const emptyPath = path.join(tempRootDir, 'empty.txt'); await fsp.writeFile(emptyPath, '', 'utf-8'); - const params: ReadFileToolParams = { absolute_path: emptyPath }; + const params: ReadFileToolParams = { file_path: emptyPath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -424,7 +424,7 @@ describe('ReadFileTool', () => { await fsp.writeFile(filePath, fileContent, 'utf-8'); const params: ReadFileToolParams = { - absolute_path: filePath, + file_path: filePath, offset: 5, // Start from line 6 limit: 3, }; @@ -452,7 +452,7 @@ describe('ReadFileTool', () => { const tempFileContent = 'This is temporary output content'; await fsp.writeFile(tempFilePath, tempFileContent, 'utf-8'); - const params: ReadFileToolParams = { absolute_path: tempFilePath }; + const params: ReadFileToolParams = { file_path: tempFilePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -472,7 +472,7 @@ describe('ReadFileTool', () => { await fsp.writeFile(tempFilePath, tempFileContent, 'utf-8'); try { - const params: ReadFileToolParams = { absolute_path: tempFilePath }; + const params: ReadFileToolParams = { file_path: tempFilePath }; const invocation = tool.build(params) as ToolInvocation< ReadFileToolParams, ToolResult @@ -497,7 +497,7 @@ describe('ReadFileTool', () => { const ignoredFilePath = path.join(tempRootDir, 'foo.bar'); await fsp.writeFile(ignoredFilePath, 'content', 'utf-8'); const params: ReadFileToolParams = { - absolute_path: ignoredFilePath, + file_path: ignoredFilePath, }; const expectedError = `File path '${ignoredFilePath}' is ignored by .qwenignore pattern(s).`; expect(() => tool.build(params)).toThrow(expectedError); @@ -509,7 +509,7 @@ describe('ReadFileTool', () => { const ignoredFilePath = path.join(ignoredDirPath, 'file.txt'); await fsp.writeFile(ignoredFilePath, 'content', 'utf-8'); const params: ReadFileToolParams = { - absolute_path: ignoredFilePath, + file_path: ignoredFilePath, }; const expectedError = `File path '${ignoredFilePath}' is ignored by .qwenignore pattern(s).`; expect(() => tool.build(params)).toThrow(expectedError); @@ -519,7 +519,7 @@ describe('ReadFileTool', () => { const allowedFilePath = path.join(tempRootDir, 'allowed.txt'); await fsp.writeFile(allowedFilePath, 'content', 'utf-8'); const params: ReadFileToolParams = { - absolute_path: allowedFilePath, + file_path: allowedFilePath, }; const invocation = tool.build(params); expect(typeof invocation).not.toBe('string'); diff --git a/packages/core/src/tools/read-file.ts b/packages/core/src/tools/read-file.ts index 832e11e0a..7296594ac 100644 --- a/packages/core/src/tools/read-file.ts +++ b/packages/core/src/tools/read-file.ts @@ -32,7 +32,7 @@ export interface ReadFileToolParams { /** * The absolute path to the file to read */ - absolute_path: string; + file_path: string; /** * The line number to start reading from (optional) @@ -58,7 +58,7 @@ class ReadFileToolInvocation extends BaseToolInvocation< getDescription(): string { const relativePath = makeRelative( - this.params.absolute_path, + this.params.file_path, this.config.getTargetDir(), ); const shortPath = shortenPath(relativePath); @@ -76,7 +76,7 @@ class ReadFileToolInvocation extends BaseToolInvocation< } override toolLocations(): ToolLocation[] { - return [{ path: this.params.absolute_path, line: this.params.offset }]; + return [{ path: this.params.file_path, line: this.params.offset }]; } /** @@ -84,7 +84,7 @@ class ReadFileToolInvocation extends BaseToolInvocation< * so that external file reads require user confirmation. */ override async getDefaultPermission(): Promise { - const filePath = path.resolve(this.params.absolute_path); + const filePath = path.resolve(this.params.file_path); const workspaceContext = this.config.getWorkspaceContext(); const globalTempDir = Storage.getGlobalTempDir(); const projectTempDir = this.config.storage.getProjectTempDir(); @@ -107,7 +107,7 @@ class ReadFileToolInvocation extends BaseToolInvocation< async execute(): Promise { const result = await processSingleFileContent( - this.params.absolute_path, + this.params.file_path, this.config, this.params.offset, this.params.limit, @@ -137,9 +137,9 @@ class ReadFileToolInvocation extends BaseToolInvocation< typeof result.llmContent === 'string' ? result.llmContent.split('\n').length : undefined; - const mimetype = getSpecificMimeType(this.params.absolute_path); + const mimetype = getSpecificMimeType(this.params.file_path); const programming_language = getProgrammingLanguage({ - absolute_path: this.params.absolute_path, + file_path: this.params.file_path, }); logFileOperation( this.config, @@ -148,7 +148,7 @@ class ReadFileToolInvocation extends BaseToolInvocation< FileOperation.READ, lines, mimetype, - path.extname(this.params.absolute_path), + path.extname(this.params.file_path), programming_language, ), ); @@ -177,7 +177,7 @@ export class ReadFileTool extends BaseDeclarativeTool< Kind.Read, { properties: { - absolute_path: { + file_path: { description: "The absolute path to the file to read (e.g., '/home/user/project/file.txt'). Relative paths are not supported. You must provide an absolute path.", type: 'string', @@ -193,7 +193,7 @@ export class ReadFileTool extends BaseDeclarativeTool< type: 'number', }, }, - required: ['absolute_path'], + required: ['file_path'], type: 'object', }, ); @@ -202,9 +202,9 @@ export class ReadFileTool extends BaseDeclarativeTool< protected override validateToolParamValues( params: ReadFileToolParams, ): string | null { - const filePath = params.absolute_path; - if (params.absolute_path.trim() === '') { - return "The 'absolute_path' parameter must be non-empty."; + const filePath = params.file_path; + if (params.file_path.trim() === '') { + return "The 'file_path' parameter must be non-empty."; } if (!path.isAbsolute(filePath)) { @@ -219,7 +219,7 @@ export class ReadFileTool extends BaseDeclarativeTool< } const fileService = this.config.getFileService(); - if (fileService.shouldQwenIgnoreFile(params.absolute_path)) { + if (fileService.shouldQwenIgnoreFile(params.file_path)) { return `File path '${filePath}' is ignored by .qwenignore pattern(s).`; }