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

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 (115) hide show
  1. package/dist/cc.js +207 -47
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/index.js +9 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/logger-proxy.js +24 -1
  8. package/dist/logger-proxy.js.map +1 -1
  9. package/dist/metrics/MetricsManager.js +1 -1
  10. package/dist/metrics/MetricsManager.js.map +1 -1
  11. package/dist/metrics/behavioral-events.js +89 -0
  12. package/dist/metrics/behavioral-events.js.map +1 -1
  13. package/dist/metrics/constants.js +32 -2
  14. package/dist/metrics/constants.js.map +1 -1
  15. package/dist/services/AddressBook.js +271 -0
  16. package/dist/services/AddressBook.js.map +1 -0
  17. package/dist/services/EntryPoint.js +227 -0
  18. package/dist/services/EntryPoint.js.map +1 -0
  19. package/dist/services/Queue.js +261 -0
  20. package/dist/services/Queue.js.map +1 -0
  21. package/dist/services/config/constants.js +36 -2
  22. package/dist/services/config/constants.js.map +1 -1
  23. package/dist/services/config/index.js +29 -21
  24. package/dist/services/config/index.js.map +1 -1
  25. package/dist/services/config/types.js +33 -1
  26. package/dist/services/config/types.js.map +1 -1
  27. package/dist/services/core/GlobalTypes.js.map +1 -1
  28. package/dist/services/core/Utils.js +181 -2
  29. package/dist/services/core/Utils.js.map +1 -1
  30. package/dist/services/core/aqm-reqs.js +0 -4
  31. package/dist/services/core/aqm-reqs.js.map +1 -1
  32. package/dist/services/core/constants.js +17 -1
  33. package/dist/services/core/constants.js.map +1 -1
  34. package/dist/services/core/websocket/WebSocketManager.js +0 -4
  35. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  36. package/dist/services/task/TaskManager.js +151 -7
  37. package/dist/services/task/TaskManager.js.map +1 -1
  38. package/dist/services/task/TaskUtils.js +104 -0
  39. package/dist/services/task/TaskUtils.js.map +1 -0
  40. package/dist/services/task/constants.js +26 -1
  41. package/dist/services/task/constants.js.map +1 -1
  42. package/dist/services/task/contact.js +86 -0
  43. package/dist/services/task/contact.js.map +1 -1
  44. package/dist/services/task/index.js +428 -91
  45. package/dist/services/task/index.js.map +1 -1
  46. package/dist/services/task/types.js +12 -0
  47. package/dist/services/task/types.js.map +1 -1
  48. package/dist/types/cc.d.ts +121 -35
  49. package/dist/types/constants.d.ts +1 -0
  50. package/dist/types/index.d.ts +4 -3
  51. package/dist/types/metrics/constants.d.ts +25 -1
  52. package/dist/types/services/AddressBook.d.ts +74 -0
  53. package/dist/types/services/EntryPoint.d.ts +67 -0
  54. package/dist/types/services/Queue.d.ts +76 -0
  55. package/dist/types/services/config/constants.d.ts +35 -1
  56. package/dist/types/services/config/index.d.ts +6 -9
  57. package/dist/types/services/config/types.d.ts +79 -58
  58. package/dist/types/services/core/GlobalTypes.d.ts +25 -0
  59. package/dist/types/services/core/Utils.d.ts +55 -1
  60. package/dist/types/services/core/constants.d.ts +14 -0
  61. package/dist/types/services/task/TaskUtils.d.ts +42 -0
  62. package/dist/types/services/task/constants.d.ts +23 -0
  63. package/dist/types/services/task/contact.d.ts +10 -0
  64. package/dist/types/services/task/index.d.ts +85 -4
  65. package/dist/types/services/task/types.d.ts +245 -21
  66. package/dist/types/types.d.ts +162 -0
  67. package/dist/types/utils/PageCache.d.ts +173 -0
  68. package/dist/types.js +17 -0
  69. package/dist/types.js.map +1 -1
  70. package/dist/utils/PageCache.js +192 -0
  71. package/dist/utils/PageCache.js.map +1 -0
  72. package/dist/webex.js +1 -1
  73. package/package.json +10 -9
  74. package/src/cc.ts +232 -52
  75. package/src/constants.ts +1 -0
  76. package/src/index.ts +17 -2
  77. package/src/logger-proxy.ts +24 -1
  78. package/src/metrics/MetricsManager.ts +1 -1
  79. package/src/metrics/behavioral-events.ts +94 -0
  80. package/src/metrics/constants.ts +37 -1
  81. package/src/services/AddressBook.ts +291 -0
  82. package/src/services/EntryPoint.ts +241 -0
  83. package/src/services/Queue.ts +277 -0
  84. package/src/services/config/constants.ts +42 -2
  85. package/src/services/config/index.ts +30 -30
  86. package/src/services/config/types.ts +59 -58
  87. package/src/services/core/GlobalTypes.ts +27 -0
  88. package/src/services/core/Utils.ts +215 -1
  89. package/src/services/core/aqm-reqs.ts +0 -5
  90. package/src/services/core/constants.ts +16 -0
  91. package/src/services/core/websocket/WebSocketManager.ts +0 -4
  92. package/src/services/task/TaskManager.ts +182 -9
  93. package/src/services/task/TaskUtils.ts +113 -0
  94. package/src/services/task/constants.ts +25 -0
  95. package/src/services/task/contact.ts +80 -0
  96. package/src/services/task/index.ts +497 -71
  97. package/src/services/task/types.ts +264 -20
  98. package/src/types.ts +180 -0
  99. package/src/utils/PageCache.ts +252 -0
  100. package/test/unit/spec/cc.ts +282 -85
  101. package/test/unit/spec/metrics/MetricsManager.ts +0 -1
  102. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  103. package/test/unit/spec/services/AddressBook.ts +332 -0
  104. package/test/unit/spec/services/EntryPoint.ts +259 -0
  105. package/test/unit/spec/services/Queue.ts +323 -0
  106. package/test/unit/spec/services/config/index.ts +279 -65
  107. package/test/unit/spec/services/core/Utils.ts +282 -1
  108. package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
  109. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
  110. package/test/unit/spec/services/task/TaskManager.ts +760 -2
  111. package/test/unit/spec/services/task/TaskUtils.ts +131 -0
  112. package/test/unit/spec/services/task/contact.ts +31 -1
  113. package/test/unit/spec/services/task/index.ts +873 -163
  114. package/umd/contact-center.min.js +2 -2
  115. package/umd/contact-center.min.js.map +1 -1
