@webex/contact-center 3.9.0-next.10 → 3.9.0-next.11

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 (46) hide show
  1. package/dist/cc.js +1 -0
  2. package/dist/cc.js.map +1 -1
  3. package/dist/metrics/behavioral-events.js +37 -0
  4. package/dist/metrics/behavioral-events.js.map +1 -1
  5. package/dist/metrics/constants.js +14 -0
  6. package/dist/metrics/constants.js.map +1 -1
  7. package/dist/services/config/types.js +22 -0
  8. package/dist/services/config/types.js.map +1 -1
  9. package/dist/services/core/Utils.js +42 -1
  10. package/dist/services/core/Utils.js.map +1 -1
  11. package/dist/services/task/TaskManager.js +73 -2
  12. package/dist/services/task/TaskManager.js.map +1 -1
  13. package/dist/services/task/constants.js +7 -1
  14. package/dist/services/task/constants.js.map +1 -1
  15. package/dist/services/task/contact.js +86 -0
  16. package/dist/services/task/contact.js.map +1 -1
  17. package/dist/services/task/index.js +239 -1
  18. package/dist/services/task/index.js.map +1 -1
  19. package/dist/services/task/types.js +14 -0
  20. package/dist/services/task/types.js.map +1 -1
  21. package/dist/types/metrics/constants.d.ts +13 -0
  22. package/dist/types/services/config/types.d.ts +44 -0
  23. package/dist/types/services/core/Utils.d.ts +14 -1
  24. package/dist/types/services/task/constants.d.ts +6 -0
  25. package/dist/types/services/task/contact.d.ts +10 -0
  26. package/dist/types/services/task/index.d.ts +43 -1
  27. package/dist/types/services/task/types.d.ts +123 -1
  28. package/dist/webex.js +1 -1
  29. package/package.json +1 -1
  30. package/src/cc.ts +1 -0
  31. package/src/metrics/behavioral-events.ts +38 -0
  32. package/src/metrics/constants.ts +15 -0
  33. package/src/services/config/types.ts +22 -0
  34. package/src/services/core/Utils.ts +44 -0
  35. package/src/services/task/TaskManager.ts +78 -3
  36. package/src/services/task/constants.ts +6 -0
  37. package/src/services/task/contact.ts +80 -0
  38. package/src/services/task/index.ts +285 -1
  39. package/src/services/task/types.ts +133 -0
  40. package/test/unit/spec/cc.ts +1 -0
  41. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  42. package/test/unit/spec/services/task/TaskManager.ts +137 -0
  43. package/test/unit/spec/services/task/contact.ts +31 -1
  44. package/test/unit/spec/services/task/index.ts +184 -1
  45. package/umd/contact-center.min.js +2 -2
  46. package/umd/contact-center.min.js.map +1 -1
@@ -51,6 +51,13 @@ type Enum<T extends Record<string, unknown>> = T[keyof T];
51
51
  * @property {string} TASK_ACCEPT_CONSULT_SUCCESS - Event name for successful consult acceptance.
52
52
  * @property {string} TASK_ACCEPT_CONSULT_FAILED - Event name for failed consult acceptance.
53
53
  *
54
+ * @property {string} TASK_CONFERENCE_START_SUCCESS - Event name for successful conference start.
55
+ * @property {string} TASK_CONFERENCE_START_FAILED - Event name for failed conference start.
56
+ * @property {string} TASK_CONFERENCE_END_SUCCESS - Event name for successful conference end.
57
+ * @property {string} TASK_CONFERENCE_END_FAILED - Event name for failed conference end.
58
+ * @property {string} TASK_CONFERENCE_TRANSFER_SUCCESS - Event name for successful conference transfer.
59
+ * @property {string} TASK_CONFERENCE_TRANSFER_FAILED - Event name for failed conference transfer.
60
+ *
54
61
  * @property {string} TASK_OUTDIAL_SUCCESS - Event name for successful outdial task.
55
62
  * @property {string} TASK_OUTDIAL_FAILED - Event name for failed outdial task.
56
63
  *
