@webex/contact-center 3.12.0-llmrefactor.11 → 3.12.0-llmrefactor.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.sdd/manifest.json +5 -5
- package/ai-docs/CONTRACTS.md +2 -0
- package/ai-docs/SERVICE_STATE.md +4 -0
- package/ai-docs/contact-center-spec.md +26 -3
- package/dist/cc.js +163 -2
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +25 -10
- package/dist/index.js.map +1 -1
- package/dist/metrics/constants.js +5 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +116 -1
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +5 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +10 -1
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +51 -0
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +4 -3
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Utils.js +5 -1
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/types/cc.d.ts +21 -1
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/index.d.ts +11 -1
- package/dist/types/metrics/constants.d.ts +5 -0
- package/dist/types/services/ApiAiAssistant.d.ts +35 -2
- package/dist/types/services/config/constants.d.ts +5 -0
- package/dist/types/services/config/types.d.ts +13 -2
- package/dist/types/services/core/Utils.d.ts +2 -0
- package/dist/types/types.d.ts +56 -1
- package/dist/types.js +39 -2
- package/dist/types.js.map +1 -1
- package/dist/webex.js +9 -1
- package/dist/webex.js.map +1 -1
- package/package.json +9 -9
- package/src/cc.ts +251 -18
- package/src/constants.ts +4 -0
- package/src/index.ts +11 -0
- package/src/metrics/ai-docs/metrics-spec.md +19 -20
- package/src/metrics/constants.ts +5 -0
- package/src/services/ApiAiAssistant.ts +153 -1
- package/src/services/ai-docs/services-spec.md +11 -3
- package/src/services/config/Util.ts +6 -0
- package/src/services/config/ai-docs/config-spec.md +14 -3
- package/src/services/config/constants.ts +12 -0
- package/src/services/config/index.ts +65 -0
- package/src/services/config/types.ts +11 -2
- package/src/services/core/Utils.ts +4 -0
- package/src/types.ts +63 -0
- package/src/webex.js +8 -0
- package/test/unit/spec/cc.ts +268 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +134 -2
- package/test/unit/spec/services/config/Util.ts +44 -0
- package/test/unit/spec/services/config/index.ts +68 -0
- package/test/unit/spec/wellnessSampleUtils.ts +288 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/src/cc.ts
CHANGED
|
@@ -26,6 +26,9 @@ import {
|
|
|
26
26
|
UpdateDeviceTypeResponse,
|
|
27
27
|
GenericError,
|
|
28
28
|
ConfigFlags,
|
|
29
|
+
WellnessBreakEvent,
|
|
30
|
+
WellnessBreakNotificationAction,
|
|
31
|
+
WELLNESS_BREAK_NOTIFICATION_ACTIONS,
|
|
29
32
|
} from './types';
|
|
30
33
|
import {
|
|
31
34
|
READY,
|
|
@@ -38,6 +41,7 @@ import {
|
|
|
38
41
|
UNKNOWN_ERROR,
|
|
39
42
|
MERCURY_DISCONNECTED_SUCCESS,
|
|
40
43
|
METHODS,
|
|
44
|
+
WELLNESS_BREAK_HANDLER,
|
|
41
45
|
} from './constants';
|
|
42
46
|
import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
|
|
43
47
|
import {AGENT, RTD_SUBSCRIBE_API, SUBSCRIBE_API, WEB_RTC_PREFIX} from './services/constants';
|
|
@@ -45,13 +49,14 @@ import Services from './services';
|
|
|
45
49
|
import WebexRequest from './services/core/WebexRequest';
|
|
46
50
|
import LoggerProxy from './logger-proxy';
|
|
47
51
|
import {StateChange, Logout, StateChangeSuccess, AGENT_EVENTS} from './services/agent/types';
|
|
48
|
-
import {getErrorDetails, isValidDialNumber} from './services/core/Utils';
|
|
52
|
+
import {getErrorDetails, isRecord, isValidDialNumber} from './services/core/Utils';
|
|
49
53
|
import {
|
|
50
54
|
Profile,
|
|
51
55
|
WelcomeEvent,
|
|
52
56
|
CC_EVENTS,
|
|
53
57
|
OutdialAniEntriesResponse,
|
|
54
58
|
OutdialAniParams,
|
|
59
|
+
Entity,
|
|
55
60
|
} from './services/config/types';
|
|
56
61
|
import {ConnectionLostDetails} from './services/core/websocket/types';
|
|
57
62
|
import TaskManager from './services/task/TaskManager';
|
|
@@ -296,6 +301,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
296
301
|
*/
|
|
297
302
|
private metricsManager: MetricsManager;
|
|
298
303
|
|
|
304
|
+
/** Current login/relogin session used for agent-scoped wellness validation. */
|
|
305
|
+
private currentAgentSessionId?: string;
|
|
306
|
+
|
|
307
|
+
/** Registered-session cache for the system WellbeingBreak idle code. */
|
|
308
|
+
private wellbeingBreakIdleCode?: Entity;
|
|
309
|
+
|
|
310
|
+
/** Registration generation that owns the wellness idle-code cache and in-flight lookups. */
|
|
311
|
+
private wellnessRegistrationGeneration = 0;
|
|
312
|
+
|
|
313
|
+
private updateWellnessSession(agentSessionId?: string): void {
|
|
314
|
+
this.currentAgentSessionId = agentSessionId;
|
|
315
|
+
}
|
|
316
|
+
|
|
299
317
|
/**
|
|
300
318
|
* API instance for managing Webex Contact Center entry points
|
|
301
319
|
* Provides functionality to fetch entry points with caching support
|
|
@@ -440,7 +458,11 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
440
458
|
this.answerCallOnWebexService = new AnswerCallOnWebexService(this.$webex);
|
|
441
459
|
this.webexCrossClientService = new WebexCrossClientService(this.$webex);
|
|
442
460
|
this.wxAppTelephonyMercurySync = new WxAppTelephonyMercurySync(this.$webex);
|
|
443
|
-
this.apiAIAssistant = new ApiAIAssistant(this.$webex)
|
|
461
|
+
this.apiAIAssistant = new ApiAIAssistant(this.$webex, () => ({
|
|
462
|
+
isWellnessBreakEnabled: this.agentConfig?.isWellnessBreakEnabled === true,
|
|
463
|
+
agentId: this.agentConfig?.agentId,
|
|
464
|
+
agentSessionId: this.currentAgentSessionId,
|
|
465
|
+
}));
|
|
444
466
|
this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
|
|
445
467
|
this.taskManager = TaskManager.getTaskManager(
|
|
446
468
|
this.apiAIAssistant,
|
|
@@ -512,6 +534,114 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
512
534
|
this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
513
535
|
};
|
|
514
536
|
|
|
537
|
+
private trackInvalidWellnessEvent(reason: string): void {
|
|
538
|
+
this.metricsManager.trackEvent(
|
|
539
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_EVENT_INVALID,
|
|
540
|
+
{reason},
|
|
541
|
+
['operational']
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private normalizeWellnessBreakEvent(payload: Record<string, unknown>): WellnessBreakEvent | null {
|
|
546
|
+
if (this.agentConfig?.isWellnessBreakEnabled !== true) {
|
|
547
|
+
this.trackInvalidWellnessEvent('wellness_disabled');
|
|
548
|
+
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const notificationData = isRecord(payload.data) ? payload.data : null;
|
|
553
|
+
const notificationDetails =
|
|
554
|
+
notificationData && isRecord(notificationData.notifDetails)
|
|
555
|
+
? notificationData.notifDetails
|
|
556
|
+
: null;
|
|
557
|
+
const sessionData =
|
|
558
|
+
notificationData && isRecord(notificationData.data) ? notificationData.data : null;
|
|
559
|
+
const actionEvent = notificationDetails?.actionEvent;
|
|
560
|
+
|
|
561
|
+
if (
|
|
562
|
+
payload.type !== WELLNESS_BREAK_HANDLER ||
|
|
563
|
+
notificationData?.notifType !== WELLNESS_BREAK_HANDLER ||
|
|
564
|
+
typeof actionEvent !== 'string'
|
|
565
|
+
) {
|
|
566
|
+
this.trackInvalidWellnessEvent('malformed_wellness_event');
|
|
567
|
+
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (
|
|
572
|
+
!Object.values(WELLNESS_BREAK_NOTIFICATION_ACTIONS).includes(
|
|
573
|
+
actionEvent as WellnessBreakNotificationAction
|
|
574
|
+
)
|
|
575
|
+
) {
|
|
576
|
+
this.trackInvalidWellnessEvent('unknown_wellness_action');
|
|
577
|
+
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const agentId = notificationData.agentId;
|
|
582
|
+
const eventOrgId = notificationData.orgId;
|
|
583
|
+
const sessionOrgId = sessionData?.orgId;
|
|
584
|
+
const envelopeOrgId = payload.orgId;
|
|
585
|
+
const agentSessionId = sessionData?.agentSessionId;
|
|
586
|
+
const currentOrgId = this.$webex.credentials.getOrgId();
|
|
587
|
+
|
|
588
|
+
if (
|
|
589
|
+
typeof agentId !== 'string' ||
|
|
590
|
+
typeof eventOrgId !== 'string' ||
|
|
591
|
+
typeof sessionOrgId !== 'string' ||
|
|
592
|
+
typeof envelopeOrgId !== 'string' ||
|
|
593
|
+
typeof agentSessionId !== 'string' ||
|
|
594
|
+
!agentId ||
|
|
595
|
+
!eventOrgId ||
|
|
596
|
+
!sessionOrgId ||
|
|
597
|
+
!envelopeOrgId ||
|
|
598
|
+
!agentSessionId
|
|
599
|
+
) {
|
|
600
|
+
this.trackInvalidWellnessEvent('missing_wellness_identity');
|
|
601
|
+
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (
|
|
606
|
+
agentId !== this.agentConfig?.agentId ||
|
|
607
|
+
eventOrgId !== currentOrgId ||
|
|
608
|
+
sessionOrgId !== currentOrgId ||
|
|
609
|
+
envelopeOrgId !== currentOrgId
|
|
610
|
+
) {
|
|
611
|
+
this.trackInvalidWellnessEvent('mismatched_wellness_identity');
|
|
612
|
+
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const interactionValue = sessionData.interactionId ?? sessionData.InteractionId;
|
|
617
|
+
const actionText = notificationDetails.actionText;
|
|
618
|
+
const trackingId = payload.trackingId;
|
|
619
|
+
|
|
620
|
+
return {
|
|
621
|
+
agentId,
|
|
622
|
+
orgId: currentOrgId,
|
|
623
|
+
agentSessionId,
|
|
624
|
+
actionEvent: actionEvent as WellnessBreakNotificationAction,
|
|
625
|
+
...(typeof actionText === 'string' ? {actionText} : {}),
|
|
626
|
+
...(typeof interactionValue === 'string' ? {interactionId: interactionValue} : {}),
|
|
627
|
+
...(typeof trackingId === 'string' ? {trackingId} : {}),
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
private handleWellnessBreakWebsocketPayload(payload: Record<string, unknown>): boolean {
|
|
632
|
+
if (payload.type !== WELLNESS_BREAK_HANDLER) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const wellnessEvent = this.normalizeWellnessBreakEvent(payload);
|
|
637
|
+
if (wellnessEvent) {
|
|
638
|
+
// @ts-ignore - WebexPlugin emit is available at runtime but absent from its declaration.
|
|
639
|
+
this.emit(CC_EVENTS.WELLNESS_BREAK, wellnessEvent);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
|
|
515
645
|
private handleRTDWebsocketMessage = (event: string) => {
|
|
516
646
|
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
517
647
|
};
|
|
@@ -569,6 +699,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
569
699
|
* ```
|
|
570
700
|
*/
|
|
571
701
|
public async register(): Promise<Profile> {
|
|
702
|
+
this.wellnessRegistrationGeneration += 1;
|
|
703
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
572
704
|
LoggerProxy.log('Starting CC SDK registration', {
|
|
573
705
|
module: CC_FILE,
|
|
574
706
|
method: METHODS.REGISTER,
|
|
@@ -651,6 +783,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
651
783
|
|
|
652
784
|
*/
|
|
653
785
|
public async deregister(): Promise<void> {
|
|
786
|
+
this.wellnessRegistrationGeneration += 1;
|
|
787
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
788
|
+
this.updateWellnessSession();
|
|
654
789
|
try {
|
|
655
790
|
this.metricsManager.timeEvent([
|
|
656
791
|
METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS,
|
|
@@ -703,7 +838,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
703
838
|
}
|
|
704
839
|
|
|
705
840
|
// Clear any cached agent configuration
|
|
841
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
706
842
|
this.agentConfig = null;
|
|
843
|
+
this.updateWellnessSession();
|
|
707
844
|
|
|
708
845
|
if (wxAppPublishError) {
|
|
709
846
|
throw wxAppPublishError;
|
|
@@ -873,6 +1010,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
873
1010
|
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
874
1011
|
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
875
1012
|
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
1013
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
1014
|
+
this.updateWellnessSession();
|
|
876
1015
|
|
|
877
1016
|
/**
|
|
878
1017
|
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
@@ -945,6 +1084,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
945
1084
|
|
|
946
1085
|
return this.agentConfig;
|
|
947
1086
|
} catch (error) {
|
|
1087
|
+
this.updateWellnessSession();
|
|
948
1088
|
LoggerProxy.error(`Error during register: ${error}`, {
|
|
949
1089
|
module: CC_FILE,
|
|
950
1090
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
@@ -1055,6 +1195,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1055
1195
|
|
|
1056
1196
|
const resp = await loginResponse;
|
|
1057
1197
|
const {channelsMap, ...loginData} = resp.data;
|
|
1198
|
+
this.updateWellnessSession(resp.data.agentSessionId);
|
|
1058
1199
|
this.agentConfig.currentTeamId = resp.data.teamId;
|
|
1059
1200
|
const response = {
|
|
1060
1201
|
...loginData,
|
|
@@ -1170,6 +1311,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1170
1311
|
}
|
|
1171
1312
|
|
|
1172
1313
|
await this.teardownWxAppLocalState();
|
|
1314
|
+
this.updateWellnessSession();
|
|
1173
1315
|
|
|
1174
1316
|
LoggerProxy.log(`Agent station logout completed successfully`, {
|
|
1175
1317
|
module: CC_FILE,
|
|
@@ -1307,6 +1449,77 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1307
1449
|
}
|
|
1308
1450
|
}
|
|
1309
1451
|
|
|
1452
|
+
/**
|
|
1453
|
+
* Returns the system-managed `WellbeingBreak` idle code for the current registration.
|
|
1454
|
+
* The value is fetched outside Desktop Profile filtering and cached until deregistration.
|
|
1455
|
+
* @returns The normalized system idle-code entity
|
|
1456
|
+
* @throws Structured Contact Center error when wellness is disabled or the code is unavailable
|
|
1457
|
+
* @example
|
|
1458
|
+
* const idleCode = await webex.cc.getWellbeingBreakIdleCode();
|
|
1459
|
+
* @public
|
|
1460
|
+
*/
|
|
1461
|
+
public async getWellbeingBreakIdleCode(): Promise<Entity> {
|
|
1462
|
+
const method = METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
|
|
1463
|
+
try {
|
|
1464
|
+
if (!this.agentConfig?.isWellnessBreakEnabled) {
|
|
1465
|
+
const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED') as GenericError;
|
|
1466
|
+
validationError.details = {
|
|
1467
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1468
|
+
orgId: this.$webex.credentials.getOrgId(),
|
|
1469
|
+
trackingId: EMPTY_STRING,
|
|
1470
|
+
data: {reason: 'WELLNESS_BREAK_NOT_ENABLED'},
|
|
1471
|
+
};
|
|
1472
|
+
throw validationError;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
if (this.wellbeingBreakIdleCode) {
|
|
1476
|
+
return this.wellbeingBreakIdleCode;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
this.metricsManager.timeEvent([
|
|
1480
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1481
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1482
|
+
]);
|
|
1483
|
+
const registrationGeneration = this.wellnessRegistrationGeneration;
|
|
1484
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
1485
|
+
const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
|
|
1486
|
+
|
|
1487
|
+
if (
|
|
1488
|
+
registrationGeneration !== this.wellnessRegistrationGeneration ||
|
|
1489
|
+
orgId !== this.$webex.credentials.getOrgId() ||
|
|
1490
|
+
!this.agentConfig?.isWellnessBreakEnabled
|
|
1491
|
+
) {
|
|
1492
|
+
const staleRegistrationError = new Error(
|
|
1493
|
+
'WELLNESS_BREAK_REGISTRATION_CHANGED'
|
|
1494
|
+
) as GenericError;
|
|
1495
|
+
staleRegistrationError.details = {
|
|
1496
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1497
|
+
orgId,
|
|
1498
|
+
trackingId: EMPTY_STRING,
|
|
1499
|
+
data: {reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'},
|
|
1500
|
+
};
|
|
1501
|
+
throw staleRegistrationError;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
this.wellbeingBreakIdleCode = idleCode;
|
|
1505
|
+
this.metricsManager.trackEvent(
|
|
1506
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1507
|
+
{},
|
|
1508
|
+
['operational']
|
|
1509
|
+
);
|
|
1510
|
+
|
|
1511
|
+
return idleCode;
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
this.metricsManager.trackEvent(
|
|
1514
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1515
|
+
{reason: error instanceof Error ? error.message : UNKNOWN_ERROR},
|
|
1516
|
+
['operational']
|
|
1517
|
+
);
|
|
1518
|
+
const {error: detailedError} = getErrorDetails(error, method, CC_FILE);
|
|
1519
|
+
throw detailedError;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1310
1523
|
/**
|
|
1311
1524
|
* Processes incoming websocket messages and emits corresponding events
|
|
1312
1525
|
* Handles various event types including agent state changes, login events,
|
|
@@ -1345,8 +1558,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1345
1558
|
]);
|
|
1346
1559
|
}
|
|
1347
1560
|
|
|
1561
|
+
if (this.handleWellnessBreakWebsocketPayload(eventData)) {
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1348
1565
|
switch (eventData.type) {
|
|
1349
1566
|
case CC_EVENTS.AGENT_MULTI_LOGIN:
|
|
1567
|
+
if (
|
|
1568
|
+
eventData.data?.type === 'AgentMultiLoginCloseSession' &&
|
|
1569
|
+
(!eventData.data.agentSessionId ||
|
|
1570
|
+
eventData.data.agentSessionId === this.currentAgentSessionId)
|
|
1571
|
+
) {
|
|
1572
|
+
this.updateWellnessSession();
|
|
1573
|
+
}
|
|
1350
1574
|
// @ts-ignore
|
|
1351
1575
|
this.emit(AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
|
|
1352
1576
|
break;
|
|
@@ -1365,6 +1589,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1365
1589
|
switch (eventData.data.type) {
|
|
1366
1590
|
case CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS: {
|
|
1367
1591
|
const {channelsMap, ...loginData} = eventData.data;
|
|
1592
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1368
1593
|
const stationLoginData = {
|
|
1369
1594
|
...loginData,
|
|
1370
1595
|
mmProfile: {
|
|
@@ -1380,23 +1605,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1380
1605
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, stationLoginData);
|
|
1381
1606
|
break;
|
|
1382
1607
|
}
|
|
1383
|
-
case CC_EVENTS.AGENT_RELOGIN_SUCCESS:
|
|
1384
|
-
{
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
}
|
|
1608
|
+
case CC_EVENTS.AGENT_RELOGIN_SUCCESS: {
|
|
1609
|
+
const {channelsMap, ...loginData} = eventData.data;
|
|
1610
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1611
|
+
const stationReLoginData = {
|
|
1612
|
+
...loginData,
|
|
1613
|
+
mmProfile: {
|
|
1614
|
+
chat: channelsMap.chat?.length,
|
|
1615
|
+
email: channelsMap.email?.length,
|
|
1616
|
+
social: channelsMap.social?.length,
|
|
1617
|
+
telephony: channelsMap.telephony?.length,
|
|
1618
|
+
},
|
|
1619
|
+
notifsTrackingId: eventData.trackingId,
|
|
1620
|
+
};
|
|
1621
|
+
// @ts-ignore
|
|
1622
|
+
this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
|
|
1399
1623
|
break;
|
|
1624
|
+
}
|
|
1400
1625
|
case CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
|
|
1401
1626
|
// @ts-ignore
|
|
1402
1627
|
this.emit(AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
|
|
@@ -1410,6 +1635,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1410
1635
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
|
|
1411
1636
|
break;
|
|
1412
1637
|
case CC_EVENTS.AGENT_LOGOUT_SUCCESS:
|
|
1638
|
+
if (
|
|
1639
|
+
!eventData.data.agentSessionId ||
|
|
1640
|
+
eventData.data.agentSessionId === this.currentAgentSessionId
|
|
1641
|
+
) {
|
|
1642
|
+
this.updateWellnessSession();
|
|
1643
|
+
}
|
|
1413
1644
|
// @ts-ignore
|
|
1414
1645
|
this.emit(AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
|
|
1415
1646
|
break;
|
|
@@ -2079,6 +2310,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
2079
2310
|
|
|
2080
2311
|
try {
|
|
2081
2312
|
const reLoginResponse = await this.services.agent.reload();
|
|
2313
|
+
this.updateWellnessSession(reLoginResponse.data.agentSessionId);
|
|
2314
|
+
|
|
2082
2315
|
const {
|
|
2083
2316
|
agentId,
|
|
2084
2317
|
lastStateChangeReason,
|
package/src/constants.ts
CHANGED
|
@@ -8,6 +8,7 @@ export const LOG_PREFIX = 'PLUGIN_CC';
|
|
|
8
8
|
export const WEB_CALLING_SERVICE_FILE = 'WebCallingService';
|
|
9
9
|
export const CONFIG_FILE_NAME = 'config-index';
|
|
10
10
|
export const CC_FILE = 'cc';
|
|
11
|
+
export const WELLNESS_BREAK_HANDLER = 'Wellness_Break_Handler';
|
|
11
12
|
export const CONNECTION_SERVICE_FILE = 'connection-service';
|
|
12
13
|
export const WEB_SOCKET_MANAGER_FILE = 'WebSocketManager';
|
|
13
14
|
export const AQM_REQS_FILE = 'aqm-reqs';
|
|
@@ -72,6 +73,9 @@ export const METHODS = {
|
|
|
72
73
|
GET_REAL_TIME_ASSISTANCE: 'getRealTimeAssistance',
|
|
73
74
|
SEND_REAL_TIME_ASSISTANCE_USER_ACTION: 'sendRealTimeAssistanceUserAction',
|
|
74
75
|
FETCH_HISTORIC_TRANSCRIPTS: 'fetchHistoricTranscripts',
|
|
76
|
+
REQUEST_WELLNESS_BREAK: 'requestWellnessBreak',
|
|
77
|
+
RESPOND_TO_WELLNESS_BREAK: 'respondToWellnessBreak',
|
|
78
|
+
GET_WELLBEING_BREAK_IDLE_CODE: 'getWellbeingBreakIdleCode',
|
|
75
79
|
SET_MANAGE_WEBEX_CALLING_IN_WXCC: 'setManageWebexCallingInWxcc',
|
|
76
80
|
IS_WX_BETTER_TOGETHER_ENABLED: 'isWxBetterTogetherEnabled',
|
|
77
81
|
SYNC_WXAPP_MUTE_FROM_MERCURY: 'syncWxAppMuteFromMercury',
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,9 @@ export {default as AddressBook} from './services/AddressBook';
|
|
|
30
30
|
export {default as ApiAIAssistant} from './services/ApiAiAssistant';
|
|
31
31
|
export {default as UserPreference} from './services/UserPreference';
|
|
32
32
|
|
|
33
|
+
/** Agent Wellness Break notification and action wire values. */
|
|
34
|
+
export {WELLNESS_BREAK_NOTIFICATION_ACTIONS, WELLNESS_BREAK_USER_ACTIONS} from './types';
|
|
35
|
+
|
|
33
36
|
/** EntryPoint API types */
|
|
34
37
|
export type {
|
|
35
38
|
EntryPointRecord,
|
|
@@ -120,6 +123,14 @@ export type {
|
|
|
120
123
|
GenericError,
|
|
121
124
|
/** Set state response */
|
|
122
125
|
SetStateResponse,
|
|
126
|
+
/** Wellness notification action */
|
|
127
|
+
WellnessBreakNotificationAction,
|
|
128
|
+
/** Wellness action API value */
|
|
129
|
+
WellnessBreakUserAction,
|
|
130
|
+
/** Validated agent-scoped wellness notification */
|
|
131
|
+
WellnessBreakEvent,
|
|
132
|
+
/** Wellness offer-response parameters */
|
|
133
|
+
RespondToWellnessBreakParams,
|
|
123
134
|
} from './types';
|
|
124
135
|
|
|
125
136
|
/** Task related types */
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
| Doc kind | Module spec |
|
|
12
12
|
| Coverage score | Partial (manifest-authoritative); 15/15 required document fields present |
|
|
13
13
|
| Generated from | `module-spec` @ SDLC template library `0.2.1` |
|
|
14
|
-
| generated_by / approved_by / updated_at | Codex generator / developer-approved
|
|
15
|
-
| Validation status |
|
|
14
|
+
| generated_by / approved_by / updated_at | Codex generator / developer-approved Agent Wellness Break delta / 2026-09-02 |
|
|
15
|
+
| Validation status | Agent Wellness Break v0.4 delta independently validated by claude-code on 2026-09-07; coverage remains Partial until the remaining baseline promotion criteria are satisfied |
|
|
16
16
|
|
|
17
17
|
## Evidence Rules
|
|
18
18
|
Every requirement cites stable source and test file paths. Code/tests are the behavioral referee; routed source text supplies explicit intent and rationale. Missing or contradictory evidence blocks promotion.
|
|
@@ -260,6 +260,9 @@ All event names are defined in `METRIC_EVENT_NAMES` (`constants.ts`). Events fol
|
|
|
260
260
|
| `ADDRESSBOOK_FETCH_SUCCESS` / `FAILED` | `'AddressBook Fetch ...'` | Address book fetch result |
|
|
261
261
|
| `QUEUE_FETCH_SUCCESS` / `FAILED` | `'Queue Fetch ...'` | Queue fetch result |
|
|
262
262
|
| `OUTDIAL_ANI_EP_FETCH_SUCCESS` / `FAILED` | `'Outdial ANI Entries Fetch ...'` | Outdial ANI entries fetch result |
|
|
263
|
+
| `AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED` / `FAILED` | `'AI Assistant Wellness Action ...'` | Direct wellness custom-event result |
|
|
264
|
+
| `AI_ASSISTANT_WELLNESS_EVENT_INVALID` | `'AI Assistant Wellness Event Invalid'` | Malformed or mismatched wellness notification ignored |
|
|
265
|
+
| `WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS` / `FAILED` | `'Wellbeing Break Idle Code Fetch ...'` | System wellness idle-code lookup result |
|
|
263
266
|
|
|
264
267
|
All event names are defined in `constants.ts` as `METRIC_EVENT_NAMES`. Events follow a `{Domain} {Action} {Success|Failed}` naming convention:
|
|
265
268
|
|
|
@@ -334,11 +337,11 @@ Special events (no success/failure pair):
|
|
|
334
337
|
|
|
335
338
|
- `WXAPP_SESSION_SKIPPED` — has behavioral taxonomy (`wxcc_sdk.user.webex_together_session_init.ignore`)
|
|
336
339
|
|
|
337
|
-
Of the
|
|
340
|
+
Of the 116 defined metric names, 96 have behavioral taxonomy and 20 do not. The Agent Wellness Break names are intentionally operational-only and therefore have no `eventTaxonomyMap` entries.
|
|
338
341
|
|
|
339
342
|
### Complete METRIC_EVENT_NAMES catalog
|
|
340
343
|
|
|
341
|
-
This table contains all
|
|
344
|
+
This table contains all 116 names from `src/metrics/constants.ts`; taxonomy presence is checked against `src/metrics/behavioral-events.ts`: 96 mapped and 20 unmapped.
|
|
342
345
|
|
|
343
346
|
| Constant | Emitted name | Behavioral taxonomy? |
|
|
344
347
|
|---|---|---|
|
|
@@ -441,6 +444,11 @@ This table contains all 107 names from `src/metrics/constants.ts`; taxonomy pres
|
|
|
441
444
|
| `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED` | `AI Assistant Send Real Time Assistance User Action Failed` | no |
|
|
442
445
|
| `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS` | `AI Assistant Fetch Historic Transcripts Success` | no |
|
|
443
446
|
| `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED` | `AI Assistant Fetch Historic Transcripts Failed` | no |
|
|
447
|
+
| `AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED` | `AI Assistant Wellness Action Accepted` | no |
|
|
448
|
+
| `AI_ASSISTANT_WELLNESS_ACTION_FAILED` | `AI Assistant Wellness Action Failed` | no |
|
|
449
|
+
| `AI_ASSISTANT_WELLNESS_EVENT_INVALID` | `AI Assistant Wellness Event Invalid` | no |
|
|
450
|
+
| `WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS` | `Wellbeing Break Idle Code Fetch Success` | no |
|
|
451
|
+
| `WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED` | `Wellbeing Break Idle Code Fetch Failed` | no |
|
|
444
452
|
| `USER_PREFERENCE_GET_SUCCESS` | `User Preference Get Success` | yes |
|
|
445
453
|
| `USER_PREFERENCE_GET_FAILED` | `User Preference Get Failed` | yes |
|
|
446
454
|
| `USER_PREFERENCE_CREATE_SUCCESS` | `User Preference Create Success` | yes |
|
|
@@ -450,7 +458,7 @@ This table contains all 107 names from `src/metrics/constants.ts`; taxonomy pres
|
|
|
450
458
|
| `USER_PREFERENCE_DELETE_SUCCESS` | `User Preference Delete Success` | yes |
|
|
451
459
|
| `USER_PREFERENCE_DELETE_FAILED` | `User Preference Delete Failed` | yes |
|
|
452
460
|
|
|
453
|
-
Defined names without an `eventTaxonomyMap` entry: `
|
|
461
|
+
Defined names without an `eventTaxonomyMap` entry: `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_EVENT_RECEIVED`, all eight AI Assistant request names, and the seven Agent Wellness Break notification/action/system-code/ASC names listed above.
|
|
454
462
|
|
|
455
463
|
## Requires (dependencies)
|
|
456
464
|
- `webex.internal.newMetrics` submission APIs
|
|
@@ -473,6 +481,7 @@ Defined names without an `eventTaxonomyMap` entry: `AI_ASSISTANT_FETCH_HISTORIC_
|
|
|
473
481
|
| METRICS-R-003 | When metrics are disabled, clear pending events and make `timeEvent` plus all tracking methods return without recording/submitting. | Telemetry must never block or alter product behavior and disablement must be comprehensive. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
474
482
|
| METRICS-R-004 | Queue submissions until the host SDK is ready and flush through the correct behavioral/operational/business service. | Early lifecycle telemetry must not be lost solely because the host is not ready. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
475
483
|
| METRICS-R-005 | Submit through the host SDK's `webex.internal.newMetrics` client without storing credentials or implementing authorization policy in MetricsManager. | Host-owned authentication keeps telemetry credential handling outside the Contact Center metrics module. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; authentication is inherited and credential ownership is explicitly N/A. | PRESENT |
|
|
484
|
+
| METRICS-R-006 | Track wellness action, invalid notification, system-code lookup, and ASC state-change outcomes as operational metrics using fixed names and reason-only/context-minimized payloads. | The feature needs diagnosable lifecycle signals without logging wellness payloads or agent PII. | `src/metrics/constants.ts`, `src/cc.ts`, `src/services/ApiAiAssistant.ts` | `test/unit/spec/cc.ts`, `test/unit/spec/services/ApiAiAssistant.ts` | These names intentionally have no behavioral taxonomy. | PRESENT |
|
|
476
485
|
|
|
477
486
|
## Design Overview
|
|
478
487
|
Metrics separates its stable consumption boundary from collaborators so ownership and failure behavior stay explicit. Telemetry is deliberately non-blocking and queue-backed so product behavior never waits for metrics; failures are logged rather than propagated.
|
|
@@ -495,18 +504,7 @@ Each behavioral event maps to a structured taxonomy in `behavioral-events.ts`:
|
|
|
495
504
|
|
|
496
505
|
**Example**: `STATION_LOGIN_SUCCESS` maps to `wxcc_sdk.user.station_login.complete`
|
|
497
506
|
|
|
498
|
-
> **Note**: The
|
|
499
|
-
> - `AI_ASSISTANT_SEND_EVENT_SUCCESS`
|
|
500
|
-
> - `AI_ASSISTANT_SEND_EVENT_FAILED`
|
|
501
|
-
> - `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_SUCCESS`
|
|
502
|
-
> - `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_FAILED`
|
|
503
|
-
> - `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_SUCCESS`
|
|
504
|
-
> - `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED`
|
|
505
|
-
> - `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS`
|
|
506
|
-
> - `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED`
|
|
507
|
-
> - `WEBSOCKET_DEREGISTER_SUCCESS`
|
|
508
|
-
> - `WEBSOCKET_DEREGISTER_FAIL`
|
|
509
|
-
> - `WEBSOCKET_EVENT_RECEIVED`
|
|
507
|
+
> **Note**: The 20 names catalogued above as lacking `eventTaxonomyMap` entries, including all nine new wellness-related names, are operational-only.
|
|
510
508
|
>
|
|
511
509
|
> Calling `trackBehavioralEvent` with these event names will push an event with an `undefined` taxonomy.
|
|
512
510
|
|
|
@@ -796,7 +794,7 @@ MetricsManager holds three pending queues, running timing entries, a readiness f
|
|
|
796
794
|
|
|
797
795
|
## Business Rules & Invariants
|
|
798
796
|
- `timeEvent` and every tracking method return immediately while metrics are disabled.
|
|
799
|
-
- Only names present in `eventTaxonomyMap` receive behavioral taxonomy;
|
|
797
|
+
- Only names present in `eventTaxonomyMap` receive behavioral taxonomy; 81 of 101 names are mapped and all 20 exceptions are explicitly catalogued as unmapped.
|
|
800
798
|
- `submittingEvents` prevents overlapping queue-drain executions.
|
|
801
799
|
- Authentication is inherited from the host Webex SDK's metrics client; MetricsManager owns no credentials, tokens, or authorization policy.
|
|
802
800
|
|
|
@@ -831,7 +829,7 @@ stateDiagram-v2
|
|
|
831
829
|
- **metricsDisabled**: When `true`, `timeEvent` and all `track*` methods return early, and `clearPendingEvents()` empties all queues.
|
|
832
830
|
|
|
833
831
|
## Pitfalls
|
|
834
|
-
- `METRIC_EVENT_NAMES` and `eventTaxonomyMap` are different inventories:
|
|
832
|
+
- `METRIC_EVENT_NAMES` and `eventTaxonomyMap` are different inventories: 20 defined names intentionally have no behavioral taxonomy.
|
|
835
833
|
- `setMetricsDisabled(true)` clears pending queues but does not create a delivery receipt; callers must not infer that previously submitted events were accepted.
|
|
836
834
|
- Submission helpers hand events to `webex.internal.newMetrics` without a module-level retry/requeue policy, so telemetry must remain non-blocking and non-authoritative.
|
|
837
835
|
|
|
@@ -892,10 +890,11 @@ Use `test/unit/spec/metrics/MetricsManager.ts` for readiness queues, timing/trac
|
|
|
892
890
|
| Behavior / Requirement | Existing test evidence | Gap |
|
|
893
891
|
|---|---|---|
|
|
894
892
|
| `METRICS-R-001` | `test/unit/spec/metrics/MetricsManager.ts` | Add a catalog parity assertion if constants change. |
|
|
895
|
-
| `METRICS-R-002` | `test/unit/spec/metrics/behavioral-events.ts` | Keep explicit coverage for all
|
|
893
|
+
| `METRICS-R-002` | `test/unit/spec/metrics/behavioral-events.ts` | Keep explicit coverage for all 20 unmapped names. |
|
|
896
894
|
| `METRICS-R-003` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
|
|
897
895
|
| `METRICS-R-004` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
|
|
898
896
|
| `METRICS-R-005` | `test/unit/spec/metrics/MetricsManager.ts` | Authentication ownership is verified indirectly through the host metrics client. |
|
|
897
|
+
| `METRICS-R-006` | `test/unit/spec/cc.ts`, `test/unit/spec/services/ApiAiAssistant.ts` | Add direct catalog-parity coverage if metrics constants receive a dedicated test. |
|
|
899
898
|
|
|
900
899
|
## Traceability
|
|
901
900
|
- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../ai-docs/SPEC_INDEX.md`
|
package/src/metrics/constants.ts
CHANGED
|
@@ -203,6 +203,11 @@ export const METRIC_EVENT_NAMES = {
|
|
|
203
203
|
AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS:
|
|
204
204
|
'AI Assistant Fetch Historic Transcripts Success',
|
|
205
205
|
AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED: 'AI Assistant Fetch Historic Transcripts Failed',
|
|
206
|
+
AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED: 'AI Assistant Wellness Action Accepted',
|
|
207
|
+
AI_ASSISTANT_WELLNESS_ACTION_FAILED: 'AI Assistant Wellness Action Failed',
|
|
208
|
+
AI_ASSISTANT_WELLNESS_EVENT_INVALID: 'AI Assistant Wellness Event Invalid',
|
|
209
|
+
WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS: 'Wellbeing Break Idle Code Fetch Success',
|
|
210
|
+
WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED: 'Wellbeing Break Idle Code Fetch Failed',
|
|
206
211
|
|
|
207
212
|
// User Preference API Events
|
|
208
213
|
USER_PREFERENCE_GET_SUCCESS: 'User Preference Get Success',
|