@@ -1,10 +1,11 @@
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 {getErrorDetails} from '../core/Utils';
4
+ import {generateTaskErrorObject, calculateDestAgentId, calculateDestType} from '../core/Utils';
5
+ import {Failure} from '../core/GlobalTypes';
5
6
  import {LoginOption} from '../../types';
6
7
  import {TASK_FILE} from '../../constants';
7
- import {METHODS} from './constants';
8
+ import {METHODS, KEYS_TO_NOT_DELETE} from './constants';
8
9
  import routingContact from './contact';
9
10
  import LoggerProxy from '../../logger-proxy';
10
11
  import {
@@ -19,14 +20,12 @@ import {
19
20
  ConsultEndPayload,
20
21
  TransferPayLoad,
21
22
  DESTINATION_TYPE,
22
- CONSULT_TRANSFER_DESTINATION_TYPE,
23
23
  ConsultTransferPayLoad,
24
24
  MEDIA_CHANNEL,
25
25
  } from './types';
26
26
  import WebCallingService from '../WebCallingService';
27
27
  import MetricsManager from '../../metrics/MetricsManager';
28
28
  import {METRIC_EVENT_NAMES} from '../../metrics/constants';
29
- import {Failure} from '../core/GlobalTypes';
30
29
  import AutoWrapup from './AutoWrapup';
31
30
  import {WrapupData} from '../config/types';
32
31
 
@@ -135,6 +134,7 @@ export default class Task extends EventEmitter implements ITask {
135
134
  public webCallMap: Record<TaskId, CallId>;
136
135
  private wrapupData: WrapupData;
137
136
  public autoWrapup?: AutoWrapup;
137
+ private agentId: string;
138
138
 
139
139
  /**
140
140
  * Creates a new Task instance which provides the following features:
@@ -147,7 +147,8 @@ export default class Task extends EventEmitter implements ITask {
147
147
  contact: ReturnType<typeof routingContact>,
148
148
  webCallingService: WebCallingService,
149
149
  data: TaskData,
150
- wrapupData: WrapupData
150
+ wrapupData: WrapupData,
151
+ agentId: string
151
152
  ) {
152
153
  super();
153
154
  this.contact = contact;
@@ -158,6 +159,7 @@ export default class Task extends EventEmitter implements ITask {
158
159
  this.metricsManager = MetricsManager.getInstance();
159
160
  this.registerWebCallListeners();
160
161
  this.setupAutoWrapupTimer();
162
+ this.agentId = agentId;
161
163
  }
162
164
 
163
165
  /**
@@ -274,9 +276,24 @@ export default class Task extends EventEmitter implements ITask {
274
276
  * @private
275
277
  */
276
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
277
287
  Object.keys(newData).forEach((key) => {
278
- if (newData[key] && typeof newData[key] === 'object' && !Array.isArray(newData[key])) {
279
- oldData[key] = this.reconcileData({...oldData[key]}, newData[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]);
280
297
  } else {
281
298
  oldData[key] = newData[key];
282
299
  }
@@ -373,17 +390,25 @@ export default class Task extends EventEmitter implements ITask {
373
390
 
374
391
  return Promise.resolve(); // TODO: reject for extension as part of refactor
375
392
  } catch (error) {
376
- const {error: detailedError} = getErrorDetails(error, METHODS.ACCEPT, TASK_FILE);
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
+ };
377
401
  this.metricsManager.trackEvent(
378
402
  METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
379
403
  {
380
404
  taskId: this.data.interactionId,
381
405
  error: error.toString(),
406
+ ...taskErrorProps,
382
407
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details as Failure),
383
408
  },
384
409
  ['operational', 'behavioral', 'business']
385
410
  );
386
- throw detailedError;
411
+ throw err;
387
412
  }
388
413
  }
389
414
 
@@ -422,8 +447,8 @@ export default class Task extends EventEmitter implements ITask {
422
447
 
423
448
  return Promise.resolve();
424
449
  } catch (error) {
425
- const {error: detailedError} = getErrorDetails(error, METHODS.TOGGLE_MUTE, TASK_FILE);
426
- throw detailedError;
450
+ const err = generateTaskErrorObject(error, METHODS.TOGGLE_MUTE, TASK_FILE);
451
+ throw err;
427
452
  }
428
453
  }
