@webex/contact-center 3.11.0 → 3.12.0-mobius-socket.1

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 (126) hide show
  1. package/dist/cc.js +121 -28
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +5 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/index.js +7 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/metrics/behavioral-events.js +13 -0
  8. package/dist/metrics/behavioral-events.js.map +1 -1
  9. package/dist/metrics/constants.js +9 -1
  10. package/dist/metrics/constants.js.map +1 -1
  11. package/dist/services/ApiAiAssistant.js +173 -0
  12. package/dist/services/ApiAiAssistant.js.map +1 -0
  13. package/dist/services/agent/types.js.map +1 -1
  14. package/dist/services/config/Util.js +6 -2
  15. package/dist/services/config/Util.js.map +1 -1
  16. package/dist/services/config/constants.js +12 -0
  17. package/dist/services/config/constants.js.map +1 -1
  18. package/dist/services/config/index.js +41 -2
  19. package/dist/services/config/index.js.map +1 -1
  20. package/dist/services/config/types.js +19 -1
  21. package/dist/services/config/types.js.map +1 -1
  22. package/dist/services/constants.js +27 -1
  23. package/dist/services/constants.js.map +1 -1
  24. package/dist/services/core/Err.js.map +1 -1
  25. package/dist/services/core/Utils.js +28 -6
  26. package/dist/services/core/Utils.js.map +1 -1
  27. package/dist/services/core/aqm-reqs.js +92 -17
  28. package/dist/services/core/aqm-reqs.js.map +1 -1
  29. package/dist/services/core/websocket/WebSocketManager.js +20 -5
  30. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  31. package/dist/services/core/websocket/connection-service.js +3 -1
  32. package/dist/services/core/websocket/connection-service.js.map +1 -1
  33. package/dist/services/index.js +6 -0
  34. package/dist/services/index.js.map +1 -1
  35. package/dist/services/task/TaskManager.js +117 -24
  36. package/dist/services/task/TaskManager.js.map +1 -1
  37. package/dist/services/task/TaskUtils.js +16 -3
  38. package/dist/services/task/TaskUtils.js.map +1 -1
  39. package/dist/services/task/constants.js +15 -1
  40. package/dist/services/task/constants.js.map +1 -1
  41. package/dist/services/task/dialer.js +51 -0
  42. package/dist/services/task/dialer.js.map +1 -1
  43. package/dist/services/task/types.js +15 -0
  44. package/dist/services/task/types.js.map +1 -1
  45. package/dist/types/cc.d.ts +801 -0
  46. package/dist/types/config.d.ts +66 -0
  47. package/dist/types/constants.d.ts +50 -0
  48. package/dist/types/index.d.ts +184 -0
  49. package/dist/types/logger-proxy.d.ts +71 -0
  50. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  51. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  52. package/dist/types/metrics/constants.d.ts +161 -0
  53. package/dist/types/services/AddressBook.d.ts +74 -0
  54. package/dist/types/services/ApiAiAssistant.d.ts +31 -0
  55. package/dist/types/services/EntryPoint.d.ts +67 -0
  56. package/dist/types/services/Queue.d.ts +76 -0
  57. package/dist/types/services/WebCallingService.d.ts +1 -0
  58. package/dist/types/services/agent/index.d.ts +46 -0
  59. package/dist/types/services/agent/types.d.ts +413 -0
  60. package/dist/types/services/config/Util.d.ts +20 -0
  61. package/dist/types/services/config/constants.d.ts +249 -0
  62. package/dist/types/services/config/index.d.ts +177 -0
  63. package/dist/types/services/config/types.d.ts +1207 -0
  64. package/dist/types/services/constants.d.ts +110 -0
  65. package/dist/types/services/core/Err.d.ts +121 -0
  66. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  67. package/dist/types/services/core/Utils.d.ts +101 -0
  68. package/dist/types/services/core/WebexRequest.d.ts +22 -0
  69. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  70. package/dist/types/services/core/constants.d.ts +99 -0
  71. package/dist/types/services/core/types.d.ts +47 -0
  72. package/dist/types/services/core/websocket/WebSocketManager.d.ts +35 -0
  73. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  74. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  75. package/dist/types/services/core/websocket/types.d.ts +37 -0
  76. package/dist/types/services/index.d.ts +54 -0
  77. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  78. package/dist/types/services/task/TaskManager.d.ts +1 -0
  79. package/dist/types/services/task/TaskUtils.d.ts +92 -0
  80. package/dist/types/services/task/constants.d.ts +84 -0
  81. package/dist/types/services/task/contact.d.ts +69 -0
  82. package/dist/types/services/task/dialer.d.ts +43 -0
  83. package/dist/types/services/task/index.d.ts +650 -0
  84. package/dist/types/services/task/types.d.ts +1319 -0
  85. package/dist/types/types.d.ts +643 -0
  86. package/dist/types/utils/PageCache.d.ts +173 -0
  87. package/dist/types/webex-config.d.ts +53 -0
  88. package/dist/types/webex.d.ts +7 -0
  89. package/dist/types.js +14 -1
  90. package/dist/types.js.map +1 -1
  91. package/dist/webex.js +1 -1
  92. package/package.json +9 -9
  93. package/src/cc.ts +157 -30
  94. package/src/constants.ts +4 -0
  95. package/src/index.ts +1 -0
  96. package/src/metrics/behavioral-events.ts +14 -0
  97. package/src/metrics/constants.ts +11 -0
  98. package/src/services/ApiAiAssistant.ts +217 -0
  99. package/src/services/agent/types.ts +1 -1
  100. package/src/services/config/Util.ts +8 -0
  101. package/src/services/config/constants.ts +12 -0
  102. package/src/services/config/index.ts +45 -1
  103. package/src/services/config/types.ts +67 -0
  104. package/src/services/constants.ts +29 -0
  105. package/src/services/core/Err.ts +1 -0
  106. package/src/services/core/Utils.ts +32 -5
  107. package/src/services/core/aqm-reqs.ts +100 -22
  108. package/src/services/core/websocket/WebSocketManager.ts +21 -6
  109. package/src/services/core/websocket/connection-service.ts +5 -1
  110. package/src/services/index.ts +4 -0
  111. package/src/services/task/TaskManager.ts +174 -27
  112. package/src/services/task/TaskUtils.ts +12 -0
  113. package/src/services/task/constants.ts +16 -0
  114. package/src/services/task/dialer.ts +56 -1
  115. package/src/services/task/types.ts +24 -0
  116. package/src/types.ts +40 -1
  117. package/test/unit/spec/cc.ts +163 -23
  118. package/test/unit/spec/services/ApiAiAssistant.ts +115 -0
  119. package/test/unit/spec/services/config/index.ts +56 -0
  120. package/test/unit/spec/services/core/Utils.ts +63 -1
  121. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +82 -12
  122. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  123. package/test/unit/spec/services/task/TaskManager.ts +1119 -251
  124. package/test/unit/spec/services/task/dialer.ts +198 -112
  125. package/umd/contact-center.min.js +2 -2
  126. package/umd/contact-center.min.js.map +1 -1
