( sessionId: UUID, )
| 4088 | } |
| 4089 | |
| 4090 | export async function getLastSessionLog( |
| 4091 | sessionId: UUID, |
| 4092 | ): Promise<LogOption | null> { |
| 4093 | // Single read: load all session data at once instead of reading the file twice |
| 4094 | const { |
| 4095 | messages, |
| 4096 | summaries, |
| 4097 | customTitles, |
| 4098 | tags, |
| 4099 | agentSettings, |
| 4100 | worktreeStates, |
| 4101 | fileHistorySnapshots, |
| 4102 | attributionSnapshots, |
| 4103 | contentReplacements, |
| 4104 | contextCollapseCommits, |
| 4105 | contextCollapseSnapshot, |
| 4106 | } = await loadSessionFile(sessionId) |
| 4107 | if (messages.size === 0) return null |
| 4108 | // Prime getSessionMessages cache so recordTranscript (called after REPL |
| 4109 | // mount on --resume) skips a second full file load. -170~227ms on large sessions. |
| 4110 | // Guard: only prime if cache is empty. Mid-session callers (e.g. IssueFeedback) |
| 4111 | // may call getLastSessionLog on the current session — overwriting a live cache |
| 4112 | // with a stale disk snapshot would lose unflushed UUIDs and break dedup. |
| 4113 | if (!getSessionMessages.cache.has(sessionId)) { |
| 4114 | getSessionMessages.cache.set( |
| 4115 | sessionId, |
| 4116 | Promise.resolve(new Set(messages.keys())), |
| 4117 | ) |
| 4118 | } |
| 4119 | |
| 4120 | // Find the most recent non-sidechain message |
| 4121 | const lastMessage = findLatestMessage(messages.values(), m => !m.isSidechain) |
| 4122 | if (!lastMessage) return null |
| 4123 | |
| 4124 | // Build the transcript chain from the last message |
| 4125 | const transcript = buildConversationChain(messages, lastMessage, { |
| 4126 | includeLogicalParents: true, |
| 4127 | }) |
| 4128 | |
| 4129 | const summary = summaries.get(lastMessage.uuid) |
| 4130 | const customTitle = customTitles.get(lastMessage.sessionId as UUID) |
| 4131 | const tag = tags.get(lastMessage.sessionId as UUID) |
| 4132 | const agentSetting = agentSettings.get(sessionId) |
| 4133 | return { |
| 4134 | ...convertToLogOption( |
| 4135 | transcript, |
| 4136 | 0, |
| 4137 | summary, |
| 4138 | customTitle, |
| 4139 | buildFileHistorySnapshotChain(fileHistorySnapshots, transcript), |
| 4140 | tag, |
| 4141 | getTranscriptPathForSession(sessionId), |
| 4142 | buildAttributionSnapshotChain(attributionSnapshots, transcript), |
| 4143 | agentSetting, |
| 4144 | contentReplacements.get(sessionId) ?? [], |
| 4145 | ), |
| 4146 | worktreeSession: worktreeStates.get(sessionId), |
| 4147 | contextCollapseCommits: contextCollapseCommits.filter( |
no test coverage detected