diff --git a/.changeset/fix-read-tool-symlink.md b/.changeset/fix-read-tool-symlink.md new file mode 100644 index 0000000000..726c75c95d --- /dev/null +++ b/.changeset/fix-read-tool-symlink.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix the web backend's file-reading tools mishandling symbolic links: Read rejected them as not regular files, and media size checks measured the link instead of its target. diff --git a/packages/agent-core-v2/src/agent/media/tools/read-media.ts b/packages/agent-core-v2/src/agent/media/tools/read-media.ts index c1e73c8db7..b1c0211bf3 100644 --- a/packages/agent-core-v2/src/agent/media/tools/read-media.ts +++ b/packages/agent-core-v2/src/agent/media/tools/read-media.ts @@ -329,7 +329,7 @@ export class ReadMediaFileTool implements BuiltinTool { }; } - const stat = await this.fs.stat(safePath); + const stat = await this.fs.stat(safePath, { followSymlinks: true }); if (stat.size === 0) { return { isError: true, output: `"${args.path}" is empty.` }; } diff --git a/packages/agent-core-v2/src/os/backends/node-local/tools/read.ts b/packages/agent-core-v2/src/os/backends/node-local/tools/read.ts index 95220a9e5e..33a59d09c2 100644 --- a/packages/agent-core-v2/src/os/backends/node-local/tools/read.ts +++ b/packages/agent-core-v2/src/os/backends/node-local/tools/read.ts @@ -280,7 +280,7 @@ export class ReadTool implements BuiltinTool { try { let stat: Awaited>; try { - stat = await this.fs.stat(safePath); + stat = await this.fs.stat(safePath, { followSymlinks: true }); } catch (error) { if (isFileNotFoundError(error)) { return { isError: true, output: `"${args.path}" does not exist.` }; diff --git a/packages/agent-core-v2/test/os/backends/node-local/tools/read.test.ts b/packages/agent-core-v2/test/os/backends/node-local/tools/read.test.ts index 2296480fa9..19659c5045 100644 --- a/packages/agent-core-v2/test/os/backends/node-local/tools/read.test.ts +++ b/packages/agent-core-v2/test/os/backends/node-local/tools/read.test.ts @@ -222,6 +222,16 @@ describe('ReadTool', () => { }); }); + it('stats with followSymlinks so symlinked files stay readable', async () => { + const { fs, stat } = createSpiedFs('alpha\n'); + const tool = new ReadTool(fs, createTestEnv(), PERMISSIVE_WORKSPACE); + + const result = await execute(tool, { path: '/tmp/a.txt' }); + + expect(result.isError).not.toBe(true); + expect(stat).toHaveBeenCalledWith('/tmp/a.txt', { followSymlinks: true }); + }); + it('normalizes pure CRLF files to the LF model view', async () => { const tool = toolWithContent('alpha\r\nbeta\r\n');