@webex/contact-center 3.9.0-next.9 → 3.10.0-next.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 (95) hide show
  1. package/dist/cc.js +182 -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/behavioral-events.js +89 -0
  10. package/dist/metrics/behavioral-events.js.map +1 -1
  11. package/dist/metrics/constants.js +30 -2
  12. package/dist/metrics/constants.js.map +1 -1
  13. package/dist/services/AddressBook.js +271 -0
  14. package/dist/services/AddressBook.js.map +1 -0
  15. package/dist/services/EntryPoint.js +227 -0
  16. package/dist/services/EntryPoint.js.map +1 -0
  17. package/dist/services/Queue.js +261 -0
  18. package/dist/services/Queue.js.map +1 -0
  19. package/dist/services/config/constants.js +36 -2
  20. package/dist/services/config/constants.js.map +1 -1
  21. package/dist/services/config/index.js +29 -21
  22. package/dist/services/config/index.js.map +1 -1
  23. package/dist/services/config/types.js +33 -1
  24. package/dist/services/config/types.js.map +1 -1
  25. package/dist/services/core/Utils.js +42 -1
  26. package/dist/services/core/Utils.js.map +1 -1
  27. package/dist/services/task/TaskManager.js +113 -3
  28. package/dist/services/task/TaskManager.js.map +1 -1
  29. package/dist/services/task/TaskUtils.js +76 -0
  30. package/dist/services/task/TaskUtils.js.map +1 -0
  31. package/dist/services/task/constants.js +26 -1
  32. package/dist/services/task/constants.js.map +1 -1
  33. package/dist/services/task/contact.js +86 -0
  34. package/dist/services/task/contact.js.map +1 -1
  35. package/dist/services/task/index.js +273 -16
  36. package/dist/services/task/index.js.map +1 -1
  37. package/dist/services/task/types.js +14 -0
  38. package/dist/services/task/types.js.map +1 -1
  39. package/dist/types/cc.d.ts +115 -35
  40. package/dist/types/constants.d.ts +1 -0
  41. package/dist/types/index.d.ts +3 -2
  42. package/dist/types/metrics/constants.d.ts +24 -1
  43. package/dist/types/services/AddressBook.d.ts +74 -0
  44. package/dist/types/services/EntryPoint.d.ts +67 -0
  45. package/dist/types/services/Queue.d.ts +76 -0
  46. package/dist/types/services/config/constants.d.ts +35 -1
  47. package/dist/types/services/config/index.d.ts +6 -9
  48. package/dist/types/services/config/types.d.ts +79 -58
  49. package/dist/types/services/core/Utils.d.ts +14 -1
  50. package/dist/types/services/task/TaskUtils.d.ts +28 -0
  51. package/dist/types/services/task/constants.d.ts +23 -0
  52. package/dist/types/services/task/contact.d.ts +10 -0
  53. package/dist/types/services/task/index.d.ts +84 -3
  54. package/dist/types/services/task/types.d.ts +233 -21
  55. package/dist/types/types.d.ts +162 -0
  56. package/dist/types/utils/PageCache.d.ts +173 -0
  57. package/dist/types.js +17 -0
  58. package/dist/types.js.map +1 -1
  59. package/dist/utils/PageCache.js +192 -0
  60. package/dist/utils/PageCache.js.map +1 -0
  61. package/dist/webex.js +1 -1
  62. package/package.json +9 -8
  63. package/src/cc.ts +206 -52
  64. package/src/constants.ts +1 -0
  65. package/src/index.ts +16 -2
  66. package/src/logger-proxy.ts +24 -1
  67. package/src/metrics/behavioral-events.ts +94 -0
  68. package/src/metrics/constants.ts +34 -1
  69. package/src/services/AddressBook.ts +291 -0
  70. package/src/services/EntryPoint.ts +241 -0
  71. package/src/services/Queue.ts +277 -0
  72. package/src/services/config/constants.ts +42 -2
  73. package/src/services/config/index.ts +30 -30
  74. package/src/services/config/types.ts +59 -58
  75. package/src/services/core/Utils.ts +44 -0
  76. package/src/services/task/TaskManager.ts +122 -5
  77. package/src/services/task/TaskUtils.ts +81 -0
  78. package/src/services/task/constants.ts +25 -0
  79. package/src/services/task/contact.ts +80 -0
  80. package/src/services/task/index.ts +338 -15
  81. package/src/services/task/types.ts +251 -20
  82. package/src/types.ts +180 -0
  83. package/src/utils/PageCache.ts +252 -0
  84. package/test/unit/spec/cc.ts +282 -85
  85. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  86. package/test/unit/spec/services/AddressBook.ts +332 -0
  87. package/test/unit/spec/services/EntryPoint.ts +259 -0
  88. package/test/unit/spec/services/Queue.ts +323 -0
  89. package/test/unit/spec/services/config/index.ts +279 -65
  90. package/test/unit/spec/services/task/TaskManager.ts +382 -0
  91. package/test/unit/spec/services/task/TaskUtils.ts +131 -0
  92. package/test/unit/spec/services/task/contact.ts +31 -1
  93. package/test/unit/spec/services/task/index.ts +359 -8
  94. package/umd/contact-center.min.js +2 -2
  95. package/umd/contact-center.min.js.map +1 -1
