( sessionId: UUID, )
| 3973 | } |
| 3974 | |
| 3975 | export async function getLastSessionLog( |
| 3976 | sessionId: UUID, |
| 3977 | ): Promise<LogOption | null> { |
| 3978 | // Single read: load all session data at once instead of reading the file twice |
| 3979 | const { |
| 3980 | messages, |
| 3981 | summaries, |
| 3982 | customTitles, |
| 3983 | tags, |
| 3984 | agentSettings, |
| 3985 | worktreeStates, |
| 3986 | fileHistorySnapshots, |
| 3987 | attributionSnapshots, |
| 3988 | contentReplacements, |
| 3989 | goals, |
| 3990 | contextCollapseCommits, |
| 3991 | contextCollapseSnapshot, |
| 3992 | } = await loadSessionFile(sessionId) |
| 3993 | if (messages.size === 0) return null |
| 3994 | // Prime getSessionMessages cache so recordTranscript (called after REPL |
| 3995 | // mount on --resume) skips a second full file load. -170~227ms on large sessions. |
| 3996 | // Guard: only prime if cache is empty. Mid-session callers (e.g. IssueFeedback) |
| 3997 | // may call getLastSessionLog on the current session — overwriting a live cache |
| 3998 | // with a stale disk snapshot would lose unflushed UUIDs and break dedup. |
| 3999 | if (!getSessionMessages.cache.has(sessionId)) { |
| 4000 | getSessionMessages.cache.set( |
| 4001 | sessionId, |
| 4002 | Promise.resolve(new Set(messages.keys())), |
| 4003 | ) |
| 4004 | } |
| 4005 | |
| 4006 | // Find the most recent non-sidechain message |
| 4007 | const lastMessage = findLatestMessage(messages.values(), m => !m.isSidechain) |
| 4008 | if (!lastMessage) return null |
| 4009 | |
| 4010 | // Build the transcript chain from the last message |
| 4011 | const transcript = buildConversationChain(messages, lastMessage) |
| 4012 | |
| 4013 | const summary = summaries.get(lastMessage.uuid) |
| 4014 | const customTitle = customTitles.get(lastMessage.sessionId as UUID) |
| 4015 | const tag = tags.get(lastMessage.sessionId as UUID) |
| 4016 | const agentSetting = agentSettings.get(sessionId) |
| 4017 | return { |
| 4018 | ...convertToLogOption( |
| 4019 | transcript, |
| 4020 | 0, |
| 4021 | summary, |
| 4022 | customTitle, |
| 4023 | buildFileHistorySnapshotChain(fileHistorySnapshots, transcript), |
| 4024 | tag, |
| 4025 | getTranscriptPathForSession(sessionId), |
| 4026 | buildAttributionSnapshotChain(attributionSnapshots, transcript), |
| 4027 | agentSetting, |
| 4028 | contentReplacements.get(sessionId) ?? [], |
| 4029 | ), |
| 4030 | worktreeSession: worktreeStates.get(sessionId), |
| 4031 | goal: goals.get(sessionId), |
| 4032 | contextCollapseCommits: contextCollapseCommits.filter( |
no test coverage detected