MCPcopy Index your code
hub / github.com/codeaashu/claude-code / recoverOrphanedParallelToolResults

Function recoverOrphanedParallelToolResults

src/utils/sessionStorage.ts:2118–2206  ·  view source on GitHub ↗

* Post-pass for buildConversationChain: recover sibling assistant blocks and * tool_results that the single-parent walk orphaned. * * Streaming (claude.ts:~2024) emits one AssistantMessage per content_block_stop * — N parallel tool_uses → N messages, distinct uuid, same message.id. Each * tool_

(
  messages: Map<UUID, TranscriptMessage>,
  chain: TranscriptMessage[],
  seen: Set<UUID>,
)

Source from the content-addressed store, hash-verified

2116 * this recovery pass handles them.
2117 */
2118function recoverOrphanedParallelToolResults(
2119 messages: Map<UUID, TranscriptMessage>,
2120 chain: TranscriptMessage[],
2121 seen: Set<UUID>,
2122): TranscriptMessage[] {
2123 type ChainAssistant = Extract<TranscriptMessage, { type: 'assistant' }>
2124 const chainAssistants = chain.filter(
2125 (m): m is ChainAssistant => m.type === 'assistant',
2126 )
2127 if (chainAssistants.length === 0) return chain
2128
2129 // Anchor = last on-chain member of each sibling group. chainAssistants is
2130 // already in chain order, so later iterations overwrite → last wins.
2131 const anchorByMsgId = new Map<string, ChainAssistant>()
2132 for (const a of chainAssistants) {
2133 if (a.message.id) anchorByMsgId.set(a.message.id, a)
2134 }
2135
2136 // O(n) precompute: sibling groups and TR index.
2137 // TRs indexed by parentUuid — insertMessageChain:~894 already wrote that
2138 // as the srcUUID, and --fork-session strips srcUUID but keeps parentUuid.
2139 const siblingsByMsgId = new Map<string, TranscriptMessage[]>()
2140 const toolResultsByAsst = new Map<UUID, TranscriptMessage[]>()
2141 for (const m of messages.values()) {
2142 if (m.type === 'assistant' && m.message.id) {
2143 const group = siblingsByMsgId.get(m.message.id)
2144 if (group) group.push(m)
2145 else siblingsByMsgId.set(m.message.id, [m])
2146 } else if (
2147 m.type === 'user' &&
2148 m.parentUuid &&
2149 Array.isArray(m.message.content) &&
2150 m.message.content.some(b => b.type === 'tool_result')
2151 ) {
2152 const group = toolResultsByAsst.get(m.parentUuid)
2153 if (group) group.push(m)
2154 else toolResultsByAsst.set(m.parentUuid, [m])
2155 }
2156 }
2157
2158 // For each message.id group touching the chain: collect off-chain siblings,
2159 // then off-chain TRs for ALL members. Splice right after the last on-chain
2160 // member so the group stays contiguous for normalizeMessagesForAPI's merge
2161 // and every TR lands after its tool_use.
2162 const processedGroups = new Set<string>()
2163 const inserts = new Map<UUID, TranscriptMessage[]>()
2164 let recoveredCount = 0
2165 for (const asst of chainAssistants) {
2166 const msgId = asst.message.id
2167 if (!msgId || processedGroups.has(msgId)) continue
2168 processedGroups.add(msgId)
2169
2170 const group = siblingsByMsgId.get(msgId) ?? [asst]
2171 const orphanedSiblings = group.filter(s => !seen.has(s.uuid))
2172 const orphanedTRs: TranscriptMessage[] = []
2173 for (const member of group) {
2174 const trs = toolResultsByAsst.get(member.uuid)
2175 if (!trs) continue

Callers 1

buildConversationChainFunction · 0.85

Calls 7

logEventFunction · 0.85
valuesMethod · 0.80
getMethod · 0.65
setMethod · 0.45
pushMethod · 0.45
hasMethod · 0.45
addMethod · 0.45

Tested by

no test coverage detected