@webex/contact-center 3.12.0-next.9 → 3.12.0-task-refactor.2

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 (200) hide show
  1. package/AGENTS.md +438 -0
  2. package/ai-docs/README.md +131 -0
  3. package/ai-docs/RULES.md +455 -0
  4. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  5. package/ai-docs/patterns/testing-patterns.md +480 -0
  6. package/ai-docs/patterns/typescript-patterns.md +365 -0
  7. package/ai-docs/templates/README.md +102 -0
  8. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  9. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  10. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  11. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  12. package/ai-docs/templates/new-method/00-master.md +80 -0
  13. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  14. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  15. package/ai-docs/templates/new-method/03-tests.md +201 -0
  16. package/ai-docs/templates/new-method/04-validation.md +141 -0
  17. package/ai-docs/templates/new-service/00-master.md +109 -0
  18. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  19. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  20. package/ai-docs/templates/new-service/03-integration.md +178 -0
  21. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  22. package/ai-docs/templates/new-service/05-validation.md +145 -0
  23. package/dist/cc.js +65 -123
  24. package/dist/cc.js.map +1 -1
  25. package/dist/constants.js +13 -2
  26. package/dist/constants.js.map +1 -1
  27. package/dist/index.js +13 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/metrics/behavioral-events.js +26 -13
  30. package/dist/metrics/behavioral-events.js.map +1 -1
  31. package/dist/metrics/constants.js +7 -6
  32. package/dist/metrics/constants.js.map +1 -1
  33. package/dist/services/ApiAiAssistant.js +0 -3
  34. package/dist/services/ApiAiAssistant.js.map +1 -1
  35. package/dist/services/config/Util.js +2 -3
  36. package/dist/services/config/Util.js.map +1 -1
  37. package/dist/services/config/types.js +16 -14
  38. package/dist/services/config/types.js.map +1 -1
  39. package/dist/services/constants.js +0 -1
  40. package/dist/services/constants.js.map +1 -1
  41. package/dist/services/core/Err.js.map +1 -1
  42. package/dist/services/core/Utils.js +79 -55
  43. package/dist/services/core/Utils.js.map +1 -1
  44. package/dist/services/core/aqm-reqs.js +17 -92
  45. package/dist/services/core/aqm-reqs.js.map +1 -1
  46. package/dist/services/core/websocket/WebSocketManager.js +5 -25
  47. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  48. package/dist/services/core/websocket/types.js.map +1 -1
  49. package/dist/services/index.js +1 -2
  50. package/dist/services/index.js.map +1 -1
  51. package/dist/services/task/Task.js +644 -0
  52. package/dist/services/task/Task.js.map +1 -0
  53. package/dist/services/task/TaskFactory.js +45 -0
  54. package/dist/services/task/TaskFactory.js.map +1 -0
  55. package/dist/services/task/TaskManager.js +570 -535
  56. package/dist/services/task/TaskManager.js.map +1 -1
  57. package/dist/services/task/TaskUtils.js +132 -28
  58. package/dist/services/task/TaskUtils.js.map +1 -1
  59. package/dist/services/task/constants.js +7 -6
  60. package/dist/services/task/constants.js.map +1 -1
  61. package/dist/services/task/dialer.js +0 -51
  62. package/dist/services/task/dialer.js.map +1 -1
  63. package/dist/services/task/digital/Digital.js +77 -0
  64. package/dist/services/task/digital/Digital.js.map +1 -0
  65. package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
  66. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  67. package/dist/services/task/state-machine/actions.js +372 -0
  68. package/dist/services/task/state-machine/actions.js.map +1 -0
  69. package/dist/services/task/state-machine/constants.js +139 -0
  70. package/dist/services/task/state-machine/constants.js.map +1 -0
  71. package/dist/services/task/state-machine/guards.js +263 -0
  72. package/dist/services/task/state-machine/guards.js.map +1 -0
  73. package/dist/services/task/state-machine/index.js +53 -0
  74. package/dist/services/task/state-machine/index.js.map +1 -0
  75. package/dist/services/task/state-machine/types.js +54 -0
  76. package/dist/services/task/state-machine/types.js.map +1 -0
  77. package/dist/services/task/state-machine/uiControlsComputer.js +377 -0
  78. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  79. package/dist/services/task/taskDataNormalizer.js +99 -0
  80. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  81. package/dist/services/task/types.js +157 -18
  82. package/dist/services/task/types.js.map +1 -1
  83. package/dist/services/task/voice/Voice.js +1031 -0
  84. package/dist/services/task/voice/Voice.js.map +1 -0
  85. package/dist/services/task/voice/WebRTC.js +149 -0
  86. package/dist/services/task/voice/WebRTC.js.map +1 -0
  87. package/dist/types/cc.d.ts +4 -33
  88. package/dist/types/constants.d.ts +13 -2
  89. package/dist/types/index.d.ts +11 -5
  90. package/dist/types/metrics/constants.d.ts +5 -3
  91. package/dist/types/services/ApiAiAssistant.d.ts +1 -1
  92. package/dist/types/services/config/types.d.ts +97 -25
  93. package/dist/types/services/core/Err.d.ts +0 -2
  94. package/dist/types/services/core/Utils.d.ts +25 -23
  95. package/dist/types/services/core/aqm-reqs.d.ts +0 -49
  96. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
  97. package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
  98. package/dist/types/services/core/websocket/types.d.ts +1 -1
  99. package/dist/types/services/index.d.ts +1 -1
  100. package/dist/types/services/task/Task.d.ts +146 -0
  101. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  102. package/dist/types/services/task/TaskUtils.d.ts +39 -8
  103. package/dist/types/services/task/constants.d.ts +5 -4
  104. package/dist/types/services/task/dialer.d.ts +0 -15
  105. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  106. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
  107. package/dist/types/services/task/state-machine/actions.d.ts +8 -0
  108. package/dist/types/services/task/state-machine/constants.d.ts +91 -0
  109. package/dist/types/services/task/state-machine/guards.d.ts +78 -0
  110. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  111. package/dist/types/services/task/state-machine/types.d.ts +256 -0
  112. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  113. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  114. package/dist/types/services/task/types.d.ts +539 -88
  115. package/dist/types/services/task/voice/Voice.d.ts +183 -0
  116. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  117. package/dist/types/types.d.ts +68 -0
  118. package/dist/types/webex.d.ts +1 -0
  119. package/dist/types.js +70 -0
  120. package/dist/types.js.map +1 -1
  121. package/dist/webex.js +14 -2
  122. package/dist/webex.js.map +1 -1
  123. package/package.json +14 -11
  124. package/src/cc.ts +91 -177
  125. package/src/constants.ts +13 -2
  126. package/src/index.ts +14 -5
  127. package/src/metrics/ai-docs/AGENTS.md +348 -0
  128. package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
  129. package/src/metrics/behavioral-events.ts +28 -14
  130. package/src/metrics/constants.ts +7 -8
  131. package/src/services/ApiAiAssistant.ts +2 -4
  132. package/src/services/agent/ai-docs/AGENTS.md +238 -0
  133. package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
  134. package/src/services/ai-docs/AGENTS.md +384 -0
  135. package/src/services/config/Util.ts +2 -3
  136. package/src/services/config/ai-docs/AGENTS.md +253 -0
  137. package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
  138. package/src/services/config/types.ts +108 -20
  139. package/src/services/constants.ts +0 -1
  140. package/src/services/core/Err.ts +0 -1
  141. package/src/services/core/Utils.ts +90 -67
  142. package/src/services/core/ai-docs/AGENTS.md +379 -0
  143. package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
  144. package/src/services/core/aqm-reqs.ts +22 -100
  145. package/src/services/core/websocket/WebSocketManager.ts +4 -23
  146. package/src/services/core/websocket/types.ts +1 -1
  147. package/src/services/index.ts +1 -2
  148. package/src/services/task/Task.ts +785 -0
  149. package/src/services/task/TaskFactory.ts +55 -0
  150. package/src/services/task/TaskManager.ts +579 -633
  151. package/src/services/task/TaskUtils.ts +175 -31
  152. package/src/services/task/ai-docs/AGENTS.md +448 -0
  153. package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
  154. package/src/services/task/constants.ts +5 -4
  155. package/src/services/task/dialer.ts +1 -56
  156. package/src/services/task/digital/Digital.ts +95 -0
  157. package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
  158. package/src/services/task/state-machine/actions.ts +422 -0
  159. package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
  160. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
  161. package/src/services/task/state-machine/constants.ts +150 -0
  162. package/src/services/task/state-machine/guards.ts +303 -0
  163. package/src/services/task/state-machine/index.ts +28 -0
  164. package/src/services/task/state-machine/types.ts +228 -0
  165. package/src/services/task/state-machine/uiControlsComputer.ts +542 -0
  166. package/src/services/task/taskDataNormalizer.ts +137 -0
  167. package/src/services/task/types.ts +641 -95
  168. package/src/services/task/voice/Voice.ts +1255 -0
  169. package/src/services/task/voice/WebRTC.ts +187 -0
  170. package/src/types.ts +88 -5
  171. package/src/utils/AGENTS.md +276 -0
  172. package/src/webex.js +2 -0
  173. package/test/unit/spec/cc.ts +59 -142
  174. package/test/unit/spec/logger-proxy.ts +70 -0
  175. package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
  176. package/test/unit/spec/services/config/index.ts +26 -55
  177. package/test/unit/spec/services/core/Utils.ts +103 -52
  178. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
  179. package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
  180. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  181. package/test/unit/spec/services/task/Task.ts +416 -0
  182. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  183. package/test/unit/spec/services/task/TaskManager.ts +781 -1735
  184. package/test/unit/spec/services/task/TaskUtils.ts +125 -0
  185. package/test/unit/spec/services/task/dialer.ts +112 -198
  186. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  187. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
  188. package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
  189. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  190. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
  191. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  192. package/test/unit/spec/services/task/voice/Voice.ts +587 -0
  193. package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
  194. package/umd/contact-center.min.js +2 -2
  195. package/umd/contact-center.min.js.map +1 -1
  196. package/dist/services/task/index.js +0 -1525
  197. package/dist/services/task/index.js.map +0 -1
  198. package/dist/types/services/task/index.d.ts +0 -650
  199. package/src/services/task/index.ts +0 -1801
  200. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -1,1801 +0,0 @@
