diff --git a/.changeset/stray-files-session-index.md b/.changeset/stray-files-session-index.md new file mode 100644 index 00000000000..809e195c594 --- /dev/null +++ b/.changeset/stray-files-session-index.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix sessions not loading in the resume picker when stray files sit inside the sessions directory. diff --git a/packages/agent-core-v2/src/persistence/backends/node-fs/fileStorageService.ts b/packages/agent-core-v2/src/persistence/backends/node-fs/fileStorageService.ts index 7104423e6ca..856b771543d 100644 --- a/packages/agent-core-v2/src/persistence/backends/node-fs/fileStorageService.ts +++ b/packages/agent-core-v2/src/persistence/backends/node-fs/fileStorageService.ts @@ -20,8 +20,9 @@ const WATCH_DEBOUNCE_MS = 150; const TORN_READ_RETRIES = 3; const TORN_READ_RETRY_DELAY_MS = 15; -function isEnoent(error: unknown): boolean { - return (error as NodeJS.ErrnoException).code === 'ENOENT'; +function isMissingEntry(error: unknown): boolean { + const code = (error as NodeJS.ErrnoException).code; + return code === 'ENOENT' || code === 'ENOTDIR'; } export class FileStorageService implements IFileSystemStorageService { @@ -42,7 +43,7 @@ export class FileStorageService implements IFileSystemStorageService { try { bytes = await readFile(filePath); } catch (error) { - if (isEnoent(error)) return undefined; + if (isMissingEntry(error)) return undefined; throw toStorageIoError(error, { path: filePath, op: 'read' }); } if (attempt >= TORN_READ_RETRIES) return bytes; @@ -72,7 +73,7 @@ export class FileStorageService implements IFileSystemStorageService { yield chunk as Uint8Array; } } catch (error) { - if (isEnoent(error)) return; + if (isMissingEntry(error)) return; throw toStorageIoError(error, { path: filePath, op: 'read' }); } } @@ -144,7 +145,7 @@ export class FileStorageService implements IFileSystemStorageService { try { entries = await readdir(this.scopePath(scope)); } catch (error) { - if (isEnoent(error)) return []; + if (isMissingEntry(error)) return []; throw toStorageIoError(error, { path: this.scopePath(scope), op: 'list' }); } return prefix === undefined ? entries : entries.filter((entry) => entry.startsWith(prefix)); @@ -155,7 +156,7 @@ export class FileStorageService implements IFileSystemStorageService { try { await unlink(filePath); } catch (error) { - if (isEnoent(error)) return; + if (isMissingEntry(error)) return; throw toStorageIoError(error, { path: filePath, op: 'delete' }); } } @@ -165,7 +166,7 @@ export class FileStorageService implements IFileSystemStorageService { try { return (await stat(filePath)).size; } catch (error) { - if (isEnoent(error)) return undefined; + if (isMissingEntry(error)) return undefined; throw toStorageIoError(error, { path: filePath, op: 'stat' }); } } @@ -175,7 +176,7 @@ export class FileStorageService implements IFileSystemStorageService { try { return (await stat(filePath)).mtimeMs; } catch (error) { - if (isEnoent(error)) return undefined; + if (isMissingEntry(error)) return undefined; throw toStorageIoError(error, { path: filePath, op: 'stat' }); } } @@ -263,7 +264,7 @@ export class FileStorageService implements IFileSystemStorageService { await syncDir(dir); this.syncedDirs.add(dir); } catch (error) { - if (!isEnoent(error)) throw error; + if (!isMissingEntry(error)) throw error; } } } diff --git a/packages/agent-core-v2/test/persistence/backends/node-fs/fileStorageService.test.ts b/packages/agent-core-v2/test/persistence/backends/node-fs/fileStorageService.test.ts index e8f140c4a2a..2c6081b9eaf 100644 --- a/packages/agent-core-v2/test/persistence/backends/node-fs/fileStorageService.test.ts +++ b/packages/agent-core-v2/test/persistence/backends/node-fs/fileStorageService.test.ts @@ -62,6 +62,16 @@ describe('FileStorageService — error translation', () => { await expect(svc.delete('scope', 'missing.json')).resolves.toBeUndefined(); }); + it('treats a regular file in place of a scope directory as missing', async () => { + const svc = new FileStorageService(dir); + await writeFile(join(dir, 'junk'), 'x'); + expect(await svc.read('junk', 'state.json')).toBeUndefined(); + expect(await svc.size('junk', 'state.json')).toBeUndefined(); + expect(await svc.mtime('junk', 'state.json')).toBeUndefined(); + expect(await svc.list('junk')).toEqual([]); + await expect(svc.delete('junk', 'state.json')).resolves.toBeUndefined(); + }); + it.skipIf(isWin)('translates non-ENOENT failures into StorageError(io_failed)', async () => { const svc = new FileStorageService(dir); await mkdir(join(dir, 'scope', 'adir'), { recursive: true });