( normalizedMessages: NormalizedMessage[], messages: Message[], )
| 1168 | * getSiblingToolUseIDs, and hasUnresolvedHooks for each message. |
| 1169 | */ |
| 1170 | export function buildMessageLookups( |
| 1171 | normalizedMessages: NormalizedMessage[], |
| 1172 | messages: Message[], |
| 1173 | ): MessageLookups { |
| 1174 | // First pass: group assistant messages by ID and collect all tool use IDs per message |
| 1175 | const toolUseIDsByMessageID = new Map<string, Set<string>>() |
| 1176 | const toolUseIDToMessageID = new Map<string, string>() |
| 1177 | const toolUseByToolUseID = new Map<string, ToolUseBlockParam>() |
| 1178 | for (const msg of messages) { |
| 1179 | if (msg.type === 'assistant') { |
| 1180 | const id = msg.message.id |
| 1181 | let toolUseIDs = toolUseIDsByMessageID.get(id) |
| 1182 | if (!toolUseIDs) { |
| 1183 | toolUseIDs = new Set() |
| 1184 | toolUseIDsByMessageID.set(id, toolUseIDs) |
| 1185 | } |
| 1186 | for (const content of msg.message.content) { |
| 1187 | if (content.type === 'tool_use') { |
| 1188 | toolUseIDs.add(content.id) |
| 1189 | toolUseIDToMessageID.set(content.id, id) |
| 1190 | toolUseByToolUseID.set(content.id, content) |
| 1191 | } |
| 1192 | } |
| 1193 | } |
| 1194 | } |
| 1195 | |
| 1196 | // Build sibling lookup - each tool use ID maps to all sibling tool use IDs |
| 1197 | const siblingToolUseIDs = new Map<string, Set<string>>() |
| 1198 | for (const [toolUseID, messageID] of toolUseIDToMessageID) { |
| 1199 | siblingToolUseIDs.set(toolUseID, toolUseIDsByMessageID.get(messageID)!) |
| 1200 | } |
| 1201 | |
| 1202 | // Single pass over normalizedMessages to build progress, hook, and tool result lookups |
| 1203 | const progressMessagesByToolUseID = new Map<string, ProgressMessage[]>() |
| 1204 | const inProgressHookCounts = new Map<string, Map<HookEvent, number>>() |
| 1205 | // Track unique hook names per (toolUseID, hookEvent) to match getResolvedHookCount behavior. |
| 1206 | // A single hook can produce multiple attachment messages (e.g., hook_success + hook_additional_context), |
| 1207 | // so we deduplicate by hookName. |
| 1208 | const resolvedHookNames = new Map<string, Map<HookEvent, Set<string>>>() |
| 1209 | const toolResultByToolUseID = new Map<string, NormalizedMessage>() |
| 1210 | // Track resolved/errored tool use IDs (replaces separate useMemos in Messages.tsx) |
| 1211 | const resolvedToolUseIDs = new Set<string>() |
| 1212 | const erroredToolUseIDs = new Set<string>() |
| 1213 | |
| 1214 | for (const msg of normalizedMessages) { |
| 1215 | if (msg.type === 'progress') { |
| 1216 | // Build progress messages lookup |
| 1217 | const toolUseID = msg.parentToolUseID |
| 1218 | const existing = progressMessagesByToolUseID.get(toolUseID) |
| 1219 | if (existing) { |
| 1220 | existing.push(msg) |
| 1221 | } else { |
| 1222 | progressMessagesByToolUseID.set(toolUseID, [msg]) |
| 1223 | } |
| 1224 | |
| 1225 | // Count in-progress hooks |
| 1226 | if (msg.data.type === 'hook_progress') { |
| 1227 | const hookEvent = msg.data.hookEvent |
no test coverage detected