@webex/contact-center 3.12.0-llmrefactor.12 → 3.12.0-llmrefactor.14

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 (107) hide show
  1. package/.sdd/manifest.json +6 -5
  2. package/ai-docs/CONTRACTS.md +8 -0
  3. package/ai-docs/SERVICE_STATE.md +4 -0
  4. package/ai-docs/contact-center-spec.md +26 -3
  5. package/ai-docs/features/contact-owner-change-refactor.md +78 -0
  6. package/dist/cc.js +202 -9
  7. package/dist/cc.js.map +1 -1
  8. package/dist/constants.js +5 -1
  9. package/dist/constants.js.map +1 -1
  10. package/dist/index.js +25 -10
  11. package/dist/index.js.map +1 -1
  12. package/dist/metrics/behavioral-events.js +6 -0
  13. package/dist/metrics/behavioral-events.js.map +1 -1
  14. package/dist/metrics/constants.js +6 -0
  15. package/dist/metrics/constants.js.map +1 -1
  16. package/dist/services/AnswerCallOnWebexService.js +4 -4
  17. package/dist/services/AnswerCallOnWebexService.js.map +1 -1
  18. package/dist/services/ApiAiAssistant.js +116 -1
  19. package/dist/services/ApiAiAssistant.js.map +1 -1
  20. package/dist/services/WebexCrossClientService.js +5 -2
  21. package/dist/services/WebexCrossClientService.js.map +1 -1
  22. package/dist/services/WxAppTelephonyMercurySync.js +6 -3
  23. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
  24. package/dist/services/config/Util.js +5 -1
  25. package/dist/services/config/Util.js.map +1 -1
  26. package/dist/services/config/constants.js +10 -1
  27. package/dist/services/config/constants.js.map +1 -1
  28. package/dist/services/config/index.js +51 -0
  29. package/dist/services/config/index.js.map +1 -1
  30. package/dist/services/config/types.js +4 -3
  31. package/dist/services/config/types.js.map +1 -1
  32. package/dist/services/core/Utils.js +5 -1
  33. package/dist/services/core/Utils.js.map +1 -1
  34. package/dist/services/task/TaskFactory.js +1 -0
  35. package/dist/services/task/TaskFactory.js.map +1 -1
  36. package/dist/services/task/TaskManager.js +50 -58
  37. package/dist/services/task/TaskManager.js.map +1 -1
  38. package/dist/services/task/types.js.map +1 -1
  39. package/dist/services/task/voice/Voice.js +63 -44
  40. package/dist/services/task/voice/Voice.js.map +1 -1
  41. package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
  42. package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
  43. package/dist/services/task/voice/wxAppVoiceMethods.js +52 -11
  44. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
  45. package/dist/services/wxAppDiagnosticLogging.js +50 -13
  46. package/dist/services/wxAppDiagnosticLogging.js.map +1 -1
  47. package/dist/types/cc.d.ts +21 -1
  48. package/dist/types/constants.d.ts +4 -0
  49. package/dist/types/index.d.ts +11 -1
  50. package/dist/types/metrics/constants.d.ts +6 -0
  51. package/dist/types/services/ApiAiAssistant.d.ts +35 -2
  52. package/dist/types/services/config/constants.d.ts +5 -0
  53. package/dist/types/services/config/types.d.ts +15 -2
  54. package/dist/types/services/core/Utils.d.ts +2 -0
  55. package/dist/types/services/task/types.d.ts +2 -0
  56. package/dist/types/services/task/voice/Voice.d.ts +15 -2
  57. package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
  58. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +19 -6
  59. package/dist/types/services/wxAppDiagnosticLogging.d.ts +12 -0
  60. package/dist/types/types.d.ts +56 -1
  61. package/dist/types.js +39 -2
  62. package/dist/types.js.map +1 -1
  63. package/dist/webex.js +9 -1
  64. package/dist/webex.js.map +1 -1
  65. package/package.json +9 -9
  66. package/src/cc.ts +294 -25
  67. package/src/constants.ts +4 -0
  68. package/src/index.ts +11 -0
  69. package/src/metrics/ai-docs/metrics-spec.md +61 -20
  70. package/src/metrics/behavioral-events.ts +6 -0
  71. package/src/metrics/constants.ts +6 -0
  72. package/src/services/AnswerCallOnWebexService.ts +4 -4
  73. package/src/services/ApiAiAssistant.ts +153 -1
  74. package/src/services/WebexCrossClientService.ts +7 -2
  75. package/src/services/WxAppTelephonyMercurySync.ts +3 -3
  76. package/src/services/ai-docs/services-spec.md +11 -3
  77. package/src/services/config/Util.ts +6 -0
  78. package/src/services/config/ai-docs/config-spec.md +14 -3
  79. package/src/services/config/constants.ts +12 -0
  80. package/src/services/config/index.ts +65 -0
  81. package/src/services/config/types.ts +13 -2
  82. package/src/services/core/Utils.ts +4 -0
  83. package/src/services/task/TaskFactory.ts +1 -0
  84. package/src/services/task/TaskManager.ts +85 -87
  85. package/src/services/task/ai-docs/task-spec.md +7 -3
  86. package/src/services/task/types.ts +16 -0
  87. package/src/services/task/voice/Voice.ts +74 -52
  88. package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
  89. package/src/services/task/voice/wxAppVoiceMethods.ts +83 -24
  90. package/src/services/wxAppDiagnosticLogging.ts +135 -23
  91. package/src/types.ts +65 -0
  92. package/src/webex.js +8 -0
  93. package/test/unit/spec/cc.ts +469 -19
  94. package/test/unit/spec/services/ApiAiAssistant.ts +134 -2
  95. package/test/unit/spec/services/WebexCrossClientService.ts +45 -3
  96. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +1 -1
  97. package/test/unit/spec/services/config/Util.ts +44 -0
  98. package/test/unit/spec/services/config/index.ts +68 -0
  99. package/test/unit/spec/services/task/TaskManager.ts +214 -3
  100. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +33 -0
  101. package/test/unit/spec/services/task/voice/Voice.ts +10 -51
  102. package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
  103. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +121 -5
  104. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +44 -8
  105. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  106. package/umd/contact-center.min.js +2 -2
  107. package/umd/contact-center.min.js.map +1 -1