429
454
 
@@ -470,17 +495,25 @@ export default class Task extends EventEmitter implements ITask {
470
495
 
471
496
  return Promise.resolve();
472
497
  } catch (error) {
473
- const {error: detailedError} = getErrorDetails(error, METHODS.DECLINE, TASK_FILE);
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
+ };
474
506
  this.metricsManager.trackEvent(
475
507
  METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
476
508
  {
477
509
  taskId: this.data.interactionId,
478
510
  error: error.toString(),
511
+ ...taskErrorProps,
479
512
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
480
513
  },
481
514
  ['operational', 'behavioral']
482
515
  );
483
- throw detailedError;
516
+ throw err;
484
517
  }
485
518
  }
486
519
 
@@ -488,6 +521,7 @@ export default class Task extends EventEmitter implements ITask {
488
521
  * Puts the current task/interaction on hold.
489
522
  * Emits task:hold event when successful. For voice tasks, this mutes the audio.
490
523
  *
524
+ * @param mediaResourceId - Optional media resource ID to use for the hold operation. If not provided, uses the task's current mediaResourceId
491
525
  * @returns Promise<TaskResponse>
492
526
  * @throws Error if hold operation fails
493
527
  * @example
@@ -508,9 +542,17 @@ export default class Task extends EventEmitter implements ITask {
508
542
  * console.error('Failed to place task on hold:', error);
509
543
  * // Handle error (e.g., show error message, reset UI state)
510
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
+ * }
511
553
  * ```
512
554
  */
513
- public async hold(): Promise<TaskResponse> {
555
+ public async hold(mediaResourceId?: string): Promise<TaskResponse> {
514
556
  try {
515
557
  LoggerProxy.info(`Holding task`, {
516
558
  module: TASK_FILE,
@@ -523,9 +565,11 @@ export default class Task extends EventEmitter implements ITask {
523
565
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
524
566
  ]);
525
567
 
568
+ const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
569
+
526
570
  const response = await this.contact.hold({
527
571
  interactionId: this.data.interactionId,
528
- data: {mediaResourceId: this.data.mediaResourceId},
572
+ data: {mediaResourceId: effectiveMediaResourceId},
529
573
  });
530
574
 
531
575
  this.metricsManager.trackEvent(
@@ -533,7 +577,7 @@ export default class Task extends EventEmitter implements ITask {
533
577
  {
534
578
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
535
579
  taskId: this.data.interactionId,
536
- mediaResourceId: this.data.mediaResourceId,
580
+ mediaResourceId: effectiveMediaResourceId,
537
581
  },
538
582
  ['operational', 'behavioral']
539
583
  );
@@ -547,18 +591,28 @@ export default class Task extends EventEmitter implements ITask {
547
591
 
548
592
  return response;
549
593
  } catch (error) {
550
- const {error: detailedError} = getErrorDetails(error, METHODS.HOLD, TASK_FILE);
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
+
551
604
  this.metricsManager.trackEvent(
552
605
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
553
606
  {
554
607
  taskId: this.data.interactionId,
555
- mediaResourceId: this.data.mediaResourceId,
608
+ mediaResourceId: effectiveMediaResourceId,
556
609
  error: error.toString(),
610
+ ...taskErrorProps,
557
611
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
558
612
  },
559
613
  ['operational', 'behavioral']
560
614
  );
561
- throw detailedError;
615
+ throw err;
562
616
  }
563
617
  }
564
618
 
@@ -566,6 +620,7 @@ export default class Task extends EventEmitter implements ITask {
566
620
  * Resumes the task/interaction that was previously put on hold.
567
621
  * Emits task:resume event when successful. For voice tasks, this restores the audio.
568
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
569
624
  * @returns Promise<TaskResponse>
570
625
  * @throws Error if resume operation fails
571
626
  * @example
@@ -586,9 +641,17 @@ export default class Task extends EventEmitter implements ITask {
586
641
  * console.error('Failed to resume task:', error);
587
642
  * // Handle error (e.g., show error message)
588
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
+ * }
589
652
  * ```
590
653
  */
591
- public async resume(): Promise<TaskResponse> {
654
+ public async resume(mediaResourceId?: string): Promise<TaskResponse> {
592
655
  try {
593
656
  LoggerProxy.info(`Resuming task`, {
594
657
  module: TASK_FILE,
@@ -596,7 +659,9 @@ export default class Task extends EventEmitter implements ITask {
596
659
  interactionId: this.data.interactionId,
597
660
  });
598
661
  const {mainInteractionId} = this.data.interaction;
599
- const {mediaResourceId} = this.data.interaction.media[mainInteractionId];
662
+ const defaultMediaResourceId =
663
+ this.data.interaction.media[mainInteractionId]?.mediaResourceId;
664
+ const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
600
665
 
601
666
  this.metricsManager.timeEvent([
602
667
  METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
@@ -605,7 +670,7 @@ export default class Task extends EventEmitter implements ITask {
605
670
 
606
671
  const response = await this.contact.unHold({
607
672
  interactionId: this.data.interactionId,
608
- data: {mediaResourceId},
673
+ data: {mediaResourceId: effectiveMediaResourceId},
609
674
  });
610
675
 
611
676
  this.metricsManager.trackEvent(
@@ -613,7 +678,7 @@ export default class Task extends EventEmitter implements ITask {
613
678
  {
614
679
  taskId: this.data.interactionId,
615
680
  mainInteractionId,
616
- mediaResourceId,
681
+ mediaResourceId: effectiveMediaResourceId,
617
682
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
618
683
  },
619
684
  ['operational', 'behavioral']
@@ -628,21 +693,32 @@ export default class Task extends EventEmitter implements ITask {
628
693
 
629
694
  return response;
630
695
  } catch (error) {
631
- const {error: detailedError} = getErrorDetails(error, METHODS.RESUME, TASK_FILE);
696
+ const err = generateTaskErrorObject(error, METHODS.RESUME, TASK_FILE);
632
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
+ };
633
710
  this.metricsManager.trackEvent(
634
711
  METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
635
712
  {
636
713
  taskId: this.data.interactionId,
637
714
  mainInteractionId,
638
- mediaResourceId: mainInteractionId
639
- ? this.data.interaction.media[mainInteractionId].mediaResourceId
640
- : '',
715
+ mediaResourceId: effectiveMediaResourceId,
716
+ ...taskErrorProps,
641
717
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
642
718
  },
643
719
  ['operational', 'behavioral']
644
720
  );
645
- throw detailedError;
721
+ throw err;
646
722
  }
647
723
  }
648
724
 
@@ -722,16 +798,24 @@ export default class Task extends EventEmitter implements ITask {
722
798
 
723
799
  return response;
724
800
  } catch (error) {
725
- const {error: detailedError} = getErrorDetails(error, METHODS.END, TASK_FILE);
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
+ };
726
809
  this.metricsManager.trackEvent(
727
810
  METRIC_EVENT_NAMES.TASK_END_FAILED,
728
811
  {
729
812
  taskId: this.data.interactionId,
813
+ ...taskErrorProps,
730
814
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
731
815
  },
732
816
  ['operational', 'behavioral', 'business']
733
817
  );
734
- throw detailedError;
818
+ throw err;
735
819
  }
736
820
  }
737
821
 
@@ -826,18 +910,26 @@ export default class Task extends EventEmitter implements ITask {
826
910
 
827
911
  return response;
828
912
  } catch (error) {
829
- const {error: detailedError} = getErrorDetails(error, METHODS.WRAPUP, TASK_FILE);
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
+ };
830
921
  this.metricsManager.trackEvent(
831
922
  METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
832
923
  {
833
924
  taskId: this.data.interactionId,
834
925
  wrapUpCode: wrapupPayload.auxCodeId,
835
926
  wrapUpReason: wrapupPayload.wrapUpReason,
927
+ ...taskErrorProps,
836
928
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
837
929
  },
838
930
  ['operational', 'behavioral', 'business']
839
931
  );
840
- throw detailedError;
932
+ throw err;
841
933
  }
842
934
  }
843
935
 
@@ -906,17 +998,25 @@ export default class Task extends EventEmitter implements ITask {
906
998
 
907
999
  return result;
908
1000
  } catch (error) {
909
- const {error: detailedError} = getErrorDetails(error, METHODS.PAUSE_RECORDING, TASK_FILE);
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
+ };
910
1009
  this.metricsManager.trackEvent(
911
1010
  METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED,
912
1011
  {
913
1012
  taskId: this.data.interactionId,
914
1013
  error: error.toString(),
1014
+ ...taskErrorProps,
915
1015
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
916
1016
  },
917
1017
  ['operational', 'behavioral', 'business']
918
1018
  );
919
- throw detailedError;
1019
+ throw err;
920
1020
  }
921
1021
  }
922
1022
 
@@ -997,17 +1097,25 @@ export default class Task extends EventEmitter implements ITask {
997
1097
 
998
1098
  return result;
999
1099
  } catch (error) {
1000
- const {error: detailedError} = getErrorDetails(error, METHODS.RESUME_RECORDING, TASK_FILE);
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
+ };
1001
1108
  this.metricsManager.trackEvent(
1002
1109
  METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED,
1003
1110
  {
1004
1111
  taskId: this.data.interactionId,
1005
1112
  error: error.toString(),
1113
+ ...taskErrorProps,
1006
1114
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1007
1115
  },
1008
1116
  ['operational', 'behavioral', 'business']
1009
1117
  );
1010
- throw detailedError;
1118
+ throw err;
1011
1119
  }
1012
1120
  }
