@@ -231,6 +231,69 @@ describe('parseBlocks span-identity tree', () => {
231231 expect ( nested . group . agentName ) . toBe ( 'file' )
232232 } )
233233
234+ it ( 'renders subagent thinking as a muted thinking item and keeps the delegating spinner' , ( ) => {
235+ const blocks : ContentBlock [ ] = [
236+ subagentStart ( 'workflow' , 'S1' , 'main' ) ,
237+ {
238+ type : 'subagent_thinking' ,
239+ content : 'reasoning about the fix' ,
240+ spanId : 'S1' ,
241+ subagent : 'workflow' ,
242+ timestamp : 2 ,
243+ } ,
244+ ]
245+
246+ const segments = parseBlocks ( blocks )
247+ expect ( segments ) . toHaveLength ( 1 )
248+ if ( segments [ 0 ] . type !== 'agent_group' ) throw new Error ( 'expected workflow group' )
249+ // Thinking renders in the lane…
250+ expect ( segments [ 0 ] . items ) . toEqual ( [ { type : 'thinking' , content : 'reasoning about the fix' } ] )
251+ // …but does not clear the delegating spinner (no real output yet).
252+ expect ( segments [ 0 ] . isDelegating ) . toBe ( true )
253+ } )
254+
255+ it ( 'creates the lane on demand when thinking arrives before its subagent start' , ( ) => {
256+ const blocks : ContentBlock [ ] = [
257+ {
258+ type : 'subagent_thinking' ,
259+ content : 'early reasoning' ,
260+ spanId : 'S1' ,
261+ parentSpanId : 'main' ,
262+ subagent : 'workflow' ,
263+ timestamp : 1 ,
264+ } ,
265+ subagentStart ( 'workflow' , 'S1' , 'main' ) ,
266+ ]
267+
268+ const segments = parseBlocks ( blocks )
269+ const group = segments . find ( ( s ) => s . type === 'agent_group' )
270+ if ( ! group || group . type !== 'agent_group' ) throw new Error ( 'expected workflow group' )
271+ expect ( group . agentName ) . toBe ( 'workflow' )
272+ expect ( group . items ) . toEqual ( [ { type : 'thinking' , content : 'early reasoning' } ] )
273+ } )
274+
275+ it ( 'orders thinking before the lane text that follows it' , ( ) => {
276+ const blocks : ContentBlock [ ] = [
277+ subagentStart ( 'workflow' , 'S1' , 'main' ) ,
278+ {
279+ type : 'subagent_thinking' ,
280+ content : 'planning' ,
281+ spanId : 'S1' ,
282+ subagent : 'workflow' ,
283+ timestamp : 2 ,
284+ } ,
285+ { type : 'subagent_text' , content : 'done' , spanId : 'S1' , subagent : 'workflow' , timestamp : 3 } ,
286+ ]
287+
288+ const segments = parseBlocks ( blocks )
289+ if ( segments [ 0 ] . type !== 'agent_group' ) throw new Error ( 'expected workflow group' )
290+ expect ( segments [ 0 ] . items ) . toEqual ( [
291+ { type : 'thinking' , content : 'planning' } ,
292+ { type : 'text' , content : 'done' } ,
293+ ] )
294+ expect ( segments [ 0 ] . isDelegating ) . toBe ( false )
295+ } )
296+
234297 it ( 'falls back to legacy flat grouping when blocks have no span identity' , ( ) => {
235298 const blocks : ContentBlock [ ] = [
236299 { type : 'subagent' , content : 'workflow' , parentToolCallId : 'tc-1' , timestamp : 1 } ,
@@ -309,4 +372,25 @@ describe('parseBlocks legacy — thinking between top-level tools', () => {
309372 // The untagged chunk after thinking must NOT merge into the flushed lane.
310373 expect ( groups [ 0 ] . items ) . toHaveLength ( 1 )
311374 } )
375+
376+ it ( 'renders subagent thinking inside the legacy lane' , ( ) => {
377+ const blocks : ContentBlock [ ] = [
378+ { type : 'subagent' , content : 'workflow' , parentToolCallId : 'd1' , timestamp : 1 } ,
379+ {
380+ type : 'subagent_thinking' ,
381+ content : 'legacy reasoning' ,
382+ parentToolCallId : 'd1' ,
383+ timestamp : 2 ,
384+ } ,
385+ { type : 'subagent_text' , content : 'output' , parentToolCallId : 'd1' , timestamp : 3 } ,
386+ ]
387+ const segments = parseBlocks ( blocks )
388+ const groups = segments . filter ( ( s ) => s . type === 'agent_group' )
389+ expect ( groups ) . toHaveLength ( 1 )
390+ if ( groups [ 0 ] . type !== 'agent_group' ) throw new Error ( 'expected group' )
391+ expect ( groups [ 0 ] . items ) . toEqual ( [
392+ { type : 'thinking' , content : 'legacy reasoning' } ,
393+ { type : 'text' , content : 'output' } ,
394+ ] )
395+ } )
312396} )
0 commit comments