@@ -690,6 +690,7 @@ class TaskManager extends _events.default {
690
690
  const interactionId = message.data.interactionId;
691
691
  const reservationInteractionId = message.data.reservationInteractionId;
692
692
  let task = this.taskCollection[interactionId];
693
+ let ownerChangeResolvedByRelatedInteraction = false;
693
694
 
694
695
  // When a campaign preview contact is accepted, the assigned event may arrive
695
696
  // with a new interactionId while the task is stored under the original
@@ -703,7 +704,35 @@ class TaskManager extends _events.default {
703
704
  }
704
705
  }
705
706
  if (!task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
706
- task = this.findTaskForContactOwnerChange(message.data);
707
+ const incomingShowsCurrentAgentPromotion = Boolean(this.agentId && message.data.interaction?.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(message.data, this.agentId));
708
+ task = this.findUniqueTaskByRelatedInteraction(message.data, candidate => {
709
+ const currentAgentId = this.agentId || candidate.data?.agentId;
710
+ return Boolean(currentAgentId && (TaskManager.isParticipantActiveOnMainInteraction(candidate.data, currentAgentId) || incomingShowsCurrentAgentPromotion));
711
+ }, {
712
+ includeCollectionKeys: true,
713
+ includeMainCallMediaKeys: true
714
+ });
715
+ ownerChangeResolvedByRelatedInteraction = Boolean(task);
716
+ }
717
+ let ownerChangeMainInteractionId;
718
+ if (task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
719
+ const taskMainInteractionId = task.data?.interaction?.mainInteractionId;
720
+ const payloadMainInteractionId = message.data.interaction?.mainInteractionId;
721
+ const hasConflictingMainInteractionIds = Boolean(taskMainInteractionId && payloadMainInteractionId && taskMainInteractionId !== payloadMainInteractionId);
722
+ // A promoted child task can retain stale embedded IDs. Prefer the
723
+ // authoritative incoming main-call identity before local snapshot IDs.
724
+ const stableMainInteractionId = payloadMainInteractionId || TaskManager.getMainCallMediaId(message.data.interaction) || TaskManager.getMainCallMediaId(task.data?.interaction) || taskMainInteractionId || message.data.interaction?.interactionId || task.data?.interaction?.interactionId || task.data?.interactionId;
725
+ const taskAtStableKey = stableMainInteractionId ? this.taskCollection[stableMainInteractionId] : undefined;
726
+ if (!stableMainInteractionId || hasConflictingMainInteractionIds || taskAtStableKey && taskAtStableKey !== task) {
727
+ _loggerProxy.default.warn('Unable to safely correlate owner change to the main interaction task', {
728
+ module: _constants.TASK_MANAGER_FILE,
729
+ method: 'prepareEventContext',
730
+ interactionId: payloadMainInteractionId || taskMainInteractionId || message.data.interactionId
731
+ });
732
+ task = undefined;
733
+ } else if (ownerChangeResolvedByRelatedInteraction) {
734
+ ownerChangeMainInteractionId = stableMainInteractionId;
735
+ }
707
736
  }