@@ -109,6 +116,14 @@ export const METRIC_EVENT_NAMES = {
109
116
  TASK_ACCEPT_CONSULT_SUCCESS: 'Task Accept Consult Success',
110
117
  TASK_ACCEPT_CONSULT_FAILED: 'Task Accept Consult Failed',
111
118
 
119
+ // Conference Tasks
120
+ TASK_CONFERENCE_START_SUCCESS: 'Task Conference Start Success',
121
+ TASK_CONFERENCE_START_FAILED: 'Task Conference Start Failed',
122
+ TASK_CONFERENCE_END_SUCCESS: 'Task Conference End Success',
123
+ TASK_CONFERENCE_END_FAILED: 'Task Conference End Failed',
124
+ TASK_CONFERENCE_TRANSFER_SUCCESS: 'Task Conference Transfer Success',
125
+ TASK_CONFERENCE_TRANSFER_FAILED: 'Task Conference Transfer Failed',
126
+
112
127
  TASK_OUTDIAL_SUCCESS: 'Task Outdial Success',
113
128
  TASK_OUTDIAL_FAILED: 'Task Outdial Failed',
114
129
 
@@ -45,6 +45,28 @@ export const CC_TASK_EVENTS = {
45
45
  AGENT_CONSULT_END_FAILED: 'AgentConsultEndFailed',
46
46
  /** Event emitted when consultation conference ends */
47
47
  AGENT_CONSULT_CONFERENCE_ENDED: 'AgentConsultConferenceEnded',
48
+ /** Event emitted when consultation conference is in progress */
49
+ AGENT_CONSULT_CONFERENCING: 'AgentConsultConferencing',
50
+ /** Event emitted when consultation conference starts */
51
+ AGENT_CONSULT_CONFERENCED: 'AgentConsultConferenced',
52
+ /** Event emitted when consultation conference fails */
53
+ AGENT_CONSULT_CONFERENCE_FAILED: 'AgentConsultConferenceFailed',
54
+ /** Event emitted when participant joins conference */
55
+ PARTICIPANT_JOINED_CONFERENCE: 'ParticipantJoinedConference',
56
+ /** Event emitted when participant leaves conference */
57
+ PARTICIPANT_LEFT_CONFERENCE: 'ParticipantLeftConference',
58
+ /** Event emitted when participant leaving conference fails */
59
+ PARTICIPANT_LEFT_CONFERENCE_FAILED: 'ParticipantLeftConferenceFailed',
60
+ /** Event emitted when consultation conference end fails */
61
+ AGENT_CONSULT_CONFERENCE_END_FAILED: 'AgentConsultConferenceEndFailed',
62
+ /** Event emitted when conference is successfully transferred */
63
+ AGENT_CONFERENCE_TRANSFERRED: 'AgentConferenceTransferred',
64
+ /** Event emitted when conference transfer fails */
65
+ AGENT_CONFERENCE_TRANSFER_FAILED: 'AgentConferenceTransferFailed',
66
+ /** Event emitted when consulted participant is moving/being transferred */
67
+ CONSULTED_PARTICIPANT_MOVING: 'ConsultedParticipantMoving',
68
+ /** Event emitted for post-call activity by participant */
69
+ PARTICIPANT_POST_CALL_ACTIVITY: 'ParticipantPostCallActivity',
48
70
  /** Event emitted when contact is blind transferred */
49
71
  AGENT_BLIND_TRANSFERRED: 'AgentBlindTransferred',
50
72
  /** Event emitted when blind transfer fails */
@@ -6,6 +6,8 @@ import WebexRequest from './WebexRequest';
6
6
  import {
7
7
  TaskData,
8
8
  ConsultTransferPayLoad,
9
+ ConsultConferenceData,
10
+ consultConferencePayloadData,
9
11
  CONSULT_TRANSFER_DESTINATION_TYPE,
10
12
  Interaction,
11
13
  } from '../task/types';
@@ -284,3 +286,45 @@ export const deriveConsultTransferDestinationType = (
284
286
 
285
287
  return CONSULT_TRANSFER_DESTINATION_TYPE.AGENT;
286
288
  };
289
+
290
+ /**
291
+ * Builds consult conference parameter data using EXACT Agent Desktop logic.
292
+ * This matches the Agent Desktop's consultConference implementation exactly.
293
+ *
294
+ * @param dataPassed - Original consultation data from Agent Desktop format
295
+ * @param interactionIdPassed - The interaction ID for the task
296
+ * @returns Object with interactionId and ConsultConferenceData matching Agent Desktop format
297
+ * @public
298
+ */
299
+ export const buildConsultConferenceParamData = (
300
+ dataPassed: consultConferencePayloadData,
301
+ interactionIdPassed: string
302
+ ): {interactionId: string; data: ConsultConferenceData} => {
303
+ const data: ConsultConferenceData = {
304
+ // Include agentId if present in input data
305
+ ...('agentId' in dataPassed && {agentId: dataPassed.agentId}),
306
+ // Handle destAgentId from consultation data
307
+ to: dataPassed.destAgentId,
308
+ destinationType: '',
309
+ };
310
+
311
+ // Agent Desktop destination type logic
312
+ if ('destinationType' in dataPassed) {
313
+ if (dataPassed.destinationType === 'DN') {
314
+ data.destinationType = CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER;
315
+ } else if (dataPassed.destinationType === 'EP_DN') {
316
+ data.destinationType = CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT;
317
+ } else {
318
+ // Keep the existing destinationType if it's something else (like "agent" or "Agent")
319
+ // Convert "Agent" to lowercase for consistency
320
+ data.destinationType = dataPassed.destinationType.toLowerCase();
321
+ }
322
+ } else {
323
+ data.destinationType = CONSULT_TRANSFER_DESTINATION_TYPE.AGENT;
324
+ }
325
+
326
+ return {
327
+ interactionId: interactionIdPassed,
328
+ data,
329
+ };
330
+ };
@@ -28,6 +28,7 @@ export default class TaskManager extends EventEmitter {
28
28
  private metricsManager: MetricsManager;
29
29
  private static taskManager;
30
30
  private wrapupData: WrapupData;
31
+ private agentId: string;
31
32
  /**
32
33
  * @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
33
34
  * @param webCallingService - Webrtc Service Layer
@@ -52,6 +53,19 @@ export default class TaskManager extends EventEmitter {
52
53
  this.wrapupData = wrapupData;
53
54
  }
54
55
 
56
+ public setAgentId(agentId: string) {
57
+ this.agentId = agentId;
58
+ }
59
+
60
+ /**
61
+ * Gets the current agent ID
62
+ * @returns {string} The agent ID set for this task manager instance
63
+ * @public
64
+ */
65
+ public getAgentId(): string {
66
+ return this.agentId;
67
+ }
68
+
55
69
  private handleIncomingWebCall = (call: ICall) => {
56
70
  const currentTask = Object.values(this.taskCollection).find(
57
71
  (task) => task.data.interaction.mediaType === 'telephony'
@@ -117,7 +131,8 @@ export default class TaskManager extends EventEmitter {
117
131
  payload.data.interaction?.participants?.[payload.data.agentId]?.isWrapUp ||
118
132
  false,
119
133
  },
120
- this.wrapupData
134
+ this.wrapupData,
135
+ this.agentId
121
136
  );
122
137
  this.taskCollection[payload.data.interactionId] = task;
123
138
  // Condition 1: The state is=new i.e it is a incoming task
@@ -153,8 +168,9 @@ export default class TaskManager extends EventEmitter {
153
168
  ...payload.data,
154
169
  isConsulted: false,
155
170
  },
156
- this.wrapupData
157
- ); // Ensure isConsulted prop exists
171
+ this.wrapupData,
172
+ this.agentId
173
+ );
158
174
  this.taskCollection[payload.data.interactionId] = task;
159
175
  if (
160
176
  this.webCallingService.loginOption !== LoginOption.BROWSER ||
@@ -324,6 +340,65 @@ export default class TaskManager extends EventEmitter {
324
340
  task = this.updateTaskData(task, payload.data);
325
341
  task.emit(TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, task);
326
342
  break;
343
+ case CC_EVENTS.AGENT_CONSULT_CONFERENCING:
344
+ // Conference is being established - update task state and emit establishing event
345
+ task = this.updateTaskData(task, payload.data);
346
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_ESTABLISHING, task);
347
+ break;
348
+ case CC_EVENTS.AGENT_CONSULT_CONFERENCED:
349
+ // Conference started successfully - update task state and emit event
350
+ task = this.updateTaskData(task, payload.data);
351
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
352
+ break;
353
+ case CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED:
354
+ // Conference failed - update task state and emit failure event
355
+ task = this.updateTaskData(task, payload.data);
356
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_FAILED, task);
357
+ break;
358
+ case CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED:
359
+ // Conference ended - update task state and emit event
360
+ task = this.updateTaskData(task, payload.data);
361
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_ENDED, task);
362
+ break;
363
+ case CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE:
364
+ // Participant joined conference - update task state with participant information and emit event
365
+ task = this.updateTaskData(task, payload.data);
366
+ task.emit(TASK_EVENTS.TASK_PARTICIPANT_JOINED, task);
367
+ break;
368
+ case CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE:
369
+ // Conference ended - update task state and emit event
370
+ task = this.updateTaskData(task, payload.data);
371
+ task.emit(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
372
+ break;
373
+ case CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE_FAILED:
374
+ // Conference exit failed - update task state and emit failure event
375
+ task = this.updateTaskData(task, payload.data);
376
+ task.emit(TASK_EVENTS.TASK_PARTICIPANT_LEFT_FAILED, task);
377
+ break;
378
+ case CC_EVENTS.AGENT_CONSULT_CONFERENCE_END_FAILED:
379
+ // Conference end failed - update task state with error details and emit failure event
380
+ task = this.updateTaskData(task, payload.data);
381
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_END_FAILED, task);
382
+ break;
383
+ case CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED:
384
+ // Conference was transferred - update task state and emit transfer success event
385
+ // Note: Backend should provide hasLeft and wrapUpRequired status
386
+ task = this.updateTaskData(task, payload.data);
387
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_TRANSFERRED, task);
388
+ break;
389
+ case CC_EVENTS.AGENT_CONFERENCE_TRANSFER_FAILED:
390
+ // Conference transfer failed - update task state with error details and emit failure event
391
+ task = this.updateTaskData(task, payload.data);
392
+ task.emit(TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, task);
393
+ break;
394
+ case CC_EVENTS.CONSULTED_PARTICIPANT_MOVING:
395
+ // Participant is being moved/transferred - update task state with movement info
396
+ task = this.updateTaskData(task, payload.data);
397
+ break;
398
+ case CC_EVENTS.PARTICIPANT_POST_CALL_ACTIVITY:
399
+ // Post-call activity for participant - update task state with activity details
400
+ task = this.updateTaskData(task, payload.data);
401
+ break;
327
402
  default:
