@webex/contact-center 3.11.0-next.3 → 3.11.0-next.5

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/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  "typedoc": "^0.25.0",
81
81
  "typescript": "4.9.5"
82
82
  },
83
- "version": "3.11.0-next.3"
83
+ "version": "3.11.0-next.5"
84
84
  }
@@ -204,6 +204,7 @@ function parseAgentConfigs(profileData: {
204
204
  siteId: userData.siteId,
205
205
  enterpriseId: orgInfoData.tenantId,
206
206
  tenantTimezone: orgInfoData.timezone,
207
+ environment: orgInfoData.environment,
207
208
  privacyShieldVisible: tenantData.privacyShieldVisible,
208
209
  organizationIdleCodes: [], // TODO: for supervisor, getOrgFilteredIdleCodes(auxCodes, false),
209
210
  idleCodesAccess: agentProfileData.accessIdleCode as 'ALL' | 'SPECIFIC',
@@ -580,6 +580,8 @@ export type OrgInfo = {
580
580
  tenantId: string;
581
581
  /** Organization timezone */
582
582
  timezone: string;
583
+ /** Current environment (e.g., 'produs1', 'intgus1') */
584
+ environment: string;
583
585
  };
584
586
 
585
587
  /**
@@ -1031,6 +1033,8 @@ export type Profile = {
1031
1033
  isAnalyzerEnabled?: boolean;
1032
1034
  /** Tenant timezone */
1033
1035
  tenantTimezone?: string;
1036
+ /** Current environment (e.g., 'produs1', 'intgus1') */
1037
+ environment?: string;
1034
1038
  /** Available voice login options */
1035
1039
  loginVoiceOptions?: LoginOption[];
1036
1040
  /** Current login device type */
@@ -660,13 +660,16 @@ export default class TaskManager extends EventEmitter {
660
660
 
661
661
  const isOutdial = task.data.interaction.outboundType === 'OUTDIAL';
662
662
  const isNew = task.data.interaction.state === 'new';
663
- const needsWrapUp = task.data.agentsPendingWrapUp?.length > 0;
663
+ const needsWrapUp = task.data.agentsPendingWrapUp?.includes(this.agentId) ?? false;
664
664
 
665
665
  // For OUTDIAL: only remove if NOT terminated (user-declined, no wrap-up follows)
666
- // If terminated, keep task for wrap-up flow (CONTACT_ENDED → AGENT_WRAPUP)
667
666
  // For non-OUTDIAL: remove if state is 'new'
668
667
  // Always remove if secondary EpDn agent
669
- if ((isNew && !(isOutdial && needsWrapUp)) || isSecondaryEpDnAgent(task.data.interaction)) {
668
+ if (
669
+ (isNew && !(isOutdial && needsWrapUp)) ||
670
+ isSecondaryEpDnAgent(task.data.interaction) ||
671
+ (!needsWrapUp && isOutdial) // For outdial tasks, needs wrap-up is false and state is "WRAPUP". We need to just remove the task.
672
+ ) {
670
673
  this.removeTaskFromCollection(task);
671
674
  }
672
675
  }
@@ -718,6 +718,7 @@ describe('AgentConfigService', () => {
718
718
  const mockOrgInfo = {
719
719
  tenantId: 'tenant123',
720
720
  timezone: 'GMT',
721
+ environment: 'produs1',
721
722
  };
722
723
 
723
724
  const mockOrgSettings = {
@@ -857,6 +858,7 @@ describe('AgentConfigService', () => {
857
858
  const mockOrgInfo = {
858
859
  tenantId: 'tenant123',
859
860
  timezone: 'GMT',
861
+ environment: 'produs1',
860
862
  };
861
863
 
862
864
  const mockOrgSettings = {
@@ -896,6 +896,9 @@ describe('TaskManager', () => {
896
896
  });
897
897
 
898
898
  it('should NOT remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp exists', () => {
899
+ const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
900
+ taskManager.setAgentId(agentId);
901
+
899
902
  const task = taskManager.getTask(taskId);
900
903
  task.updateTaskData = jest.fn().mockImplementation((newData) => {
901
904
  task.data = {
@@ -907,7 +910,7 @@ describe('TaskManager', () => {
907
910
  state: 'new',
908
911
  mediaType: 'telephony',
909
912
  },
910
- agentsPendingWrapUp: ['agent-123'],
913
+ agentsPendingWrapUp: [agentId],
911
914
  };
912
915
  return task;
913
916
  });
@@ -923,7 +926,7 @@ describe('TaskManager', () => {
923
926
  state: 'new',
924
927
  mediaType: 'telephony',
925
928
  },
926
- agentsPendingWrapUp: ['agent-123'],
929
+ agentsPendingWrapUp: [agentId],
927
930
  },
928
931
  };
929
932
 
@@ -2530,6 +2533,318 @@ describe('TaskManager', () => {
2530
2533
  });
2531
2534
  });
2532
2535
 
2536
+ describe('handleTaskCleanup - stage changes', () => {
2537
+ const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
2538
+
2539
+ beforeEach(() => {
2540
+ taskManager.setAgentId(agentId);
2541
+ });
2542
+
2543
+ it('should remove OUTDIAL task on CONTACT_ENDED when current agent is NOT in agentsPendingWrapUp', () => {
2544
+ const task = taskManager.getTask(taskId);
2545
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2546
+ task.data = {
2547
+ ...task.data,
2548
+ ...newData,
2549
+ interaction: {
2550
+ ...task.data.interaction,
2551
+ outboundType: 'OUTDIAL',
2552
+ state: 'new',
2553
+ mediaType: 'telephony',
2554
+ },
2555
+ agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
2556
+ };
2557
+ return task;
2558
+ });
2559
+ task.unregisterWebCallListeners = jest.fn();
2560
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2561
+
2562
+ const payload = {
2563
+ data: {
2564
+ type: CC_EVENTS.CONTACT_ENDED,
2565
+ interactionId: taskId,
2566
+ interaction: {
2567
+ outboundType: 'OUTDIAL',
2568
+ state: 'new',
2569
+ mediaType: 'telephony',
2570
+ },
2571
+ agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
2572
+ },
2573
+ };
2574
+
2575
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2576
+
2577
+ expect(removeTaskSpy).toHaveBeenCalled();
2578
+ expect(taskManager.getTask(taskId)).toBeUndefined();
2579
+ });
2580
+
2581
+ it('should NOT remove OUTDIAL task on CONTACT_ENDED when current agent IS in agentsPendingWrapUp', () => {
2582
+ const task = taskManager.getTask(taskId);
2583
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2584
+ task.data = {
2585
+ ...task.data,
2586
+ ...newData,
2587
+ interaction: {
2588
+ ...task.data.interaction,
2589
+ outboundType: 'OUTDIAL',
2590
+ state: 'new',
2591
+ mediaType: 'telephony',
2592
+ },
2593
+ agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
2594
+ };
2595
+ return task;
2596
+ });
2597
+ task.unregisterWebCallListeners = jest.fn();
2598
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2599
+
2600
+ const payload = {
2601
+ data: {
2602
+ type: CC_EVENTS.CONTACT_ENDED,
2603
+ interactionId: taskId,
2604
+ interaction: {
2605
+ outboundType: 'OUTDIAL',
2606
+ state: 'new',
2607
+ mediaType: 'telephony',
2608
+ },
2609
+ agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
2610
+ },
2611
+ };
2612
+
2613
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2614
+
2615
+ expect(removeTaskSpy).not.toHaveBeenCalled();
2616
+ expect(taskManager.getTask(taskId)).toBeDefined();
2617
+ });
2618
+
2619
+ it('should remove OUTDIAL task when needsWrapUp is false and task is outdial', () => {
2620
+ const task = taskManager.getTask(taskId);
2621
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2622
+ task.data = {
2623
+ ...task.data,
2624
+ ...newData,
2625
+ interaction: {
2626
+ ...task.data.interaction,
2627
+ outboundType: 'OUTDIAL',
2628
+ state: 'WRAPUP', // Not 'new' state
2629
+ mediaType: 'telephony',
2630
+ },
2631
+ agentsPendingWrapUp: [], // No agents pending wrap-up
2632
+ };
2633
+ return task;
2634
+ });
2635
+ task.unregisterWebCallListeners = jest.fn();
2636
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2637
+
2638
+ const payload = {
2639
+ data: {
2640
+ type: CC_EVENTS.CONTACT_ENDED,
2641
+ interactionId: taskId,
2642
+ interaction: {
2643
+ outboundType: 'OUTDIAL',
2644
+ state: 'WRAPUP', // Not 'new' state
2645
+ mediaType: 'telephony',
2646
+ },
2647
+ agentsPendingWrapUp: [], // No agents pending wrap-up
2648
+ },
2649
+ };
2650
+
2651
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2652
+
2653
+ expect(removeTaskSpy).toHaveBeenCalled();
2654
+ expect(taskManager.getTask(taskId)).toBeUndefined();
2655
+ });
2656
+
2657
+ it('should remove OUTDIAL task when needsWrapUp is false (agentsPendingWrapUp is undefined)', () => {
2658
+ const task = taskManager.getTask(taskId);
2659
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2660
+ task.data = {
2661
+ ...task.data,
2662
+ ...newData,
2663
+ interaction: {
2664
+ ...task.data.interaction,
2665
+ outboundType: 'OUTDIAL',
2666
+ state: 'WRAPUP',
2667
+ mediaType: 'telephony',
2668
+ },
2669
+ agentsPendingWrapUp: undefined, // No agentsPendingWrapUp field
2670
+ };
2671
+ return task;
2672
+ });
2673
+ task.unregisterWebCallListeners = jest.fn();
2674
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2675
+
2676
+ const payload = {
2677
+ data: {
2678
+ type: CC_EVENTS.CONTACT_ENDED,
2679
+ interactionId: taskId,
2680
+ interaction: {
2681
+ outboundType: 'OUTDIAL',
2682
+ state: 'WRAPUP',
2683
+ mediaType: 'telephony',
2684
+ },
2685
+ // agentsPendingWrapUp not included
2686
+ },
2687
+ };
2688
+
2689
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2690
+
2691
+ expect(removeTaskSpy).toHaveBeenCalled();
2692
+ expect(taskManager.getTask(taskId)).toBeUndefined();
2693
+ });
2694
+
2695
+ it('should NOT remove OUTDIAL task when needsWrapUp is true (current agent in agentsPendingWrapUp) even if state is WRAPUP', () => {
2696
+ const task = taskManager.getTask(taskId);
2697
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2698
+ task.data = {
2699
+ ...task.data,
2700
+ ...newData,
2701
+ interaction: {
2702
+ ...task.data.interaction,
2703
+ outboundType: 'OUTDIAL',
2704
+ state: 'WRAPUP',
2705
+ mediaType: 'telephony',
2706
+ },
2707
+ agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
2708
+ };
2709
+ return task;
2710
+ });
2711
+ task.unregisterWebCallListeners = jest.fn();
2712
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2713
+
2714
+ const payload = {
2715
+ data: {
2716
+ type: CC_EVENTS.CONTACT_ENDED,
2717
+ interactionId: taskId,
2718
+ interaction: {
2719
+ outboundType: 'OUTDIAL',
2720
+ state: 'WRAPUP',
2721
+ mediaType: 'telephony',
2722
+ },
2723
+ agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
2724
+ },
2725
+ };
2726
+
2727
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2728
+
2729
+ expect(removeTaskSpy).not.toHaveBeenCalled();
2730
+ expect(taskManager.getTask(taskId)).toBeDefined();
2731
+ });
2732
+
2733
+ it('should remove non-OUTDIAL task when state is new regardless of agentsPendingWrapUp', () => {
2734
+ const task = taskManager.getTask(taskId);
2735
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2736
+ task.data = {
2737
+ ...task.data,
2738
+ ...newData,
2739
+ interaction: {
2740
+ ...task.data.interaction,
2741
+ outboundType: 'PREVIEW', // Not OUTDIAL
2742
+ state: 'new',
2743
+ mediaType: 'telephony',
2744
+ },
2745
+ agentsPendingWrapUp: [agentId],
2746
+ };
2747
+ return task;
2748
+ });
2749
+ task.unregisterWebCallListeners = jest.fn();
2750
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2751
+
2752
+ const payload = {
2753
+ data: {
2754
+ type: CC_EVENTS.CONTACT_ENDED,
2755
+ interactionId: taskId,
2756
+ interaction: {
2757
+ outboundType: 'PREVIEW',
2758
+ state: 'new',
2759
+ mediaType: 'telephony',
2760
+ },
2761
+ agentsPendingWrapUp: [agentId],
2762
+ },
2763
+ };
2764
+
2765
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2766
+
2767
+ expect(removeTaskSpy).toHaveBeenCalled();
2768
+ expect(taskManager.getTask(taskId)).toBeUndefined();
2769
+ });
2770
+
2771
+ it('should handle agentsPendingWrapUp with multiple agents correctly - remove if current agent not in list', () => {
2772
+ const task = taskManager.getTask(taskId);
2773
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2774
+ task.data = {
2775
+ ...task.data,
2776
+ ...newData,
2777
+ interaction: {
2778
+ ...task.data.interaction,
2779
+ outboundType: 'OUTDIAL',
2780
+ state: 'new',
2781
+ mediaType: 'telephony',
2782
+ },
2783
+ agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'], // Current agent not in the list
2784
+ };
2785
+ return task;
2786
+ });
2787
+ task.unregisterWebCallListeners = jest.fn();
2788
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2789
+
2790
+ const payload = {
2791
+ data: {
2792
+ type: CC_EVENTS.CONTACT_ENDED,
2793
+ interactionId: taskId,
2794
+ interaction: {
2795
+ outboundType: 'OUTDIAL',
2796
+ state: 'new',
2797
+ mediaType: 'telephony',
2798
+ },
2799
+ agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'],
2800
+ },
2801
+ };
2802
+
2803
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2804
+
2805
+ expect(removeTaskSpy).toHaveBeenCalled();
2806
+ expect(taskManager.getTask(taskId)).toBeUndefined();
2807
+ });
2808
+
2809
+ it('should handle agentsPendingWrapUp with multiple agents correctly - keep if current agent is in list', () => {
2810
+ const task = taskManager.getTask(taskId);
2811
+ task.updateTaskData = jest.fn().mockImplementation((newData) => {
2812
+ task.data = {
2813
+ ...task.data,
2814
+ ...newData,
2815
+ interaction: {
2816
+ ...task.data.interaction,
2817
+ outboundType: 'OUTDIAL',
2818
+ state: 'new',
2819
+ mediaType: 'telephony',
2820
+ },
2821
+ agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'], // Current agent IS in the list
2822
+ };
2823
+ return task;
2824
+ });
2825
+ task.unregisterWebCallListeners = jest.fn();
2826
+ const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
2827
+
2828
+ const payload = {
2829
+ data: {
2830
+ type: CC_EVENTS.CONTACT_ENDED,
2831
+ interactionId: taskId,
2832
+ interaction: {
2833
+ outboundType: 'OUTDIAL',
2834
+ state: 'new',
2835
+ mediaType: 'telephony',
2836
+ },
2837
+ agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'],
2838
+ },
2839
+ };
2840
+
2841
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
2842
+
2843
+ expect(removeTaskSpy).not.toHaveBeenCalled();
2844
+ expect(taskManager.getTask(taskId)).toBeDefined();
2845
+ });
2846
+ });
2847
+
2533
2848
  describe('CONTACT_MERGED event handling', () => {
2534
2849
  let task;
2535
2850
  let taskEmitSpy;