diff --git a/packages/server/src/server/agent/providers/codex-rollout-timeline.ts b/packages/server/src/server/agent/providers/codex-rollout-timeline.ts index f30bd342b..bcd825d98 100644 --- a/packages/server/src/server/agent/providers/codex-rollout-timeline.ts +++ b/packages/server/src/server/agent/providers/codex-rollout-timeline.ts @@ -20,28 +20,40 @@ function resolveCodexSessionRoot(): string | null { } async function findRolloutFile(threadId: string, root: string): Promise { - const stack: { dir: string; depth: number }[] = [{ dir: root, depth: 0 }]; - while (stack.length > 0) { - const { dir, depth } = stack.pop()!; - let entries: Dirent[]; - try { - entries = await fs.readdir(dir, { withFileTypes: true }); - } catch { - continue; - } - for (const entry of entries) { - const entryPath = path.join(dir, entry.name); - if (entry.isFile()) { - const matchesThread = entry.name.includes(threadId); - const matchesPrefix = entry.name.startsWith("rollout-"); - const matchesExtension = entry.name.endsWith(".json") || entry.name.endsWith(".jsonl"); - if (matchesThread && matchesPrefix && matchesExtension) { - return entryPath; + let currentLevel: { dir: string; depth: number }[] = [{ dir: root, depth: 0 }]; + while (currentLevel.length > 0) { + const readings = await Promise.all( + currentLevel.map(async ({ dir, depth }) => { + let entries: Dirent[]; + try { + entries = await fs.readdir(dir, { withFileTypes: true }); + } catch { + return { match: null as string | null, next: [] as { dir: string; depth: number }[] }; } - } else if (entry.isDirectory() && depth < MAX_ROLLOUT_SEARCH_DEPTH) { - stack.push({ dir: entryPath, depth: depth + 1 }); - } + let match: string | null = null; + const next: { dir: string; depth: number }[] = []; + for (const entry of entries) { + const entryPath = path.join(dir, entry.name); + if (entry.isFile()) { + const matchesThread = entry.name.includes(threadId); + const matchesPrefix = entry.name.startsWith("rollout-"); + const matchesExtension = + entry.name.endsWith(".json") || entry.name.endsWith(".jsonl"); + if (matchesThread && matchesPrefix && matchesExtension && match === null) { + match = entryPath; + } + } else if (entry.isDirectory() && depth < MAX_ROLLOUT_SEARCH_DEPTH) { + next.push({ dir: entryPath, depth: depth + 1 }); + } + } + return { match, next }; + }), + ); + const firstMatch = readings.find((reading) => reading.match !== null); + if (firstMatch?.match) { + return firstMatch.match; } + currentLevel = readings.flatMap((reading) => reading.next); } return null; }