1013
1121
 
@@ -1082,7 +1190,14 @@ export default class Task extends EventEmitter implements ITask {
1082
1190
 
1083
1191
  return result;
1084
1192
  } catch (error) {
1085
- const {error: detailedError} = getErrorDetails(error, METHODS.CONSULT, TASK_FILE);
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
+ };
1086
1201
  this.metricsManager.trackEvent(
1087
1202
  METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED,
1088
1203
  {
@@ -1090,11 +1205,12 @@ export default class Task extends EventEmitter implements ITask {
1090
1205
  destination: consultPayload.to,
1091
1206
  destinationType: consultPayload.destinationType,
1092
1207
  error: error.toString(),
1208
+ ...taskErrorProps,
1093
1209
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1094
1210
  },
1095
1211
  ['operational', 'behavioral', 'business']
1096
1212
  );
1097
- throw detailedError;
1213
+ throw err;
1098
1214
  }
1099
1215
  }
1100
1216
 
@@ -1167,17 +1283,25 @@ export default class Task extends EventEmitter implements ITask {
1167
1283
 
1168
1284
  return result;
1169
1285
  } catch (error) {
1170
- const {error: detailedError} = getErrorDetails(error, METHODS.END_CONSULT, TASK_FILE);
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
+ };
1171
1294
  this.metricsManager.trackEvent(
1172
1295
  METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED,
1173
1296
  {
1174
1297
  taskId: this.data.interactionId,
1175
1298
  error: error.toString(),
1299
+ ...taskErrorProps,
1176
1300
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1177
1301
  },
1178
1302
  ['operational', 'behavioral', 'business']
1179
1303
  );