328
403
  break;
329
404
  }
@@ -17,6 +17,9 @@ export const PAUSE = '/record/pause';
17
17
  export const RESUME = '/record/resume';
18
18
  export const WRAPUP = '/wrapup';
19
19
  export const END = '/end';
20
+ export const CONSULT_CONFERENCE = '/consult/conference';
21
+ export const CONFERENCE_EXIT = '/conference/exit';
22
+ export const CONFERENCE_TRANSFER = '/conference/transfer';
20
23
  export const TASK_MANAGER_FILE = 'taskManager';
21
24
  export const TASK_FILE = 'task';
22
25
 
@@ -36,6 +39,9 @@ export const METHODS = {
36
39
  END_CONSULT: 'endConsult',
37
40
  TRANSFER: 'transfer',
38
41
  CONSULT_TRANSFER: 'consultTransfer',
42
+ CONSULT_CONFERENCE: 'consultConference',
43
+ EXIT_CONFERENCE: 'exitConference',
44
+ TRANSFER_CONFERENCE: 'transferConference',
39
45
  UPDATE_TASK_DATA: 'updateTaskData',
40
46
  RECONCILE_DATA: 'reconcileData',
41
47
 
@@ -17,6 +17,9 @@ import {
17
17
  TRANSFER,
18
18
  UNHOLD,
19
19
  WRAPUP,
20
+ CONSULT_CONFERENCE,
21
+ CONFERENCE_EXIT,
22
+ CONFERENCE_TRANSFER,
20
23
  } from './constants';
21
24
  import * as Contact from './types';
22
25
  import {DESTINATION_TYPE} from './types';
@@ -425,5 +428,82 @@ export default function routingContact(aqm: AqmReqs) {
425
428
  errId: 'Service.aqm.task.cancelCtq',
426
429
  },
427
430
  })),