708
737
  if (!task && MAIN_INTERACTION_CORRELATED_EVENTS.has(eventType)) {
709
738
  task = this.findUniqueTaskByRelatedInteraction(message.data);
@@ -722,8 +751,11 @@ class TaskManager extends _events.default {
722
751
  ...message.data,
723
752
  wrapUpRequired: computeWrapUpRequired()
724
753
  } : message.data;
725
- if (task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
726
- adjustedPayload = TaskManager.preserveMainTaskIdentity(task, adjustedPayload);
754
+ if (task && ownerChangeMainInteractionId) {
755
+ adjustedPayload = {
756
+ ...adjustedPayload,
757
+ interactionId: ownerChangeMainInteractionId
758
+ };
727
759
  }
728
760
  if (task && eventType === _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE) {
729
761
  adjustedPayload = TaskManager.preserveConfirmedOwner(task, adjustedPayload);
@@ -744,47 +776,12 @@ class TaskManager extends _events.default {
744
776
  stateMachineEvent
745
777
  };
746
778
  }
747
-
748
- /**
749
- * ContactOwnerChanged can identify the promoted agent's child interaction while
750
- * the surviving task is stored under the main interaction. Accept one related
751
- * task when its existing snapshot or the authoritative incoming promotion proves
752
- * current-agent main-call membership, while excluding non-promoted and ambiguous matches.
753
- */
754
- findTaskForContactOwnerChange(payload) {
755
- const relatedCandidates = this.findRelatedTasks(payload, {
756
- includeCollectionKeys: true,
757
- includeMainCallMediaKeys: true
758
- });
759
- const incomingShowsCurrentAgentPromotion = this.isCurrentAgentPromotedOnIncomingMainCall(payload);
760
- const eligibleCandidates = relatedCandidates.filter(candidate => this.isCurrentAgentActiveOnMainInteraction(candidate.data) || incomingShowsCurrentAgentPromotion);
761
- if (eligibleCandidates.length === 1) {
762
- return eligibleCandidates[0];
763
- }
764
- if (eligibleCandidates.length > 1) {
765
- _loggerProxy.default.warn('Unable to correlate task event to a unique main interaction task', {
766
- module: _constants.TASK_MANAGER_FILE,
767
- method: 'findTaskForContactOwnerChange',
768
- interactionId: payload.interaction?.mainInteractionId || payload.interaction?.parentInteractionId || payload.interaction?.callProcessingDetails?.parentInteractionId || payload.interactionId
769
- });
770
- return undefined;
771
- }
772
- return undefined;
773
- }
774
- isCurrentAgentActiveOnMainInteraction(taskData) {
775
- const currentAgentId = this.agentId || taskData?.agentId;
776
- if (!currentAgentId) return false;
777
- return TaskManager.isParticipantActiveOnMainInteraction(taskData, currentAgentId);
778
- }
779
779
  static isParticipantActiveOnMainInteraction(taskData, participantId) {
780
780
  const interaction = taskData?.interaction;
781
781
  const participant = interaction?.participants?.[participantId];
782
782
  if (!participant || participant.hasLeft === true) return false;
783
783
  return Object.values(interaction?.media ?? {}).some(media => media?.mType === _constants3.MEDIA_TYPE_MAIN_CALL && media.participants?.includes(participantId));
784
784
  }
785
- isCurrentAgentPromotedOnIncomingMainCall(payload) {
786
- return Boolean(this.agentId && payload.interaction?.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(payload, this.agentId));
787
- }
788
785
  static getMainCallMediaId(interaction) {
789
786
  return Object.entries(interaction?.media ?? {}).find(([, media]) => media?.mType === _constants3.MEDIA_TYPE_MAIN_CALL)?.[0];
790
787
  }
@@ -797,22 +794,6 @@ class TaskManager extends _events.default {
797
794
  return Boolean(this.agentId && stableInteractionId && interaction?.mediaType === _types.MEDIA_CHANNEL.TELEPHONY && interaction.isTerminated !== true && interaction.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(payload, this.agentId));
798
795
  }
799
796
 
800
- /** Keep a related owner-change event from re-keying the surviving main task to a child ID. */
801
- static preserveMainTaskIdentity(task, payload) {
802
- const currentMainInteractionId = task.data?.interaction?.mainInteractionId;
803
- const payloadMainInteractionId = payload.interaction?.mainInteractionId;
804
- const currentMainMediaId = TaskManager.getMainCallMediaId(task.data?.interaction);
805
- const payloadMainMediaId = TaskManager.getMainCallMediaId(payload.interaction);
806
- const stableInteractionId = currentMainInteractionId || payloadMainInteractionId || currentMainMediaId || payloadMainMediaId || payload.interaction?.interactionId || task.data?.interaction?.interactionId || task.data?.interactionId;
807
- if (!stableInteractionId || stableInteractionId === payload.interactionId) {
808
- return payload;
809
- }
810
- return {
811
- ...payload,
812
- interactionId: stableInteractionId
813
- };
814
- }
815
-
816
797
  /**
817
798
  * ParticipantLeftConference may arrive after an owner update while still carrying
818
799
  * the departed owner. Keep the confirmed owner only when the new roster proves that
@@ -845,8 +826,8 @@ class TaskManager extends _events.default {
845
826
  * keys are handled before this fallback. Multiple aliases of the same task are
846
827
  * treated as one candidate; genuinely ambiguous matches are intentionally ignored.
847
828
  */
848
- findUniqueTaskByRelatedInteraction(payload, candidateFilter = () => true) {
849
- const candidates = this.findRelatedTasks(payload).filter(candidateFilter);
829
+ findUniqueTaskByRelatedInteraction(payload, candidateFilter = () => true, options = {}) {
830
+ const candidates = this.findRelatedTasks(payload, options).filter(candidateFilter);
850
831
  if (candidates.length === 1) {
851
832
  return candidates[0];
852
833
  }
@@ -864,12 +845,12 @@ class TaskManager extends _events.default {
864
845
  includeCollectionKeys = false,
865
846
  includeMainCallMediaKeys = false
866
847
  } = options;
867
- const payloadInteractionIds = new Set([payload.interactionId, payload.interaction?.interactionId, payload.interaction?.mainInteractionId, payload.interaction?.parentInteractionId, payload.interaction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(payload.interaction)] : [])].filter(interactionId => Boolean(interactionId)));
848
+ const payloadInteractionIds = new Set([payload.interactionId, payload.interaction?.interactionId, payload.interaction?.mainInteractionId, payload.interaction?.parentInteractionId, payload.interaction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(payload.interaction)] : [])].filter(relatedInteractionId => Boolean(relatedInteractionId)));
868
849
  if (payloadInteractionIds.size === 0) return [];