1180
- throw detailedError;
1304
+ throw err;
1181
1305
  }
1182
1306
  }
1183
1307
 
@@ -1258,7 +1382,14 @@ export default class Task extends EventEmitter implements ITask {
1258
1382
 
1259
1383
  return result;
1260
1384
  } catch (error) {
1261
- const {error: detailedError} = getErrorDetails(error, METHODS.TRANSFER, TASK_FILE);
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
+ };
1262
1393
  this.metricsManager.trackEvent(
1263
1394
  METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
1264
1395
  {
@@ -1267,11 +1398,12 @@ export default class Task extends EventEmitter implements ITask {
1267
1398
  destinationType: transferPayload.destinationType,
1268
1399
  isConsultTransfer: false,
1269
1400
  error: error.toString(),
1401
+ ...taskErrorProps,
1270
1402
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1271
1403
  },
1272
1404
  ['operational', 'behavioral', 'business']
1273
1405
  );
1274
- throw detailedError;
1406
+ throw err;
1275
1407
  }
1276
1408
  }
1277
1409
 
@@ -1308,68 +1440,362 @@ export default class Task extends EventEmitter implements ITask {
1308
1440
  * ```
1309
1441
  */
1310
1442
  public async consultTransfer(
1311
- consultTransferPayload: ConsultTransferPayLoad
1443
+ consultTransferPayload?: ConsultTransferPayLoad
1312
1444
  ): Promise<TaskResponse> {
1313
- try {
1314
- LoggerProxy.info(`Initiating consult transfer to ${consultTransferPayload.to}`, {
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
+ {
1315
1456
  module: TASK_FILE,
1316
1457
  method: METHODS.CONSULT_TRANSFER,
1317
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,
1318
1473
  });
1319
1474
 
1320
- // For queue destinations, use the destAgentId from task data
1321
- if (consultTransferPayload.destinationType === CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE) {
1322
- if (!this.data.destAgentId) {
1323
- throw new Error('No agent has accepted this queue consult yet');
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,
1324
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
+ });
1325
1657
 
1326
- // Override the destination with the agent who accepted the queue consult
1327
- consultTransferPayload = {
1328
- to: this.data.destAgentId,
1329
- destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
1330
- };
1658
+ // Validate that interaction ID exists
1659
+ if (!this.data.interactionId) {
1660
+ throw new Error('Invalid interaction ID');
1331
1661
  }
1332
1662
 
1333
- const result = await this.contact.consultTransfer({
1663
+ const response = await this.contact.exitConference({
1334
1664
  interactionId: this.data.interactionId,
1335
- data: consultTransferPayload,
1336
1665
  });
1337
1666
 
1667
+ // Track success metrics (following consultTransfer pattern)
1338
1668
  this.metricsManager.trackEvent(
1339
- METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
1669
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_END_SUCCESS,
1340
1670
  {
1341
1671
  taskId: this.data.interactionId,
1342
- destination: consultTransferPayload.to,
1343
- destinationType: consultTransferPayload.destinationType,
1344
- isConsultTransfer: true,
1345
- ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1672
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1346
1673
  },
1347
1674
  ['operational', 'behavioral', 'business']
1348
1675
  );
1349
1676
 
1350
- LoggerProxy.log(`Consult transfer completed successfully to ${consultTransferPayload.to}`, {
1677
+ LoggerProxy.log(`Consult conference exited successfully`, {
1351
1678
  module: TASK_FILE,
1352
- method: METHODS.CONSULT_TRANSFER,
1353
- trackingId: result.trackingId,
1679
+ method: METHODS.EXIT_CONFERENCE,
1354
1680
  interactionId: this.data.interactionId,
1355
1681
  });
1356
1682
 
1357
- return result;
1683
+ return response;
1358
1684
  } catch (error) {
1359
- const {error: detailedError} = getErrorDetails(error, METHODS.CONSULT_TRANSFER, TASK_FILE);
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)
1360
1695
  this.metricsManager.trackEvent(
1361
- METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
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,
1362
1783
  {
1363
1784
  taskId: this.data.interactionId,
1364
- destination: consultTransferPayload.to,
1365
- destinationType: consultTransferPayload.destinationType,
1366
- isConsultTransfer: true,
1367
1785
  error: error.toString(),
1786
+ ...taskErrorProps,
1368
1787
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1369
1788
  },
1370
1789
  ['operational', 'behavioral', 'business']
1371
1790
  );
1372
- throw detailedError;
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;
1373
1799
  }
1374
1800
  }
1375
1801
  }