431
+
432
+ /*
433
+ * Start consult conference
434
+ */
435
+ consultConference: aqm.req(
436
+ (p: {interactionId: string; data: Contact.ConsultConferenceData}) => ({
437
+ url: `${TASK_API}${p.interactionId}${CONSULT_CONFERENCE}`,
438
+ data: p.data,
439
+ host: WCC_API_GATEWAY,
440
+ err,
441
+ notifSuccess: {
442
+ bind: {
443
+ type: TASK_MESSAGE_TYPE,
444
+ data: {
445
+ type: [CC_EVENTS.AGENT_CONSULT_CONFERENCED, CC_EVENTS.AGENT_CONSULT_CONFERENCING],
446
+ interactionId: p.interactionId,
447
+ }, // any of the two events can be received for API success event
448
+ },
449
+ msg: {} as Contact.AgentContact,
450
+ },
451
+ notifFail: {
452
+ bind: {
453
+ type: TASK_MESSAGE_TYPE,
454
+ data: {type: CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED},
455
+ },
456
+ errId: 'Service.aqm.task.consultConference',
457
+ },
458
+ })
459
+ ),
460
+
461
+ /*
462
+ * Exit conference
463
+ */
464
+ exitConference: aqm.req((p: {interactionId: string}) => ({
465
+ url: `${TASK_API}${p.interactionId}${CONFERENCE_EXIT}`,
466
+ data: {},
467
+ host: WCC_API_GATEWAY,
468
+ err,
469
+ notifSuccess: {
470
+ bind: {
471
+ type: TASK_MESSAGE_TYPE,
472
+ data: {type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE, interactionId: p.interactionId},
473
+ },
474
+ msg: {} as Contact.AgentContact,
475
+ },
476
+ notifFail: {
477
+ bind: {
478
+ type: TASK_MESSAGE_TYPE,
479
+ data: {type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE_FAILED}, // to be finalized
480
+ },
481
+ errId: 'Service.aqm.task.consultConference',
482
+ },
483
+ })),
484
+
485
+ /*
486
+ * Transfer conference
487
+ */
488
+ conferenceTransfer: aqm.req((p: {interactionId: string}) => ({
489
+ url: `${TASK_API}${p.interactionId}${CONFERENCE_TRANSFER}`,
490
+ data: {},
491
+ host: WCC_API_GATEWAY,
492
+ err,
493
+ notifSuccess: {
494
+ bind: {
495
+ type: TASK_MESSAGE_TYPE,
496
+ data: {type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE, interactionId: p.interactionId},
497
+ },
498
+ msg: {} as Contact.AgentContact,
499
+ },
500
+ notifFail: {
501
+ bind: {
502
+ type: TASK_MESSAGE_TYPE,
503
+ data: {type: CC_EVENTS.AGENT_CONFERENCE_TRANSFER_FAILED},
504
+ },
505
+ errId: 'Service.aqm.task.consultConference',
506
+ },
507
+ })),
428
508
  };
