@webex/contact-center 3.10.0-next.9 → 3.10.0-wxc-disconnect.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cc.js +1 -12
  2. package/dist/cc.js.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/services/config/types.js +2 -2
  5. package/dist/services/config/types.js.map +1 -1
  6. package/dist/services/core/Utils.js +71 -90
  7. package/dist/services/core/Utils.js.map +1 -1
  8. package/dist/services/core/constants.js +1 -17
  9. package/dist/services/core/constants.js.map +1 -1
  10. package/dist/services/task/TaskManager.js +17 -89
  11. package/dist/services/task/TaskManager.js.map +1 -1
  12. package/dist/services/task/constants.js +1 -20
  13. package/dist/services/task/constants.js.map +1 -1
  14. package/dist/services/task/index.js +98 -123
  15. package/dist/services/task/index.js.map +1 -1
  16. package/dist/services/task/types.js +4 -2
  17. package/dist/services/task/types.js.map +1 -1
  18. package/dist/types/cc.d.ts +0 -6
  19. package/dist/types/index.d.ts +1 -1
  20. package/dist/types/services/config/types.d.ts +4 -4
  21. package/dist/types/services/core/Utils.d.ts +17 -32
  22. package/dist/types/services/core/constants.d.ts +0 -14
  23. package/dist/types/services/task/constants.d.ts +0 -17
  24. package/dist/types/services/task/index.d.ts +2 -41
  25. package/dist/types/services/task/types.d.ts +33 -133
  26. package/dist/webex.js +1 -1
  27. package/package.json +9 -9
  28. package/src/cc.ts +1 -12
  29. package/src/index.ts +0 -1
  30. package/src/services/config/types.ts +2 -2
  31. package/src/services/core/Utils.ts +85 -101
  32. package/src/services/core/constants.ts +0 -16
  33. package/src/services/task/TaskManager.ts +15 -112
  34. package/src/services/task/constants.ts +0 -19
  35. package/src/services/task/index.ts +82 -108
  36. package/src/services/task/types.ts +33 -142
  37. package/test/unit/spec/services/core/Utils.ts +31 -262
  38. package/test/unit/spec/services/task/TaskManager.ts +6 -620
  39. package/test/unit/spec/services/task/index.ts +79 -502
  40. package/umd/contact-center.min.js +2 -2
  41. package/umd/contact-center.min.js.map +1 -1
  42. package/dist/services/task/TaskUtils.js +0 -104
  43. package/dist/services/task/TaskUtils.js.map +0 -1
  44. package/dist/types/services/task/TaskUtils.d.ts +0 -42
  45. package/src/services/task/TaskUtils.ts +0 -113
  46. package/test/unit/spec/services/task/TaskUtils.ts +0 -131
@@ -12,13 +12,6 @@ import LoggerProxy from '../../logger-proxy';
12
12
  import Task from '.';
13
13
  import MetricsManager from '../../metrics/MetricsManager';
14
14
  import {METRIC_EVENT_NAMES} from '../../metrics/constants';
15
- import {
16
- checkParticipantNotInInteraction,
17
- getIsConferenceInProgress,
18
- isParticipantInMainInteraction,
19
- isPrimary,
20
- isSecondaryEpDnAgent,
21
- } from './TaskUtils';
22
15
 
23
16
  /** @internal */
24
17
  export default class TaskManager extends EventEmitter {
@@ -129,15 +122,14 @@ export default class TaskManager extends EventEmitter {
129
122
  method: METHODS.REGISTER_TASK_LISTENERS,
130
123
  interactionId: payload.data.interactionId,
131
124
  });
132
-
133
125
  task = new Task(
134
126
  this.contact,
135
127
  this.webCallingService,
136
128
  {
137
129
  ...payload.data,
138
130
  wrapUpRequired:
139
- payload.data.interaction?.participants?.[this.agentId]?.isWrapUp || false,
140
- isConferenceInProgress: getIsConferenceInProgress(payload.data),
131
+ payload.data.interaction?.participants?.[payload.data.agentId]?.isWrapUp ||
132
+ false,
141
133
  },
142
134
  this.wrapupData,
143
135
  this.agentId
@@ -168,7 +160,6 @@ export default class TaskManager extends EventEmitter {
168
160
  }
169
161
  }
170
162
  break;
171
-
172
163
  case CC_EVENTS.AGENT_CONTACT_RESERVED:
173
164
  task = new Task(
174
165
  this.contact,
@@ -248,22 +239,13 @@ export default class TaskManager extends EventEmitter {
248
239
  break;
249
240
  }
250
241
  case CC_EVENTS.CONTACT_ENDED:
251
- // Update task data
252
242
  task = this.updateTaskData(task, {
253
243
  ...payload.data,
254
- wrapUpRequired:
255
- payload.data.interaction.state !== 'new' &&
256
- !isSecondaryEpDnAgent(payload.data.interaction),
244
+ wrapUpRequired: payload.data.interaction.state !== 'new',
257
245
  });
258
-
259
- // Handle cleanup based on whether task should be deleted
260
246
  this.handleTaskCleanup(task);
247
+ task.emit(TASK_EVENTS.TASK_END, task);
261
248
 
262
- task?.emit(TASK_EVENTS.TASK_END, task);
263
-
264
- break;
265
- case CC_EVENTS.CONTACT_MERGED:
266
- task = this.handleContactMerged(task, payload.data);
267
249
  break;
268
250
  case CC_EVENTS.AGENT_CONTACT_HELD:
269
251
  // As soon as the main interaction is held, we need to emit TASK_HOLD
@@ -376,45 +358,18 @@ export default class TaskManager extends EventEmitter {
376
358
  case CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED:
377
359
  // Conference ended - update task state and emit event
378
360
  task = this.updateTaskData(task, payload.data);
379
- if (
380
- !task ||
381
- isPrimary(task, this.agentId) ||
382
- isParticipantInMainInteraction(task, this.agentId)
383
- ) {
384
- LoggerProxy.log('Primary or main interaction participant leaving conference');
385
- } else {
386
- this.removeTaskFromCollection(task);
387
- }
388
361
  task.emit(TASK_EVENTS.TASK_CONFERENCE_ENDED, task);
389
362
  break;
390
- case CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE: {
391
- task = this.updateTaskData(task, {
392
- ...payload.data,
393
- isConferenceInProgress: getIsConferenceInProgress(payload.data),
394
- });
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);
395
366
  task.emit(TASK_EVENTS.TASK_PARTICIPANT_JOINED, task);
396
367
  break;
397
- }
398
- case CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE: {
368
+ case CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE:
399
369
  // Conference ended - update task state and emit event
400
-
401
- task = this.updateTaskData(task, {
402
- ...payload.data,
403
- isConferenceInProgress: getIsConferenceInProgress(payload.data),
404
- });
405
- if (checkParticipantNotInInteraction(task, this.agentId)) {
406
- if (
407
- isParticipantInMainInteraction(task, this.agentId) ||
408
- isPrimary(task, this.agentId)
409
- ) {
410
- LoggerProxy.log('Primary or main interaction participant leaving conference');
411
- } else {
412
- this.removeTaskFromCollection(task);
413
- }
414
- }
370
+ task = this.updateTaskData(task, payload.data);
415
371
  task.emit(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
416
372
  break;
417
- }
418
373
  case CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE_FAILED:
419
374
  // Conference exit failed - update task state and emit failure event
420
375
  task = this.updateTaskData(task, payload.data);
@@ -436,10 +391,13 @@ export default class TaskManager extends EventEmitter {
436
391
  task = this.updateTaskData(task, payload.data);
437
392
  task.emit(TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, task);
438
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;
439
398
  case CC_EVENTS.PARTICIPANT_POST_CALL_ACTIVITY:
440
399
  // Post-call activity for participant - update task state with activity details
441
400
  task = this.updateTaskData(task, payload.data);
442
- task.emit(TASK_EVENTS.TASK_POST_CALL_ACTIVITY, task);
443
401
  break;
444
402
  default:
445
403
  break;
@@ -479,54 +437,6 @@ export default class TaskManager extends EventEmitter {
479
437
  }
480
438
  }
481
439
 
482
- /**
483
- * Handles CONTACT_MERGED event logic
484
- * @param task - The task to process
485
- * @param taskData - The task data from the event payload
486
- * @returns Updated or newly created task
487
- * @private
488
- */
489
- private handleContactMerged(task: ITask, taskData: TaskData): ITask {
490
- if (taskData.childInteractionId) {
491
- // remove the child task from collection
492
- this.removeTaskFromCollection(this.taskCollection[taskData.childInteractionId]);
493
- }
494
-
495
- if (this.taskCollection[taskData.interactionId]) {
496
- LoggerProxy.log(`Got CONTACT_MERGED: Task already exists in collection`, {
497
- module: TASK_MANAGER_FILE,
498
- method: METHODS.REGISTER_TASK_LISTENERS,
499
- interactionId: taskData.interactionId,
500
- });
501
- // update the task data
502
- task = this.updateTaskData(task, taskData);
503
- } else {
504
- // Case2 : Task is not present in taskCollection
505
- LoggerProxy.log(`Got CONTACT_MERGED : Creating new task in taskManager`, {
506
- module: TASK_MANAGER_FILE,
507
- method: METHODS.REGISTER_TASK_LISTENERS,
508
- interactionId: taskData.interactionId,
509
- });
510
-
511
- task = new Task(
512
- this.contact,
513
- this.webCallingService,
514
- {
515
- ...taskData,
516
- wrapUpRequired: taskData.interaction?.participants?.[this.agentId]?.isWrapUp || false,
517
- isConferenceInProgress: getIsConferenceInProgress(taskData),
518
- },
519
- this.wrapupData,
520
- this.agentId
521
- );
522
- this.taskCollection[taskData.interactionId] = task;
523
- }
524
-
525
- this.emit(TASK_EVENTS.TASK_MERGED, task);
526
-
527
- return task;
528
- }
529
-
530
440
  private removeTaskFromCollection(task: ITask) {
531
441
  if (task?.data?.interactionId) {
532
442
  delete this.taskCollection[task.data.interactionId];
@@ -538,13 +448,7 @@ export default class TaskManager extends EventEmitter {
538
448
  }
539
449
  }
540
450
 
541
- /**
542
- * Handles cleanup of task resources including Desktop/WebRTC call cleanup and task removal
543
- * @param task - The task to clean up
544
- * @private
545
- */
546
451
  private handleTaskCleanup(task: ITask) {
547
- // Clean up Desktop/WebRTC calling resources for browser-based telephony tasks
548
452
  if (
549
453
  this.webCallingService.loginOption === LoginOption.BROWSER &&
550
454
  task.data.interaction.mediaType === 'telephony'
@@ -552,9 +456,8 @@ export default class TaskManager extends EventEmitter {
552
456
  task.unregisterWebCallListeners();
553
457
  this.webCallingService.cleanUpCall();
554
458
  }
555
-
556
- if (task.data.interaction.state === 'new' || isSecondaryEpDnAgent(task.data.interaction)) {
557
- // Only remove tasks in 'new' state or isSecondaryEpDnAgent immediately. For other states,
459
+ if (task.data.interaction.state === 'new') {
460
+ // Only remove tasks in 'new' state immediately. For other states,
558
461
  // retain tasks until they complete wrap-up, unless the task disconnected before being answered.
559
462
  this.removeTaskFromCollection(task);
560
463
  }
@@ -23,25 +23,6 @@ export const CONFERENCE_TRANSFER = '/conference/transfer';
23
23
  export const TASK_MANAGER_FILE = 'taskManager';
24
24
  export const TASK_FILE = 'task';
25
25
 
26
- /**
27
- * Task data field names that should be preserved during reconciliation
28
- * These fields are retained even if not present in new data during updates
29
- */
30
- export const PRESERVED_TASK_DATA_FIELDS = {
31
- /** Indicates if the task is in consultation state */
32
- IS_CONSULTED: 'isConsulted',
33
- /** Indicates if wrap-up is required for this task */
34
- WRAP_UP_REQUIRED: 'wrapUpRequired',
35
- /** Indicates if a conference is currently in progress (2+ active agents) */
36
- IS_CONFERENCE_IN_PROGRESS: 'isConferenceInProgress',
37
- };
38
-
39
- /**
40
- * Array of task data field names that should not be deleted during reconciliation
41
- * Used by reconcileData method to preserve important task state fields
42
- */
43
- export const KEYS_TO_NOT_DELETE: string[] = Object.values(PRESERVED_TASK_DATA_FIELDS);
44
-
45
26
  // METHOD NAMES
46
27
  export const METHODS = {
47
28
  // Task class methods
@@ -1,11 +1,16 @@
1
1
  import EventEmitter from 'events';
2
2
  import {CALL_EVENT_KEYS, LocalMicrophoneStream} from '@webex/calling';
3
3
  import {CallId} from '@webex/calling/dist/types/common/types';
4
- import {generateTaskErrorObject, calculateDestAgentId, calculateDestType} from '../core/Utils';
4
+ import {
5
+ generateTaskErrorObject,
6
+ deriveConsultTransferDestinationType,
7
+ getDestinationAgentId,
8
+ buildConsultConferenceParamData,
9
+ } from '../core/Utils';
5
10
  import {Failure} from '../core/GlobalTypes';
6
11
  import {LoginOption} from '../../types';
7
12
  import {TASK_FILE} from '../../constants';
8
- import {METHODS, KEYS_TO_NOT_DELETE} from './constants';
13
+ import {METHODS} from './constants';
9
14
  import routingContact from './contact';
10
15
  import LoggerProxy from '../../logger-proxy';
11
16
  import {
@@ -276,24 +281,9 @@ export default class Task extends EventEmitter implements ITask {
276
281
  * @private
277
282
  */
278
283
  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
284
  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]);
285
+ if (newData[key] && typeof newData[key] === 'object' && !Array.isArray(newData[key])) {
286
+ oldData[key] = this.reconcileData({...oldData[key]}, newData[key]);
297
287
  } else {
298
288
  oldData[key] = newData[key];
299
289
  }
@@ -521,7 +511,6 @@ export default class Task extends EventEmitter implements ITask {
521
511
  * Puts the current task/interaction on hold.
522
512
  * Emits task:hold event when successful. For voice tasks, this mutes the audio.
523
513
  *
524
- * @param mediaResourceId - Optional media resource ID to use for the hold operation. If not provided, uses the task's current mediaResourceId
525
514
  * @returns Promise<TaskResponse>
526
515
  * @throws Error if hold operation fails
527
516
  * @example
@@ -542,17 +531,9 @@ export default class Task extends EventEmitter implements ITask {
542
531
  * console.error('Failed to place task on hold:', error);
543
532
  * // Handle error (e.g., show error message, reset UI state)
544
533
  * }
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
534
  * ```
554
535
  */
555
- public async hold(mediaResourceId?: string): Promise<TaskResponse> {
536
+ public async hold(): Promise<TaskResponse> {
556
537
  try {
557
538
  LoggerProxy.info(`Holding task`, {
558
539
  module: TASK_FILE,
@@ -565,11 +546,9 @@ export default class Task extends EventEmitter implements ITask {
565
546
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
566
547
  ]);
567
548
 
568
- const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
569
-
570
549
  const response = await this.contact.hold({
571
550
  interactionId: this.data.interactionId,
572
- data: {mediaResourceId: effectiveMediaResourceId},
551
+ data: {mediaResourceId: this.data.mediaResourceId},
573
552
  });
574
553
 
575
554
  this.metricsManager.trackEvent(
@@ -577,7 +556,7 @@ export default class Task extends EventEmitter implements ITask {
577
556
  {
578
557
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
579
558
  taskId: this.data.interactionId,
580
- mediaResourceId: effectiveMediaResourceId,
559
+ mediaResourceId: this.data.mediaResourceId,
581
560
  },
582
561
  ['operational', 'behavioral']
583
562
  );
@@ -599,13 +578,11 @@ export default class Task extends EventEmitter implements ITask {
599
578
  errorData: err.data?.errorData,
600
579
  reasonCode: err.data?.reasonCode,
601
580
  };
602
- const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
603
-
604
581
  this.metricsManager.trackEvent(
605
582
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
606
583
  {
607
584
  taskId: this.data.interactionId,
608
- mediaResourceId: effectiveMediaResourceId,
585
+ mediaResourceId: this.data.mediaResourceId,
609
586
  error: error.toString(),
610
587
  ...taskErrorProps,
611
588
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
@@ -620,7 +597,6 @@ export default class Task extends EventEmitter implements ITask {
620
597
  * Resumes the task/interaction that was previously put on hold.
621
598
  * Emits task:resume event when successful. For voice tasks, this restores the audio.
622
599
  *
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
600
  * @returns Promise<TaskResponse>
625
601
  * @throws Error if resume operation fails
626
602
  * @example
@@ -641,17 +617,9 @@ export default class Task extends EventEmitter implements ITask {
641
617
  * console.error('Failed to resume task:', error);
642
618
  * // Handle error (e.g., show error message)
643
619
  * }
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
620
  * ```
653
621
  */
654
- public async resume(mediaResourceId?: string): Promise<TaskResponse> {
622
+ public async resume(): Promise<TaskResponse> {
655
623
  try {
656
624
  LoggerProxy.info(`Resuming task`, {
657
625
  module: TASK_FILE,
@@ -659,9 +627,7 @@ export default class Task extends EventEmitter implements ITask {
659
627
  interactionId: this.data.interactionId,
660
628
  });
661
629
  const {mainInteractionId} = this.data.interaction;
662
- const defaultMediaResourceId =
663
- this.data.interaction.media[mainInteractionId]?.mediaResourceId;
664
- const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
630
+ const {mediaResourceId} = this.data.interaction.media[mainInteractionId];
665
631
 
666
632
  this.metricsManager.timeEvent([
667
633
  METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
@@ -670,7 +636,7 @@ export default class Task extends EventEmitter implements ITask {
670
636
 
671
637
  const response = await this.contact.unHold({
672
638
  interactionId: this.data.interactionId,
673
- data: {mediaResourceId: effectiveMediaResourceId},
639
+ data: {mediaResourceId},
674
640
  });
675
641
 
676
642
  this.metricsManager.trackEvent(
@@ -678,7 +644,7 @@ export default class Task extends EventEmitter implements ITask {
678
644
  {
679
645
  taskId: this.data.interactionId,
680
646
  mainInteractionId,
681
- mediaResourceId: effectiveMediaResourceId,
647
+ mediaResourceId,
682
648
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
683
649
  },
684
650
  ['operational', 'behavioral']
@@ -695,11 +661,6 @@ export default class Task extends EventEmitter implements ITask {
695
661
  } catch (error) {
696
662
  const err = generateTaskErrorObject(error, METHODS.RESUME, TASK_FILE);
697
663
  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
664
  const taskErrorProps = {
704
665
  trackingId: err.data?.trackingId,
705
666
  errorMessage: err.data?.message,
@@ -712,7 +673,9 @@ export default class Task extends EventEmitter implements ITask {
712
673
  {
713
674
  taskId: this.data.interactionId,
714
675
  mainInteractionId,
715
- mediaResourceId: effectiveMediaResourceId,
676
+ mediaResourceId: mainInteractionId
677
+ ? this.data.interaction.media[mainInteractionId].mediaResourceId
678
+ : '',
716
679
  ...taskErrorProps,
717
680
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
718
681
  },
@@ -1442,31 +1405,35 @@ export default class Task extends EventEmitter implements ITask {
1442
1405
  public async consultTransfer(
1443
1406
  consultTransferPayload?: ConsultTransferPayLoad
1444
1407
  ): 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
- }
1408
+ try {
1409
+ // Get the destination agent ID using custom logic from participants data
1410
+ const destAgentId = getDestinationAgentId(
1411
+ this.data.interaction?.participants,
1412
+ this.data.agentId
1413
+ );
1452
1414
 
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,
1415
+ // Resolve the target id (queue consult transfers go to the accepted agent)
1416
+ if (!destAgentId) {
1417
+ throw new Error('No agent has accepted this queue consult yet');
1459
1418
  }
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 {
1419
+
1420
+ LoggerProxy.info(
1421
+ `Initiating consult transfer to ${consultTransferPayload?.to || destAgentId}`,
1422
+ {
1423
+ module: TASK_FILE,
1424
+ method: METHODS.CONSULT_TRANSFER,
1425
+ interactionId: this.data.interactionId,
1426
+ }
1427
+ );
1428
+ // Obtain payload based on desktop logic using TaskData
1429
+ const finalDestinationType = deriveConsultTransferDestinationType(this.data);
1430
+
1431
+ // By default we always use the computed destAgentId as the target id
1432
+ const consultTransferRequest: ConsultTransferPayLoad = {
1433
+ to: destAgentId,
1434
+ destinationType: finalDestinationType,
1435
+ };
1436
+
1470
1437
  const result = await this.contact.consultTransfer({
1471
1438
  interactionId: this.data.interactionId,
1472
1439
  data: consultTransferRequest,
@@ -1504,12 +1471,17 @@ export default class Task extends EventEmitter implements ITask {
1504
1471
  errorData: err.data?.errorData,
1505
1472
  reasonCode: err.data?.reasonCode,
1506
1473
  };
1474
+ const failedDestinationType = deriveConsultTransferDestinationType(this.data);
1475
+ const failedDestAgentId = getDestinationAgentId(
1476
+ this.data.interaction?.participants,
1477
+ this.data.agentId
1478
+ );
1507
1479
  this.metricsManager.trackEvent(
1508
1480
  METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
1509
1481
  {
1510
1482
  taskId: this.data.interactionId,
1511
- destination: destAgentId || '',
1512
- destinationType: destType,
1483
+ destination: failedDestAgentId || '',
1484
+ destinationType: failedDestinationType,
1513
1485
  isConsultTransfer: true,
1514
1486
  error: error.toString(),
1515
1487
  ...taskErrorProps,
@@ -1542,36 +1514,28 @@ export default class Task extends EventEmitter implements ITask {
1542
1514
  * ```
1543
1515
  */
1544
1516
  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
1517
  // Extract consultation conference data from task data (used in both try and catch)
1559
1518
  const consultationData = {
1560
1519
  agentId: this.agentId,
1561
- to: destAgentId,
1562
- destinationType: destAgentType || this.data.destinationType || 'agent',
1520
+ destAgentId: this.data.destAgentId,
1521
+ destinationType: this.data.destinationType || 'agent',
1563
1522
  };
1564
1523
 
1565
1524
  try {
1566
- LoggerProxy.info(`Initiating consult conference to ${destAgentId}`, {
1525
+ LoggerProxy.info(`Initiating consult conference to ${consultationData.destAgentId}`, {
1567
1526
  module: TASK_FILE,
1568
1527
  method: METHODS.CONSULT_CONFERENCE,
1569
1528
  interactionId: this.data.interactionId,
1570
1529
  });
1571
1530
 
1531
+ const paramsDataForConferenceV2 = buildConsultConferenceParamData(
1532
+ consultationData,
1533
+ this.data.interactionId
1534
+ );
1535
+
1572
1536
  const response = await this.contact.consultConference({
1573
- interactionId: this.data.interactionId,
1574
- data: consultationData,
1537
+ interactionId: paramsDataForConferenceV2.interactionId,
1538
+ data: paramsDataForConferenceV2.data,
1575
1539
  });
1576
1540
 
1577
1541
  // Track success metrics (following consultTransfer pattern)
@@ -1579,9 +1543,9 @@ export default class Task extends EventEmitter implements ITask {
1579
1543
  METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS,
1580
1544
  {
1581
1545
  taskId: this.data.interactionId,
1582
- destination: consultationData.to,
1583
- destinationType: consultationData.destinationType,
1584
- agentId: consultationData.agentId,
1546
+ destination: paramsDataForConferenceV2.data.to,
1547
+ destinationType: paramsDataForConferenceV2.data.destinationType,
1548
+ agentId: paramsDataForConferenceV2.data.agentId,
1585
1549
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1586
1550
  },
1587
1551
  ['operational', 'behavioral', 'business']
@@ -1604,13 +1568,20 @@ export default class Task extends EventEmitter implements ITask {
1604
1568
  reasonCode: err.data?.reasonCode,
1605
1569
  };
1606
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
+
1607
1578
  this.metricsManager.trackEvent(
1608
1579
  METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED,
1609
1580
  {
1610
1581
  taskId: this.data.interactionId,
1611
- destination: consultationData.to,
1612
- destinationType: consultationData.destinationType,
1613
- agentId: consultationData.agentId,
1582
+ destination: failedParamsData.data.to,
1583
+ destinationType: failedParamsData.data.destinationType,
1584
+ agentId: failedParamsData.data.agentId,
1614
1585
  error: error.toString(),
1615
1586
  ...taskErrorProps,
1616
1587
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
@@ -1713,6 +1684,9 @@ export default class Task extends EventEmitter implements ITask {
1713
1684
  }
1714
1685
  }
1715
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
+ /*
1716
1690
  /**
1717
1691
  * Transfers the current conference to another agent
1718
1692
  *
@@ -1733,7 +1707,7 @@ export default class Task extends EventEmitter implements ITask {
1733
1707
  * }
1734
1708
  * ```
1735
1709
  */
1736
- public async transferConference(): Promise<TaskResponse> {
1710
+ /* public async transferConference(): Promise<TaskResponse> {
1737
1711
  try {
1738
1712
  LoggerProxy.info(`Transferring conference`, {
1739
1713
  module: TASK_FILE,
@@ -1797,5 +1771,5 @@ export default class Task extends EventEmitter implements ITask {
1797
1771
 
1798
1772
  throw err;
1799
1773
  }
1800
- }
1774
+ } */
1801
1775
  }