@@ -5,11 +5,12 @@ 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';
11
12
  import {TASK_FILE} from '../../constants';
12
- import {METHODS} from './constants';
13
+ import {METHODS, KEYS_TO_NOT_DELETE} from './constants';
13
14
  import routingContact from './contact';
14
15
  import LoggerProxy from '../../logger-proxy';
15
16
  import {
@@ -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
  /**
@@ -277,9 +281,24 @@ export default class Task extends EventEmitter implements ITask {
277
281
  * @private
278
282
  */
279
283
  private reconcileData(oldData: TaskData, newData: TaskData): TaskData {
284
+ // Remove keys from oldData that are not in newData
285
+ Object.keys(oldData).forEach((key) => {
286
+ if (!(key in newData) && !KEYS_TO_NOT_DELETE.includes(key as string)) {
287
+ delete oldData[key];
288
+ }
289
+ });
290
+
291
+ // Merge or update keys from newData
280
292
  Object.keys(newData).forEach((key) => {
281
- if (newData[key] && typeof newData[key] === 'object' && !Array.isArray(newData[key])) {
282
- oldData[key] = this.reconcileData({...oldData[key]}, newData[key]);
293
+ if (
294
+ newData[key] &&
295
+ typeof newData[key] === 'object' &&
296
+ !Array.isArray(newData[key]) &&
297
+ oldData[key] &&
298
+ typeof oldData[key] === 'object' &&
299
+ !Array.isArray(oldData[key])
300
+ ) {
301
+ this.reconcileData(oldData[key], newData[key]);
283
302
  } else {
284
303
  oldData[key] = newData[key];
285
304
  }
@@ -507,6 +526,7 @@ export default class Task extends EventEmitter implements ITask {
507
526
  * Puts the current task/interaction on hold.
508
527
  * Emits task:hold event when successful. For voice tasks, this mutes the audio.
509
528
  *
529
+ * @param mediaResourceId - Optional media resource ID to use for the hold operation. If not provided, uses the task's current mediaResourceId
510
530
  * @returns Promise<TaskResponse>
511
531
  * @throws Error if hold operation fails
512
532
  * @example
@@ -527,9 +547,17 @@ export default class Task extends EventEmitter implements ITask {
527
547
  * console.error('Failed to place task on hold:', error);
528
548
  * // Handle error (e.g., show error message, reset UI state)
529
549
  * }
550
+ *
551
+ * // Place task on hold with custom mediaResourceId
552
+ * try {
553
+ * await task.hold('custom-media-resource-id');
554
+ * console.log('Successfully placed task on hold with custom mediaResourceId');
555
+ * } catch (error) {
556
+ * console.error('Failed to place task on hold:', error);
557
+ * }
530
558
  * ```
531
559
  */
532
- public async hold(): Promise<TaskResponse> {
560
+ public async hold(mediaResourceId?: string): Promise<TaskResponse> {
533
561
  try {
534
562
  LoggerProxy.info(`Holding task`, {
535
563
  module: TASK_FILE,
@@ -542,9 +570,11 @@ export default class Task extends EventEmitter implements ITask {
542
570
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
543
571
  ]);
544
572
 
573
+ const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
574
+
545
575
  const response = await this.contact.hold({
546
576
  interactionId: this.data.interactionId,
547
- data: {mediaResourceId: this.data.mediaResourceId},
577
+ data: {mediaResourceId: effectiveMediaResourceId},
548
578
  });
549
579
 
550
580
  this.metricsManager.trackEvent(
@@ -552,7 +582,7 @@ export default class Task extends EventEmitter implements ITask {
552
582
  {
553
583
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
554
584
  taskId: this.data.interactionId,
555
- mediaResourceId: this.data.mediaResourceId,
585
+ mediaResourceId: effectiveMediaResourceId,
556
586
  },
557
587
  ['operational', 'behavioral']
558
588
  );
@@ -574,11 +604,13 @@ export default class Task extends EventEmitter implements ITask {
574
604
  errorData: err.data?.errorData,
575
605
  reasonCode: err.data?.reasonCode,
576
606
  };
607
+ const effectiveMediaResourceId = mediaResourceId ?? this.data.mediaResourceId;
608
+
577
609
  this.metricsManager.trackEvent(
578
610
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
579
611
  {
580
612
  taskId: this.data.interactionId,
581
- mediaResourceId: this.data.mediaResourceId,
613
+ mediaResourceId: effectiveMediaResourceId,
582
614
  error: error.toString(),
583
615
  ...taskErrorProps,
584
616
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
@@ -593,6 +625,7 @@ export default class Task extends EventEmitter implements ITask {
593
625
  * Resumes the task/interaction that was previously put on hold.
594
626
  * Emits task:resume event when successful. For voice tasks, this restores the audio.
595
627
  *
628
+ * @param mediaResourceId - Optional media resource ID to use for the resume operation. If not provided, uses the task's current mediaResourceId from interaction media
596
629
  * @returns Promise<TaskResponse>
597
630
  * @throws Error if resume operation fails
598
631
  * @example
@@ -613,9 +646,17 @@ export default class Task extends EventEmitter implements ITask {
613
646
  * console.error('Failed to resume task:', error);
614
647
  * // Handle error (e.g., show error message)
615
648
  * }
649
+ *
650
+ * // Resume task from hold with custom mediaResourceId
651
+ * try {
652
+ * await task.resume('custom-media-resource-id');
653
+ * console.log('Successfully resumed task from hold with custom mediaResourceId');
654
+ * } catch (error) {
655
+ * console.error('Failed to resume task:', error);
656
+ * }
616
657
  * ```
617
658
  */
618
- public async resume(): Promise<TaskResponse> {
659
+ public async resume(mediaResourceId?: string): Promise<TaskResponse> {
619
660
  try {
620
661
  LoggerProxy.info(`Resuming task`, {
621
662
  module: TASK_FILE,
@@ -623,7 +664,9 @@ export default class Task extends EventEmitter implements ITask {
623
664
  interactionId: this.data.interactionId,
624
665
  });
625
666
  const {mainInteractionId} = this.data.interaction;
626
- const {mediaResourceId} = this.data.interaction.media[mainInteractionId];
667
+ const defaultMediaResourceId =
668
+ this.data.interaction.media[mainInteractionId]?.mediaResourceId;
669
+ const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
627
670
 
628
671
  this.metricsManager.timeEvent([
629
672
  METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
@@ -632,7 +675,7 @@ export default class Task extends EventEmitter implements ITask {
632
675
 
633
676
  const response = await this.contact.unHold({
634
677
  interactionId: this.data.interactionId,
635
- data: {mediaResourceId},
678
+ data: {mediaResourceId: effectiveMediaResourceId},
636
679
  });
637
680
 
638
681
  this.metricsManager.trackEvent(
@@ -640,7 +683,7 @@ export default class Task extends EventEmitter implements ITask {
640
683
  {
641
684
  taskId: this.data.interactionId,
642
685
  mainInteractionId,
643
- mediaResourceId,
686
+ mediaResourceId: effectiveMediaResourceId,
644
687
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
645
688
  },
646
689
  ['operational', 'behavioral']
@@ -657,6 +700,11 @@ export default class Task extends EventEmitter implements ITask {
657
700
  } catch (error) {
658
701
  const err = generateTaskErrorObject(error, METHODS.RESUME, TASK_FILE);
659
702
  const mainInteractionId = this.data.interaction?.mainInteractionId;
703
+ const defaultMediaResourceId = mainInteractionId
704
+ ? this.data.interaction.media[mainInteractionId]?.mediaResourceId
705
+ : '';
706
+ const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
707
+
660
708
  const taskErrorProps = {
661
709
  trackingId: err.data?.trackingId,
662
710
  errorMessage: err.data?.message,
@@ -669,9 +717,7 @@ export default class Task extends EventEmitter implements ITask {
669
717
  {
670
718
  taskId: this.data.interactionId,
671
719
  mainInteractionId,
672
- mediaResourceId: mainInteractionId
673
- ? this.data.interaction.media[mainInteractionId].mediaResourceId
674
- : '',
720
+ mediaResourceId: effectiveMediaResourceId,
675
721
  ...taskErrorProps,
676
722
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
677
723
  },
@@ -1488,4 +1534,281 @@ export default class Task extends EventEmitter implements ITask {
1488
1534
  throw err;
1489
1535
  }
1490
1536
  }
1537
+
1538
+ /**
1539
+ * Starts a consultation conference by merging the consultation call with the main call
1540
+ *
1541
+ * Creates a three-way conference between the agent, customer, and consulted party
1542
+ * Extracts required consultation data from the current task data
1543
+ * On success, emits a `task:conferenceStarted` event
1544
+ *
1545
+ * @returns Promise<TaskResponse> - Response from the consultation conference API
1546
+ * @throws Error if the operation fails or if consultation data is invalid
1547
+ *
1548
+ * @example
1549
+ * ```typescript
1550
+ * try {
1551
+ * await task.consultConference();
1552
+ * console.log('Conference started successfully');
1553
+ * } catch (error) {
1554
+ * console.error('Failed to start conference:', error);
1555
+ * }
1556
+ * ```
1557
+ */
1558
+ public async consultConference(): Promise<TaskResponse> {
1559
+ // Extract consultation conference data from task data (used in both try and catch)
1560
+ const consultationData = {
1561
+ agentId: this.agentId,
1562
+ destAgentId: this.data.destAgentId,
1563
+ destinationType: this.data.destinationType || 'agent',
1564
+ };
1565
+
1566
+ try {
1567
+ LoggerProxy.info(`Initiating consult conference to ${consultationData.destAgentId}`, {
1568
+ module: TASK_FILE,
1569
+ method: METHODS.CONSULT_CONFERENCE,
1570
+ interactionId: this.data.interactionId,
1571
+ });
1572
+
1573
+ const paramsDataForConferenceV2 = buildConsultConferenceParamData(
1574
+ consultationData,
1575
+ this.data.interactionId
1576
+ );
1577
+
1578
+ const response = await this.contact.consultConference({
1579
+ interactionId: paramsDataForConferenceV2.interactionId,
1580
+ data: paramsDataForConferenceV2.data,
1581
+ });
1582
+
1583
+ // Track success metrics (following consultTransfer pattern)
1584
+ this.metricsManager.trackEvent(
1585
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS,
1586
+ {
1587
+ taskId: this.data.interactionId,
1588
+ destination: paramsDataForConferenceV2.data.to,
1589
+ destinationType: paramsDataForConferenceV2.data.destinationType,
1590
+ agentId: paramsDataForConferenceV2.data.agentId,
1591
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1592
+ },
1593
+ ['operational', 'behavioral', 'business']
1594
+ );
1595
+
1596
+ LoggerProxy.log(`Consult conference started successfully`, {
1597
+ module: TASK_FILE,
1598
+ method: METHODS.CONSULT_CONFERENCE,
1599
+ interactionId: this.data.interactionId,
1600
+ });
1601
+
1602
+ return response;
1603
+ } catch (error) {
1604
+ const err = generateTaskErrorObject(error, METHODS.CONSULT_CONFERENCE, TASK_FILE);
1605
+ const taskErrorProps = {
1606
+ trackingId: err.data?.trackingId,
1607
+ errorMessage: err.data?.message,
1608
+ errorType: err.data?.errorType,
1609
+ errorData: err.data?.errorData,
1610
+ reasonCode: err.data?.reasonCode,
1611
+ };
1612
+
1613
+ // Track failure metrics (following consultTransfer pattern)
1614
+ // Build conference data for error tracking using extracted data
1615
+ const failedParamsData = buildConsultConferenceParamData(
1616
+ consultationData,
1617
+ this.data.interactionId
1618
+ );
1619
+
1620
+ this.metricsManager.trackEvent(
1621
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED,
1622
+ {
1623
+ taskId: this.data.interactionId,
1624
+ destination: failedParamsData.data.to,
1625
+ destinationType: failedParamsData.data.destinationType,
1626
+ agentId: failedParamsData.data.agentId,
1627
+ error: error.toString(),
1628
+ ...taskErrorProps,
1629
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1630
+ },
1631
+ ['operational', 'behavioral', 'business']
1632
+ );
1633
+
1634
+ LoggerProxy.error(`Failed to start consult conference`, {
1635
+ module: TASK_FILE,
1636
+ method: METHODS.CONSULT_CONFERENCE,
1637
+ interactionId: this.data.interactionId,
1638
+ });
1639
+
1640
+ throw err;
1641
+ }
1642
+ }
1643
+
1644
+ /**
1645
+ * Exits the current conference by removing the agent from the conference call
1646
+ *
1647
+ * Exits the agent from the conference, leaving the customer and consulted party connected
1648
+ * On success, emits a `task:conferenceEnded` event
1649
+ *
1650
+ * @returns Promise<TaskResponse> - Response from the conference exit API
1651
+ * @throws Error if the operation fails or if no active conference exists
1652
+ *
1653
+ * @example
1654
+ * ```typescript
1655
+ * try {
1656
+ * await task.exitConference();
1657
+ * console.log('Successfully exited conference');
1658
+ * } catch (error) {
1659
+ * console.error('Failed to exit conference:', error);
1660
+ * }
1661
+ * ```
1662
+ */
1663
+ public async exitConference(): Promise<TaskResponse> {
1664
+ try {
1665
+ LoggerProxy.info(`Exiting consult conference`, {
1666
+ module: TASK_FILE,
1667
+ method: METHODS.EXIT_CONFERENCE,
1668
+ interactionId: this.data.interactionId,
1669
+ });
1670
+
1671
+ // Validate that interaction ID exists
1672
+ if (!this.data.interactionId) {
1673
+ throw new Error('Invalid interaction ID');
1674
+ }
1675
+
1676
+ const response = await this.contact.exitConference({
1677
+ interactionId: this.data.interactionId,
1678
+ });
1679
+
1680
+ // Track success metrics (following consultTransfer pattern)
1681
+ this.metricsManager.trackEvent(
1682
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_END_SUCCESS,
1683
+ {
1684
+ taskId: this.data.interactionId,
1685
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1686
+ },
1687
+ ['operational', 'behavioral', 'business']
1688
+ );
1689
+
1690
+ LoggerProxy.log(`Consult conference exited successfully`, {
1691
+ module: TASK_FILE,
1692
+ method: METHODS.EXIT_CONFERENCE,
1693
+ interactionId: this.data.interactionId,
1694
+ });
1695
+
1696
+ return response;
1697
+ } catch (error) {
1698
+ const err = generateTaskErrorObject(error, METHODS.EXIT_CONFERENCE, TASK_FILE);
1699
+ const taskErrorProps = {
1700
+ trackingId: err.data?.trackingId,
1701
+ errorMessage: err.data?.message,
1702
+ errorType: err.data?.errorType,
1703
+ errorData: err.data?.errorData,
1704
+ reasonCode: err.data?.reasonCode,
1705
+ };
1706
+
1707
+ // Track failure metrics (following consultTransfer pattern)
1708
+ this.metricsManager.trackEvent(
1709
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_END_FAILED,
1710
+ {
1711
+ taskId: this.data.interactionId,
1712
+ error: error.toString(),
1713
+ ...taskErrorProps,
1714
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1715
+ },
1716
+ ['operational', 'behavioral', 'business']
1717
+ );
1718
+
1719
+ LoggerProxy.error(`Failed to exit consult conference`, {
1720
+ module: TASK_FILE,
1721
+ method: METHODS.EXIT_CONFERENCE,
1722
+ interactionId: this.data.interactionId,
1723
+ });
1724
+
1725
+ throw err;
1726
+ }
1727
+ }
1728
+
1729
+ /**
1730
+ * Transfers the current conference to another agent
1731
+ *
1732
+ * Moves the entire conference (including all participants) to a new agent,
1733
+ * while the current agent exits and goes to wrapup
1734
+ * On success, the current agent receives `task:conferenceEnded` event
1735
+ *
1736
+ * @returns Promise<TaskResponse> - Response from the conference transfer API
1737
+ * @throws Error if the operation fails or if no active conference exists
1738
+ *
1739
+ * @example
1740
+ * ```typescript
1741
+ * try {
1742
+ * await task.transferConference();
1743
+ * console.log('Conference transferred successfully');
1744
+ * } catch (error) {
1745
+ * console.error('Failed to transfer conference:', error);
1746
+ * }
1747
+ * ```
1748
+ */
1749
+ public async transferConference(): Promise<TaskResponse> {
1750
+ try {
1751
+ LoggerProxy.info(`Transferring conference`, {
1752
+ module: TASK_FILE,
1753
+ method: METHODS.TRANSFER_CONFERENCE,
1754
+ interactionId: this.data.interactionId,
1755
+ });
1756
+
1757
+ // Validate that interaction ID exists
1758
+ if (!this.data.interactionId) {
1759
+ throw new Error('Invalid interaction ID');
1760
+ }
1761
+
1762
+ const response = await this.contact.conferenceTransfer({
1763
+ interactionId: this.data.interactionId,
1764
+ });
1765
+
1766
+ // Track success metrics (following consultTransfer pattern)
1767
+ this.metricsManager.trackEvent(
1768
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS,
1769
+ {
1770
+ taskId: this.data.interactionId,
1771
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
1772
+ },
1773
+ ['operational', 'behavioral', 'business']
1774
+ );
1775
+
1776
+ LoggerProxy.log(`Conference transferred successfully`, {
1777
+ module: TASK_FILE,
1778
+ method: METHODS.TRANSFER_CONFERENCE,
1779
+ interactionId: this.data.interactionId,
1780
+ });
1781
+
1782
+ return response;
1783
+ } catch (error) {
1784
+ const err = generateTaskErrorObject(error, METHODS.TRANSFER_CONFERENCE, TASK_FILE);
1785
+ const taskErrorProps = {
1786
+ trackingId: err.data?.trackingId,
1787
+ errorMessage: err.data?.message,
1788
+ errorType: err.data?.errorType,
1789
+ errorData: err.data?.errorData,
1790
+ reasonCode: err.data?.reasonCode,
1791
+ };
1792
+
1793
+ // Track failure metrics (following consultTransfer pattern)
1794
+ this.metricsManager.trackEvent(
1795
+ METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED,
1796
+ {
1797
+ taskId: this.data.interactionId,
1798
+ error: error.toString(),
1799
+ ...taskErrorProps,
1800
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
1801
+ },
1802
+ ['operational', 'behavioral', 'business']
1803
+ );
1804
+
1805
+ LoggerProxy.error(`Failed to transfer conference`, {
1806
+ module: TASK_FILE,
1807
+ method: METHODS.TRANSFER_CONFERENCE,
1808
+ interactionId: this.data.interactionId,
1809
+ });
1810
+
1811
+ throw err;
1812
+ }
1813
+ }
1491
1814
  }