• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alkem-io / server / #7853

02 Aug 2024 08:51AM UTC coverage: 13.641%. First build
#7853

Pull #4336

travis-ci

Pull Request #4336: Configuration service with typings

79 of 4106 branches covered (1.92%)

Branch coverage included in aggregate %.

31 of 78 new or added lines in 20 files covered. (39.74%)

1886 of 10299 relevant lines covered (18.31%)

2.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

26.7
/src/core/bootstrap/bootstrap.service.ts
1
import { Inject, Injectable, LoggerService } from '@nestjs/common';
1✔
2
import { ConfigService } from '@nestjs/config';
1✔
3
import { InjectRepository } from '@nestjs/typeorm';
1✔
4
import { SpaceService } from '@domain/space/space/space.service';
1✔
5
import { UserService } from '@domain/community/user/user.service';
1✔
6
import { Repository } from 'typeorm';
1✔
7
import * as defaultRoles from './platform-template-definitions/user/users.json';
1✔
8
import * as defaultLicensePlan from './platform-template-definitions/license-plan/license-plans.json';
1✔
9
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
1✔
10
import { Profiling } from '@common/decorators';
1✔
11
import { LogContext } from '@common/enums';
1✔
12
import { BootstrapException } from '@common/exceptions/bootstrap.exception';
1✔
13
import { UserAuthorizationService } from '@domain/community/user/user.service.authorization';
1✔
14
import {
1✔
15
  DEFAULT_HOST_ORG_DISPLAY_NAME,
16
  DEFAULT_HOST_ORG_NAMEID,
17
  DEFAULT_SPACE_DISPLAYNAME,
18
  DEFAULT_SPACE_NAMEID,
19
} from '@common/constants';
20
import { OrganizationService } from '@domain/community/organization/organization.service';
1✔
21
import { OrganizationAuthorizationService } from '@domain/community/organization/organization.service.authorization';
1✔
22
import { PlatformService } from '@platform/platform/platform.service';
1✔
23
import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service';
1✔
24
import { PlatformAuthorizationService } from '@platform/platform/platform.service.authorization';
1✔
25
import { SpaceLevel } from '@common/enums/space.level';
1✔
26
import { CreateSpaceOnAccountInput } from '@domain/space/account/dto/account.dto.create.space';
1✔
27
import { AccountService } from '@domain/space/account/account.service';
1✔
28
import { SpaceAuthorizationService } from '@domain/space/space/space.service.authorization';
1✔
29
import { AccountAuthorizationService } from '@domain/space/account/account.service.authorization';
1✔
30
import { AiServerAuthorizationService } from '@services/ai-server/ai-server/ai.server.service.authorization';
1✔
31
import { AiServerService } from '@services/ai-server/ai-server/ai.server.service';
1✔
32
import { Space } from '@domain/space/space/space.entity';
1✔
33
import { AgentInfo } from '@core/authentication.agent.info/agent.info';
34
import { TemplatesSetService } from '@domain/template/templates-set/templates.set.service';
1✔
35
import { TemplateDefaultService } from '@domain/template/template-default/template.default.service';
36
import { TemplateDefaultType } from '@common/enums/template.default.type';
37
import { TemplateType } from '@common/enums/template.type';
38
import { LicenseService } from '@domain/common/license/license.service';
39
import { AccountLicenseService } from '@domain/space/account/account.service.license';
1✔
40
import { LicensePlanService } from '@platform/licensing/credential-based/license-plan/license.plan.service';
41
import { LicensingFrameworkService } from '@platform/licensing/credential-based/licensing-framework/licensing.framework.service';
1✔
42
import { AiPersonaEngine } from '@common/enums/ai.persona.engine';
1✔
43
import { VirtualContributorDataAccessMode } from '@common/enums/virtual.contributor.data.access.mode';
1✔
44
import { UserLookupService } from '@domain/community/user-lookup/user.lookup.service';
1✔
45
import { OrganizationLookupService } from '@domain/community/organization-lookup/organization.lookup.service';
1✔
46
import { CreateTemplateContentSpaceInput } from '@domain/template/template-content-space/dto/template.content.space.dto.create';
1✔
47
import { bootstrapTemplateSpaceContentSpaceL0 } from './platform-template-definitions/default-templates/bootstrap.template.space.content.space.l0';
1✔
48
import { bootstrapTemplateSpaceContentSubspace } from './platform-template-definitions/default-templates/bootstrap.template.space.content.subspace';
1✔
49
import { bootstrapTemplateSpaceContentCalloutsSpaceL0Tutorials } from './platform-template-definitions/default-templates/bootstrap.template.space.content.callouts.space.l0.tutorials';
1✔
50
import { bootstrapTemplateSpaceContentCalloutsVcKnowledgeBase } from './platform-template-definitions/default-templates/bootstrap.template.space.content.callouts.vc.knowledge.base';
1✔
51
import { PlatformTemplatesService } from '@platform/platform-templates/platform.templates.service';
1✔
52
import { AgentInfoService } from '@core/authentication.agent.info/agent.info.service';
1✔
53
import { AdminAuthorizationService } from '@src/platform-admin/domain/authorization/admin.authorization.service';
1✔
54
import { VirtualContributorBodyOfKnowledgeType } from '@common/enums/virtual.contributor.body.of.knowledge.type';
55
import { VirtualContributorInteractionMode } from '@common/enums/virtual.contributor.interaction.mode';
1✔
56
import { MessagingService } from '@domain/communication/messaging/messaging.service';
57
import { PlatformWellKnownVirtualContributorsService } from '@platform/platform.well.known.virtual.contributors/platform.well.known.virtual.contributors.service';
1✔
58
import { VirtualContributorWellKnown } from '@common/enums/virtual.contributor.well.known';
1✔
59
import { RoleSetService } from '@domain/access/role-set/role.set.service';
60
import { RoleName } from '@common/enums/role.name';
1✔
61

