@@ -173,26 +173,28 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
173173 return groupKey ( name , undefined )
174174 }
175175
176- const ensureGroup = ( name : string , parentToolCallId : string | undefined ) : AgentGroupSegment => {
176+ const ensureGroup = (
177+ name : string ,
178+ parentToolCallId : string | undefined
179+ ) : { group : AgentGroupSegment ; created : boolean } => {
177180 const key = resolveGroupKey ( name , parentToolCallId )
178- let g = groupsByKey . get ( key )
179- if ( ! g ) {
180- g = {
181- type : 'agent_group' ,
182- // Suffix with segments.length so a later flushLanes / explicit delete
183- // followed by re-ensure for the same key produces a fresh React key
184- // instead of colliding with the stranded prior segment.
185- id : `agent-${ key } -${ segments . length } ` ,
186- agentName : name ,
187- agentLabel : resolveAgentLabel ( name ) ,
188- items : [ ] ,
189- isDelegating : false ,
190- isOpen : false ,
191- }
192- segments . push ( g )
193- groupsByKey . set ( key , g )
181+ const existing = groupsByKey . get ( key )
182+ if ( existing ) return { group : existing , created : false }
183+ const group : AgentGroupSegment = {
184+ type : 'agent_group' ,
185+ // Suffix with segments.length so a later flushLanes / explicit delete
186+ // followed by re-ensure for the same key produces a fresh React key
187+ // instead of colliding with the stranded prior segment.
188+ id : `agent-${ key } -${ segments . length } ` ,
189+ agentName : name ,
190+ agentLabel : resolveAgentLabel ( name ) ,
191+ items : [ ] ,
192+ isDelegating : false ,
193+ isOpen : false ,
194194 }
195- return g
195+ segments . push ( group )
196+ groupsByKey . set ( key , group )
197+ return { group, created : true }
196198 }
197199
198200 const findGroupForSubagentChunk = (
@@ -302,7 +304,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
302304 // arrives after this subagent finishes should render below the lane,
303305 // not get back-filled into the mothership group sitting above it.
304306 groupsByKey . delete ( groupKey ( 'mothership' , undefined ) )
305- const g = ensureGroup ( key , block . parentToolCallId )
307+ const { group : g } = ensureGroup ( key , block . parentToolCallId )
306308 if ( inheritedDelegation ) g . isDelegating = true
307309 g . isOpen = block . endedAt === undefined
308310 activeGroupKey = resolveGroupKey ( key , block . parentToolCallId )
@@ -318,7 +320,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
318320
319321 if ( isDispatch ) {
320322 groupsByKey . delete ( groupKey ( 'mothership' , undefined ) )
321- const g = ensureGroup ( tc . name , tc . id )
323+ const { group : g } = ensureGroup ( tc . name , tc . id )
322324 g . isDelegating = isDelegatingTool ( tc )
323325 g . isOpen = g . isDelegating
324326 continue
@@ -327,13 +329,16 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
327329 const tool = toToolData ( tc )
328330
329331 if ( tc . calledBy ) {
330- const g = ensureGroup ( tc . calledBy , block . parentToolCallId )
332+ const { group : g , created } = ensureGroup ( tc . calledBy , block . parentToolCallId )
331333 g . isDelegating = false
332- if ( block . parentToolCallId ) g . isOpen = true
334+ // Only mark the lane open when we just created it. Late tool_calls
335+ // arriving after a subagent_end (out-of-order persistence, replay,
336+ // partial state hand-off) must NOT reopen a closed lane.
337+ if ( created && block . parentToolCallId ) g . isOpen = true
333338 g . items . push ( { type : 'tool' , data : tool } )
334339 activeGroupKey = resolveGroupKey ( tc . calledBy , block . parentToolCallId )
335340 } else {
336- const g = ensureGroup ( 'mothership' , undefined )
341+ const { group : g } = ensureGroup ( 'mothership' , undefined )
337342 g . items . push ( { type : 'tool' , data : tool } )
338343 }
339344 continue
0 commit comments