From acddb946f9523c7674f7c1bb3abdac49dc3aaaab Mon Sep 17 00:00:00 2001 From: ktz03 Date: Wed, 19 Aug 2026 15:57:29 +0800 Subject: [PATCH 1/2] fix(os): normalize Windows drive-root paths for fs.watch Avoid ENOENT when watching `/D:` / `D:` by normalizing to `D:\\`. Related: MoonshotAI/kimi-cli#2600 --- .../backends/node-local/hostFsWatchService.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/agent-core-v2/src/os/backends/node-local/hostFsWatchService.ts b/packages/agent-core-v2/src/os/backends/node-local/hostFsWatchService.ts index 17fc565b18..dc32a85c1e 100644 --- a/packages/agent-core-v2/src/os/backends/node-local/hostFsWatchService.ts +++ b/packages/agent-core-v2/src/os/backends/node-local/hostFsWatchService.ts @@ -1,3 +1,10 @@ +/** + * Host filesystem watch service. + * + * On Windows, drive-root watch paths such as `D:` / `/D:` are normalized to + * `D:\\` before `fs.watch`, avoiding ENOENT when the process CWD is a drive root. + */ + import { watch as fsWatch } from 'node:fs'; import { basename, isAbsolute, join, relative } from 'node:path'; @@ -230,11 +237,21 @@ export class HostFsWatchService implements IHostFsWatchService { constructor(private readonly runtime: HostFsWatchRuntime = NODE_HOST_FS_WATCH_RUNTIME) {} watch(path: string, options?: HostFsWatchOptions): IHostFsWatchHandle { + const root = normalizeWatchRoot(path, this.runtime.platform); if (useNativeRecursive(options, this.runtime.platform)) { - return new SignalWatchHandle(path, options, this.runtime); + return new SignalWatchHandle(root, options, this.runtime); } - return new HostFsWatchHandle(path, options); + return new HostFsWatchHandle(root, options); + } +} + +function normalizeWatchRoot(path: string, platform: NodeJS.Platform): string { + if (platform !== 'win32') return path; + const driveOnly = path.match(/^\/?([A-Za-z]):\/?$/); + if (driveOnly) { + return `${driveOnly[1].toUpperCase()}:\\`; } + return path; } function useNativeRecursive( From 777fe5951c2ac4c28a2256cc2d952b7871b0d6a4 Mon Sep 17 00:00:00 2001 From: ktz03 Date: Wed, 19 Aug 2026 15:57:32 +0800 Subject: [PATCH 2/2] test(os): cover Windows drive-root watch path normalization --- .../node-local/hostFsWatchService.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/agent-core-v2/test/os/backends/node-local/hostFsWatchService.test.ts b/packages/agent-core-v2/test/os/backends/node-local/hostFsWatchService.test.ts index aef8e4f3ab..031c42a0bc 100644 --- a/packages/agent-core-v2/test/os/backends/node-local/hostFsWatchService.test.ts +++ b/packages/agent-core-v2/test/os/backends/node-local/hostFsWatchService.test.ts @@ -289,6 +289,26 @@ describe('host filesystem change notifications', () => { expect(events).toHaveLength(0); }); + it('normalizes Windows drive-root watch paths before calling watchNative', () => { + const watched: string[] = []; + const runtime: HostFsWatchRuntime = { + platform: 'win32', + watchNative: (root, _listener) => { + watched.push(root); + return new TestNativeWatcher(); + }, + scheduleRetry: () => ({ dispose: () => undefined }), + }; + const service = new HostFsWatchService(runtime); + handle = service.watch('/D:', { signal: true }); + expect(watched).toEqual(['D:\\']); + handle.dispose(); + handle = undefined; + + handle = service.watch('E:', { signal: true }); + expect(watched).toEqual(['D:\\', 'E:\\']); + }); + it.skipIf(process.platform !== 'darwin')( 'signal mode keeps the fd footprint bounded on a fat subtree', async () => {