@@ -359,26 +359,34 @@ fn get_project_agents_dir() -> Option<PathBuf> {
359359}
360360
361361/// Load all agents from various sources.
362+ ///
363+ /// Agents are loaded in order: builtin -> personal -> project.
364+ /// When an agent with the same name exists in multiple sources,
365+ /// the later source takes precedence (project > personal > builtin).
362366fn load_all_agents ( ) -> Result < Vec < AgentInfo > > {
363- let mut agents = Vec :: new ( ) ;
367+ let mut agents_map : HashMap < String , AgentInfo > = HashMap :: new ( ) ;
364368
365- // Load built-in agents
366- agents. extend ( load_builtin_agents ( ) ) ;
369+ // Load built-in agents first (lowest priority)
370+ for agent in load_builtin_agents ( ) {
371+ agents_map. insert ( agent. name . clone ( ) , agent) ;
372+ }
367373
368- // Load personal agents from ~/.cortex/agents/
374+ // Load personal agents from ~/.cortex/agents/ (overrides builtin)
369375 let personal_dir = get_agents_dir ( ) ?;
370376 if personal_dir. exists ( ) {
371- let personal_agents = load_agents_from_dir ( & personal_dir, AgentSource :: Personal ) ?;
372- agents. extend ( personal_agents) ;
377+ for agent in load_agents_from_dir ( & personal_dir, AgentSource :: Personal ) ? {
378+ agents_map. insert ( agent. name . clone ( ) , agent) ;
379+ }
373380 }
374381
375- // Load project agents from .cortex/agents/
382+ // Load project agents from .cortex/agents/ (highest priority, overrides all)
376383 if let Some ( project_dir) = get_project_agents_dir ( ) {
377- let project_agents = load_agents_from_dir ( & project_dir, AgentSource :: Project ) ?;
378- agents. extend ( project_agents) ;
384+ for agent in load_agents_from_dir ( & project_dir, AgentSource :: Project ) ? {
385+ agents_map. insert ( agent. name . clone ( ) , agent) ;
386+ }
379387 }
380388
381- Ok ( agents )
389+ Ok ( agents_map . into_values ( ) . collect ( ) )
382390}
383391
384392/// Load built-in agents.
@@ -1687,4 +1695,72 @@ This is the system prompt.
16871695 let result = format_color_with_preview ( "invalid" ) ;
16881696 assert_eq ! ( result, "invalid" ) ;
16891697 }
1698+
1699+ #[ test]
1700+ fn test_agent_deduplication_by_name ( ) {
1701+ // Simulate the deduplication logic used in load_all_agents()
1702+ let builtin_agent = AgentInfo {
1703+ name : "build" . to_string ( ) ,
1704+ display_name : Some ( "Build" . to_string ( ) ) ,
1705+ description : Some ( "Builtin build agent" . to_string ( ) ) ,
1706+ mode : AgentMode :: Primary ,
1707+ native : true ,
1708+ hidden : false ,
1709+ prompt : None ,
1710+ temperature : None ,
1711+ top_p : None ,
1712+ color : Some ( "#22c55e" . to_string ( ) ) ,
1713+ model : None ,
1714+ tools : HashMap :: new ( ) ,
1715+ allowed_tools : None ,
1716+ denied_tools : Vec :: new ( ) ,
1717+ max_turns : None ,
1718+ can_delegate : true ,
1719+ tags : vec ! [ "development" . to_string( ) ] ,
1720+ source : AgentSource :: Builtin ,
1721+ path : None ,
1722+ } ;
1723+
1724+ let personal_agent = AgentInfo {
1725+ name : "build" . to_string ( ) , // Same name as builtin
1726+ display_name : Some ( "Custom Build" . to_string ( ) ) ,
1727+ description : Some ( "Personal build agent override" . to_string ( ) ) ,
1728+ mode : AgentMode :: Primary ,
1729+ native : false ,
1730+ hidden : false ,
1731+ prompt : Some ( "Custom prompt" . to_string ( ) ) ,
1732+ temperature : Some ( 0.5 ) ,
1733+ top_p : None ,
1734+ color : Some ( "#ff0000" . to_string ( ) ) ,
1735+ model : None ,
1736+ tools : HashMap :: new ( ) ,
1737+ allowed_tools : None ,
1738+ denied_tools : Vec :: new ( ) ,
1739+ max_turns : None ,
1740+ can_delegate : true ,
1741+ tags : vec ! [ "custom" . to_string( ) ] ,
1742+ source : AgentSource :: Personal ,
1743+ path : Some ( PathBuf :: from ( "/home/user/.cortex/agents/build.md" ) ) ,
1744+ } ;
1745+
1746+ // Test deduplication: later source should override earlier
1747+ let mut agents_map: HashMap < String , AgentInfo > = HashMap :: new ( ) ;
1748+ agents_map. insert ( builtin_agent. name . clone ( ) , builtin_agent) ;
1749+ agents_map. insert ( personal_agent. name . clone ( ) , personal_agent) ;
1750+
1751+ let agents: Vec < _ > = agents_map. into_values ( ) . collect ( ) ;
1752+
1753+ // Should have only one agent named "build"
1754+ let build_agents: Vec < _ > = agents. iter ( ) . filter ( |a| a. name == "build" ) . collect ( ) ;
1755+ assert_eq ! ( build_agents. len( ) , 1 ) ;
1756+
1757+ // The personal agent should have overridden the builtin
1758+ let build = build_agents[ 0 ] ;
1759+ assert_eq ! ( build. source, AgentSource :: Personal ) ;
1760+ assert_eq ! (
1761+ build. description,
1762+ Some ( "Personal build agent override" . to_string( ) )
1763+ ) ;
1764+ assert ! ( !build. native) ;
1765+ }
16901766}
0 commit comments