@@ -223,8 +223,11 @@ describe('AgentGroup inline main activity', () => {
223223
224224 it . each ( [
225225 [ 'success' , 'Checked search requirements' ] ,
226- [ 'error' , 'Failed checking search requirements' ] ,
227- [ 'cancelled' , 'Stopped checking search requirements' ] ,
226+ [ 'error' , 'Failed searching' ] ,
227+ [ 'cancelled' , 'Stopped searching' ] ,
228+ [ 'skipped' , 'Skipped searching' ] ,
229+ [ 'interrupted' , 'Stopped searching' ] ,
230+ [ 'rejected' , 'Failed searching' ] ,
228231 ] as const ) ( 'uses an honest grouped activity label after %s' , ( status , expected ) => {
229232 const item = tool ( status )
230233 act ( ( ) =>
@@ -234,12 +237,11 @@ describe('AgentGroup inline main activity', () => {
234237 agentLabel : 'Sim' ,
235238 activity : {
236239 id : 'search' ,
237- title : 'Checking search requirements' ,
238240 completedTitle : 'Checked search requirements' ,
239241 } ,
240242 items : [ item ] ,
241243 isStreaming : true ,
242- isLaneOpen : true ,
244+ isLaneOpen : false ,
243245 } )
244246 )
245247 )
@@ -255,7 +257,6 @@ describe('AgentGroup inline main activity', () => {
255257 agentLabel : 'Sim' ,
256258 activity : {
257259 id : 'search' ,
258- title : 'Checking requirements' ,
259260 completedTitle : 'Checked requirements' ,
260261 } ,
261262 items : [ tool ( 'executing' ) ] ,
@@ -268,6 +269,162 @@ describe('AgentGroup inline main activity', () => {
268269 expect ( container . textContent ) . not . toContain ( 'Checking requirements' )
269270 } )
270271
272+ it ( 'counts unresolved calls and waits for a lane boundary before showing completed activity' , ( ) => {
273+ vi . useFakeTimers ( )
274+ const render = ( statuses : ToolCallStatus [ ] , isLaneOpen = true ) => {
275+ act ( ( ) =>
276+ root . render (
277+ createElement ( AgentGroup , {
278+ agentName : 'mothership' ,
279+ agentLabel : 'Sim' ,
280+ activity : { id : 'build' , completedTitle : 'Built Search API' } ,
281+ isStreaming : true ,
282+ isLaneOpen,
283+ items : statuses . map ( ( status , index ) => ( {
284+ type : 'tool' as const ,
285+ data : {
286+ id : `call-${ index } ` ,
287+ toolName : 'read' ,
288+ displayTitle : `Reading document ${ index } ` ,
289+ status,
290+ } ,
291+ } ) ) ,
292+ } )
293+ )
294+ )
295+ act ( ( ) => vi . advanceTimersByTime ( 1000 ) )
296+ return container . querySelector ( '[role="status"]' ) !
297+ }
298+ expect ( render ( [ 'executing' , 'executing' , 'success' ] ) . textContent ) . toBe ( 'Reading document 1 + 1' )
299+ expect ( render ( [ 'executing' , 'success' , 'success' ] ) . textContent ) . toBe ( 'Reading document 0' )
300+ expect ( render ( [ 'success' , 'success' , 'success' ] ) . textContent ) . toBe ( 'Read document 2' )
301+ expect ( container . querySelector ( '[class*="shimmer"]' ) ) . toBeNull ( )
302+ const completed = render ( [ 'success' , 'success' , 'success' ] , false )
303+ expect ( completed . textContent ) . toBe ( 'Built Search API' )
304+ expect ( completed . querySelector ( 'svg' ) ) . toBeNull ( )
305+ act ( ( ) => container . querySelector < HTMLElement > ( '[role="button"]' ) ?. click ( ) )
306+ expect ( container . querySelector ( '[data-state="open"] svg' ) ) . not . toBeNull ( )
307+ } )
308+
309+ it . each ( [ 'sim_cli' , 'run_code' ] ) (
310+ 'shows %s argument preparation before its concrete call' ,
311+ ( toolName ) => {
312+ vi . useFakeTimers ( )
313+ const render = (
314+ params : Record < string , unknown > ,
315+ status : ToolCallStatus ,
316+ isLaneOpen = true
317+ ) => {
318+ act ( ( ) =>
319+ root . render (
320+ createElement ( AgentGroup , {
321+ agentName : 'mothership' ,
322+ agentLabel : 'Sim' ,
323+ isStreaming : true ,
324+ isLaneOpen,
325+ items : [
326+ {
327+ type : 'tool' ,
328+ data : {
329+ id : 'call' ,
330+ toolName,
331+ displayTitle : 'Running checks' ,
332+ status,
333+ params,
334+ streamingArgs : '{"activity":{"id":"check","completedTitle":"Checked inputs"},' ,
335+ } ,
336+ } ,
337+ ] ,
338+ } )
339+ )
340+ )
341+ act ( ( ) => vi . advanceTimersByTime ( 1000 ) )
342+ return container . textContent
343+ }
344+ expect ( render ( { } , 'executing' ) ) . toBe ( 'Preparing tool call…' )
345+ const params = { code : '1' , activity : { id : 'check' , completedTitle : 'Checked inputs' } }
346+ expect ( render ( params , 'executing' ) ) . toBe ( 'Running checks' )
347+ expect ( render ( params , 'success' ) ) . toBe ( 'Ran checks' )
348+ expect ( render ( params , 'success' , false ) ) . toBe ( 'Checked inputs' )
349+ }
350+ )
351+
352+ it ( 'keeps an activity unfinished while a standalone approval is pending' , ( ) => {
353+ act ( ( ) =>
354+ root . render (
355+ createElement ( AgentGroupView , {
356+ agentName : 'mothership' ,
357+ agentLabel : 'Sim' ,
358+ activity : { id : 'build' , completedTitle : 'Built API' } ,
359+ items : [
360+ {
361+ type : 'tool' ,
362+ data : {
363+ id : 'read' ,
364+ toolName : 'read' ,
365+ displayTitle : 'Read configuration' ,
366+ status : 'success' ,
367+ } ,
368+ } ,
369+ {
370+ type : 'tool' ,
371+ data : {
372+ id : 'approval' ,
373+ toolName : 'create' ,
374+ displayTitle : 'Waiting for approval' ,
375+ status : 'awaiting_approval' ,
376+ } ,
377+ } ,
378+ ] ,
379+ ToolCallComponent : ( { displayTitle, renderStatus } : ToolCallItemProps ) =>
380+ renderStatus
381+ ? renderStatus ( { label : displayTitle , activeLabel : displayTitle , isActive : false } )
382+ : createElement ( 'div' , null , displayTitle ) ,
383+ } )
384+ )
385+ )
386+ expect ( container . textContent ) . toBe ( 'Read configurationWaiting for approval' )
387+ expect ( container . textContent ) . not . toContain ( 'Built API' )
388+ } )
389+
390+ it ( 'retains earlier failed outcomes when completed activities collapse into one summary' , ( ) => {
391+ act ( ( ) =>
392+ root . render (
393+ createElement ( AgentGroup , {
394+ agentName : 'mothership' ,
395+ agentLabel : 'Sim' ,
396+ activity : { id : 'second' , completedTitle : 'Checked inputs' } ,
397+ completedGroupCount : 2 ,
398+ items : [
399+ {
400+ type : 'tool' ,
401+ data : {
402+ id : 'first' ,
403+ toolName : 'read' ,
404+ displayTitle : 'Reading first document' ,
405+ status : 'error' ,
406+ params : { activity : { id : 'first' } } ,
407+ } ,
408+ } ,
409+ {
410+ type : 'tool' ,
411+ data : {
412+ id : 'second' ,
413+ toolName : 'read' ,
414+ displayTitle : 'Reading second document' ,
415+ status : 'success' ,
416+ params : { activity : { id : 'second' } } ,
417+ } ,
418+ } ,
419+ ] ,
420+ } )
421+ )
422+ )
423+ expect ( container . querySelector ( '[role="status"]' ) ?. textContent ) . toBe (
424+ 'Checked inputs + 1 · 1 failed'
425+ )
426+ } )
427+
271428 it ( 'paces the active status in place and expands the full completed history' , ( ) => {
272429 vi . useFakeTimers ( )
273430 const first : AgentGroupItem = {
@@ -300,7 +457,7 @@ describe('AgentGroup inline main activity', () => {
300457 expect ( container . firstElementChild ) . toBe ( activity )
301458 expect ( container . textContent ) . toBe ( 'Searching files' )
302459 act ( ( ) => vi . advanceTimersByTime ( 1000 ) )
303- expect ( container . textContent ) . toBe ( 'Reading notes' )
460+ expect ( container . textContent ) . toBe ( 'Reading notes + 1 ' )
304461 expect ( container . querySelector ( '[class*="shimmer"]' ) ) . not . toBeNull ( )
305462 expect (
306463 container . querySelector < HTMLElement > ( '[role="button"]' ) ?. getAttribute ( 'aria-expanded' )
@@ -315,7 +472,7 @@ describe('AgentGroup inline main activity', () => {
315472 ] ,
316473 false
317474 )
318- expect ( container . textContent ) . toBe ( 'Searched files, read files ' )
475+ expect ( container . textContent ) . toBe ( 'Read notes + 1 ' )
319476 expect ( container . querySelector ( '[class*="shimmer"]' ) ) . toBeNull ( )
320477 const header = container . querySelector < HTMLElement > ( '[role="button"]' )
321478 act ( ( ) => header ?. click ( ) )
@@ -325,7 +482,7 @@ describe('AgentGroup inline main activity', () => {
325482 )
326483 act ( ( ) => header ?. click ( ) )
327484 expect ( header ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
328- expect ( container . textContent ) . toBe ( 'Searched files, read files ' )
485+ expect ( container . textContent ) . toBe ( 'Read notes + 1 ' )
329486 } )
330487
331488 it ( 'keeps history expanded as new tools arrive' , ( ) => {
@@ -445,7 +602,7 @@ describe('AgentGroup inline main activity', () => {
445602 read ,
446603 { ...wait , data : { ...wait . data , id : 'wait-second' , status : 'success' } } ,
447604 ] )
448- expect ( header ?. textContent ) . toBe ( 'Waited, read files ' )
605+ expect ( header ?. textContent ) . toBe ( 'Waited + 2 ' )
449606 expect ( container . querySelector ( '.overflow-y-auto' ) ) . toBe ( viewport )
450607 expect ( clearIntervalSpy ) . toHaveBeenCalledTimes ( 2 )
451608 } finally {
@@ -655,7 +812,8 @@ describe('AgentGroup inline main activity', () => {
655812 Array . from ( container . querySelectorAll ( '[data-tool-call-id]' ) , ( row ) =>
656813 row . getAttribute ( 'data-tool-call-id' )
657814 )
658- ) . toEqual ( [ 'permission' , 'handoff' , 'latest' ] )
815+ ) . toEqual ( [ 'permission' , 'handoff' ] )
816+ expect ( container . querySelector ( '[role="status"]' ) ?. textContent ) . toBe ( 'Reading notes' )
659817 expect (
660818 container . querySelector ( '[data-tool-call-id="permission"]' ) ?. closest ( '[data-state]' )
661819 ) . toBeNull ( )
0 commit comments