@@ -3,6 +3,7 @@ import { AgentService } from './agent.service';
33import type { RulesService } from '../rules/rules.service' ;
44import type { CustomService } from '../custom' ;
55import type { ConfigService } from '../config/config.service' ;
6+ import type { TeamsCapabilityService } from './teams-capability.service' ;
67import type { AgentProfile } from '../rules/rules.types' ;
78import type { AgentContext , DispatchResult } from './agent.types' ;
89
@@ -11,6 +12,7 @@ describe('AgentService', () => {
1112 let mockRulesService : Partial < RulesService > ;
1213 let mockCustomService : Partial < CustomService > ;
1314 let mockConfigService : Partial < ConfigService > ;
15+ let mockTeamsCapability : Partial < TeamsCapabilityService > ;
1416
1517 const mockSecurityAgent : AgentProfile = {
1618 name : 'Security Specialist' ,
@@ -52,11 +54,20 @@ describe('AgentService', () => {
5254 mockConfigService = {
5355 getProjectRoot : vi . fn ( ) . mockReturnValue ( '/test/project' ) ,
5456 } ;
57+ mockTeamsCapability = {
58+ isAvailable : vi . fn ( ) . mockResolvedValue ( true ) ,
59+ getStatus : vi . fn ( ) . mockResolvedValue ( {
60+ available : true ,
61+ reason : 'Enabled for testing' ,
62+ source : 'environment' ,
63+ } ) ,
64+ } ;
5565
5666 service = new AgentService (
5767 mockRulesService as RulesService ,
5868 mockCustomService as CustomService ,
5969 mockConfigService as ConfigService ,
70+ mockTeamsCapability as TeamsCapabilityService ,
6071 ) ;
6172 } ) ;
6273
@@ -918,6 +929,85 @@ describe('AgentService', () => {
918929 } ) ;
919930 } ) ;
920931
932+ describe ( 'dispatchAgents taskmaestro+teams inner coordination bootstrap' , ( ) => {
933+ it ( 'should populate innerCoordination on assignments when Teams capability is enabled' , async ( ) => {
934+ vi . mocked ( mockTeamsCapability . isAvailable ! ) . mockResolvedValue ( true ) ;
935+ vi . mocked ( mockRulesService . getAgent ! )
936+ . mockResolvedValueOnce ( mockSecurityAgent )
937+ . mockResolvedValueOnce ( mockPerformanceAgent ) ;
938+
939+ const result = await service . dispatchAgents ( {
940+ mode : 'EVAL' ,
941+ specialists : [ 'security-specialist' , 'performance-specialist' ] ,
942+ executionStrategy : 'taskmaestro+teams' ,
943+ } ) ;
944+
945+ expect ( result . taskmaestro ) . toBeDefined ( ) ;
946+ const assignments = result . taskmaestro ! . assignments ;
947+ expect ( assignments ) . toHaveLength ( 2 ) ;
948+
949+ // Each assignment carries inner coordination metadata
950+ for ( const assignment of assignments ) {
951+ expect ( assignment . innerCoordination ) . toBeDefined ( ) ;
952+ expect ( assignment . innerCoordination ! . type ) . toBe ( 'teams' ) ;
953+ expect ( assignment . innerCoordination ! . teamSpec . team_name ) . toBe ( 'eval-specialists' ) ;
954+ expect ( assignment . innerCoordination ! . teammates ) . toHaveLength ( 2 ) ;
955+ expect ( assignment . innerCoordination ! . teammates [ 0 ] . subagent_type ) . toBe ( 'general-purpose' ) ;
956+ }
957+ } ) ;
958+
959+ it ( 'should omit innerCoordination and fall back to pure taskmaestro when Teams capability is disabled' , async ( ) => {
960+ vi . mocked ( mockTeamsCapability . isAvailable ! ) . mockResolvedValue ( false ) ;
961+ vi . mocked ( mockRulesService . getAgent ! )
962+ . mockResolvedValueOnce ( mockSecurityAgent )
963+ . mockResolvedValueOnce ( mockPerformanceAgent ) ;
964+
965+ const result = await service . dispatchAgents ( {
966+ mode : 'EVAL' ,
967+ specialists : [ 'security-specialist' , 'performance-specialist' ] ,
968+ executionStrategy : 'taskmaestro+teams' ,
969+ } ) ;
970+
971+ // Falls back to pure taskmaestro
972+ expect ( result . executionStrategy ) . toBe ( 'taskmaestro' ) ;
973+ expect ( result . taskmaestro ) . toBeDefined ( ) ;
974+ expect ( result . teams ) . toBeUndefined ( ) ;
975+
976+ // No inner coordination on assignments
977+ for ( const assignment of result . taskmaestro ! . assignments ) {
978+ expect ( assignment . innerCoordination ) . toBeUndefined ( ) ;
979+ }
980+
981+ // executionPlan should be simple (no inner)
982+ expect ( result . executionPlan ) . toBeDefined ( ) ;
983+ expect ( result . executionPlan ! . outerExecution . type ) . toBe ( 'taskmaestro' ) ;
984+ expect ( result . executionPlan ! . innerCoordination ) . toBeUndefined ( ) ;
985+ } ) ;
986+
987+ it ( 'should include teammate list matching loaded agents in innerCoordination' , async ( ) => {
988+ vi . mocked ( mockTeamsCapability . isAvailable ! ) . mockResolvedValue ( true ) ;
989+ vi . mocked ( mockRulesService . getAgent ! )
990+ . mockResolvedValueOnce ( mockSecurityAgent )
991+ . mockRejectedValueOnce ( new Error ( 'Agent not found' ) )
992+ . mockResolvedValueOnce ( mockPerformanceAgent ) ;
993+
994+ const result = await service . dispatchAgents ( {
995+ mode : 'EVAL' ,
996+ specialists : [ 'security-specialist' , 'invalid-agent' , 'performance-specialist' ] ,
997+ executionStrategy : 'taskmaestro+teams' ,
998+ } ) ;
999+
1000+ // Only successfully loaded agents appear
1001+ expect ( result . taskmaestro ! . assignments ) . toHaveLength ( 2 ) ;
1002+ expect ( result . failedAgents ) . toHaveLength ( 1 ) ;
1003+
1004+ // innerCoordination teammates match the successfully loaded agents
1005+ const teammates = result . taskmaestro ! . assignments [ 0 ] . innerCoordination ! . teammates ;
1006+ expect ( teammates ) . toHaveLength ( 2 ) ;
1007+ expect ( teammates . map ( t => t . name ) ) . toEqual ( [ 'security-specialist' , 'performance-specialist' ] ) ;
1008+ } ) ;
1009+ } ) ;
1010+
9211011 describe ( 'executionPlan in all strategies' , ( ) => {
9221012 it ( 'subagent strategy populates executionPlan' , async ( ) => {
9231013 vi . mocked ( mockRulesService . getAgent ! ) . mockResolvedValueOnce ( mockSecurityAgent ) ;
0 commit comments