@@ -5,9 +5,9 @@ import routingContact from './contact';
5
5
  import WebCallingService from '../WebCallingService';
6
6
  import {ITask, MEDIA_CHANNEL, TASK_EVENTS, TaskData, TaskId} from './types';
7
7
  import {TASK_MANAGER_FILE} from '../../constants';
8
- import {METHODS} from './constants';
8
+ import {METHODS, TRANSCRIPT_EVENT_MAP} from './constants';
9
9
  import {CC_EVENTS, CC_TASK_EVENTS, WrapupData} from '../config/types';
10
- import {LoginOption} from '../../types';
10
+ import {AIAssistantEventName, AIAssistantEventType, LoginOption} from '../../types';
11
11
  import LoggerProxy from '../../logger-proxy';
12
12
  import Task from '.';
13
13
  import MetricsManager from '../../metrics/MetricsManager';
@@ -15,11 +15,13 @@ import {METRIC_EVENT_NAMES} from '../../metrics/constants';
15
15
  import {
16
16
  checkParticipantNotInInteraction,
17
17
  getIsConferenceInProgress,
18
+ isCampaignPreviewReservation,
18
19
  isParticipantInMainInteraction,
19
20
  isPrimary,
20
21
  isSecondaryEpDnAgent,
21
22
  shouldAutoAnswerTask,
22
23
  } from './TaskUtils';