62
@Injectable()
63
export class BootstrapService {
64
  constructor(
65
    private accountService: AccountService,
×
66
    private accountAuthorizationService: AccountAuthorizationService,
×
67
    private agentInfoService: AgentInfoService,
68
    private spaceService: SpaceService,
×
NEW
69
    private userService: UserService,
×
NEW
70
    private userLookupService: UserLookupService,
×
NEW
71
    private userAuthorizationService: UserAuthorizationService,
×
72
    private organizationService: OrganizationService,
73
    private organizationLookupService: OrganizationLookupService,
74
    private organizationAuthorizationService: OrganizationAuthorizationService,
×
75
    private spaceAuthorizationService: SpaceAuthorizationService,
×
76
    private adminAuthorizationService: AdminAuthorizationService,
×
77
    private configService: ConfigService,
×
78
    private platformService: PlatformService,
79
    private platformAuthorizationService: PlatformAuthorizationService,
×
80
    private authorizationPolicyService: AuthorizationPolicyService,
81
    @InjectRepository(Space)
82
    private spaceRepository: Repository<Space>,
×
83
    @Inject(WINSTON_MODULE_NEST_PROVIDER)
84
    private readonly logger: LoggerService,
×
85
    private aiServer: AiServerService,
86
    private aiServerAuthorizationService: AiServerAuthorizationService,
87
    private templatesSetService: TemplatesSetService,
×
88
    private templateDefaultService: TemplateDefaultService,
89
    private platformTemplatesService: PlatformTemplatesService,
90
    private accountLicenseService: AccountLicenseService,
91
    private licenseService: LicenseService,
NEW
92
    private licensingFrameworkService: LicensingFrameworkService,
×
93
    private licensePlanService: LicensePlanService,
×
94
    private readonly messagingService: MessagingService,
×
95
    private platformWellKnownVirtualContributorsService: PlatformWellKnownVirtualContributorsService,
96
    private roleSetService: RoleSetService
97
  ) {}
98

×
99
  async bootstrap() {
100
    // this.ingestService.ingest(); // todo remove later
NEW
101
    try {
×
102
      this.logger.verbose?.('Bootstrapping...', LogContext.BOOTSTRAP);
103

×
104
      Profiling.logger = this.logger;
105
      const profilingEnabled = this.configService.get(
106
        'monitoring.logging.profiling_enabled',
107
        { infer: true }
×
108
      );
×
109
      if (profilingEnabled) {
110
        Profiling.profilingEnabled = profilingEnabled;
111
      }
112

×
113
      const anonymousAgentInfo =
114
        this.agentInfoService.createAnonymousAgentInfo();
115

116
      // Order matters:
×
117
      // 1. Infrastructure: Forum, Messaging
×
118
      // 2. Templates (needed for VC creation)
×
119
      // 3. Organization (created without admin first)
×
120
      // 4. Guidance VC (needs organization and templates)
121
      // 5. Users (including Admin) - will get guidance conversation created successfully
122
      // 6. Link Admin to Organization
123
      // 7. License plans
×
124
      // 8. Authorization policies
125
      // 9. Space
×
126

127
      await this.platformService.ensureForumCreated();
128
      await this.ensureMessagingCreated();
129
      await this.ensurePlatformTemplatesArePresent();
130

131
      // Create Org first (without admin if needed)
×
132
      await this.ensureOrganizationSingleton();
×
133

×
134
      // Create VC (needs Org)
135
      await this.ensureGuidanceChat();
136

137
      // Create Users (including Admin)
138
      await this.bootstrapUserProfiles();
×
139

140
      // Ensure Admin is linked to Org
141
      await this.ensureAdminUserLinkedToOrganization();
142

143
      await this.bootstrapLicensePlans();
1✔
144
      await this.ensureAuthorizationsPopulated();
×
145
      await this.ensureSpaceSingleton(anonymousAgentInfo);
×
146
      // reset auth as last in the actions
×
147
      // await this.ensureSpaceNamesInElastic();
148
    } catch (error: any) {
149
      this.logger.error(
×
150
        `Unable to complete bootstrap process: ${error}`,
×
151
        error?.stack,
152
        LogContext.BOOTSTRAP
153
      );
154
      throw new BootstrapException(error.message, { originalException: error });
×
155
    }
156
  }
157

158
  /**
159
   * Ensures the platform Messaging exists.
160
   * Creates it if missing (should happen only on fresh deployments).
161
   */
162
  private async ensureMessagingCreated(): Promise<void> {
163
    const messaging = await this.platformService.ensureMessagingCreated();
164
    this.logger.verbose?.(
×
165
      `Platform Messaging ensured: ${messaging.id}`,
×
166
      LogContext.BOOTSTRAP
×
167
    );
168
  }
169

170
  private async ensurePlatformTemplatesArePresent() {
171
    let authResetNeeded = await this.ensureSpaceTemplateIsPresent(
172
      TemplateDefaultType.PLATFORM_SPACE,
×
NEW
173
      'space',
×
174
      bootstrapTemplateSpaceContentSpaceL0
175
    );
176
    authResetNeeded =
177
      (await this.ensureSpaceTemplateIsPresent(
178
        TemplateDefaultType.PLATFORM_SUBSPACE,
×
179
        'subspace',
180
        bootstrapTemplateSpaceContentSubspace
181
      )) || authResetNeeded;
182
    authResetNeeded =
183
      (await this.ensureSpaceTemplateIsPresent(
184
        TemplateDefaultType.PLATFORM_SPACE_TUTORIALS,
185
        'space-tutorials',
186
        bootstrapTemplateSpaceContentCalloutsSpaceL0Tutorials
×
187
      )) || authResetNeeded;
188
    authResetNeeded =
189
      (await this.ensureSpaceTemplateIsPresent(
190
        TemplateDefaultType.PLATFORM_SUBSPACE_KNOWLEDGE,
191
        'knowledge',
192
        bootstrapTemplateSpaceContentCalloutsVcKnowledgeBase
193
      )) || authResetNeeded;
194
    if (authResetNeeded) {
×
195
      this.logger.verbose?.(
196
        '=== Identified that template defaults had not been reset; resetting auth now ===',
197
        LogContext.BOOTSTRAP
198
      );
199
      const updatedAuthorizations =
×
200
        await this.platformAuthorizationService.applyAuthorizationPolicy();
201
      await this.authorizationPolicyService.saveAll(updatedAuthorizations);
202
    }
NEW
203
  }
×
204

×
205
  private async ensureSpaceTemplateIsPresent(
×
206
    templateDefaultType: TemplateDefaultType,
207
    nameID: string,
208
    spaceContentData: CreateTemplateContentSpaceInput
209
  ): Promise<boolean> {
210
    const templatesSet =
×
211
      await this.platformTemplatesService.getPlatformTemplatesSet();
×
212
    const templateDefault =
213
      await this.platformTemplatesService.getPlatformTemplateDefault(
214
        templateDefaultType
215
      );
×
216

217
    if (!templateDefault) {
×
218
      throw new BootstrapException(
×
219
        `Unable to load Template Default for ${templateDefaultType}`
220
      );
221
    }
222
    if (!templateDefault.template) {
×
223
      this.logger.verbose?.(
224
        `No template set for ${templateDefaultType}, setting it...`,
225
        LogContext.BOOTSTRAP
226
      );
227
      // No template set, so create one and then set it
×
228
      const template = await this.templatesSetService.createTemplate(
229
        templatesSet,
230
        {
231
          profileData: {
×
232
            displayName: `${nameID}-Template`,
×
233
          },
×
234
          type: TemplateType.SPACE,
×
235
          contentSpaceData: spaceContentData,
236
        }
×
237
      );
238
      // Set the default template
239
      templateDefault.template = template;
×
240
      await this.templateDefaultService.save(templateDefault);
×
241
      return true;
242
    }
243
    return false;
244
  }
245

246
  async bootstrapUserProfiles() {
×
247
    const bootstrapAuthorizationRolesJson = {
248
      ...defaultRoles,
249
    };
250

251
    this.logger.verbose?.(
×
252
      'Authorization bootstrap: default configuration being loaded',
253
      LogContext.BOOTSTRAP
254
    );
255

256
    const users = bootstrapAuthorizationRolesJson.users;
257
    if (!users) {
258
      this.logger.verbose?.(
259
        'No users section in the authorization bootstrap file!',
260
        LogContext.BOOTSTRAP
261
      );
262
    } else {
263
      await this.createUserProfiles(users);
264
    }
×
265
  }
×
266

267
  async bootstrapLicensePlans() {
268
    const bootstrapLicensePlans = {
269
      ...defaultLicensePlan,
×
270
    };
271

272
    const licensePlans = bootstrapLicensePlans.licensePlans;
NEW
273
    if (!licensePlans) {
×
274
      this.logger.verbose?.(
275
        'No licensePlans section in the license plans bootstrap file!',
276
        LogContext.BOOTSTRAP
277
      );
×
278
    } else {
279
      await this.createLicensePlans(licensePlans);
280
    }
281
  }
282

283
  async createLicensePlans(licensePlansData: any[]) {
284
    try {
285
      const licensing =
286
        await this.licensingFrameworkService.getDefaultLicensingOrFail();
287
      for (const licensePlanData of licensePlansData) {
288
        const planExists =
289
          await this.licensePlanService.licensePlanByNameExists(
290
            licensePlanData.name
291
          );
292
        if (!planExists) {
293
          await this.licensingFrameworkService.createLicensePlan({
294
            ...licensePlanData,
295
            licensingID: licensing.id,
296
          });
297
        }
298
      }
299
    } catch (error: any) {
300
      throw new BootstrapException(
301
        `Unable to create license plans ${error.message}`
302
      );
303
    }
304
  }
305

306
  async createUserProfiles(usersData: any[]) {
307
    try {
308
      for (const userData of usersData) {
309
        const userExists = await this.userLookupService.isRegisteredUser(
310
          userData.email
311
        );
312
        if (!userExists) {
313
          const user = await this.userService.createUser({
314
            email: userData.email,
315
            firstName: userData.firstName,
316
            lastName: userData.lastName,
317
            profileData: {
318
              displayName: `${userData.firstName} ${userData.lastName}`,
319
            },
320
          });
321

322
          // Once all is done, reset the user authorizations
323
          const userAuthorizations =
324
            await this.userAuthorizationService.applyAuthorizationPolicy(
325
              user.id
326
            );
327
          await this.authorizationPolicyService.saveAll(userAuthorizations);
328

329
          const account = await this.userService.getAccount(user);
330
          const accountAuthorizations =
331
            await this.accountAuthorizationService.applyAuthorizationPolicy(
332
              account
333
            );
334
          await this.authorizationPolicyService.saveAll(accountAuthorizations);
335

336
          const credentialsData = userData.credentials;
337
          for (const credentialData of credentialsData) {
338
            await this.adminAuthorizationService.grantCredentialToUser({
339
              userID: user.id,
340
              type: credentialData.type,
341
              resourceID: credentialData.resourceID,
342
            });
343
          }
344
          await this.userAuthorizationService.grantCredentialsAllUsersReceive(
345
            user.id
346
          );
347
        }
348
      }
349
    } catch (error: any) {
350
      throw new BootstrapException(
351
        `Unable to create profiles ${error.message}`
352
      );
353
    }
354
  }
355

356
  private async ensureAuthorizationsPopulated() {
357
    // For platform
358
    const platform = await this.platformService.getPlatformOrFail();
359
    const platformAuthorization =
360
      this.authorizationPolicyService.validateAuthorization(
361
        platform.authorization
362
      );
363
    const platformCredentialRules =
364
      this.authorizationPolicyService.getCredentialRules(platformAuthorization);
365
    // Assume that zero rules means that the policy has not been reset
366
    if (platformCredentialRules.length == 0) {
367
      this.logger.verbose?.(
368
        '=== Identified that platform authorization had not been reset; resetting now ===',
369
        LogContext.BOOTSTRAP
370
      );
371
      const updatedAuthorizations =
372
        await this.platformAuthorizationService.applyAuthorizationPolicy();
373
      await this.authorizationPolicyService.saveAll(updatedAuthorizations);
374
    }
375

376
    // Also do same for AI Server until it is moved out of the server
377
    const aiServer = await this.aiServer.getAiServerOrFail();
378
    const aiServerAuthorization =
379
      this.authorizationPolicyService.validateAuthorization(
380
        aiServer.authorization
381
      );
382
    const aiServerCredentialRules =
383
      this.authorizationPolicyService.getCredentialRules(aiServerAuthorization);
384
    // Assume that zero rules means that the policy has not been reset
385
    if (aiServerCredentialRules.length == 0) {
386
      this.logger.verbose?.(
387
        '=== Identified that AI Server authorization had not been reset; resetting now ===',
388
        LogContext.BOOTSTRAP
389
      );
390
      const authorizations =
391
        await this.aiServerAuthorizationService.applyAuthorizationPolicy();
392
      await this.authorizationPolicyService.saveAll(authorizations);
393
    }
394
  }
395

396
  private async ensureOrganizationSingleton(agentInfo?: AgentInfo) {
397
    // create a default host org
398
    let hostOrganization =
399
      await this.organizationLookupService.getOrganizationByNameId(
400
        DEFAULT_HOST_ORG_NAMEID
401
      );
402
    if (!hostOrganization) {
403
      // If agentInfo is not provided, we create without an admin initially
404
      // The admin will be linked later
405
      hostOrganization = await this.organizationService.createOrganization(
406
        {
407
          nameID: DEFAULT_HOST_ORG_NAMEID,
408
          profileData: {
409
            displayName: DEFAULT_HOST_ORG_DISPLAY_NAME,
410
          },
411
        },
412
        agentInfo
413
      );
414
      const orgAuthorizations =
415
        await this.organizationAuthorizationService.applyAuthorizationPolicy(
416
          hostOrganization
417
        );
418
      await this.authorizationPolicyService.saveAll(orgAuthorizations);
419

420
      const account =
421
        await this.organizationService.getAccount(hostOrganization);
422
      const accountAuthorizations =
423
        await this.accountAuthorizationService.applyAuthorizationPolicy(
424
          account
425
        );
426
      await this.authorizationPolicyService.saveAll(accountAuthorizations);
427

428
      const accountEntitlements =
429
        await this.accountLicenseService.applyLicensePolicy(account.id);
430
      await this.licenseService.saveAll(accountEntitlements);
431
    }
432
  }
433

434
  private async ensureAdminUserLinkedToOrganization() {
435
    const adminAgentInfo = await this.getAdminAgentInfo();
436
    const hostOrganization =
437
      await this.organizationLookupService.getOrganizationByNameIdOrFail(
438
        DEFAULT_HOST_ORG_NAMEID
439
      );
440

441
    const roleSet = await this.organizationService.getRoleSet(hostOrganization);
442

443
    // Assign Admin as Associate and Admin
444
    await this.roleSetService.assignUserToRole(
445
      roleSet,
446
      RoleName.ASSOCIATE,
447
      adminAgentInfo.userID,
448
      adminAgentInfo,
449
      false
450
    );
451

452
    await this.roleSetService.assignUserToRole(
453
      roleSet,
454
      RoleName.ADMIN,
455
      adminAgentInfo.userID,
456
      adminAgentInfo,
457
      false
458
    );
459

460
    this.logger.verbose?.(
461
      `Ensured Admin user linked to Organization: ${hostOrganization.id}`,
462
      LogContext.BOOTSTRAP
463
    );
464
  }
465

466
  private async getAdminAgentInfo(): Promise<AgentInfo> {
467
    const adminUserEmail = 'admin@alkem.io';
468
    const adminUser = await this.userService.getUserByEmail(adminUserEmail, {
469
      relations: {
470
        agent: true,
471
      },
472
    });
473
    if (!adminUser) {
474
      throw new BootstrapException(
475
        `Unable to load fixed admin user for creating organization: ${adminUserEmail}`
476
      );
477
    }
478
    return {
479
      isAnonymous: false,
480
      userID: adminUser.id,
481
      email: adminUser.email,
482
      emailVerified: true,
483
      firstName: adminUser.firstName,
484
      lastName: adminUser.lastName,
485
      guestName: '',
486
      avatarURL: '',
487
      credentials: adminUser.agent?.credentials || [],
488
      agentID: adminUser.agent?.id,
489
      authenticationID: adminUser.authenticationID ?? '',
490
    };
491
  }
492

493
  private async ensureSpaceSingleton(agentInfo: AgentInfo) {
494
    this.logger.verbose?.(
495
      '=== Ensuring at least one Account with a space is present ===',
496
      LogContext.BOOTSTRAP
497
    );
498
    const spaceCount = await this.spaceRepository.count();
499
    if (spaceCount == 0) {
500
      this.logger.verbose?.('...No space present...', LogContext.BOOTSTRAP);
501
      this.logger.verbose?.(
502
        '........creating on default organization',
503
        LogContext.BOOTSTRAP
504
      );
505
      const hostOrganization =
506
        await this.organizationLookupService.getOrganizationByNameIdOrFail(
507
          DEFAULT_HOST_ORG_NAMEID
508
        );
509

510
      const account =
511
        await this.organizationService.getAccount(hostOrganization);
512
      const spaceInput: CreateSpaceOnAccountInput = {
513
        accountID: account.id,
514
        nameID: DEFAULT_SPACE_NAMEID,
515
        about: {
516
          profileData: {
517
            displayName: DEFAULT_SPACE_DISPLAYNAME,
518
            tagline: 'An empty space to be populated',
519
          },
520
        },
521
        level: SpaceLevel.L0,
522
        levelZeroSpaceID: '',
523
        collaborationData: {
524
          calloutsSetData: {},
525
        },
526
      };
527

528
      const space = await this.accountService.createSpaceOnAccount(
529
        spaceInput,
530
        agentInfo
531
      );
532
      const spaceAuthorizations =
533
        await this.spaceAuthorizationService.applyAuthorizationPolicy(space.id);
534
      await this.authorizationPolicyService.saveAll(spaceAuthorizations);
535

536
      const accountEntitlements =
537
        await this.accountLicenseService.applyLicensePolicy(account.id);
538
      await this.licenseService.saveAll(accountEntitlements);
539

540
      return this.spaceService.getSpaceOrFail(space.id);
541
    }
542
  }
543

544
  private async ensureGuidanceChat() {
545
    // Check if the CHAT_GUIDANCE well-known VC is configured
546
    const wellKnownVCId =
547
      await this.platformWellKnownVirtualContributorsService.getVirtualContributorID(
548
        VirtualContributorWellKnown.CHAT_GUIDANCE
549
      );
550

551
    if (!wellKnownVCId) {
552
      // Get admin account:
553
      const hostOrganization =
554
        await this.organizationLookupService.getOrganizationByNameIdOrFail(
555
          DEFAULT_HOST_ORG_NAMEID
556
        );
557
      const account =
558
        await this.organizationService.getAccount(hostOrganization);
559

560
      // Create the VC
561
      const vc = await this.accountService.createVirtualContributorOnAccount({
562
        accountID: account.id,
563
        aiPersona: {
564
          engine: AiPersonaEngine.GUIDANCE,
565
          prompt: [],
566
          externalConfig: undefined,
567
        },
568
        profileData: {
569
          displayName: 'Guidance',
570
          description: 'Guidance Virtual Contributor',
571
        },
572
        dataAccessMode: VirtualContributorDataAccessMode.NONE,
573
        bodyOfKnowledgeType: VirtualContributorBodyOfKnowledgeType.WEBSITE,
574
        interactionModes: [
575
          VirtualContributorInteractionMode.DISCUSSION_TAGGING,
576
        ],
577
        knowledgeBaseData: {
578
          profile: {
579
            displayName: 'Knowledge Base for Virtual Contributor',
580
          },
581
          calloutsSetData: {},
582
        },
583
      });
584

585
      // Register the VC as the CHAT_GUIDANCE well-known VC
586
      await this.platformWellKnownVirtualContributorsService.setMapping(
587
        VirtualContributorWellKnown.CHAT_GUIDANCE,
588
        vc.id
589
      );
590
    }
591
  }
592
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc