@@ -3,7 +3,7 @@ import { RecoilRoot } from 'recoil';
33import { useAtomValue , useStore } from 'jotai' ;
44import { MemoryRouter } from 'react-router-dom' ;
55import { act , fireEvent , render , screen , waitFor , within } from '@testing-library/react' ;
6- import type { SubagentUpdateEvent } from 'librechat-data-provider' ;
6+ import type { SubagentUpdateEvent , SubagentIdentity } from 'librechat-data-provider' ;
77import type {
88 SubagentAggregatorState ,
99 SubagentContentPart ,
@@ -61,8 +61,17 @@ jest.mock('lucide-react', () => ({
6161 Users : ( ) => < span > users</ span > ,
6262} ) ) ;
6363
64- jest . mock ( '~/Providers' , ( ) => ( { useAgentsMapContext : ( ) => ( { } ) } ) ) ;
65- jest . mock ( '~/components/Share/MessageIcon' , ( ) => ( { __esModule : true , default : ( ) => null } ) ) ;
64+ jest . mock ( '~/Providers' , ( ) => ( {
65+ useAgentsMapContext : ( ) => ( {
66+ 'agent-1' : { id : 'agent-1' , name : 'Analyst One' , avatar : { filepath : '/analyst.png' } } ,
67+ } ) ,
68+ } ) ) ;
69+ jest . mock ( '~/components/Share/MessageIcon' , ( ) => ( {
70+ __esModule : true ,
71+ default : ( { agent } : { agent : { name : string ; avatar : { filepath : string } } } ) => (
72+ < img alt = { agent . name } src = { agent . avatar . filepath } />
73+ ) ,
74+ } ) ) ;
6675jest . mock ( '~/hooks/MCP' , ( ) => ( { useMCPServerNames : ( ) => mockMCPServerNames } ) ) ;
6776jest . mock ( '~/utils' , ( ) => ( {
6877 ...jest . requireActual ( '~/utils/toolLabels' ) ,
@@ -111,6 +120,7 @@ function renderWithState(args: {
111120 progress ?: SubagentProgress | null ;
112121 output ?: string ;
113122 toolArgs ?: Record < string , unknown > ;
123+ subagentIdentity ?: SubagentIdentity ;
114124} ) {
115125 const setter = { current : null as null | ( ( next : SubagentProgress | null ) => void ) } ;
116126 let selection : ActiveSubagentPanel | null = null ;
@@ -146,6 +156,7 @@ function renderWithState(args: {
146156 isSubmitting = { args . isSubmitting ?? false }
147157 args = { args . toolArgs ?? { subagent_type : 'self' , description : 'compute' } }
148158 output = { args . output }
159+ subagentIdentity = { args . subagentIdentity }
149160 />
150161 </ MessageContext . Provider >
151162 </ RecoilRoot >
@@ -174,6 +185,58 @@ const event = (
174185} ) ;
175186
176187describe ( 'SubagentCall' , ( ) => {
188+ it ( 'keeps the configured name and avatar after live progress is cleared' , ( ) => {
189+ const { setProgress } = renderWithState ( {
190+ toolCallId : 'identity' ,
191+ initialProgress : 1 ,
192+ toolArgs : { subagent_type : 'agent-1' } ,
193+ subagentIdentity : { subagentKind : 'agent' , subagentAgentId : 'agent-1' } ,
194+ progress : progressFromEvents ( {
195+ subagentRunId : 'child-run' ,
196+ subagentType : 'agent-1' ,
197+ subagentAgentId : 'agent-1' ,
198+ status : 'stop' ,
199+ events : [ ] ,
200+ } ) ,
201+ } ) ;
202+ expect ( screen . getByText ( 'Analyst One' ) ) . toBeInTheDocument ( ) ;
203+ expect ( screen . getByRole ( 'img' , { hidden : true } ) ) . toHaveAttribute ( 'src' , '/analyst.png' ) ;
204+ setProgress ( null ) ;
205+ expect ( screen . getByText ( 'Analyst One' ) ) . toBeInTheDocument ( ) ;
206+ expect ( screen . getByRole ( 'img' , { hidden : true } ) ) . toHaveAttribute ( 'src' , '/analyst.png' ) ;
207+ } ) ;
208+
209+ it . each ( [ undefined , { subagentKind : 'graph' as const , subagentAgentId : 'graph:agent-1' } ] ) (
210+ 'does not infer a saved agent from an ambiguous graph or legacy type' ,
211+ ( subagentIdentity ) => {
212+ const { getSelection } = renderWithState ( {
213+ toolCallId : 'graph-identity' ,
214+ initialProgress : 1 ,
215+ toolArgs : { subagent_type : 'agent-1' } ,
216+ output : 'Graph result' ,
217+ subagentIdentity,
218+ } ) ;
219+ expect ( screen . queryByText ( 'Analyst One' ) ) . not . toBeInTheDocument ( ) ;
220+ expect ( screen . getByText ( 'users' ) ) . toBeInTheDocument ( ) ;
221+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'Ran agent' } ) ) ;
222+ expect ( getSelection ( ) ?. subagentIdentity ) . toEqual ( subagentIdentity ) ;
223+ } ,
224+ ) ;
225+
226+ it . each ( [ 'agent-1' , 'missing-agent' , 'self' ] ) (
227+ 'renders saved identity %s without streaming state' ,
228+ ( agentId ) => {
229+ renderWithState ( {
230+ toolCallId : 'saved-identity' ,
231+ initialProgress : 1 ,
232+ toolArgs : { subagent_type : agentId } ,
233+ subagentIdentity : { subagentKind : 'agent' , subagentAgentId : agentId } ,
234+ } ) ;
235+ expect ( screen . queryByText ( 'Analyst One' ) != null ) . toBe ( agentId === 'agent-1' ) ;
236+ expect ( screen . queryByText ( 'users' ) != null ) . toBe ( agentId !== 'agent-1' ) ;
237+ } ,
238+ ) ;
239+
177240 it . each ( [
178241 [ 'Running agent' , 0.3 , true , 'run_step' ] ,
179242 [ 'Ran agent' , 1 , false , undefined ] ,
0 commit comments