24
+ import ApiAIAssistant from '../ApiAiAssistant';
23
25
 
24
26
  /** @internal */
25
27
  export default class TaskManager extends EventEmitter {
@@ -38,17 +40,20 @@ export default class TaskManager extends EventEmitter {
38
40
  private wrapupData: WrapupData;
39
41
  private agentId: string;
40
42
  private webRtcEnabled: boolean;
43
+ private apiAIAssistant?: ApiAIAssistant;
41
44
  /**
42
45
  * @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
43
46
  * @param webCallingService - Webrtc Service Layer
44
47
  * @param webSocketManager - Websocket Manager to maintain websocket connection and keepalives
45
48
  */
46
49
  constructor(
50
+ apiAIAssistant: ApiAIAssistant,
47
51
  contact: ReturnType<typeof routingContact>,
48
52
  webCallingService: WebCallingService,
49
53
  webSocketManager: WebSocketManager
50
54
  ) {
51
55
  super();
56
+ this.apiAIAssistant = apiAIAssistant;
52
57
  this.contact = contact;
53
58
  this.taskCollection = {};
54
59
  this.webCallingService = webCallingService;
@@ -79,9 +84,39 @@ export default class TaskManager extends EventEmitter {
79
84
  this.webRtcEnabled = webRtcEnabled;
80
85
  }
81
86
 
87
+ public handleRealtimeWebsocketEvent(event: string) {
88
+ try {
89
+ const payload = JSON.parse(event);
90
+
91
+ const eventType = payload?.type || payload?.data?.notifType;
92
+ const interactionId = payload?.data?.data?.conversationId;
93
+ if (!eventType || !interactionId) return;
94
+
95
+ const task = this.taskCollection[interactionId];
96
+ if (!task) {
97
+ LoggerProxy.info(`Realtime transcription task not found`, {
98
+ module: TASK_MANAGER_FILE,
99
+ method: METHODS.HANDLE_REAL_TIME_WEBSOCKET_EVENT,
100
+ interactionId,
101
+ });
102
+
103
+ return;
104
+ }
105
+
106
+ task.emit(eventType, payload.data);
107
+ } catch (error) {
108
+ LoggerProxy.error('Failed to parse RTD WebSocket message', {
109
+ module: TASK_MANAGER_FILE,
110
+ method: METHODS.HANDLE_REAL_TIME_WEBSOCKET_EVENT,
111
+ error,
112
+ });
113
+ }
114
+ }
115
+
82
116
  private handleIncomingWebCall = (call: ICall) => {
83
117
  const currentTask = Object.values(this.taskCollection).find(
84
- (task) => task.data.interaction.mediaType === 'telephony'
118
+ (task) =>
119
+ task.data.interaction.mediaType === 'telephony' && !isCampaignPreviewReservation(task)
85
120
  );
86
121
 
87
122
  if (currentTask) {
@@ -109,9 +144,11 @@ export default class TaskManager extends EventEmitter {
109
144
  const payload = JSON.parse(event);
110
145
  // Re-emit the task events to the task object
111
146
  let task: ITask;
112
- if (payload.data?.type) {
113
- if (Object.values(CC_TASK_EVENTS).includes(payload.data.type)) {
114
- task = this.taskCollection[payload.data.interactionId];
147
+ if (payload.data?.type || payload.type) {
148
+ if (Object.values(CC_TASK_EVENTS).includes(payload.data.type || payload.type)) {
149
+ task =
150
+ this.taskCollection[payload.data?.interactionId] ||
151
+ this.taskCollection[payload.data?.data?.conversationId];
115
152
  }
116
153
  LoggerProxy.info(`Handling task event ${payload.data?.type}`, {
117
154
  module: TASK_MANAGER_FILE,
@@ -249,8 +286,21 @@ export default class TaskManager extends EventEmitter {
249
286
  }
250
287
  break;
251
288
  case CC_EVENTS.AGENT_CONTACT_ASSIGNED:
252
- task = this.updateTaskData(task, payload.data);
253
- task.emit(TASK_EVENTS.TASK_ASSIGNED, task);
289
+ // When a campaign preview contact is accepted, the assigned event may arrive
290
+ // with a new interactionId while the task is stored under the original
291
+ // reservationInteractionId. Fall back to that key so the task is found.
292
+ if (!task && payload.data.reservationInteractionId) {
293
+ task = this.taskCollection[payload.data.reservationInteractionId];
294
+ if (task) {
295
+ // Re-key the task under the new interaction ID and remove the old entry
296
+ delete this.taskCollection[payload.data.reservationInteractionId];
297
+ this.taskCollection[payload.data.interactionId] = task;
298
+ }
299
+ }
300
+ if (task) {
301
+ task = this.updateTaskData(task, payload.data);
302
+ task.emit(TASK_EVENTS.TASK_ASSIGNED, task);
303
+ }
254
304
  break;
255
305
  case CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
256
306
  task = this.updateTaskData(task, {
@@ -262,6 +312,14 @@ export default class TaskManager extends EventEmitter {
262
312
  case CC_EVENTS.AGENT_CONTACT_OFFER_RONA:
263
313
  case CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED:
264
314
  case CC_EVENTS.AGENT_INVITE_FAILED: {
315
+ LoggerProxy.warn(
316
+ `[DEBUG-CAMPAIGN-CLEAR] Task removal triggered by ${payload.data.type}, interactionId=${payload.data.interactionId}, taskType=${task?.data?.type}`,
317
+ {
318
+ module: TASK_MANAGER_FILE,
319
+ method: METHODS.REGISTER_TASK_LISTENERS,
320
+ interactionId: payload.data.interactionId,
321
+ }
322
+ );
265
323
  task = this.updateTaskData(task, payload.data);
266
324
 
267
325
  const eventTypeToMetricMap: Record<string, keyof typeof METRIC_EVENT_NAMES> = {
@@ -287,11 +345,17 @@ export default class TaskManager extends EventEmitter {
287
345
  case CC_EVENTS.CONTACT_ENDED:
288
346
  // Update task data
289
347
  if (task) {
348
+ LoggerProxy.warn(
349
+ `[DEBUG-CAMPAIGN-CLEAR] CONTACT_ENDED, interactionId=${payload.data.interactionId}, taskType=${task?.data?.type}, state=${task?.data?.interaction?.state}`,
350
+ {
351
+ module: TASK_MANAGER_FILE,
352
+ method: METHODS.REGISTER_TASK_LISTENERS,
353
+ interactionId: payload.data.interactionId,
354
+ }
355
+ );
290
356
  task = this.updateTaskData(task, {
291
357
  ...payload.data,
292
- wrapUpRequired:
293
- payload.data.interaction.state !== 'new' &&
294
- !isSecondaryEpDnAgent(payload.data.interaction),
358
+ wrapUpRequired: payload.data.agentsPendingWrapUp?.includes(this.agentId) || false,
295
359
  });
296
360
 
297
361
  // Handle cleanup based on whether task should be deleted
@@ -300,6 +364,14 @@ export default class TaskManager extends EventEmitter {
300
364
  task?.emit(TASK_EVENTS.TASK_END, task);
301
365
  }
302
366
  break;
367
+ case CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
368
+ // CampaignContactUpdated is a non-terminal event (intermediate update during accept).
369
+ // Only update the task data — do NOT remove the task or emit TASK_END.
370
+ // Task cleanup is handled by CONTACT_ENDED or other terminal events.
371
+ if (task) {
372
+ task = this.updateTaskData(task, payload.data);
373
+ }
374
+ break;
303
375
  case CC_EVENTS.CONTACT_MERGED:
304
376
  task = this.handleContactMerged(task, payload.data);
305
377
  break;
@@ -482,12 +554,55 @@ export default class TaskManager extends EventEmitter {
482
554
  task = this.updateTaskData(task, payload.data);
483
555
  task.emit(TASK_EVENTS.TASK_POST_CALL_ACTIVITY, task);
484
556
  break;
557
+ case CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION: {
558
+ // Campaign preview contact offered to agent
559
+ // Create a task in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED
560
+ // after acceptPreviewContact) can find and update it.
561
+ // Emit TASK_CAMPAIGN_PREVIEW_RESERVATION instead of TASK_INCOMING so the call
562
+ // does not ring out to the customer before the agent explicitly accepts the preview contact.
563
+ LoggerProxy.log('Campaign preview reservation received', {
564
+ module: TASK_MANAGER_FILE,
565
+ method: METHODS.REGISTER_TASK_LISTENERS,
566
+ interactionId: payload.data.interactionId,
567
+ });
568
+
569
+ if (!this.taskCollection[payload.data.interactionId]) {
570
+ task = new Task(
571
+ this.contact,
572
+ this.webCallingService,
573
+ {
574
+ ...payload.data,
575
+ wrapUpRequired: false,
576
+ isConferenceInProgress: false,
577
+ isAutoAnswering: false,
578
+ },
579
+ this.wrapupData,
580
+ this.agentId
581
+ );
582
+ this.taskCollection[payload.data.interactionId] = task;
583
+ } else {
584
+ task = this.updateTaskData(task, payload.data);
585
+ }
586
+
587
+ this.emit(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
588
+ break;
589
+ }
590
+
485
591
  default:
486
592
  break;
487
593
  }
488
594
  if (task) {
489
595
  task.emit(payload.data.type, payload.data);
490
596
  }
597
+
598
+ const transcriptInteractionId =
599
+ payload.data?.interactionId ||
600
+ payload.data?.data?.conversationId ||
601
+ task?.data?.interactionId;
602
+
603
+ if (TRANSCRIPT_EVENT_MAP[payload.data.type] && transcriptInteractionId) {
604
+ this.requestRealTimeTranscripts(payload.data.type, transcriptInteractionId);
605
+ }
491
606
  }
492
607
  });
493
608
  }
@@ -662,23 +777,54 @@ export default class TaskManager extends EventEmitter {
662
777
 
663
778
  const isOutdial = task.data.interaction.outboundType === 'OUTDIAL';
664
779
  const isNew = task.data.interaction.state === 'new';
665
- const needsWrapUp = task.data.agentsPendingWrapUp?.length > 0;
780
+ const needsWrapUp = task.data.agentsPendingWrapUp?.includes(this.agentId) ?? false;
666
781
 
667
782
  // For OUTDIAL: only remove if NOT terminated (user-declined, no wrap-up follows)
668
- // If terminated, keep task for wrap-up flow (CONTACT_ENDED → AGENT_WRAPUP)
669
783
  // For non-OUTDIAL: remove if state is 'new'
670
784
  // Always remove if secondary EpDn agent
671
- if ((isNew && !(isOutdial && needsWrapUp)) || isSecondaryEpDnAgent(task.data.interaction)) {
785
+ if (
786
+ (isNew && !(isOutdial && needsWrapUp)) ||
787
+ isSecondaryEpDnAgent(task.data.interaction) ||
788
+ (!needsWrapUp && isOutdial) // For outdial tasks, needs wrap-up is false and state is "WRAPUP". We need to just remove the task.
789
+ ) {
672
790
  this.removeTaskFromCollection(task);
673
791
  }
674
792
  }
675
793
 
676
794
  /**
677
- * @param taskId - Unique identifier for each task
795
+ * Sends transcript start/stop event based on the CC event type.
796
+ * Fire-and-forget; errors are logged but do not interrupt event processing.
678
797
  */
679
- public getTask = (taskId: string) => {
798
+ private requestRealTimeTranscripts(eventType: string, interactionId: string): void {
799
+ const action = TRANSCRIPT_EVENT_MAP[eventType];
800
+ if (
801
+ !action ||
802
+ !this.apiAIAssistant ||
803
+ this.apiAIAssistant.aiFeature?.realtimeTranscripts?.enable === false
804
+ )
805
+ return;
806
+
807
+ this.apiAIAssistant
808
+ .sendEvent(
809
+ this.agentId,
810
+ interactionId,
811
+ AIAssistantEventType.CUSTOM_EVENT,
812
+ AIAssistantEventName.GET_TRANSCRIPTS,
813
+ action
814
+ )
815
+ .catch((error) => {
816
+ LoggerProxy.error(`Failed to send transcript ${action} event`, {
817
+ module: TASK_MANAGER_FILE,
818
+ method: 'requestRealTimeTranscripts',
819
+ interactionId,
820
+ error,
821
+ });
822
+ });
823
+ }
824
+
825
+ public getTask(taskId: TaskId): ITask {
680
826
  return this.taskCollection[taskId];
681
- };
827
+ }
682
828
 
683
829
  /**
684
830
  * @param taskId - Unique identifier for each task
@@ -687,20 +833,21 @@ export default class TaskManager extends EventEmitter {
687
833
  return this.taskCollection;
688
834
  };
689
835
 
690
- /**
691
- * @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
692
- * @param webCallingService - Webrtc Service Layer
693
- * @param webSocketManager - Websocket Manager to maintain websocket connection and keepalives
694
- */
695
- public static getTaskManager = (
836
+ public static getTaskManager(
837
+ apiAIAssistant: ApiAIAssistant,
696
838
  contact: ReturnType<typeof routingContact>,
697
839
  webCallingService: WebCallingService,
698
840
  webSocketManager: WebSocketManager
699
- ): TaskManager => {
700
- if (!this.taskManager) {
701
- this.taskManager = new TaskManager(contact, webCallingService, webSocketManager);
841
+ ): TaskManager {
842
+ if (!TaskManager.taskManager) {
843
+ TaskManager.taskManager = new TaskManager(
844
+ apiAIAssistant,
845
+ contact,
846
+ webCallingService,
847
+ webSocketManager
848
+ );
702
849
  }
703
850
 
704
851
  return this.taskManager;
705
- };
852
+ }
706
853
  }
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
2
  import {Interaction, ITask, TaskData, MEDIA_CHANNEL} from './types';
3
+ import {CC_EVENTS} from '../config/types';
3
4
  import {OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE, OUTBOUND_TYPE} from '../../constants';
4
5
  import {LoginOption} from '../../types';
5
6
 
@@ -219,3 +220,14 @@ export const shouldAutoAnswerTask = (
219
220
 
220
221
  return false;
221
222
  };
223
+
224
+ /**
225
+ * Checks if a task is a campaign preview reservation that has not yet been accepted.
226
+ * Campaign preview tasks should not trigger incoming call handling until the agent
227
+ * explicitly accepts the preview contact.
228
+ * @param task - The task to check
229
+ * @returns true if the task is a pending campaign preview reservation, false otherwise
230
+ */
231
+ export const isCampaignPreviewReservation = (task: ITask): boolean => {
232
+ return task?.data?.type === CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION;
233
+ };
@@ -4,6 +4,8 @@
4
4
  * @ignore
5
5
  */
6
6
 
7
+ import {CC_EVENTS} from '../config/types';
8
+
7
9
  export const TASK_MESSAGE_TYPE = 'RoutingMessage';
8
10
  export const TASK_API = '/v1/tasks/';
9
11
  export const HOLD = '/hold';
@@ -20,6 +22,10 @@ export const END = '/end';
20
22
  export const CONSULT_CONFERENCE = '/consult/conference';
21
23
  export const CONFERENCE_EXIT = '/conference/exit';
22
24
  export const CONFERENCE_TRANSFER = '/conference/transfer';
25
+ export const DIALER_API = '/v1/dialer';
26
+ export const CAMPAIGN_PREVIEW_ACCEPT = '/accept';
27
+ /** 80-second timeout for accepting preview contact (outbound call setup takes longer than default 20s) */
28
+ export const TIMEOUT_PREVIEW_ACCEPT = 80000;
23
29
  export const TASK_MANAGER_FILE = 'taskManager';
24
30
  export const TASK_FILE = 'task';
25
31
 
@@ -69,6 +75,7 @@ export const METHODS = {
69
75
  // TaskManager class methods
70
76
  HANDLE_INCOMING_WEB_CALL: 'handleIncomingWebCall',
71
77
  REGISTER_TASK_LISTENERS: 'registerTaskListeners',
78
+ HANDLE_REAL_TIME_WEBSOCKET_EVENT: 'handleRealtimeWebsocketEvent',
72
79
  REMOVE_TASK_FROM_COLLECTION: 'removeTaskFromCollection',
73
80
  HANDLE_TASK_CLEANUP: 'handleTaskCleanup',
74
81
  GET_TASK: 'getTask',
@@ -77,3 +84,12 @@ export const METHODS = {
77
84
  SETUP_AUTO_WRAPUP_TIMER: 'setupAutoWrapupTimer',
78
85
  CANCEL_AUTO_WRAPUP_TIMER: 'cancelAutoWrapupTimer',
79
86
  };
87
+
88
+ export const TRANSCRIPT_EVENT_MAP = {
89
+ [CC_EVENTS.AGENT_CONTACT_ASSIGNED]: 'START',
90
+ [CC_EVENTS.AGENT_CONSULTING]: 'START',
91
+ [CC_EVENTS.AGENT_CONSULT_CONFERENCED]: 'START',
92
+ [CC_EVENTS.AGENT_WRAPUP]: 'STOP',
93
+ [CC_EVENTS.AGENT_CONSULT_ENDED]: 'STOP',
94
+ [CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE]: 'STOP',
95
+ };
@@ -1,7 +1,14 @@
1
1
  import {CC_EVENTS} from '../config/types';
2
2
  import {WCC_API_GATEWAY} from '../constants';
3
+ import {HTTP_METHODS} from '../../types';
3
4
  import {createErrDetailsObject as err} from '../core/Utils';
4
- import {TASK_MESSAGE_TYPE, TASK_API} from './constants';
5
+ import {
6
+ TASK_MESSAGE_TYPE,
7
+ TASK_API,
8
+ DIALER_API,
9
+ CAMPAIGN_PREVIEW_ACCEPT,
10
+ TIMEOUT_PREVIEW_ACCEPT,
11
+ } from './constants';
5
12
  import * as Contact from './types';
6
13
  import AqmReqs from '../core/aqm-reqs';
7
14
 
@@ -48,5 +55,53 @@ export default function aqmDialer(aqm: AqmReqs) {
48
55
  errId: 'Service.aqm.dialer.startOutdial',
49
56
  },
50
57
  })),
58
+
59
+ /**
60
+ * Accepts a campaign preview contact, initiating the outbound call.
61
+ *
62
+ * @param {Object} p - Parameters object.
63
+ * @param {Contact.PreviewContactPayload} p.data - Payload containing interactionId and campaignId.
64
+ * @returns {Promise<Contact.AgentContact>} A promise that resolves with agent contact on success.
65
+ *
66
+ * Emits:
67
+ * - `CC_EVENTS.AGENT_CONTACT_ASSIGNED` on success
68
+ * - `CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED` on failure
69
+ * @ignore
70
+ */
71
+ acceptPreviewContact: aqm.req((p: {data: Contact.PreviewContactPayload}) => ({
72
+ url: `${DIALER_API}/campaign/${encodeURIComponent(p.data.campaignId)}/preview-task/${
73
+ p.data.interactionId
74
+ }${CAMPAIGN_PREVIEW_ACCEPT}`,
75
+ host: WCC_API_GATEWAY,
76
+ data: {},
77
+ method: HTTP_METHODS.POST,
78
+ timeout: TIMEOUT_PREVIEW_ACCEPT,
79
+ err,
80
+ notifSuccess: {
81
+ bind: {
82
+ type: TASK_MESSAGE_TYPE,
83
+ data: {
84
+ type: [CC_EVENTS.AGENT_CONTACT_ASSIGNED, CC_EVENTS.CONTACT_ENDED],
85
+ __typeMap: {
86
+ typeField: 'type',
87
+ conditions: {
88
+ [CC_EVENTS.AGENT_CONTACT_ASSIGNED]: {
89
+ reservationInteractionId: p.data.interactionId,
90
+ },
91
+ [CC_EVENTS.CONTACT_ENDED]: {interactionId: p.data.interactionId},
92
+ },
93
+ },
94
+ },
95
+ },
96
+ msg: {} as Contact.AgentContact,
97
+ },
98
+ notifFail: {
99
+ bind: {
100
+ type: TASK_MESSAGE_TYPE,
101
+ data: {type: CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED, campaignId: p.data.campaignId},
102
+ },
103
+ errId: 'Service.aqm.dialer.acceptPreviewContact',
104
+ },
105
+ })),
51
106
  };
52
107
  }
@@ -542,6 +542,18 @@ export enum TASK_EVENTS {
542
542
  * ```
543
543
  */
544
544
  TASK_POST_CALL_ACTIVITY = 'task:postCallActivity',
545
+
546
+ /**
547
+ * Triggered when a campaign preview contact is offered to the agent
548
+ * @example
549
+ * ```typescript
550
+ * task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, (data: AgentContact) => {
551
+ * console.log('Campaign preview contact received:', data.interactionId);
552
+ * // Handle campaign preview reservation
553
+ * });
554
+ * ```
555
+ */
556
+ TASK_CAMPAIGN_PREVIEW_RESERVATION = 'task:campaignPreviewReservation',
545
557
  }
546
558
 
547
559
  /**
@@ -1117,6 +1129,18 @@ export type DialerPayload = {
1117
1129
  origin: string;
1118
1130
  };
1119
1131
 
1132
+ /**
1133
+ * Payload for campaign preview contact operations (accept, skip, remove)
1134
+ * @public
1135
+ */
1136
+ export type PreviewContactPayload = {
1137
+ /** The interaction ID from the campaign reservation */
1138
+ interactionId: string;
1139
+ /** The campaign name (not a UUID). Available from the reservation event at
1140
+ * `task.data.interaction.callProcessingDetails.campaignId` or `task.data.campaignId`. */
1141
+ campaignId: string;
1142
+ };
1143
+
1120
1144
  /**
1121
1145
  * Data structure for cleaning up contact resources
1122
1146
  * @public
package/src/types.ts CHANGED
@@ -295,6 +295,8 @@ interface IWebexInternal {
295
295
  get: (service: string) => string;
296
296
  /** Wait for service catalog to be loaded */
297
297
  waitForCatalog: (service: string) => Promise<void>;
298
+ /** Check if current environment is INT (integration) */
299
+ isIntegrationEnvironment: () => boolean;
298
300
  /** Host catalog for service discovery */
299
301
  _hostCatalog: Record<string, ServiceHost[]>;
300
302
  /** Service URLs cache */
@@ -539,7 +541,8 @@ export type RequestBody =
539
541
  | Contact.ConsultTransferPayLoad
540
542
  | Contact.cancelCtq
541
543
  | Contact.WrapupPayLoad
542
- | Contact.DialerPayload;
544
+ | Contact.DialerPayload
545
+ | Contact.PreviewContactPayload;
543
546
 
544
547
  /**
545
548
  * Represents the options to fetch buddy agents for the logged in agent.
@@ -817,3 +820,39 @@ export type BuddyAgentsResponse = Agent.BuddyAgentsSuccess | Error;
817
820
  * function handleUpdateDeviceType(resp: UpdateDeviceTypeResponse) { ... }
818
821
  */
819
822
  export type UpdateDeviceTypeResponse = Agent.DeviceTypeUpdateSuccess | Error;
823
+
824
+ export type TranscriptAction = 'START' | 'STOP';
825
+
826
+ export const AIAssistantEventType = {
827
+ CUSTOM_EVENT: 'CUSTOM_EVENT',
828
+ CTI_EVENT: 'CTI_EVENT',
829
+ } as const;
830
+
831
+ export type AIAssistantEventType = Enum<typeof AIAssistantEventType>;
832
+
833
+ export const AIAssistantEventName = {
834
+ GET_TRANSCRIPTS: 'GET_TRANSCRIPTS',
835
+ GET_MID_CALL_SUMMARY: 'GET_MID_CALL_SUMMARY',
836
+ GET_POST_CALL_SUMMARY: 'GET_POST_CALL_SUMMARY',
837
+ MID_CALL_SUMMARY_RESPONSE: 'MID_CALL_SUMMARY_RESPONSE',
838
+ POST_CALL_SUMMARY_RESPONSE: 'POST_CALL_SUMMARY_RESPONSE',
839
+ SUGGESTED_RESPONSES_DIGITAL: 'SUGGESTED_RESPONSES_DIGITAL',
840
+ } as const;
841
+
842
+ export type AIAssistantEventName = Enum<typeof AIAssistantEventName>;
843
+
844
+ export type TranscriptMessage = {
845
+ role: string;
846
+ content: string;
847
+ messageId: string;
848
+ publishTimestamp: number;
849
+ };
850
+
851
+ export type HistoricTranscriptsResponse = {
852
+ orgId: string;
853
+ agentId: string;
854
+ conversationId: string | null;
855
+ interactionId: string;
856
+ source: string;
857
+ data: TranscriptMessage[];
858
+ };