@@ -603,27 +603,49 @@ it('passes explicitly selected skill workspace contexts through the ordinary Bui
603603 } )
604604} )
605605
606- describe ( 'same-chat mode selection' , ( ) => {
607- it ( 'preserves an existing chat and draft and applies the selected harness to its next turn' , async ( ) => {
606+ describe ( 'conversation mode selection' , ( ) => {
607+ it . each ( [ 'agent' , 'assistant' ] as const ) (
608+ 'keeps an existing %s chat in its saved mode without a mode control' ,
609+ async ( mode ) => {
610+ mocks . renderer . mockImplementation ( ( { composer } : { composer : ReactNode } ) => composer )
611+ await act ( async ( ) => renderHome ( < OrganizationHome chatId = 'chat-a' requestMode = { mode } /> ) )
612+ await act ( async ( ) => composerProps ( ) . onChange ( 'Keep this draft' ) )
613+ expect ( composerProps ( ) . showModeSelector ) . toBe ( false )
614+ expect ( composerProps ( ) . onModeChange ) . toBeUndefined ( )
615+ expect ( composerProps ( ) . requestMode ) . toBe ( mode )
616+ expect ( composerProps ( ) . value ) . toBe ( 'Keep this draft' )
617+ expect ( mocks . push ) . not . toHaveBeenCalled ( )
618+ expect ( mocks . send ) . not . toHaveBeenCalled ( )
619+ await act ( async ( ) => composerProps ( ) . onSubmit ( 'Keep this draft' ) )
620+ expect ( mocks . send ) . toHaveBeenLastCalledWith (
621+ 'Keep this draft' ,
622+ undefined ,
623+ undefined ,
624+ expect . objectContaining ( { requestMode : mode } )
625+ )
626+ }
627+ )
628+
629+ it ( 'offers both modes before sending and removes mode selection with the first message' , async ( ) => {
608630 mocks . renderer . mockImplementation ( ( { composer } : { composer : ReactNode } ) => composer )
609- await act ( async ( ) => renderHome ( < OrganizationHome chatId = 'chat-a' requestMode = 'agent' /> ) )
610- await act ( async ( ) => composerProps ( ) . onChange ( 'Keep this draft' ) )
631+ await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
632+ expect ( composerProps ( ) . showModeSelector ) . toBe ( true )
611633 await act ( async ( ) => composerProps ( ) . onModeChange ?.( 'assistant' ) )
612- expect ( composerProps ( ) . value ) . toBe ( 'Keep this draft' )
613634 expect ( composerProps ( ) . requestMode ) . toBe ( 'assistant' )
614- expect ( mocks . push ) . not . toHaveBeenCalled ( )
615- expect ( mocks . send ) . not . toHaveBeenCalled ( )
616- expect ( mocks . chat ) . toHaveBeenLastCalledWith (
617- { organizationId : 'organization-a' } ,
618- 'chat-a' ,
619- expect . objectContaining ( { requestMode : 'assistant' } )
620- )
621- await act ( async ( ) => composerProps ( ) . onSubmit ( 'Keep this draft' ) )
622- expect ( mocks . send ) . toHaveBeenLastCalledWith ( 'Keep this draft' , undefined , undefined , {
623- requestMode : 'assistant' ,
624- assistantSearchLevel : 'adaptive' ,
635+ await act ( async ( ) => composerProps ( ) . onModeChange ?.( 'agent' ) )
636+ expect ( composerProps ( ) . requestMode ) . toBe ( 'agent' )
637+ mocks . send . mockImplementationOnce ( ( ) => {
638+ mocks . chat . mockReturnValue ( {
639+ ...mocks . chat ( ) ,
640+ messages : [ { id : 'user-1' , role : 'user' , content : 'First message' } ] ,
641+ isSending : true ,
642+ } )
625643 } )
626- expect ( useOrganizationChatModeStore . getState ( ) . modes [ 'reader:organization-a' ] ) . toBe ( 'assistant' )
644+ await act ( async ( ) => composerProps ( ) . onSubmit ( 'First message' ) )
645+ expect ( composerProps ( ) . isInitialView ) . toBe ( false )
646+ expect ( composerProps ( ) . showModeSelector ) . toBe ( false )
647+ expect ( composerProps ( ) . onModeChange ) . toBeUndefined ( )
648+ expect ( composerProps ( ) . requestMode ) . toBe ( 'agent' )
627649 } )
628650 it ( 'uses only this user and organization preference for new chats without changing saved chats' , async ( ) => {
629651 const store = useOrganizationChatModeStore . getState ( )
@@ -639,18 +661,18 @@ describe('same-chat mode selection', () => {
639661 expect ( useOrganizationChatModeStore . getState ( ) . modes [ 'reader:organization-a' ] ) . toBe ( 'assistant' )
640662 } )
641663 it . each ( [ 'isSending' , 'isReconnecting' , 'messageQueue' ] as const ) (
642- 'changes the next message mode while %s is active without sending or navigating ' ,
664+ 'keeps mode selection unavailable while %s is active in a conversation ' ,
643665 async ( field ) => {
644666 mocks . chat . mockReturnValue ( {
645667 ...mocks . chat ( ) ,
668+ messages : [ { id : 'user-1' , role : 'user' , content : 'First message' } ] ,
646669 [ field ] : field === 'messageQueue' ? [ { id : 'queued' } ] : true ,
647670 } )
671+ mocks . renderer . mockImplementation ( ( { composer } : { composer : ReactNode } ) => composer )
648672 await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
649- await act ( async ( ) => composerProps ( ) . onModeChange ?.( 'assistant' ) )
650- expect ( composerProps ( ) . requestMode ) . toBe ( 'assistant' )
651- expect ( useOrganizationChatModeStore . getState ( ) . modes [ 'reader:organization-a' ] ) . toBe (
652- 'assistant'
653- )
673+ expect ( composerProps ( ) . showModeSelector ) . toBe ( false )
674+ expect ( composerProps ( ) . onModeChange ) . toBeUndefined ( )
675+ expect ( composerProps ( ) . requestMode ) . toBe ( 'agent' )
654676 expect ( mocks . send ) . not . toHaveBeenCalled ( )
655677 expect ( mocks . push ) . not . toHaveBeenCalled ( )
656678 }
@@ -700,21 +722,21 @@ describe('same-chat mode selection', () => {
700722 )
701723} )
702724
703- it ( 'does not seed results for a Build turn just because Search is selected next ' , async ( ) => {
725+ it ( 'does not seed Search results for an existing Build conversation ' , async ( ) => {
704726 mocks . chat . mockReturnValue ( {
705727 ...mocks . chat ( ) ,
706728 messages : [ { id : 'user-1' , role : 'user' , content : 'Build a table' , requestMode : 'agent' } ] ,
707729 } )
708730 mocks . renderer . mockImplementation ( ( { composer } : { composer : ReactNode } ) => composer )
709731 await act ( async ( ) => renderHome ( < OrganizationHome chatId = 'chat-a' requestMode = 'agent' /> ) )
710- await act ( async ( ) => composerProps ( ) . onModeChange ?. ( 'assistant' ) )
732+ expect ( composerProps ( ) . onModeChange ) . toBeUndefined ( )
711733 expect ( mocks . resourcePanel ) . toHaveBeenLastCalledWith (
712734 expect . not . objectContaining ( { searchRequest : expect . anything ( ) } ) ,
713735 undefined
714736 )
715737} )
716738
717- it ( 'does not start a new panel search when the next turn is switched to Build ' , async ( ) => {
739+ it ( 'does not start a new panel search when an existing Search conversation loads ' , async ( ) => {
718740 mocks . chat . mockReturnValue ( {
719741 ...mocks . chat ( ) ,
720742 messages : [
@@ -723,7 +745,7 @@ it('does not start a new panel search when the next turn is switched to Build',
723745 } )
724746 mocks . renderer . mockImplementation ( ( { composer } : { composer : ReactNode } ) => composer )
725747 await act ( async ( ) => renderHome ( < OrganizationHome chatId = 'chat-a' requestMode = 'assistant' /> ) )
726- await act ( async ( ) => composerProps ( ) . onModeChange ?. ( 'agent' ) )
748+ expect ( composerProps ( ) . onModeChange ) . toBeUndefined ( )
727749 expect ( mocks . resourcePanel ) . toHaveBeenLastCalledWith (
728750 expect . not . objectContaining ( { searchRequest : expect . anything ( ) } ) ,
729751 undefined
0 commit comments