@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.
Files changed (60) hide show
  1. package/.sdd/manifest.json +5 -5
  2. package/ai-docs/CONTRACTS.md +2 -0
  3. package/ai-docs/SERVICE_STATE.md +4 -0
  4. package/ai-docs/contact-center-spec.md +26 -3
  5. package/dist/cc.js +163 -2
  6. package/dist/cc.js.map +1 -1
  7. package/dist/constants.js +5 -1
  8. package/dist/constants.js.map +1 -1
  9. package/dist/index.js +25 -10
  10. package/dist/index.js.map +1 -1
  11. package/dist/metrics/constants.js +5 -0
  12. package/dist/metrics/constants.js.map +1 -1
  13. package/dist/services/ApiAiAssistant.js +116 -1
  14. package/dist/services/ApiAiAssistant.js.map +1 -1
  15. package/dist/services/config/Util.js +5 -1
  16. package/dist/services/config/Util.js.map +1 -1
  17. package/dist/services/config/constants.js +10 -1
  18. package/dist/services/config/constants.js.map +1 -1
  19. package/dist/services/config/index.js +51 -0
  20. package/dist/services/config/index.js.map +1 -1
  21. package/dist/services/config/types.js +4 -3
  22. package/dist/services/config/types.js.map +1 -1
  23. package/dist/services/core/Utils.js +5 -1
  24. package/dist/services/core/Utils.js.map +1 -1
  25. package/dist/types/cc.d.ts +21 -1
  26. package/dist/types/constants.d.ts +4 -0
  27. package/dist/types/index.d.ts +11 -1
  28. package/dist/types/metrics/constants.d.ts +5 -0
  29. package/dist/types/services/ApiAiAssistant.d.ts +35 -2
  30. package/dist/types/services/config/constants.d.ts +5 -0
  31. package/dist/types/services/config/types.d.ts +13 -2
  32. package/dist/types/services/core/Utils.d.ts +2 -0
  33. package/dist/types/types.d.ts +56 -1
  34. package/dist/types.js +39 -2
  35. package/dist/types.js.map +1 -1
  36. package/dist/webex.js +9 -1
  37. package/dist/webex.js.map +1 -1
  38. package/package.json +9 -9
  39. package/src/cc.ts +251 -18
  40. package/src/constants.ts +4 -0
  41. package/src/index.ts +11 -0
  42. package/src/metrics/ai-docs/metrics-spec.md +19 -20
  43. package/src/metrics/constants.ts +5 -0
  44. package/src/services/ApiAiAssistant.ts +153 -1
  45. package/src/services/ai-docs/services-spec.md +11 -3
  46. package/src/services/config/Util.ts +6 -0
  47. package/src/services/config/ai-docs/config-spec.md +14 -3
  48. package/src/services/config/constants.ts +12 -0
  49. package/src/services/config/index.ts +65 -0
  50. package/src/services/config/types.ts +11 -2
  51. package/src/services/core/Utils.ts +4 -0
  52. package/src/types.ts +63 -0
  53. package/src/webex.js +8 -0
  54. package/test/unit/spec/cc.ts +268 -0
  55. package/test/unit/spec/services/ApiAiAssistant.ts +134 -2
  56. package/test/unit/spec/services/config/Util.ts +44 -0
  57. package/test/unit/spec/services/config/index.ts +68 -0
  58. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  59. package/umd/contact-center.min.js +2 -2
  60. 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
- const {channelsMap, ...loginData} = eventData.data;
1386
- const stationReLoginData = {
1387
- ...loginData,
1388
- mmProfile: {
1389
- chat: channelsMap.chat?.length,
1390
- email: channelsMap.email?.length,
1391
- social: channelsMap.social?.length,
1392
- telephony: channelsMap.telephony?.length,
1393
- },
1394
- notifsTrackingId: eventData.trackingId,
1395
- };
1396
- // @ts-ignore
1397
- this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
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 follow-up review remediation / 2026-07-21 |
15
- | Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); coverage remains Partial |
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 107 defined metric names, 96 have behavioral taxonomy and 11 do not. Events **without** an `eventTaxonomyMap` entry are the eight `AI_ASSISTANT_*` names plus `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, and `WEBSOCKET_EVENT_RECEIVED`.
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 107 names from `src/metrics/constants.ts`; taxonomy presence is checked against `src/metrics/behavioral-events.ts`: 96 mapped and 11 unmapped.
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: `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED`, `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS`, `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_FAILED`, `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_SUCCESS`, `AI_ASSISTANT_SEND_EVENT_FAILED`, `AI_ASSISTANT_SEND_EVENT_SUCCESS`, `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED`, `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_EVENT_RECEIVED`.
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 following events do **not** have behavioral taxonomy mappings in `behavioral-events.ts`:
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; 73 of 82 names are mapped and the six AI Assistant plus three WebSocket names are intentionally catalogued as unmapped.
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: eleven defined names intentionally have no behavioral taxonomy.
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 eleven unmapped names. |
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`
@@ -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',