diff --git a/src/memory/__tests__/file-path.test.ts b/src/memory/__tests__/file-path.test.ts index d1a16e4600..0f8c960012 100644 --- a/src/memory/__tests__/file-path.test.ts +++ b/src/memory/__tests__/file-path.test.ts @@ -56,7 +56,34 @@ describe('ensureMemoryFilePath', () => { const result = await ensureMemoryFilePath(); expect(path.isAbsolute(result)).toBe(true); - expect(result).toContain('custom-memory.jsonl'); + expect(result).toBe(path.resolve(process.cwd(), relativePath)); + }); + + it('should create parent directories for custom absolute paths', async () => { + const absolutePath = path.join(testDir, 'nested', 'dir', 'memory.jsonl'); + process.env.MEMORY_FILE_PATH = absolutePath; + + const result = await ensureMemoryFilePath(); + + expect(result).toBe(absolutePath); + const stat = await fs.stat(path.dirname(absolutePath)); + expect(stat.isDirectory()).toBe(true); + + await fs.rm(path.join(testDir, 'nested'), { recursive: true, force: true }); + }); + + it('should migrate custom memory.json path to memory.jsonl', async () => { + const jsonPath = path.join(testDir, 'custom-memory.json'); + const jsonlPath = `${jsonPath}l`; + process.env.MEMORY_FILE_PATH = jsonPath; + await fs.writeFile(jsonPath, '{"type":"entity","name":"a","entityType":"t","observations":[]}'); + + const result = await ensureMemoryFilePath(); + + expect(result).toBe(jsonlPath); + await fs.access(jsonlPath); + await fs.rm(jsonPath, { force: true }).catch(() => undefined); + await fs.unlink(jsonlPath).catch(() => undefined); }); it('should handle Windows absolute paths', async () => { diff --git a/src/memory/index.ts b/src/memory/index.ts index 7b4c683300..d73efdfcab 100644 --- a/src/memory/index.ts +++ b/src/memory/index.ts @@ -13,36 +13,57 @@ export const defaultMemoryPath = path.join(path.dirname(fileURLToPath(import.met // Handle backward compatibility: migrate memory.json to memory.jsonl if needed export async function ensureMemoryFilePath(): Promise { if (process.env.MEMORY_FILE_PATH) { - // Custom path provided, use it as-is (with absolute path resolution) - return path.isAbsolute(process.env.MEMORY_FILE_PATH) + const configuredPath = path.isAbsolute(process.env.MEMORY_FILE_PATH) ? process.env.MEMORY_FILE_PATH - : path.join(path.dirname(fileURLToPath(import.meta.url)), process.env.MEMORY_FILE_PATH); + : path.resolve(process.cwd(), process.env.MEMORY_FILE_PATH); + + return normalizeMemoryStoragePath(configuredPath); } - - // No custom path set, check for backward compatibility migration + + // No custom path set, check for backward compatibility migration in package dir const oldMemoryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'memory.json'); const newMemoryPath = defaultMemoryPath; - + try { - // Check if old file exists and new file doesn't await fs.access(oldMemoryPath); try { await fs.access(newMemoryPath); - // Both files exist, use new one (no migration needed) return newMemoryPath; } catch { - // Old file exists, new file doesn't - migrate console.error('DETECTED: Found legacy memory.json file, migrating to memory.jsonl for JSONL format compatibility'); await fs.rename(oldMemoryPath, newMemoryPath); console.error('COMPLETED: Successfully migrated memory.json to memory.jsonl'); return newMemoryPath; } } catch { - // Old file doesn't exist, use new path return newMemoryPath; } } +async function normalizeMemoryStoragePath(memoryPath: string): Promise { + await fs.mkdir(path.dirname(memoryPath), { recursive: true }); + + if (memoryPath.endsWith('.json') && !memoryPath.endsWith('.jsonl')) { + const jsonlPath = `${memoryPath}l`; + try { + await fs.access(memoryPath); + try { + await fs.access(jsonlPath); + return jsonlPath; + } catch { + console.error(`DETECTED: Found legacy memory file at ${memoryPath}, migrating to JSONL format`); + await fs.rename(memoryPath, jsonlPath); + console.error(`COMPLETED: Successfully migrated to ${jsonlPath}`); + return jsonlPath; + } + } catch { + return jsonlPath; + } + } + + return memoryPath; +} + // Initialize memory file path (will be set during startup) let MEMORY_FILE_PATH: string;