1
- import EventEmitter from 'events';
2
- import {CALL_EVENT_KEYS, LocalMicrophoneStream} from '@webex/calling';
3
- import {CallId} from '@webex/calling/dist/types/common/types';
4
- import {generateTaskErrorObject, calculateDestAgentId, calculateDestType} from '../core/Utils';
5
- import {Failure} from '../core/GlobalTypes';
6
- import {LoginOption} from '../../types';
7
- import {TASK_FILE} from '../../constants';
8
- import {METHODS, KEYS_TO_NOT_DELETE} from './constants';
9
- import routingContact from './contact';
10
- import LoggerProxy from '../../logger-proxy';
11
- import {
12
- ITask,
13
- TaskResponse,
14
- TaskData,
15
- TaskId,
16
- TASK_EVENTS,
17
- WrapupPayLoad,
18
- ResumeRecordingPayload,
19
- ConsultPayload,
20
- ConsultEndPayload,
21
- TransferPayLoad,
22
- DESTINATION_TYPE,
23
- ConsultTransferPayLoad,
24
- MEDIA_CHANNEL,
25
- } from './types';
26
- import WebCallingService from '../WebCallingService';
27
- import MetricsManager from '../../metrics/MetricsManager';
28
- import {METRIC_EVENT_NAMES} from '../../metrics/constants';
29
- import AutoWrapup from './AutoWrapup';
30
- import {WrapupData} from '../config/types';
31
-
32
- /**
33
- * Task class represents a contact center task/interaction that can be managed by an agent.
34
- * This class provides all the necessary methods to manage tasks in a contact center environment,
35
- * handling various call control operations and task lifecycle management.
36
- *
37
- * - Task Lifecycle Management:
38
- * - {@link accept} - Accept incoming task
39
- * - {@link decline} - Decline incoming task
40
- * - {@link end} - End active task
41
- * - Media Controls:
42
- * - {@link toggleMute} - Mute/unmute microphone for voice tasks
43
- * - {@link hold} - Place task on hold
44
- * - {@link resume} - Resume held task
45
- * - Recording Controls:
46
- * - {@link pauseRecording} - Pause task recording
47
- * - {@link resumeRecording} - Resume paused recording
48
- * - Task Transfer & Consultation:
49
- * - {@link consult} - Initiate consultation with another agent/queue
50
- * - {@link endConsult} - End ongoing consultation
51
- * - {@link transfer} - Transfer task to another agent/queue
52
- * - {@link consultTransfer} - Transfer after consultation
53
- * - Task Completion:
54
- * - {@link wrapup} - Complete task wrap-up
55
- *
56
- * Key events emitted by Task instances (see {@link TASK_EVENTS} for details):
57
- *
58
- * - Task Lifecycle:
59
- * - task:incoming — New task is being offered
60
- * - task:assigned — Task assigned to agent
61
- * - task:unassigned — Task unassigned from agent
62
- * - task:end — Task has ended
63
- * - task:wrapup — Task entered wrap-up state
64
- * - task:wrappedup — Task wrap-up completed
65
- * - task:rejected — Task was rejected/unanswered
66
- * - task:hydrate — Task data populated
67
- *
68
- * - Media & Controls:
69
- * - task:media — Voice call media track received
70
- * - task:hold — Task placed on hold
71
- * - task:unhold — Task resumed from hold
72
- *
73
- * - Consultation & Transfer:
74
- * - task:consultCreated — Consultation initiated
75
- * - task:consulting — Consultation in progress
76
- * - task:consultAccepted — Consultation accepted
77
- * - task:consultEnd — Consultation ended
78
- * - task:consultQueueCancelled — Queue consultation cancelled
79
- * - task:consultQueueFailed — Queue consultation failed
80
- * - task:offerConsult — Consultation offered
81
- * - task:offerContact — New contact offered
82
- *
83
- * - Recording:
84
- * - task:recordingPaused — Recording paused
85
- * - task:recordingPauseFailed — Recording pause failed
86
- * - task:recordingResumed — Recording resumed
87
- * - task:recordingResumeFailed — Recording resume failed
88
- *
89
- * @implements {ITask}
90
- * @example
91
- * ```typescript
92
- * // 1. Initialize task
93
- * const task = new Task(contact, webCallingService, taskData);
94
- *
95
- * // 2. Set up event listeners
96
- * task.on('task:media', (track) => {
97
- * // Handle voice call media
98
- * const audioElement = document.getElementById('remote-audio');
99
- * audioElement.srcObject = new MediaStream([track]);
100
- * });
101
- *
102
- * task.on('task:hold', () => {
103
- * console.log('Task is on hold');
104
- * // Update UI to show hold state
105
- * });
106
- *
107
- * task.on('task:end', () => {
108
- * console.log('Task ended');
109
- * if (task.data.wrapUpRequired) {
110
- * // Show wrap-up form
111
- * }
112
- * });
113
- *
114
- * // 3. Example task operations
115
- * await task.accept(); // Accept incoming task
116
- * await task.hold(); // Place on hold
117
- * await task.resume(); // Resume from hold
118
- * await task.end(); // End task
119
- *
120
- * // 4. Handle wrap-up if required
121
- * await task.wrapup({
122
- * auxCodeId: 'RESOLVED',
123
- * wrapUpReason: 'Customer issue resolved'
124
- * });
125
- * ```
126
- */
127
-
128
- export default class Task extends EventEmitter implements ITask {
129
- private contact: ReturnType<typeof routingContact>;
130
- private localAudioStream: LocalMicrophoneStream;
131
- private webCallingService: WebCallingService;
132
- public data: TaskData;
133
- private metricsManager: MetricsManager;
134
- public webCallMap: Record<TaskId, CallId>;
135
- private wrapupData: WrapupData;
136
- public autoWrapup?: AutoWrapup;
137
- private agentId: string;
138
-
139
- /**
140
- * Creates a new Task instance which provides the following features:
141
- * @param contact - The routing contact service instance
142
- * @param webCallingService - The web calling service instance
143
- * @param data - Initial task data
144
- * @param wrapupData - Wrap-up configuration data
145
- */
146
- public constructor(
147
- contact: ReturnType<typeof routingContact>,
148
- webCallingService: WebCallingService,
149
- data: TaskData,
150
- wrapupData: WrapupData,
151
- agentId: string
152
- ) {
153
- super();
154
- this.contact = contact;
155
- this.data = data;
156
- this.webCallingService = webCallingService;
157
- this.webCallMap = {};
158
- this.wrapupData = wrapupData;
159
- this.metricsManager = MetricsManager.getInstance();
160
- this.registerWebCallListeners();
161
- this.setupAutoWrapupTimer();
162
- this.agentId = agentId;
163
- }
164
-
165
- /**
166
- * Sets up the automatic wrap-up timer if wrap-up is required
167
- * @private
168
- */
169
- private setupAutoWrapupTimer() {
170
- if (
171
- this.data.wrapUpRequired && // only when wrapup required
172
- !this.autoWrapup && // if autoWrapup is not already set
173
- this.wrapupData && // wrapupData is not defined
174
- this.wrapupData.wrapUpProps // wrapUpProps is defined
175
- ) {
176
- const wrapUpProps = this.wrapupData.wrapUpProps;
177
- if (!wrapUpProps || wrapUpProps.autoWrapup === false) {
178
- LoggerProxy.info(`Auto wrap-up is not required for this task`, {
179
- module: TASK_FILE,
180
- method: METHODS.SETUP_AUTO_WRAPUP_TIMER,
181
- interactionId: this.data.interactionId,
182
- });
183
-
184
- return;
185
- }
186
- const defaultWrapupReason =
187
- wrapUpProps.wrapUpReasonList?.find((r) => r.isDefault) ?? wrapUpProps.wrapUpReasonList?.[0];
188
- if (!defaultWrapupReason) {
189
- LoggerProxy.error('No wrap-up reason configured', {
190
- module: TASK_FILE,
191
- method: METHODS.SETUP_AUTO_WRAPUP_TIMER,
192
- });
193
-
194
- return;
195
- }
196
- const intervalMs = wrapUpProps.autoWrapupInterval;
197
- if (!intervalMs || intervalMs <= 0) {
198
- LoggerProxy.error(`Invalid auto wrap-up interval: ${intervalMs}`, {
199
- module: TASK_FILE,
200
- method: METHODS.SETUP_AUTO_WRAPUP_TIMER,
201
- });
202
- }
203
- this.autoWrapup = new AutoWrapup(intervalMs, wrapUpProps.allowCancelAutoWrapup);
204
- this.autoWrapup.start(async () => {
205
- LoggerProxy.info(`Auto wrap-up timer triggered`, {
206
- module: TASK_FILE,
207
- method: METHODS.SETUP_AUTO_WRAPUP_TIMER,
208
- interactionId: this.data.interactionId,
209
- });
210
- await this.wrapup({
211
- wrapUpReason: defaultWrapupReason.name,
212
- auxCodeId: defaultWrapupReason.id,
213
- });
214
- });
215
- }
216
- }
217
-
218
- /**
219
- * Cancels the automatic wrap-up timer if it's running
220
- * @public - Public so it can be called externally when needed
221
- * Note: This is supported only in single session mode. Not supported in multi-session mode.
222
- */
223
- public cancelAutoWrapupTimer() {
224
- this.autoWrapup?.clear();
225
- this.autoWrapup = undefined;
226
- LoggerProxy.info(`Auto wrap-up timer cancelled`, {
227
- module: TASK_FILE,
228
- method: METHODS.CANCEL_AUTO_WRAPUP_TIMER,
229
- interactionId: this.data?.interactionId,
230
- });
231
- }
232
-
233
- /**
234
- * @ignore
235
- * @private
236
- */
237
- private handleRemoteMedia = (track: MediaStreamTrack) => {
238
- this.emit(TASK_EVENTS.TASK_MEDIA, track);
239
- };
240
-
241
- /**
242
- * @ignore
243
- * @private
244
- */
245
- private registerWebCallListeners() {
246
- this.webCallingService.on(CALL_EVENT_KEYS.REMOTE_MEDIA, this.handleRemoteMedia);
247
- }
248
-
249
- /**
250
- * @ignore
251
- */
252
- public unregisterWebCallListeners() {
253
- this.webCallingService.off(CALL_EVENT_KEYS.REMOTE_MEDIA, this.handleRemoteMedia);
254
- }
255
-
256
- /**
257
- * Updates the task data with new information
258
- * @param updatedData - New task data to merge with existing data
259
- * @param shouldOverwrite - If true, completely replace data instead of merging
260
- * @returns The updated task instance
261
- * @example
262
- * ```typescript
263
- * task.updateTaskData(newData);
264
- * task.updateTaskData(newData, true); // completely replace data
265
- * ```
266
- */
267
- public updateTaskData = (updatedData: TaskData, shouldOverwrite = false) => {
268
- this.data = shouldOverwrite ? updatedData : this.reconcileData(this.data, updatedData);
269
- this.setupAutoWrapupTimer();
270
-
271
- return this;
272
- };
273
-
274
- /**
275
- * Recursively merges old data with new data
276
- * @private
277
- */
278
- private reconcileData(oldData: TaskData, newData: TaskData): TaskData {
279
- // Remove keys from oldData that are not in newData
280
- Object.keys(oldData).forEach((key) => {
281
- if (!(key in newData) && !KEYS_TO_NOT_DELETE.includes(key as string)) {
282
- delete oldData[key];
283
- }
284
- });
285
-
286
- // Merge or update keys from newData
287
- Object.keys(newData).forEach((key) => {
288
- if (
289
- newData[key] &&
290
- typeof newData[key] === 'object' &&
291
- !Array.isArray(newData[key]) &&
292
- oldData[key] &&
293
- typeof oldData[key] === 'object' &&
294
- !Array.isArray(oldData[key])
295
- ) {
296
- this.reconcileData(oldData[key], newData[key]);
297
- } else {
298
- oldData[key] = newData[key];
299
- }
300
- });
301
-
302
- return oldData;
303
- }
304
-
305
- /**
306
- * Agent accepts the incoming task.
307
- * After accepting, the task will emit task:assigned event and for voice calls,
308
- * a task:media event with the audio stream.
309
- *
310
- * @returns Promise<TaskResponse>
311
- * @throws Error if accepting task fails or media requirements not met
312
- * @example
313
- * ```typescript
314
- * // Set up event handlers before accepting
315
- * task.on(TASK_EVENTS.TASK_ASSIGNED, () => {
316
- * console.log('Task assigned, ID:', task.data.interactionId);
317
- * // Update UI to show active task
318
- * });
319
- *
320
- * // For voice calls, handle media
321
- * task.on(TASK_EVENTS.TASK_MEDIA, (track) => {
322
- * const audioElement = document.getElementById('remote-audio');
323
- * audioElement.srcObject = new MediaStream([track]);
324
- * });
325
- *
326
- * // Accept the task
327
- * try {
328
- * await task.accept();
329
- * console.log('Successfully accepted task');
330
- * } catch (error) {
331
- * console.error('Failed to accept task:', error);
332
- * // Handle error (e.g., show error message to agent)
333
- * }
334
- * ```
335
- */
336
- public async accept(): Promise<TaskResponse> {
337
- try {
338
- LoggerProxy.info(`Accepting task`, {
339
- module: TASK_FILE,
340
- method: METHODS.ACCEPT,
341
- interactionId: this.data.interactionId,
342
- });
343
- this.metricsManager.timeEvent([
344
- METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
345
- METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
346
- ]);
347
-
348
- if (this.data.interaction.mediaType !== MEDIA_CHANNEL.TELEPHONY) {
349
- const response = await this.contact.accept({interactionId: this.data.interactionId});
350
- LoggerProxy.log(`Task accepted successfully`, {
351
- module: TASK_FILE,
352
- method: METHODS.ACCEPT,
353
- trackingId: response.trackingId,
354
- interactionId: this.data.interactionId,
355
- });
356
- this.metricsManager.trackEvent(
357
- METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
358
- {
359
- taskId: this.data.interactionId,
360
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(this.data),
361
- },
362
- ['operational', 'behavioral', 'business']
363
- );
364
-
365
- return response;
366
- }
367
-
368
- if (this.webCallingService.loginOption === LoginOption.BROWSER) {
369
- const constraints = {audio: true};
370
-
371
- const localStream = await navigator.mediaDevices.getUserMedia(constraints);
372
- const audioTrack = localStream.getAudioTracks()[0];
373
- this.localAudioStream = new LocalMicrophoneStream(new MediaStream([audioTrack]));
374
- this.webCallingService.answerCall(this.localAudioStream, this.data.interactionId);
375
- this.metricsManager.trackEvent(
376
- METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
377
- {
378
- taskId: this.data.interactionId,
379
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(this.data),
380
- },
381
- ['operational', 'behavioral', 'business']
382
- );
383
-
384
- LoggerProxy.log(`Task accepted successfully with webrtc calling`, {
385
- module: TASK_FILE,
386
- method: METHODS.ACCEPT,
387
- interactionId: this.data.interactionId,
388
- });
389
- }
390
-
391
- return Promise.resolve(); // TODO: reject for extension as part of refactor
392
- } catch (error) {
393
- const err = generateTaskErrorObject(error, METHODS.ACCEPT, TASK_FILE);
394
- const taskErrorProps = {
395
- trackingId: err.data?.trackingId,
396
- errorMessage: err.data?.message,
397
- errorType: err.data?.errorType,
398
- errorData: err.data?.errorData,
399
- reasonCode: err.data?.reasonCode,
400
- };
401
- this.metricsManager.trackEvent(
402
- METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
403
- {
404
- taskId: this.data.interactionId,
405
- error: error.toString(),
406
- ...taskErrorProps,
407
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details as Failure),
408
- },
409
- ['operational', 'behavioral', 'business']
410
- );
411
- throw err;
412
- }
413
- }
414
-
415
- /**
416
- * Agent can mute/unmute their microphone during a WebRTC task.
417
- * This method toggles between muted and unmuted states for the local audio stream.
418
- *
419
- * @returns Promise<void> - Resolves when mute/unmute operation completes
420
- * @throws Error if toggling mute state fails or audio stream is not available
421
- * @example
422
- * ```typescript
423
- * // Toggle mute state
424
- * task.toggleMute()
425
- * .then(() => console.log('Mute state toggled successfully'))
426
- * .catch(error => console.error('Failed to toggle mute:', error));
427
- * ```
428
- */
429
- public async toggleMute() {
430
- try {
431
- LoggerProxy.info(`Toggling mute state`, {
432
- module: TASK_FILE,
433
- method: METHODS.TOGGLE_MUTE,
434
- interactionId: this.data.interactionId,
435
- });
436
-
437
- this.webCallingService.muteUnmuteCall(this.localAudioStream);
438
-
439
- LoggerProxy.log(
440
- `Mute state toggled successfully isCallMuted: ${this.webCallingService.isCallMuted()}`,
441
- {
442
- module: TASK_FILE,
443
- method: METHODS.TOGGLE_MUTE,
444
- interactionId: this.data.interactionId,
445
- }
446
- );
447
-
448
- return Promise.resolve();
449
- } catch (error) {
450
- const err = generateTaskErrorObject(error, METHODS.TOGGLE_MUTE, TASK_FILE);
451
- throw err;
452
- }
453
- }
454
-
455
- /**
456
- * Declines the incoming task. This will reject the task and notify the routing system.
457
- * For voice calls, this is equivalent to declining the incoming call.
458
- *
459
- * @returns Promise<TaskResponse>
460
- * @throws Error if the decline operation fails
461
- * @example
462
- * ```typescript
463
- * // Decline an incoming task
464
- * task.decline()
465
- * .then(() => console.log('Task declined successfully'))
466
- * .catch(error => console.error('Failed to decline task:', error));
467
- * ```
468
- */
469
- public async decline(): Promise<TaskResponse> {
470
- try {
471
- LoggerProxy.info(`Declining task`, {
472
- module: TASK_FILE,
473
- method: METHODS.DECLINE,
474
- interactionId: this.data.interactionId,
475
- });
476
- this.metricsManager.timeEvent([
477
- METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS,
478
- METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
479
- ]);
480
-
481
- this.webCallingService.declineCall(this.data.interactionId);
482
- this.unregisterWebCallListeners();
483
-
484
- this.metricsManager.trackEvent(
485
- METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS,
486
- {taskId: this.data.interactionId},
487
- ['operational', 'behavioral']
488
- );
489
-
490
- LoggerProxy.log(`Task declined successfully`, {
491
- module: TASK_FILE,
492
- method: METHODS.DECLINE,
493
- interactionId: this.data.interactionId,
494
- });
495
-
496
- return Promise.resolve();
497
- } catch (error) {
498
- const err = generateTaskErrorObject(error, METHODS.DECLINE, TASK_FILE);
499
- const taskErrorProps = {
500
- trackingId: err.data?.trackingId,
501
- errorMessage: err.data?.message,
502
- errorType: err.data?.errorType,
503
- errorData: err.data?.errorData,
504
- reasonCode: err.data?.reasonCode,
505
- };
506
- this.metricsManager.trackEvent(
507
- METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
508
- {
509
- taskId: this.data.interactionId,
510
- error: error.toString(),
511
- ...taskErrorProps,
512
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
513
- },
514
- ['operational', 'behavioral']
515
- );
516
- throw err;
517
- }
518
- }
519
-
520
- /**
521
- * Puts the current task/interaction on hold.
522
- * Emits task:hold event when successful. For voice tasks, this mutes the audio.
523
- *
524
- * @param mediaResourceId - Optional media resource ID to use for the hold operation. If not provided, uses the task's current mediaResourceId
525
- * @returns Promise<TaskResponse>
526
- * @throws Error if hold operation fails
527
- * @example
528
- * ```typescript
529
- * // Set up hold event handler
530
- * task.on(TASK_EVENTS.TASK_HOLD, () => {
531
- * console.log('Task is now on hold');
532
- * // Update UI to show hold state (e.g., enable resume button, show hold indicator)
533
- * document.getElementById('resume-btn').disabled = false;
534
- * document.getElementById('hold-indicator').style.display = 'block';
535
- * });
536
- *
537
- * // Place task on hold
538
- * try {
539
- * await task.hold();
540
- * console.log('Successfully placed task on hold');
541
- * } catch (error) {
542
- * console.error('Failed to place task on hold:', error);
543
- * // Handle error (e.g., show error message, reset UI state)
544
- * }
545
- *
546
- * // Place task on hold with custom mediaResourceId
547
- * try {
548
- * await task.hold('custom-media-resource-id');
549
- * console.log('Successfully placed task on hold with custom mediaResourceId');
550
- * } catch (error) {
551
- * console.error('Failed to place task on hold:', error);
552
- * }
553
- * ```
554
- */
555
- public async hold(mediaResourceId?: string): Promise<TaskResponse> {
556
- try {
557
- LoggerProxy.info(`Holding task`, {
558
- module: TASK_FILE,
559
- method: METHODS.HOLD,
560
- interactionId: this.data.interactionId,
561
- });
562
-
563
- this.metricsManager.timeEvent([
564
- METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
565
- METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
566
- ]);
567
-
568
- const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
569
-
570
- const response = await this.contact.hold({
571
- interactionId: this.data.interactionId,
572
- data: {mediaResourceId: effectiveMediaResourceId},
573
- });
574
-
575
- this.metricsManager.trackEvent(
576
- METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
577
- {
578
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
579
- taskId: this.data.interactionId,
580
- mediaResourceId: effectiveMediaResourceId,
581
- },
582
- ['operational', 'behavioral']
583
- );
584
-
585
- LoggerProxy.log(`Task placed on hold successfully`, {
586
- module: TASK_FILE,
587
- method: METHODS.HOLD,
588
- trackingId: response.trackingId,
589
- interactionId: this.data.interactionId,
590
- });
591
-
592
- return response;
593
- } catch (error) {
594
- const err = generateTaskErrorObject(error, METHODS.HOLD, TASK_FILE);
595
- const taskErrorProps = {
596
- trackingId: err.data?.trackingId,
597
- errorMessage: err.data?.message,
598
- errorType: err.data?.errorType,
599
- errorData: err.data?.errorData,
600
- reasonCode: err.data?.reasonCode,
601
- };
602
- const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
603
-
604
- this.metricsManager.trackEvent(
605
- METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
606
- {
607
- taskId: this.data.interactionId,
608
- mediaResourceId: effectiveMediaResourceId,
609
- error: error.toString(),
610
- ...taskErrorProps,
611
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
612
- },
613
- ['operational', 'behavioral']
614
- );
615
- throw err;
616
- }
617
- }
618
-
619
- /**
620
- * Resumes the task/interaction that was previously put on hold.
621
- * Emits task:resume event when successful. For voice tasks, this restores the audio.
622
- *
623
- * @param mediaResourceId - Optional media resource ID to use for the resume operation. If not provided, uses the task's current mediaResourceId from interaction media
624
- * @returns Promise<TaskResponse>
625
- * @throws Error if resume operation fails
626
- * @example
627
- * ```typescript
628
- * // Set up resume event handler
629
- * task.on(TASK_EVENTS.TASK_RESUME, () => {
630
- * console.log('Task resumed from hold');
631
- * // Update UI to show active state
632
- * document.getElementById('hold-btn').disabled = false;
633
- * document.getElementById('hold-indicator').style.display = 'none';
634
- * });
635
- *
636
- * // Resume task from hold
637
- * try {
638
- * await task.resume();
639
- * console.log('Successfully resumed task from hold');
640
- * } catch (error) {
641
- * console.error('Failed to resume task:', error);
642
- * // Handle error (e.g., show error message)
643
- * }
644
- *
645
- * // Resume task from hold with custom mediaResourceId
646
- * try {
647
- * await task.resume('custom-media-resource-id');
648
- * console.log('Successfully resumed task from hold with custom mediaResourceId');
649
- * } catch (error) {
650
- * console.error('Failed to resume task:', error);
651
- * }
652
- * ```
653
- */
654
- public async resume(mediaResourceId?: string): Promise<TaskResponse> {
655
- try {
656
- LoggerProxy.info(`Resuming task`, {
657
- module: TASK_FILE,
658
- method: METHODS.RESUME,
659
- interactionId: this.data.interactionId,
660
- });
661
- const {mainInteractionId} = this.data.interaction;
662
- const defaultMediaResourceId =
663
- this.data.interaction.media[mainInteractionId]?.mediaResourceId;
664
- const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
665
-
666
- this.metricsManager.timeEvent([
667
- METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
668
- METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
669
- ]);
670
-
671
- const response = await this.contact.unHold({
672
- interactionId: this.data.interactionId,
673
- data: {mediaResourceId: effectiveMediaResourceId},
674
- });
675
-
676
- this.metricsManager.trackEvent(
677
- METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
678
- {
679
- taskId: this.data.interactionId,
680
- mainInteractionId,
681
- mediaResourceId: effectiveMediaResourceId,
682
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
683
- },
684
- ['operational', 'behavioral']
685
- );
686
-
687
- LoggerProxy.log(`Task resumed successfully`, {
688
- module: TASK_FILE,
689
- method: METHODS.RESUME,
690
- trackingId: response.trackingId,
691
- interactionId: this.data.interactionId,
692
- });
693
-
694
- return response;
695
- } catch (error) {
696
- const err = generateTaskErrorObject(error, METHODS.RESUME, TASK_FILE);
697
- const mainInteractionId = this.data.interaction?.mainInteractionId;
698
- const defaultMediaResourceId = mainInteractionId
699
- ? this.data.interaction.media[mainInteractionId]?.mediaResourceId
700
- : '';
701
- const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
702
-
703
- const taskErrorProps = {
704
- trackingId: err.data?.trackingId,
705
- errorMessage: err.data?.message,
706
- errorType: err.data?.errorType,
707
- errorData: err.data?.errorData,
708
- reasonCode: err.data?.reasonCode,
709
- };
710
- this.metricsManager.trackEvent(
711
- METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
712
- {
713
- taskId: this.data.interactionId,
714
- mainInteractionId,
715
- mediaResourceId: effectiveMediaResourceId,
716
- ...taskErrorProps,
717
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
718
- },
719
- ['operational', 'behavioral']
720
- );
721
- throw err;
722
- }
723
- }
724
-
725
- /**
726
- * Ends the task/interaction with the customer.
727
- * Emits task:end event when successful. If task requires wrap-up,
728
- * this will be indicated in the task:end event data.
729
- *
730
- * @returns Promise<TaskResponse>
731
- * @throws Error if ending task fails
732
- * @example
733
- * ```typescript
734
- * // Set up task end event handler
735
- * task.on(TASK_EVENTS.TASK_END, (data) => {
736
- * console.log('Task ended:', task.data.interactionId);
737
- *
738
- * if (data.wrapUpRequired) {
739
- * // Show wrap-up form
740
- * showWrapupForm();
741
- * } else {
742
- * // Clean up and prepare for next task
743
- * cleanupTask();
744
- * }
745
- * });
746
- *
747
- * // End the task
748
- * try {
749
- * await task.end();
750
- * console.log('Task end request successful');
751
- * } catch (error) {
752
- * console.error('Failed to end task:', error);
753
- * // Handle error (e.g., show error message, retry option)
754
- * }
755
- *
756
- * function showWrapupForm() {
757
- * // Show wrap-up UI with required codes
758
- * document.getElementById('wrapup-form').style.display = 'block';
759
- * }
760
- *
761
- * function cleanupTask() {
762
- * // Reset UI state
763
- * document.getElementById('active-task').style.display = 'none';
764
- * document.getElementById('controls').style.display = 'none';
765
- * }
766
- * ```
767
- */
768
- public async end(): Promise<TaskResponse> {
769
- try {
770
- LoggerProxy.info(`Ending task`, {
771
- module: TASK_FILE,
772
- method: METHODS.END,
773
- interactionId: this.data.interactionId,
774
- });
775
-
776
- this.metricsManager.timeEvent([
777
- METRIC_EVENT_NAMES.TASK_END_SUCCESS,
778
- METRIC_EVENT_NAMES.TASK_END_FAILED,
779
- ]);
780
-
781
- const response = await this.contact.end({interactionId: this.data.interactionId});
782
-
783
- this.metricsManager.trackEvent(
784
- METRIC_EVENT_NAMES.TASK_END_SUCCESS,
785
- {
786
- taskId: this.data.interactionId,
787
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
788
- },
789
- ['operational', 'behavioral', 'business']
790
- );
791
-
792
- LoggerProxy.log(`Task ended successfully`, {
793
- module: TASK_FILE,
794
- method: METHODS.END,
795
- trackingId: response.trackingId,
796
- interactionId: this.data.interactionId,
797
- });
798
-
799
- return response;
800
- } catch (error) {
801
- const err = generateTaskErrorObject(error, METHODS.END, TASK_FILE);
802
- const taskErrorProps = {
803
- trackingId: err.data?.trackingId,
804
- errorMessage: err.data?.message,
805
- errorType: err.data?.errorType,
806
- errorData: err.data?.errorData,
807
- reasonCode: err.data?.reasonCode,
808
- };
809
- this.metricsManager.trackEvent(
810
- METRIC_EVENT_NAMES.TASK_END_FAILED,
811
- {
812
- taskId: this.data.interactionId,
813
- ...taskErrorProps,
814
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
815
- },
816
- ['operational', 'behavioral', 'business']
817
- );
818
- throw err;
819
- }
820
- }
821
-
822
- /**
823
- * Wraps up the task/interaction with the customer.
824
- * This is called after task:end event if wrapUpRequired is true.
825
- * Emits task:wrappedup event when successful.
826
- *
827
- * @param wrapupPayload - WrapupPayLoad containing:
828
- * - auxCodeId: Required ID for the wrap-up code
829
- * - wrapUpReason: Required description of wrap-up reason
830
- * @returns Promise<TaskResponse>
831
- * @throws Error if task data is unavailable, auxCodeId is missing, or wrapUpReason is missing
832
- * @example
833
- * ```typescript
834
- * // Set up wrap-up events
835
- * task.on(TASK_EVENTS.TASK_WRAPUP, () => {
836
- * console.log('Task ready for wrap-up');
837
- * // Show wrap-up form
838
- * document.getElementById('wrapup-form').style.display = 'block';
839
- * });
840
- *
841
- * task.on(TASK_EVENTS.TASK_WRAPPEDUP, () => {
842
- * console.log('Task wrap-up completed');
843
- * // Clean up UI
844
- * document.getElementById('wrapup-form').style.display = 'none';
845
- * });
846
- *
847
- * // Submit wrap-up
848
- * try {
849
- * const wrapupPayload = {
850
- * auxCodeId: selectedCode, // e.g., 'ISSUE_RESOLVED'
851
- * wrapUpReason: 'Customer issue resolved successfully'
852
- * };
853
- * await task.wrapup(wrapupPayload);
854
- * console.log('Successfully submitted wrap-up');
855
- * } catch (error) {
856
- * console.error('Failed to submit wrap-up:', error);
857
- * // Handle validation errors
858
- * if (error.message.includes('required')) {
859
- * // Show validation error to agent
860
- * }
861
- * }
862
- * ```
863
- */
864
- public async wrapup(wrapupPayload: WrapupPayLoad): Promise<TaskResponse> {
865
- try {
866
- this.cancelAutoWrapupTimer();
867
- LoggerProxy.info(`Wrapping up task`, {
868
- module: TASK_FILE,
869
- method: METHODS.WRAPUP,
870
- interactionId: this.data.interactionId,
871
- });
872
-
873
- this.metricsManager.timeEvent([
874
- METRIC_EVENT_NAMES.TASK_WRAPUP_SUCCESS,
875
- METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
876
- ]);
877
-
878
- if (!this.data) {
879
- throw new Error('No task data available');
880
- }
881
- if (!wrapupPayload.auxCodeId || wrapupPayload.auxCodeId.length === 0) {
882
- throw new Error('AuxCodeId is required');
883
- }
884
- if (!wrapupPayload.wrapUpReason || wrapupPayload.wrapUpReason.length === 0) {
885
- throw new Error('WrapUpReason is required');
886
- }
887
-
888
- const response = await this.contact.wrapup({
889
- interactionId: this.data.interactionId,
890
- data: wrapupPayload,
891
- });
892
-
893
- this.metricsManager.trackEvent(
894
- METRIC_EVENT_NAMES.TASK_WRAPUP_SUCCESS,
895
- {
896
- taskId: this.data.interactionId,
897
- wrapUpCode: wrapupPayload.auxCodeId,
898
- wrapUpReason: wrapupPayload.wrapUpReason,
899
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
900
- },
901
- ['operational', 'behavioral', 'business']
902
- );
903
-
904
- LoggerProxy.log(`Task wrapped up successfully`, {
905
- module: TASK_FILE,
906
- method: METHODS.WRAPUP,
907
- trackingId: response.trackingId,
908
- interactionId: this.data.interactionId,
909
- });
910
-
911
- return response;
912
- } catch (error) {
913
- const err = generateTaskErrorObject(error, METHODS.WRAPUP, TASK_FILE);
914
- const taskErrorProps = {
915
- trackingId: err.data?.trackingId,
916
- errorMessage: err.data?.message,
917
- errorType: err.data?.errorType,
918
- errorData: err.data?.errorData,
919
- reasonCode: err.data?.reasonCode,
920
- };
921
- this.metricsManager.trackEvent(
922
- METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
923
- {
924
- taskId: this.data.interactionId,
925
- wrapUpCode: wrapupPayload.auxCodeId,
926
- wrapUpReason: wrapupPayload.wrapUpReason,
927
- ...taskErrorProps,
928
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
929
- },
930
- ['operational', 'behavioral', 'business']
931
- );
932
- throw err;
933
- }
934
- }
935
-
936
- /**
937
- * Pauses the recording for the current voice task.
938
- * Emits task:recordingPaused event when successful.
939
- *
940
- * @returns Promise<TaskResponse>
941
- * @throws Error if pause recording fails
942
- * @example
943
- * ```typescript
944
- * // Set up recording events
945
- * task.on(TASK_EVENTS.TASK_RECORDING_PAUSED, () => {
946
- * console.log('Recording paused');
947
- * // Update UI to show recording paused state
948
- * document.getElementById('recording-status').textContent = 'Recording Paused';
949
- * document.getElementById('pause-recording-btn').style.display = 'none';
950
- * document.getElementById('resume-recording-btn').style.display = 'block';
951
- * });
952
- *
953
- * task.on(TASK_EVENTS.TASK_RECORDING_PAUSE_FAILED, (error) => {
954
- * console.error('Failed to pause recording:', error);
955
- * // Show error to agent
956
- * });
957
- *
958
- * // Pause recording
959
- * try {
960
- * await task.pauseRecording();
961
- * console.log('Pause recording request sent');
962
- * } catch (error) {
963
- * console.error('Error sending pause recording request:', error);
964
- * // Handle error
965
- * }
966
- * ```
967
- */
968
- public async pauseRecording(): Promise<TaskResponse> {
969
- try {
970
- LoggerProxy.info(`Pausing recording`, {
971
- module: TASK_FILE,
972
- method: METHODS.PAUSE_RECORDING,
973
- interactionId: this.data.interactionId,
974
- });
975
-
976
- this.metricsManager.timeEvent([
977
- METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS,
978
- METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED,
979
- ]);
980
-
981
- const result = await this.contact.pauseRecording({interactionId: this.data.interactionId});
982
-
983
- this.metricsManager.trackEvent(
984
- METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS,
985
- {
986
- taskId: this.data.interactionId,
987
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
988
- },
989
- ['operational', 'behavioral', 'business']
990
- );
991
-
992
- LoggerProxy.log(`Recording paused successfully`, {
993
- module: TASK_FILE,
994
- method: METHODS.PAUSE_RECORDING,
995
- trackingId: result.trackingId,
996
- interactionId: this.data.interactionId,
997
- });
998
-
999
- return result;
1000
- } catch (error) {
1001
- const err = generateTaskErrorObject(error, METHODS.PAUSE_RECORDING, TASK_FILE);
1002
- const taskErrorProps = {
1003
- trackingId: err.data?.trackingId,
1004
- errorMessage: err.data?.message,
1005
- errorType: err.data?.errorType,
1006
- errorData: err.data?.errorData,
1007
- reasonCode: err.data?.reasonCode,
1008
- };
1009
- this.metricsManager.trackEvent(
1010
- METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED,
1011
- {
1012
- taskId: this.data.interactionId,
1013
- error: error.toString(),
1014
- ...taskErrorProps,
1015
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1016
- },
1017
- ['operational', 'behavioral', 'business']
1018
- );
1019
- throw err;
1020
- }
1021
- }
1022
-
1023
- /**
1024
- * Resumes the recording for the voice task that was previously paused.
1025
- * Emits task:recordingResumed event when successful.
1026
- *
1027
- * @param resumeRecordingPayload - Configuration for resuming recording:
1028
- * - autoResumed: Indicates if resume was automatic (defaults to false)
1029
- * @returns Promise<TaskResponse>
1030
- * @throws Error if resume recording fails
1031
- * @example
1032
- * ```typescript
1033
- * // Set up recording resume events
1034
- * task.on(TASK_EVENTS.TASK_RECORDING_RESUMED, () => {
1035
- * console.log('Recording resumed');
1036
- * // Update UI to show active recording state
1037
- * document.getElementById('recording-status').textContent = 'Recording Active';
1038
- * document.getElementById('pause-recording-btn').style.display = 'block';
1039
- * document.getElementById('resume-recording-btn').style.display = 'none';
1040
- * });
1041
- *
1042
- * task.on(TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, (error) => {
1043
- * console.error('Failed to resume recording:', error);
1044
- * // Show error to agent
1045
- * });
1046
- *
1047
- * // Resume recording
1048
- * try {
1049
- * const resumePayload = {
1050
- * autoResumed: false // Set to true if triggered by system
1051
- * };
1052
- * await task.resumeRecording(resumePayload);
1053
- * console.log('Resume recording request sent');
1054
- * } catch (error) {
1055
- * console.error('Error sending resume recording request:', error);
1056
- * // Handle error
1057
- * }
1058
- * ```
1059
- */
1060
- public async resumeRecording(
1061
- resumeRecordingPayload: ResumeRecordingPayload
1062
- ): Promise<TaskResponse> {
1063
- try {
1064
- LoggerProxy.info(`Resuming recording`, {
1065
- module: TASK_FILE,
1066
- method: METHODS.RESUME_RECORDING,
1067
- interactionId: this.data.interactionId,
1068
- });
1069
-
1070
- this.metricsManager.timeEvent([
1071
- METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS,
1072
- METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED,
1073
- ]);
1074
-
1075
- resumeRecordingPayload ??= {autoResumed: false};
1076
-
1077
- const result = await this.contact.resumeRecording({
1078
- interactionId: this.data.interactionId,
1079
- data: resumeRecordingPayload,
1080
- });
1081
-
1082
- this.metricsManager.trackEvent(
1083
- METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS,
1084
- {
1085
- taskId: this.data.interactionId,
1086
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1087
- },
1088
- ['operational', 'behavioral', 'business']
1089
- );
1090
-
1091
- LoggerProxy.log(`Recording resumed successfully`, {
1092
- module: TASK_FILE,
1093
- method: METHODS.RESUME_RECORDING,
1094
- trackingId: result.trackingId,
1095
- interactionId: this.data.interactionId,
1096
- });
1097
-
1098
- return result;
1099
- } catch (error) {
1100
- const err = generateTaskErrorObject(error, METHODS.RESUME_RECORDING, TASK_FILE);
1101
- const taskErrorProps = {
1102
- trackingId: err.data?.trackingId,
1103
- errorMessage: err.data?.message,
1104
- errorType: err.data?.errorType,
1105
- errorData: err.data?.errorData,
1106
- reasonCode: err.data?.reasonCode,
1107
- };
1108
- this.metricsManager.trackEvent(
1109
- METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED,
1110
- {
1111
- taskId: this.data.interactionId,
1112
- error: error.toString(),
1113
- ...taskErrorProps,
1114
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1115
- },
1116
- ['operational', 'behavioral', 'business']
1117
- );
1118
- throw err;
1119
- }
1120
- }
1121
-
1122
- /**
1123
- * Consults another agent or queue on an ongoing task for further assistance.
1124
- * During consultation, the original customer is typically placed on hold while
1125
- * the agent seeks guidance from another agent or queue.
1126
- *
1127
- * @param consultPayload - Configuration for the consultation containing:
1128
- * - to: ID of the agent or queue to consult with
1129
- * - destinationType: Type of destination (AGENT, QUEUE, etc.)
1130
- * - holdParticipants: Whether to hold other participants (defaults to true)
1131
- * @returns Promise<TaskResponse> - Resolves with consultation result
1132
- * @throws Error if consultation fails or invalid parameters provided
1133
- * @example
1134
- * ```typescript
1135
- * // Consult with another agent
1136
- * const consultPayload = {
1137
- * to: 'agentId123',
1138
- * destinationType: DESTINATION_TYPE.AGENT,
1139
- * holdParticipants: true
1140
- * };
1141
- * task.consult(consultPayload)
1142
- * .then(response => console.log('Consultation started successfully'))
1143
- * .catch(error => console.error('Failed to start consultation:', error));
1144
- *
1145
- * // Consult with a queue
1146
- * const queueConsultPayload = {
1147
- * to: 'salesQueue123',
1148
- * destinationType: DESTINATION_TYPE.QUEUE
1149
- * };
1150
- * task.consult(queueConsultPayload)
1151
- * .then(response => console.log('Queue consultation started'))
1152
- * .catch(error => console.error('Failed to start queue consultation:', error));
1153
- * ```
1154
- */
1155
- public async consult(consultPayload: ConsultPayload): Promise<TaskResponse> {
1156
- try {
1157
- LoggerProxy.info(`Starting consult`, {
1158
- module: TASK_FILE,
1159
- method: METHODS.CONSULT,
1160
- interactionId: this.data.interactionId,
1161
- });
1162
-
1163
- this.metricsManager.timeEvent([
1164
- METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
1165
- METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED,
1166
- ]);
1167
-
1168
- const result = await this.contact.consult({
1169
- interactionId: this.data.interactionId,
1170
- data: consultPayload,
1171
- });
1172
-
1173
- this.metricsManager.trackEvent(
1174
- METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
1175
- {
1176
- taskId: this.data.interactionId,
1177
- destination: consultPayload.to,
1178
- destinationType: consultPayload.destinationType,
1179
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1180
- },
1181
- ['operational', 'behavioral', 'business']
1182
- );
1183
-
1184
- LoggerProxy.log(`Consult started successfully to ${consultPayload.to}`, {
1185
- module: TASK_FILE,
1186
- method: METHODS.CONSULT,
1187
- trackingId: result.trackingId,
1188
- interactionId: this.data.interactionId,
1189
- });
1190
-
1191
- return result;
1192
- } catch (error) {
1193
- const err = generateTaskErrorObject(error, METHODS.CONSULT, TASK_FILE);
1194
- const taskErrorProps = {
1195
- trackingId: err.data?.trackingId,
1196
- errorMessage: err.data?.message,
1197
- errorType: err.data?.errorType,
1198
- errorData: err.data?.errorData,
1199
- reasonCode: err.data?.reasonCode,
1200
- };
1201
- this.metricsManager.trackEvent(
1202
- METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED,
1203
- {
1204
- taskId: this.data.interactionId,
1205
- destination: consultPayload.to,
1206
- destinationType: consultPayload.destinationType,
1207
- error: error.toString(),
1208
- ...taskErrorProps,
1209
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1210
- },
1211
- ['operational', 'behavioral', 'business']
1212
- );
1213
- throw err;
1214
- }
1215
- }
1216
-
1217
- /**
1218
- * Ends an ongoing consultation session for the task.
1219
- * This terminates the consultation while maintaining the original customer connection.
1220
- *
1221
- * @param consultEndPayload - Configuration for ending the consultation containing:
1222
- * - isConsult: Must be true to indicate this is a consultation end
1223
- * - taskId: ID of the task being consulted on
1224
- * - queueId: (Optional) Queue ID if this was a queue consultation
1225
- * - isSecondaryEpDnAgent: (Optional) Indicates if this involves a secondary entry point
1226
- * @returns Promise<TaskResponse> - Resolves when consultation is ended
1227
- * @throws Error if ending consultation fails or invalid parameters provided
1228
- * @example
1229
- * ```typescript
1230
- * // End a direct agent consultation
1231
- * const consultEndPayload = {
1232
- * isConsult: true,
1233
- * taskId: 'task123'
1234
- * };
1235
- * task.endConsult(consultEndPayload)
1236
- * .then(response => console.log('Consultation ended successfully'))
1237
- * .catch(error => console.error('Failed to end consultation:', error));
1238
- *
1239
- * // End a queue consultation
1240
- * const queueConsultEndPayload = {
1241
- * isConsult: true,
1242
- * taskId: 'task123',
1243
- * queueId: 'queue123'
1244
- * };
1245
- * task.endConsult(queueConsultEndPayload)
1246
- * .then(response => console.log('Queue consultation ended'))
1247
- * .catch(error => console.error('Failed to end queue consultation:', error));
1248
- * ```
1249
- */
1250
- public async endConsult(consultEndPayload: ConsultEndPayload): Promise<TaskResponse> {
1251
- try {
1252
- LoggerProxy.info(`Ending consult`, {
1253
- module: TASK_FILE,
1254
- method: METHODS.END_CONSULT,
1255
- interactionId: this.data.interactionId,
1256
- });
1257
-
1258
- this.metricsManager.timeEvent([
1259
- METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS,
1260
- METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED,
1261
- ]);
1262
-
1263
- const result = await this.contact.consultEnd({
1264
- interactionId: this.data.interactionId,
1265
- data: consultEndPayload,
1266
- });
1267
-
1268
- this.metricsManager.trackEvent(
1269
- METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS,
1270
- {
1271
- taskId: this.data.interactionId,
1272
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1273
- },
1274
- ['operational', 'behavioral', 'business']
1275
- );
1276
-
1277
- LoggerProxy.log(`Consult ended successfully`, {
1278
- module: TASK_FILE,
1279
- method: METHODS.END_CONSULT,
1280
- trackingId: result.trackingId,
1281
- interactionId: this.data.interactionId,
1282
- });
1283
-
1284
- return result;
1285
- } catch (error) {
1286
- const err = generateTaskErrorObject(error, METHODS.END_CONSULT, TASK_FILE);
1287
- const taskErrorProps = {
1288
- trackingId: err.data?.trackingId,
1289
- errorMessage: err.data?.message,
1290
- errorType: err.data?.errorType,
1291
- errorData: err.data?.errorData,
1292
- reasonCode: err.data?.reasonCode,
1293
- };
1294
- this.metricsManager.trackEvent(
1295
- METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED,
1296
- {
1297
- taskId: this.data.interactionId,
1298
- error: error.toString(),
1299
- ...taskErrorProps,
1300
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1301
- },
1302
- ['operational', 'behavioral', 'business']
1303
- );
1304
- throw err;
1305
- }
1306
- }
1307
-
1308
- /**
1309
- * Transfer the task to an agent directly or to a queue.
1310
- * This is a blind transfer that immediately redirects the task to the specified destination.
1311
- *
1312
- * @param transferPayload - Transfer configuration containing:
1313
- * - to: ID of the agent or queue to transfer to
1314
- * - destinationType: Type of destination (AGENT, QUEUE, etc.)
1315
- * @returns Promise<TaskResponse> - Resolves when transfer is completed
1316
- * @throws Error if transfer fails or invalid parameters provided
1317
- * @example
1318
- * ```typescript
1319
- * // Transfer to a queue
1320
- * const queueTransferPayload = {
1321
- * to: 'salesQueue123',
1322
- * destinationType: DESTINATION_TYPE.QUEUE
1323
- * };
1324
- * task.transfer(queueTransferPayload)
1325
- * .then(response => console.log('Task transferred to queue successfully'))
1326
- * .catch(error => console.error('Failed to transfer to queue:', error));
1327
- *
1328
- * // Transfer to an agent
1329
- * const agentTransferPayload = {
1330
- * to: 'agentId123',
1331
- * destinationType: DESTINATION_TYPE.AGENT
1332
- * };
1333
- * task.transfer(agentTransferPayload)
1334
- * .then(response => console.log('Task transferred to agent successfully'))
1335
- * .catch(error => console.error('Failed to transfer to agent:', error));
1336
- * ```
1337
- */
1338
- public async transfer(transferPayload: TransferPayLoad): Promise<TaskResponse> {
1339
- try {
1340
- LoggerProxy.info(`Transferring task to ${transferPayload.to}`, {
1341
- module: TASK_FILE,
1342
- method: METHODS.TRANSFER,
1343
- interactionId: this.data.interactionId,
1344
- });
1345
-
1346
- this.metricsManager.timeEvent([
1347
- METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
1348
- METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
1349
- ]);
1350
-
1351
- let result: TaskResponse;
1352
- if (transferPayload.destinationType === DESTINATION_TYPE.QUEUE) {
1353
- result = await this.contact.vteamTransfer({
1354
- interactionId: this.data.interactionId,
1355
- data: transferPayload,
1356
- });
1357
- } else {
1358
- result = await this.contact.blindTransfer({
1359
- interactionId: this.data.interactionId,
1360
- data: transferPayload,
1361
- });
1362
- }
1363
-
1364
- this.metricsManager.trackEvent(
1365
- METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
1366
- {
1367
- taskId: this.data.interactionId,
1368
- destination: transferPayload.to,
1369
- destinationType: transferPayload.destinationType,
1370
- isConsultTransfer: false,
1371
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1372
- },
1373
- ['operational', 'behavioral', 'business']
1374
- );
1375
-
1376
- LoggerProxy.log(`Task transferred successfully to ${transferPayload.to}`, {
1377
- module: TASK_FILE,
1378
- method: METHODS.TRANSFER,
1379
- trackingId: result.trackingId,
1380
- interactionId: this.data.interactionId,
1381
- });
1382
-
1383
- return result;
1384
- } catch (error) {
1385
- const err = generateTaskErrorObject(error, METHODS.TRANSFER, TASK_FILE);
1386
- const taskErrorProps = {
1387
- trackingId: err.data?.trackingId,
1388
- errorMessage: err.data?.message,
1389
- errorType: err.data?.errorType,
1390
- errorData: err.data?.errorData,
1391
- reasonCode: err.data?.reasonCode,
1392
- };
1393
- this.metricsManager.trackEvent(
1394
- METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
1395
- {
1396
- taskId: this.data.interactionId,
1397
- destination: transferPayload.to,
1398
- destinationType: transferPayload.destinationType,
1399
- isConsultTransfer: false,
1400
- error: error.toString(),
1401
- ...taskErrorProps,
1402
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1403
- },
1404
- ['operational', 'behavioral', 'business']
1405
- );
1406
- throw err;
1407
- }
1408
- }
1409
-
1410
- /**
1411
- * Transfer the task to the party that was consulted.
1412
- * This completes a consultative transfer where the agent first consulted with the target
1413
- * before transferring the task. For queue consultations, the transfer is automatically
1414
- * directed to the agent who accepted the consultation.
1415
- *
1416
- * @param consultTransferPayload - Configuration for the consultation transfer containing:
1417
- * - to: ID of the agent or queue to transfer to
1418
- * - destinationType: Type of destination (AGENT, QUEUE, etc. from CONSULT_TRANSFER_DESTINATION_TYPE)
1419
- * @returns Promise<TaskResponse> - Resolves when consultation transfer is completed
1420
- * @throws Error if transfer fails, no agent has accepted a queue consultation, or other validation errors
1421
- * @example
1422
- * ```typescript
1423
- * // Complete consultation transfer to an agent
1424
- * const agentConsultTransfer = {
1425
- * to: 'agentId123',
1426
- * destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT
1427
- * };
1428
- * task.consultTransfer(agentConsultTransfer)
1429
- * .then(response => console.log('Consultation transfer to agent completed'))
1430
- * .catch(error => console.error('Failed to complete agent consultation transfer:', error));
1431
- *
1432
- * // Complete consultation transfer to a queue agent
1433
- * const queueConsultTransfer = {
1434
- * to: 'queue123',
1435
- * destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE
1436
- * };
1437
- * task.consultTransfer(queueConsultTransfer)
1438
- * .then(response => console.log('Consultation transfer to queue agent completed'))
1439
- * .catch(error => console.error('Failed to complete queue consultation transfer:', error));
1440
- * ```
1441
- */
1442
- public async consultTransfer(
1443
- consultTransferPayload?: ConsultTransferPayLoad
1444
- ): Promise<TaskResponse> {
1445
- // Get the destination agent ID using custom logic from participants data
1446
- const destAgentId = calculateDestAgentId(this.data.interaction, this.agentId);
1447
-
1448
- // Resolve the target id (queue consult transfers go to the accepted agent)
1449
- if (!destAgentId) {
1450
- throw new Error('No agent has accepted this queue consult yet');
1451
- }
1452
-
1453
- LoggerProxy.info(
1454
- `Initiating consult transfer to ${consultTransferPayload?.to || destAgentId}`,
1455
- {
1456
- module: TASK_FILE,
1457
- method: METHODS.CONSULT_TRANSFER,
1458
- interactionId: this.data.interactionId,
1459
- }
1460
- );
1461
-
1462
- // Derive destination type from the participant's type property
1463
- const destType = calculateDestType(this.data.interaction, this.agentId);
1464
- // By default we always use the computed destAgentId as the target id
1465
- const consultTransferRequest: ConsultTransferPayLoad = {
1466
- to: destAgentId,
1467
- destinationType: destType,
1468
- };
1469
- try {
1470
- const result = await this.contact.consultTransfer({
1471
- interactionId: this.data.interactionId,
1472
- data: consultTransferRequest,
1473
- });
1474
-
1475
- this.metricsManager.trackEvent(
1476
- METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
1477
- {
1478
- taskId: this.data.interactionId,
1479
- destination: consultTransferRequest.to,
1480
- destinationType: consultTransferRequest.destinationType,
1481
- isConsultTransfer: true,
1482
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1483
- },
1484
- ['operational', 'behavioral', 'business']
1485
- );
1486
-
1487
- LoggerProxy.log(
1488
- `Consult transfer completed successfully to ${consultTransferPayload?.to || destAgentId}`,
1489
- {
1490
- module: TASK_FILE,
1491
- method: METHODS.CONSULT_TRANSFER,
1492
- trackingId: result.trackingId,
1493
- interactionId: this.data.interactionId,
1494
- }
1495
- );
1496
-
1497
- return result;
1498
- } catch (error) {
1499
- const err = generateTaskErrorObject(error, METHODS.CONSULT_TRANSFER, TASK_FILE);
1500
- const taskErrorProps = {
1501
- trackingId: err.data?.trackingId,
1502
- errorMessage: err.data?.message,
1503
- errorType: err.data?.errorType,
1504
- errorData: err.data?.errorData,
1505
- reasonCode: err.data?.reasonCode,
1506
- };
1507
- this.metricsManager.trackEvent(
1508
- METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
1509
- {
1510
- taskId: this.data.interactionId,
1511
- destination: destAgentId || '',
1512
- destinationType: destType,
1513
- isConsultTransfer: true,
1514
- error: error.toString(),
1515
- ...taskErrorProps,
1516
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1517
- },
1518
- ['operational', 'behavioral', 'business']
1519
- );
1520
- throw err;
1521
- }
1522
- }
1523
-
1524
- /**
1525
- * Starts a consultation conference by merging the consultation call with the main call
1526
- *
1527
- * Creates a three-way conference between the agent, customer, and consulted party
1528
- * Extracts required consultation data from the current task data
1529
- * On success, emits a `task:conferenceStarted` event
1530
- *
1531
- * @returns Promise<TaskResponse> - Response from the consultation conference API
1532
- * @throws Error if the operation fails or if consultation data is invalid
1533
- *
1534
- * @example
1535
- * ```typescript
1536
- * try {
1537
- * await task.consultConference();
1538
- * console.log('Conference started successfully');
1539
- * } catch (error) {
1540
- * console.error('Failed to start conference:', error);
1541
- * }
1542
- * ```
1543
- */
1544
- public async consultConference(): Promise<TaskResponse> {
1545
- // Get the destination agent ID dynamically from participants
1546
- // This handles multi-party conference scenarios, CBT (Capacity Based Team), and EP-DN cases
1547
- const destAgentId = calculateDestAgentId(this.data.interaction, this.agentId);
1548
-
1549
- // Validate that we have a destination agent (for queue consult scenarios)
1550
- if (!destAgentId) {
1551
- throw new Error('No agent has accepted this queue consult yet');
1552
- }
1553
-
1554
- // Get the destination agent ID for fetching destination type
1555
- // This helps determine the correct participant type for CBT (Capacity Based Team) and EP-DN scenarios
1556
- const destAgentType = calculateDestType(this.data.interaction, this.agentId);
1557
-
1558
- // Extract consultation conference data from task data (used in both try and catch)
1559
- const consultationData = {
1560
- agentId: this.agentId,
1561
- to: destAgentId,
1562
- destinationType: destAgentType || this.data.destinationType || 'agent',
1563
- };
1564
-
1565
- try {
1566
- LoggerProxy.info(`Initiating consult conference to ${destAgentId}`, {
1567
- module: TASK_FILE,
1568
- method: METHODS.CONSULT_CONFERENCE,
1569
- interactionId: this.data.interactionId,
1570
- });
1571
-
1572
- const response = await this.contact.consultConference({
1573
- interactionId: this.data.interactionId,
1574
- data: consultationData,
1575
- });
1576
-
1577
- // Track success metrics (following consultTransfer pattern)
1578
- this.metricsManager.trackEvent(
1579
- METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS,
1580
- {
1581
- taskId: this.data.interactionId,
1582
- destination: consultationData.to,
1583
- destinationType: consultationData.destinationType,
1584
- agentId: consultationData.agentId,
1585
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1586
- },
1587
- ['operational', 'behavioral', 'business']
1588
- );
1589
-
1590
- LoggerProxy.log(`Consult conference started successfully`, {
1591
- module: TASK_FILE,
1592
- method: METHODS.CONSULT_CONFERENCE,
1593
- interactionId: this.data.interactionId,
1594
- });
1595
-
1596
- return response;
1597
- } catch (error) {
1598
- const err = generateTaskErrorObject(error, METHODS.CONSULT_CONFERENCE, TASK_FILE);
1599
- const taskErrorProps = {
1600
- trackingId: err.data?.trackingId,
1601
- errorMessage: err.data?.message,
1602
- errorType: err.data?.errorType,
1603
- errorData: err.data?.errorData,
1604
- reasonCode: err.data?.reasonCode,
1605
- };
1606
-
1607
- this.metricsManager.trackEvent(
1608
- METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED,
1609
- {
1610
- taskId: this.data.interactionId,
1611
- destination: consultationData.to,
1612
- destinationType: consultationData.destinationType,
1613
- agentId: consultationData.agentId,
1614
- error: error.toString(),
1615
- ...taskErrorProps,
1616
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1617
- },
1618
- ['operational', 'behavioral', 'business']
1619
- );
1620
-
1621
- LoggerProxy.error(`Failed to start consult conference`, {
1622
- module: TASK_FILE,
1623
- method: METHODS.CONSULT_CONFERENCE,
1624
- interactionId: this.data.interactionId,
1625
- });
1626
-
1627
- throw err;
1628
- }
1629
- }
1630
-
1631
- /**
1632
- * Exits the current conference by removing the agent from the conference call
1633
- *
1634
- * Exits the agent from the conference, leaving the customer and consulted party connected
1635
- * On success, emits a `task:conferenceEnded` event
1636
- *
1637
- * @returns Promise<TaskResponse> - Response from the conference exit API
1638
- * @throws Error if the operation fails or if no active conference exists
1639
- *
1640
- * @example
1641
- * ```typescript
1642
- * try {
1643
- * await task.exitConference();
1644
- * console.log('Successfully exited conference');
1645
- * } catch (error) {
1646
- * console.error('Failed to exit conference:', error);
1647
- * }
1648
- * ```
1649
- */
1650
- public async exitConference(): Promise<TaskResponse> {
1651
- try {
1652
- LoggerProxy.info(`Exiting consult conference`, {
1653
- module: TASK_FILE,
1654
- method: METHODS.EXIT_CONFERENCE,
1655
- interactionId: this.data.interactionId,
1656
- });
1657
-
1658
- // Validate that interaction ID exists
1659
- if (!this.data.interactionId) {
1660
- throw new Error('Invalid interaction ID');
1661
- }
1662
-
1663
- const response = await this.contact.exitConference({
1664
- interactionId: this.data.interactionId,
1665
- });
1666
-
1667
- // Track success metrics (following consultTransfer pattern)
1668
- this.metricsManager.trackEvent(
1669
- METRIC_EVENT_NAMES.TASK_CONFERENCE_END_SUCCESS,
1670
- {
1671
- taskId: this.data.interactionId,
1672
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1673
- },
1674
- ['operational', 'behavioral', 'business']
1675
- );
1676
-
1677
- LoggerProxy.log(`Consult conference exited successfully`, {
1678
- module: TASK_FILE,
1679
- method: METHODS.EXIT_CONFERENCE,
1680
- interactionId: this.data.interactionId,
1681
- });
1682
-
1683
- return response;
1684
- } catch (error) {
1685
- const err = generateTaskErrorObject(error, METHODS.EXIT_CONFERENCE, TASK_FILE);
1686
- const taskErrorProps = {
1687
- trackingId: err.data?.trackingId,
1688
- errorMessage: err.data?.message,
1689
- errorType: err.data?.errorType,
1690
- errorData: err.data?.errorData,
1691
- reasonCode: err.data?.reasonCode,
1692
- };
1693
-
1694
- // Track failure metrics (following consultTransfer pattern)
1695
- this.metricsManager.trackEvent(
1696
- METRIC_EVENT_NAMES.TASK_CONFERENCE_END_FAILED,
1697
- {
1698
- taskId: this.data.interactionId,
1699
- error: error.toString(),
1700
- ...taskErrorProps,
1701
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1702
- },
1703
- ['operational', 'behavioral', 'business']
1704
- );
1705
-
1706
- LoggerProxy.error(`Failed to exit consult conference`, {
1707
- module: TASK_FILE,
1708
- method: METHODS.EXIT_CONFERENCE,
1709
- interactionId: this.data.interactionId,
1710
- });
1711
-
1712
- throw err;
1713
- }
1714
- }
1715
-
1716
- /**
1717
- * Transfers the current conference to another agent
1718
- *
1719
- * Moves the entire conference (including all participants) to a new agent,
1720
- * while the current agent exits and goes to wrapup
1721
- * On success, the current agent receives `task:conferenceEnded` event
1722
- *
1723
- * @returns Promise<TaskResponse> - Response from the conference transfer API
1724
- * @throws Error if the operation fails or if no active conference exists
1725
- *
1726
- * @example
1727
- * ```typescript
1728
- * try {
1729
- * await task.transferConference();
1730
- * console.log('Conference transferred successfully');
1731
- * } catch (error) {
1732
- * console.error('Failed to transfer conference:', error);
1733
- * }
1734
- * ```
1735
- */
1736
- public async transferConference(): Promise<TaskResponse> {
1737
- try {
1738
- LoggerProxy.info(`Transferring conference`, {
1739
- module: TASK_FILE,
1740
- method: METHODS.TRANSFER_CONFERENCE,
1741
- interactionId: this.data.interactionId,
1742
- });
1743
-
1744
- // Validate that interaction ID exists
1745
- if (!this.data.interactionId) {
1746
- throw new Error('Invalid interaction ID');
1747
- }
1748
-
1749
- const response = await this.contact.conferenceTransfer({
1750
- interactionId: this.data.interactionId,
1751
- });
1752
-
1753
- // Track success metrics (following consultTransfer pattern)
1754
- this.metricsManager.trackEvent(
1755
- METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS,
1756
- {
1757
- taskId: this.data.interactionId,
1758
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1759
- },
1760
- ['operational', 'behavioral', 'business']
1761
- );
1762
-
1763
- LoggerProxy.log(`Conference transferred successfully`, {
1764
- module: TASK_FILE,
1765
- method: METHODS.TRANSFER_CONFERENCE,
1766
- interactionId: this.data.interactionId,
1767
- });
1768
-
1769
- return response;
1770
- } catch (error) {
1771
- const err = generateTaskErrorObject(error, METHODS.TRANSFER_CONFERENCE, TASK_FILE);
1772
- const taskErrorProps = {
1773
- trackingId: err.data?.trackingId,
1774
- errorMessage: err.data?.message,
1775
- errorType: err.data?.errorType,
1776
- errorData: err.data?.errorData,
1777
- reasonCode: err.data?.reasonCode,
1778
- };
1779
-
1780
- // Track failure metrics (following consultTransfer pattern)
1781
- this.metricsManager.trackEvent(
1782
- METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED,
1783
- {
1784
- taskId: this.data.interactionId,
1785
- error: error.toString(),
1786
- ...taskErrorProps,
1787
- ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1788
- },
1789
- ['operational', 'behavioral', 'business']
1790
- );
1791
-
1792
- LoggerProxy.error(`Failed to transfer conference`, {
1793
- module: TASK_FILE,
1794
- method: METHODS.TRANSFER_CONFERENCE,
1795
- interactionId: this.data.interactionId,
1796
- });
1797
-
1798
- throw err;
1799
- }
1800
- }
1801
- }