@@ -9,7 +9,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
99import '@testing-library/jest-dom'
1010
1111const notifyDesktopMock = vi . hoisted ( ( ) => vi . fn ( ) )
12- const discardSideChatMock = vi . hoisted ( ( ) => vi . fn ( async ( ) => ( { } ) ) )
1312const sessionsApiMock = vi . hoisted ( ( ) => ( {
1413 delete : vi . fn ( ( ) => Promise . resolve ( ) ) ,
1514 getSlashCommands : vi . fn ( async ( ) => ( { commands : [ ] } ) ) ,
@@ -19,7 +18,6 @@ const sessionsApiMock = vi.hoisted(() => ({
1918} ) )
2019
2120vi . mock ( '../api/sessions' , ( ) => ( { sessionsApi : sessionsApiMock } ) )
22- vi . mock ( '../api/sideChats' , ( ) => ( { sideChatsApi : { discard : discardSideChatMock } } ) )
2321vi . mock ( '../api/teams' , ( ) => ( { teamsApi : { getWorkbenchForSession : vi . fn ( ) } } ) )
2422vi . mock ( '../lib/desktopNotifications' , ( ) => ( { notifyDesktop : notifyDesktopMock } ) )
2523
@@ -30,7 +28,6 @@ import { registerSideChatSession, unregisterSideChatSession } from '../lib/sideC
3028import { WARM_SESSION_LIMIT , useTabStore , type Tab } from './tabStore'
3129import { useTeamStore } from './teamStore'
3230import { useSessionStore } from './sessionStore'
33- import { useSideChatStore } from './sideChatStore'
3431import { navigateMobileUp } from '../components/mobile/mobileNavigation'
3532import type { ServerMessage } from '../types/chat'
3633
@@ -176,8 +173,6 @@ describe('releasing sessions that no tab shows (#1485)', () => {
176173 wsManager . disconnectAll ( )
177174 disconnectSpy = vi . spyOn ( wsManager , 'disconnect' )
178175 notifyDesktopMock . mockClear ( )
179- discardSideChatMock . mockClear ( )
180- useSideChatStore . setState ( { entries : { } } )
181176 uninstall = installHiddenSessionRelease ( useChatStore )
182177
183178 class ResizeObserverMock {
@@ -432,64 +427,28 @@ describe('releasing sessions that no tab shows (#1485)', () => {
432427 expect ( isHeld ( 'source' ) ) . toBe ( false )
433428 } )
434429
435- it . each ( [
436- { scope : 'open-tabs' as const , finishesWhileHidden : false } ,
437- { scope : 'open-tabs' as const , finishesWhileHidden : true } ,
438- { scope : 'active-tab' as const , finishesWhileHidden : false } ,
439- { scope : 'active-tab' as const , finishesWhileHidden : true } ,
440- ] ) ( 'keeps a temporary side chat usable until explicitly closed ($scope, finishes while hidden: $finishesWhileHidden)' , async ( { scope, finishesWhileHidden } ) => {
441- uninstall ( )
442- uninstall = installHiddenSessionRelease ( useChatStore , { scope } )
430+ it ( 'releases a side chat with its parent once both are done, keeping its transcript' , async ( ) => {
443431 registerSideChatSession ( 'side-of-parent' , 'parent' )
444432 try {
445- useSideChatStore . setState ( { entries : {
446- 'side-of-parent' : { sessionId : 'side-of-parent' , parentSessionId : 'parent' , workDir : '/repo' , title : 'Side chat' , ephemeral : true } ,
447- } } )
448433 useTabStore . setState ( { tabs : [ sessionTab ( 'parent' , 'Parent chat' ) ] , activeTabId : 'parent' } )
449434 const parentSocket = connect ( 'parent' )
450435 const sideSocket = connect ( 'side-of-parent' )
451436 serverSays ( parentSocket , TURN_COMPLETE )
452- serverSays ( sideSocket , cliRun ( 'running' ) )
453437 serverSays ( sideSocket , { type : 'status' , state : 'thinking' } )
454- for ( const message of replyText ( 'Side chat context.' ) ) serverSays ( sideSocket , message )
455- if ( ! finishesWhileHidden ) {
456- serverSays ( sideSocket , TURN_COMPLETE )
457- serverSays ( sideSocket , cliRun ( 'idle' ) )
458- }
459438 await settle ( )
439+ act ( ( ) => { vi . advanceTimersByTime ( EIGHTEEN_HOURS_MS ) } )
440+ // While the parent is in the warm set its workspace, side chat included, stays.
441+ expect ( isHeld ( 'side-of-parent' ) ) . toBe ( true )
460442
461443 moveOnFrom ( 'parent' )
462444 await settle ( )
463445 expect ( isHeld ( 'parent' ) ) . toBe ( false )
464- if ( finishesWhileHidden ) {
465- serverSays ( sideSocket , TURN_COMPLETE )
466- serverSays ( sideSocket , cliRun ( 'idle' ) )
467- }
468- await settle ( )
469- // #1513 released this socket when the parent left the warm set. The
470- // server then destroyed the non-persistent CLI after its idle grace,
471- // and a later send failed with SESSION_DELETED despite cached messages.
472- act ( ( ) => { vi . advanceTimersByTime ( EIGHTEEN_HOURS_MS ) } )
473- await settle ( )
474- expect ( sideSocket . closedByClient ) . toBe ( false )
446+ // Still answering: it finishes first.
475447 expect ( isHeld ( 'side-of-parent' ) ) . toBe ( true )
476- expect ( disconnectSpy ) . not . toHaveBeenCalledWith ( 'side-of-parent' )
477448
478- act ( ( ) => useTabStore . getState ( ) . openTab ( 'parent' , 'Parent chat' ) )
479- act ( ( ) => useChatStore . getState ( ) . sendMessage ( 'side-of-parent' , 'Continue the side chat' ) )
480- expect ( socketsFor ( 'side-of-parent' ) ) . toEqual ( [ sideSocket ] )
481- expect ( sideSocket . sent ) . toContainEqual ( expect . objectContaining ( { type : 'user_message' , content : 'Continue the side chat' } ) )
482- expect ( useChatStore . getState ( ) . sessions [ 'side-of-parent' ] ?. messages ) . toContainEqual ( expect . objectContaining ( { type : 'assistant_text' , content : 'Side chat context.' } ) )
483449 serverSays ( sideSocket , TURN_COMPLETE )
484- serverSays ( sideSocket , cliRun ( 'idle' ) )
485450 await settle ( )
486-
487- await act ( async ( ) => { await useSideChatStore . getState ( ) . close ( 'side-of-parent' ) } )
488- expect ( discardSideChatMock ) . toHaveBeenCalledWith ( 'parent' , 'side-of-parent' )
489- expect ( sideSocket . closedByClient ) . toBe ( true )
490- expect ( isHeld ( 'side-of-parent' ) ) . toBe ( false )
491- expect ( useChatStore . getState ( ) . sessions [ 'side-of-parent' ] ) . toBeUndefined ( )
492- expect ( useSideChatStore . getState ( ) . entries [ 'side-of-parent' ] ) . toBeUndefined ( )
451+ expectReleasedWithTranscript ( 'side-of-parent' )
493452 } finally {
494453 unregisterSideChatSession ( 'side-of-parent' )
495454 }
0 commit comments