429
509
  }
@@ -5,6 +5,7 @@ import {
5
5
  generateTaskErrorObject,
6
6
  deriveConsultTransferDestinationType,
7
7
  getDestinationAgentId,
8
+ buildConsultConferenceParamData,
8
9
  } from '../core/Utils';
9
10
  import {Failure} from '../core/GlobalTypes';
10
11
  import {LoginOption} from '../../types';
@@ -138,6 +139,7 @@ export default class Task extends EventEmitter implements ITask {
138
139
  public webCallMap: Record<TaskId, CallId>;
139
140
  private wrapupData: WrapupData;
140
141
  public autoWrapup?: AutoWrapup;
142
+ private agentId: string;
141
143
 
142
144
  /**
143
145
  * Creates a new Task instance which provides the following features:
@@ -150,7 +152,8 @@ export default class Task extends EventEmitter implements ITask {
150
152
  contact: ReturnType<typeof routingContact>,
151
153
  webCallingService: WebCallingService,
152
154
  data: TaskData,
153
- wrapupData: WrapupData
155
+ wrapupData: WrapupData,
156
+ agentId: string
154
157
  ) {
155
158
  super();
156
159
  this.contact = contact;
@@ -161,6 +164,7 @@ export default class Task extends EventEmitter implements ITask {
161
164
  this.metricsManager = MetricsManager.getInstance();
162
165
  this.registerWebCallListeners();
163
166
  this.setupAutoWrapupTimer();
167
+ this.agentId = agentId;
164
168
  }
165
169
 
166
170
  /**
@@ -1488,4 +1492,284 @@ export default class Task extends EventEmitter implements ITask {
1488
1492
  throw err;
1489
1493
  }
1490
1494
  }
1495
+
1496
+ /**
1497
+ * Starts a consultation conference by merging the consultation call with the main call
1498
+ *
1499
+ * Creates a three-way conference between the agent, customer, and consulted party
1500
+ * Extracts required consultation data from the current task data
1501
+ * On success, emits a `task:conferenceStarted` event
1502
+ *
1503
+ * @returns Promise<TaskResponse> - Response from the consultation conference API
1504
+ * @throws Error if the operation fails or if consultation data is invalid
1505
+ *
1506
+ * @example
1507
+ * ```typescript
1508
+ * try {
1509
+ * await task.consultConference();
1510
+ * console.log('Conference started successfully');
1511
+ * } catch (error) {
1512
+ * console.error('Failed to start conference:', error);
1513
+ * }
1514
+ * ```
1515
+ */
1516
+ public async consultConference(): Promise<TaskResponse> {
1517
+ // Extract consultation conference data from task data (used in both try and catch)
1518
+ const consultationData = {
1519
+ agentId: this.agentId,
1520
+ destAgentId: this.data.destAgentId,
1521
+ destinationType: this.data.destinationType || 'agent',
1522
+ };
1523
+
1524
+ try {
1525
+ LoggerProxy.info(`Initiating consult conference to ${consultationData.destAgentId}`, {
1526
+ module: TASK_FILE,
1527
+ method: METHODS.CONSULT_CONFERENCE,
1528
+ interactionId: this.data.interactionId,
1529
+ });
1530
+
1531
+ const paramsDataForConferenceV2 = buildConsultConferenceParamData(
1532
+ consultationData,
1533
+ this.data.interactionId
1534
+ );
1535
+
1536
+ const response = await this.contact.consultConference({
1537
+ interactionId: paramsDataForConferenceV2.interactionId,
1538
+ data: paramsDataForConferenceV2.data,
1539
+ });
1540
+
1541
+ // Track success metrics (following consultTransfer pattern)
1542
+ this.metricsManager.trackEvent(
1543
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS,
1544
+ {
1545
+ taskId: this.data.interactionId,
1546
+ destination: paramsDataForConferenceV2.data.to,
1547
+ destinationType: paramsDataForConferenceV2.data.destinationType,
1548
+ agentId: paramsDataForConferenceV2.data.agentId,
1549
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1550
+ },
1551
+ ['operational', 'behavioral', 'business']
1552
+ );
1553
+
1554
+ LoggerProxy.log(`Consult conference started successfully`, {
1555
+ module: TASK_FILE,
1556
+ method: METHODS.CONSULT_CONFERENCE,
1557
+ interactionId: this.data.interactionId,
1558
+ });
1559
+
1560
+ return response;
1561
+ } catch (error) {
1562
+ const err = generateTaskErrorObject(error, METHODS.CONSULT_CONFERENCE, TASK_FILE);
1563
+ const taskErrorProps = {
1564
+ trackingId: err.data?.trackingId,
1565
+ errorMessage: err.data?.message,
1566
+ errorType: err.data?.errorType,
1567
+ errorData: err.data?.errorData,
1568
+ reasonCode: err.data?.reasonCode,
1569
+ };
1570
+
1571
+ // Track failure metrics (following consultTransfer pattern)
1572
+ // Build conference data for error tracking using extracted data
1573
+ const failedParamsData = buildConsultConferenceParamData(
1574
+ consultationData,
1575
+ this.data.interactionId
1576
+ );
1577
+
1578
+ this.metricsManager.trackEvent(
1579
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED,
1580
+ {
1581
+ taskId: this.data.interactionId,
1582
+ destination: failedParamsData.data.to,
1583
+ destinationType: failedParamsData.data.destinationType,
1584
+ agentId: failedParamsData.data.agentId,
1585
+ error: error.toString(),
1586
+ ...taskErrorProps,
1587
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1588
+ },
1589
+ ['operational', 'behavioral', 'business']
1590
+ );
1591
+
1592
+ LoggerProxy.error(`Failed to start consult conference`, {
1593
+ module: TASK_FILE,
1594
+ method: METHODS.CONSULT_CONFERENCE,
1595
+ interactionId: this.data.interactionId,
1596
+ });
1597
+
1598
+ throw err;
1599
+ }
1600
+ }
1601
+
1602
+ /**
1603
+ * Exits the current conference by removing the agent from the conference call
1604
+ *
1605
+ * Exits the agent from the conference, leaving the customer and consulted party connected
1606
+ * On success, emits a `task:conferenceEnded` event
1607
+ *
1608
+ * @returns Promise<TaskResponse> - Response from the conference exit API
1609
+ * @throws Error if the operation fails or if no active conference exists
1610
+ *
1611
+ * @example
1612
+ * ```typescript
1613
+ * try {
1614
+ * await task.exitConference();
1615
+ * console.log('Successfully exited conference');
1616
+ * } catch (error) {
1617
+ * console.error('Failed to exit conference:', error);
1618
+ * }
1619
+ * ```
1620
+ */
1621
+ public async exitConference(): Promise<TaskResponse> {
1622
+ try {
1623
+ LoggerProxy.info(`Exiting consult conference`, {
1624
+ module: TASK_FILE,
1625
+ method: METHODS.EXIT_CONFERENCE,
1626
+ interactionId: this.data.interactionId,
1627
+ });
1628
+
1629
+ // Validate that interaction ID exists
1630
+ if (!this.data.interactionId) {
1631
+ throw new Error('Invalid interaction ID');
1632
+ }
1633
+
1634
+ const response = await this.contact.exitConference({
1635
+ interactionId: this.data.interactionId,
1636
+ });
1637
+
1638
+ // Track success metrics (following consultTransfer pattern)
1639
+ this.metricsManager.trackEvent(
1640
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_END_SUCCESS,
1641
+ {
1642
+ taskId: this.data.interactionId,
1643
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1644
+ },
1645
+ ['operational', 'behavioral', 'business']
1646
+ );
1647
+
1648
+ LoggerProxy.log(`Consult conference exited successfully`, {
1649
+ module: TASK_FILE,
1650
+ method: METHODS.EXIT_CONFERENCE,
1651
+ interactionId: this.data.interactionId,
1652
+ });
1653
+
1654
+ return response;
1655
+ } catch (error) {
1656
+ const err = generateTaskErrorObject(error, METHODS.EXIT_CONFERENCE, TASK_FILE);
1657
+ const taskErrorProps = {
1658
+ trackingId: err.data?.trackingId,
1659
+ errorMessage: err.data?.message,
1660
+ errorType: err.data?.errorType,
1661
+ errorData: err.data?.errorData,
1662
+ reasonCode: err.data?.reasonCode,
1663
+ };
1664
+
1665
+ // Track failure metrics (following consultTransfer pattern)
1666
+ this.metricsManager.trackEvent(
1667
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_END_FAILED,
1668
+ {
1669
+ taskId: this.data.interactionId,
1670
+ error: error.toString(),
1671
+ ...taskErrorProps,
1672
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1673
+ },
1674
+ ['operational', 'behavioral', 'business']
1675
+ );
1676
+
1677
+ LoggerProxy.error(`Failed to exit consult conference`, {
1678
+ module: TASK_FILE,
1679
+ method: METHODS.EXIT_CONFERENCE,
1680
+ interactionId: this.data.interactionId,
1681
+ });
1682
+
1683
+ throw err;
1684
+ }
1685
+ }
1686
+
1687
+ // TODO: Uncomment this method in future PR for Multi-Party Conference support (>3 participants)
1688
+ // Conference transfer will be supported when implementing enhanced multi-party conference functionality
1689
+ /*
1690
+ /**
1691
+ * Transfers the current conference to another agent
1692
+ *
1693
+ * Moves the entire conference (including all participants) to a new agent,
1694
+ * while the current agent exits and goes to wrapup
1695
+ * On success, the current agent receives `task:conferenceEnded` event
1696
+ *
1697
+ * @returns Promise<TaskResponse> - Response from the conference transfer API
1698
+ * @throws Error if the operation fails or if no active conference exists
1699
+ *
1700
+ * @example
1701
+ * ```typescript
1702
+ * try {
1703
+ * await task.transferConference();
1704
+ * console.log('Conference transferred successfully');
1705
+ * } catch (error) {
1706
+ * console.error('Failed to transfer conference:', error);
1707
+ * }
1708
+ * ```
1709
+ */
1710
+ /* public async transferConference(): Promise<TaskResponse> {
1711
+ try {
1712
+ LoggerProxy.info(`Transferring conference`, {
1713
+ module: TASK_FILE,
1714
+ method: METHODS.TRANSFER_CONFERENCE,
1715
+ interactionId: this.data.interactionId,
1716
+ });
1717
+
1718
+ // Validate that interaction ID exists
1719
+ if (!this.data.interactionId) {
1720
+ throw new Error('Invalid interaction ID');
1721
+ }
1722
+
1723
+ const response = await this.contact.conferenceTransfer({
1724
+ interactionId: this.data.interactionId,
1725
+ });
1726
+
1727
+ // Track success metrics (following consultTransfer pattern)
1728
+ this.metricsManager.trackEvent(
1729
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS,
1730
+ {
1731
+ taskId: this.data.interactionId,
1732
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1733
+ },
1734
+ ['operational', 'behavioral', 'business']
1735
+ );
1736
+
1737
+ LoggerProxy.log(`Conference transferred successfully`, {
1738
+ module: TASK_FILE,
1739
+ method: METHODS.TRANSFER_CONFERENCE,
1740
+ interactionId: this.data.interactionId,
1741
+ });
1742
+
1743
+ return response;
1744
+ } catch (error) {
1745
+ const err = generateTaskErrorObject(error, METHODS.TRANSFER_CONFERENCE, TASK_FILE);
1746
+ const taskErrorProps = {
1747
+ trackingId: err.data?.trackingId,
1748
+ errorMessage: err.data?.message,
1749
+ errorType: err.data?.errorType,
1750
+ errorData: err.data?.errorData,
1751
+ reasonCode: err.data?.reasonCode,
1752
+ };
1753
+
1754
+ // Track failure metrics (following consultTransfer pattern)
1755
+ this.metricsManager.trackEvent(
1756
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED,
1757
+ {
1758
+ taskId: this.data.interactionId,
1759
+ error: error.toString(),
1760
+ ...taskErrorProps,
1761
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1762
+ },
1763
+ ['operational', 'behavioral', 'business']
1764
+ );
1765
+
1766
+ LoggerProxy.error(`Failed to transfer conference`, {
1767
+ module: TASK_FILE,
1768
+ method: METHODS.TRANSFER_CONFERENCE,
1769
+ interactionId: this.data.interactionId,
1770
+ });
1771
+
1772
+ throw err;
1773
+ }
1774
+ } */
1491
1775
  }