869
850
  return [...new Set(Object.entries(this.taskCollection).filter(([taskId, candidate]) => {
870
851
  const taskInteraction = candidate?.data?.interaction;
871
852
  const candidateInteractionIds = [...(includeCollectionKeys ? [taskId] : []), candidate?.data?.interactionId, taskInteraction?.interactionId, taskInteraction?.mainInteractionId, taskInteraction?.parentInteractionId, taskInteraction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(taskInteraction)] : [])];
872
- return candidateInteractionIds.some(interactionId => Boolean(interactionId) && payloadInteractionIds.has(interactionId));
853
+ return candidateInteractionIds.some(relatedInteractionId => Boolean(relatedInteractionId) && payloadInteractionIds.has(relatedInteractionId));
873
854
  }).map(([, candidate]) => candidate))];
874
855
  }
875
856
 
@@ -1180,6 +1161,7 @@ class TaskManager extends _events.default {
1180
1161
  });
1181
1162
  }
1182
1163
  removeTaskFromCollection(task) {
1164
+ task.disposeWxAppOfferObservability?.();
1183
1165
  if (typeof task.cancelAutoWrapupTimer === 'function') {
1184
1166
  task.cancelAutoWrapupTimer();
1185
1167
  }
@@ -1353,6 +1335,16 @@ class TaskManager extends _events.default {
1353
1335
  TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
1354
1336
  });
1355
1337
  }
1338
+
1339
+ /**
1340
+ * Re-run OFFERED wxApp offer diagnostics for collected voice tasks.
1341
+ * Used after usersub publish so pre-publish offers can schedule mismatch grace.
1342
+ */
1343
+ refreshWxAppOfferObservabilityForAllTasks() {
1344
+ Object.values(this.taskCollection).forEach(task => {
1345
+ task.refreshWxAppOfferObservability?.();
1346
+ });
1347
+ }
1356
1348
  static getTaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
1357
1349
  if (!TaskManager.taskManager) {
1358
1350
  TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager);