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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/AGENTS.md +438 -0
  2. package/ai-docs/README.md +131 -0
  3. package/ai-docs/RULES.md +455 -0
  4. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  5. package/ai-docs/patterns/testing-patterns.md +480 -0
  6. package/ai-docs/patterns/typescript-patterns.md +365 -0
  7. package/ai-docs/templates/README.md +102 -0
  8. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  9. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  10. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  11. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  12. package/ai-docs/templates/new-method/00-master.md +80 -0
  13. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  14. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  15. package/ai-docs/templates/new-method/03-tests.md +201 -0
  16. package/ai-docs/templates/new-method/04-validation.md +141 -0
  17. package/ai-docs/templates/new-service/00-master.md +109 -0
  18. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  19. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  20. package/ai-docs/templates/new-service/03-integration.md +178 -0
  21. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  22. package/ai-docs/templates/new-service/05-validation.md +145 -0
  23. package/dist/cc.js +65 -123
  24. package/dist/cc.js.map +1 -1
  25. package/dist/constants.js +13 -2
  26. package/dist/constants.js.map +1 -1
  27. package/dist/index.js +13 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/metrics/behavioral-events.js +26 -13
  30. package/dist/metrics/behavioral-events.js.map +1 -1
  31. package/dist/metrics/constants.js +7 -6
  32. package/dist/metrics/constants.js.map +1 -1
  33. package/dist/services/ApiAiAssistant.js +0 -3
  34. package/dist/services/ApiAiAssistant.js.map +1 -1
  35. package/dist/services/config/Util.js +2 -3
  36. package/dist/services/config/Util.js.map +1 -1
  37. package/dist/services/config/types.js +16 -14
  38. package/dist/services/config/types.js.map +1 -1
  39. package/dist/services/constants.js +0 -1
  40. package/dist/services/constants.js.map +1 -1
  41. package/dist/services/core/Err.js.map +1 -1
  42. package/dist/services/core/Utils.js +79 -55
  43. package/dist/services/core/Utils.js.map +1 -1
  44. package/dist/services/core/aqm-reqs.js +17 -92
  45. package/dist/services/core/aqm-reqs.js.map +1 -1
  46. package/dist/services/core/websocket/WebSocketManager.js +5 -25
  47. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  48. package/dist/services/core/websocket/types.js.map +1 -1
  49. package/dist/services/index.js +1 -2
  50. package/dist/services/index.js.map +1 -1
  51. package/dist/services/task/Task.js +644 -0
  52. package/dist/services/task/Task.js.map +1 -0
  53. package/dist/services/task/TaskFactory.js +45 -0
  54. package/dist/services/task/TaskFactory.js.map +1 -0
  55. package/dist/services/task/TaskManager.js +570 -535
  56. package/dist/services/task/TaskManager.js.map +1 -1
  57. package/dist/services/task/TaskUtils.js +132 -28
  58. package/dist/services/task/TaskUtils.js.map +1 -1
  59. package/dist/services/task/constants.js +7 -6
  60. package/dist/services/task/constants.js.map +1 -1
  61. package/dist/services/task/dialer.js +0 -51
  62. package/dist/services/task/dialer.js.map +1 -1
  63. package/dist/services/task/digital/Digital.js +77 -0
  64. package/dist/services/task/digital/Digital.js.map +1 -0
  65. package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
  66. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  67. package/dist/services/task/state-machine/actions.js +372 -0
  68. package/dist/services/task/state-machine/actions.js.map +1 -0
  69. package/dist/services/task/state-machine/constants.js +139 -0
  70. package/dist/services/task/state-machine/constants.js.map +1 -0
  71. package/dist/services/task/state-machine/guards.js +263 -0
  72. package/dist/services/task/state-machine/guards.js.map +1 -0
  73. package/dist/services/task/state-machine/index.js +53 -0
  74. package/dist/services/task/state-machine/index.js.map +1 -0
  75. package/dist/services/task/state-machine/types.js +54 -0
  76. package/dist/services/task/state-machine/types.js.map +1 -0
  77. package/dist/services/task/state-machine/uiControlsComputer.js +377 -0
  78. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  79. package/dist/services/task/taskDataNormalizer.js +99 -0
  80. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  81. package/dist/services/task/types.js +157 -18
  82. package/dist/services/task/types.js.map +1 -1
  83. package/dist/services/task/voice/Voice.js +1031 -0
  84. package/dist/services/task/voice/Voice.js.map +1 -0
  85. package/dist/services/task/voice/WebRTC.js +149 -0
  86. package/dist/services/task/voice/WebRTC.js.map +1 -0
  87. package/dist/types/cc.d.ts +4 -33
  88. package/dist/types/constants.d.ts +13 -2
  89. package/dist/types/index.d.ts +11 -5
  90. package/dist/types/metrics/constants.d.ts +5 -3
  91. package/dist/types/services/ApiAiAssistant.d.ts +1 -1
  92. package/dist/types/services/config/types.d.ts +97 -25
  93. package/dist/types/services/core/Err.d.ts +0 -2
  94. package/dist/types/services/core/Utils.d.ts +25 -23
  95. package/dist/types/services/core/aqm-reqs.d.ts +0 -49
  96. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
  97. package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
  98. package/dist/types/services/core/websocket/types.d.ts +1 -1
  99. package/dist/types/services/index.d.ts +1 -1
  100. package/dist/types/services/task/Task.d.ts +146 -0
  101. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  102. package/dist/types/services/task/TaskUtils.d.ts +39 -8
  103. package/dist/types/services/task/constants.d.ts +5 -4
  104. package/dist/types/services/task/dialer.d.ts +0 -15
  105. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  106. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
  107. package/dist/types/services/task/state-machine/actions.d.ts +8 -0
  108. package/dist/types/services/task/state-machine/constants.d.ts +91 -0
  109. package/dist/types/services/task/state-machine/guards.d.ts +78 -0
  110. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  111. package/dist/types/services/task/state-machine/types.d.ts +256 -0
  112. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  113. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  114. package/dist/types/services/task/types.d.ts +539 -88
  115. package/dist/types/services/task/voice/Voice.d.ts +183 -0
  116. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  117. package/dist/types/types.d.ts +68 -0
  118. package/dist/types/webex.d.ts +1 -0
  119. package/dist/types.js +70 -0
  120. package/dist/types.js.map +1 -1
  121. package/dist/webex.js +14 -2
  122. package/dist/webex.js.map +1 -1
  123. package/package.json +14 -11
  124. package/src/cc.ts +91 -177
  125. package/src/constants.ts +13 -2
  126. package/src/index.ts +14 -5
  127. package/src/metrics/ai-docs/AGENTS.md +348 -0
  128. package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
  129. package/src/metrics/behavioral-events.ts +28 -14
  130. package/src/metrics/constants.ts +7 -8
  131. package/src/services/ApiAiAssistant.ts +2 -4
  132. package/src/services/agent/ai-docs/AGENTS.md +238 -0
  133. package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
  134. package/src/services/ai-docs/AGENTS.md +384 -0
  135. package/src/services/config/Util.ts +2 -3
  136. package/src/services/config/ai-docs/AGENTS.md +253 -0
  137. package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
  138. package/src/services/config/types.ts +108 -20
  139. package/src/services/constants.ts +0 -1
  140. package/src/services/core/Err.ts +0 -1
  141. package/src/services/core/Utils.ts +90 -67
  142. package/src/services/core/ai-docs/AGENTS.md +379 -0
  143. package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
  144. package/src/services/core/aqm-reqs.ts +22 -100
  145. package/src/services/core/websocket/WebSocketManager.ts +4 -23
  146. package/src/services/core/websocket/types.ts +1 -1
  147. package/src/services/index.ts +1 -2
  148. package/src/services/task/Task.ts +785 -0
  149. package/src/services/task/TaskFactory.ts +55 -0
  150. package/src/services/task/TaskManager.ts +579 -633
  151. package/src/services/task/TaskUtils.ts +175 -31
  152. package/src/services/task/ai-docs/AGENTS.md +448 -0
  153. package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
  154. package/src/services/task/constants.ts +5 -4
  155. package/src/services/task/dialer.ts +1 -56
  156. package/src/services/task/digital/Digital.ts +95 -0
  157. package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
  158. package/src/services/task/state-machine/actions.ts +422 -0
  159. package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
  160. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
  161. package/src/services/task/state-machine/constants.ts +150 -0
  162. package/src/services/task/state-machine/guards.ts +303 -0
  163. package/src/services/task/state-machine/index.ts +28 -0
  164. package/src/services/task/state-machine/types.ts +228 -0
  165. package/src/services/task/state-machine/uiControlsComputer.ts +542 -0
  166. package/src/services/task/taskDataNormalizer.ts +137 -0
  167. package/src/services/task/types.ts +641 -95
  168. package/src/services/task/voice/Voice.ts +1255 -0
  169. package/src/services/task/voice/WebRTC.ts +187 -0
  170. package/src/types.ts +88 -5
  171. package/src/utils/AGENTS.md +276 -0
  172. package/src/webex.js +2 -0
  173. package/test/unit/spec/cc.ts +59 -142
  174. package/test/unit/spec/logger-proxy.ts +70 -0
  175. package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
  176. package/test/unit/spec/services/config/index.ts +26 -55
  177. package/test/unit/spec/services/core/Utils.ts +103 -52
  178. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
  179. package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
  180. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  181. package/test/unit/spec/services/task/Task.ts +416 -0
  182. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  183. package/test/unit/spec/services/task/TaskManager.ts +781 -1735
  184. package/test/unit/spec/services/task/TaskUtils.ts +125 -0
  185. package/test/unit/spec/services/task/dialer.ts +112 -198
  186. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  187. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
  188. package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
  189. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  190. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
  191. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  192. package/test/unit/spec/services/task/voice/Voice.ts +587 -0
  193. package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
  194. package/umd/contact-center.min.js +2 -2
  195. package/umd/contact-center.min.js.map +1 -1
  196. package/dist/services/task/index.js +0 -1525
  197. package/dist/services/task/index.js.map +0 -1
  198. package/dist/types/services/task/index.d.ts +0 -650
  199. package/src/services/task/index.ts +0 -1801
  200. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -12,11 +12,15 @@ var _constants2 = require("./constants");
12
12
  var _types2 = require("../config/types");
13
13
  var _types3 = require("../../types");
14
14
  var _loggerProxy = _interopRequireDefault(require("../../logger-proxy"));
15
- var _ = _interopRequireDefault(require("."));
16
- var _MetricsManager = _interopRequireDefault(require("../../metrics/MetricsManager"));
17
- var _constants3 = require("../../metrics/constants");
18
15
  var _TaskUtils = require("./TaskUtils");
16
+ var _TaskFactory = _interopRequireDefault(require("./TaskFactory"));
17
+ var _WebRTC = _interopRequireDefault(require("./voice/WebRTC"));
18
+ var _stateMachine = require("./state-machine");
19
+ var _taskDataNormalizer = require("./taskDataNormalizer");
19
20
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
+ const CC_EVENT_SET = new Set(Object.values(_types2.CC_EVENTS));
22
+ const isCcEvent = value => CC_EVENT_SET.has(value);
23
+
20
24
  /** @internal */
21
25
  class TaskManager extends _events.default {
22
26
  /**
@@ -25,46 +29,30 @@ class TaskManager extends _events.default {
25
29
  * @private
26
30
  */
27
31
 
32
+ // eslint-disable-next-line no-use-before-define
33
+
28
34
  /**
29
35
  * @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
30
36
  * @param webCallingService - Webrtc Service Layer
31
37
  * @param webSocketManager - Websocket Manager to maintain websocket connection and keepalives
32
38
  */
33
- constructor(apiAIAssistant, contact, webCallingService, webSocketManager) {
39
+ constructor(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
34
40
  super();
35
41
  this.apiAIAssistant = apiAIAssistant;
36
42
  this.contact = contact;
37
- this.taskCollection = {};
38
43
  this.webCallingService = webCallingService;
39
44
  this.webSocketManager = webSocketManager;
40
- this.metricsManager = _MetricsManager.default.getInstance();
45
+ this.rtdWebSocketManager = rtdWebSocketManager;
46
+ this.taskCollection = {};
47
+ this.webRtcEnabled = false;
41
48
  this.registerTaskListeners();
42
49
  this.registerIncomingCallEvent();
43
50
  }
44
- setWrapupData(wrapupData) {
45
- this.wrapupData = wrapupData;
46
- }
47
- setAgentId(agentId) {
48
- this.agentId = agentId;
49
- }
50
-
51
- /**
52
- * Gets the current agent ID
53
- * @returns {string} The agent ID set for this task manager instance
54
- * @public
55
- */
56
- getAgentId() {
57
- return this.agentId;
58
- }
59
- setWebRtcEnabled(webRtcEnabled) {
60
- this.webRtcEnabled = webRtcEnabled;
61
- }
62
51
  handleRealtimeWebsocketEvent(event) {
63
52
  try {
64
53
  const payload = JSON.parse(event);
65
- const eventType = payload?.type || payload?.data?.notifType;
66
54
  const interactionId = payload?.data?.data?.conversationId;
67
- if (!eventType || !interactionId) return;
55
+ if (!interactionId) return;
68
56
  const task = this.taskCollection[interactionId];
69
57
  if (!task) {
70
58
  _loggerProxy.default.info(`Realtime transcription task not found`, {
@@ -74,7 +62,7 @@ class TaskManager extends _events.default {
74
62
  });
75
63
  return;
76
64
  }
77
- task.emit(eventType, payload.data);
65
+ task.emit(payload.type, payload.data);
78
66
  } catch (error) {
79
67
  _loggerProxy.default.error('Failed to parse RTD WebSocket message', {
80
68
  module: _constants.TASK_MANAGER_FILE,
@@ -83,8 +71,41 @@ class TaskManager extends _events.default {
83
71
  });
84
72
  }
85
73
  }
74
+
75
+ /**
76
+ * Set config flags for task creation
77
+ */
78
+ setConfigFlags(configFlags) {
79
+ this.configFlags = configFlags;
80
+ }
81
+
82
+ /**
83
+ * Set wrapup configuration data
84
+ */
85
+ setWrapupData(wrapupData) {
86
+ this.wrapupData = wrapupData;
87
+ }
88
+
89
+ /**
90
+ * Set agent ID for task operations
91
+ */
92
+ setAgentId(agentId) {
93
+ this.agentId = agentId;
94
+ }
95
+
96
+ /**
97
+ * Gets the current agent ID
98
+ * @returns {string} The agent ID set for this task manager instance
99
+ * @public
100
+ */
101
+ getAgentId() {
102
+ return this.agentId;
103
+ }
104
+ setWebRtcEnabled(webRtcEnabled) {
105
+ this.webRtcEnabled = webRtcEnabled;
106
+ }
86
107
  handleIncomingWebCall = call => {
87
- const currentTask = Object.values(this.taskCollection).find(task => task.data.interaction.mediaType === 'telephony' && !(0, _TaskUtils.isCampaignPreviewReservation)(task));
108
+ const currentTask = Object.values(this.taskCollection).find(t => t.data.interaction.mediaType === _types.MEDIA_CHANNEL.TELEPHONY);
88
109
  if (currentTask) {
89
110
  this.webCallingService.mapCallToTask(call.getCallId(), currentTask.data.interactionId);
90
111
  _loggerProxy.default.log(`Call mapped to task`, {
@@ -92,7 +113,12 @@ class TaskManager extends _events.default {
92
113
  method: _constants2.METHODS.HANDLE_INCOMING_WEB_CALL,
93
114
  interactionId: currentTask.data.interactionId
94
115
  });
95
- this.emit(_types.TASK_EVENTS.TASK_INCOMING, currentTask);
116
+
117
+ // Send TASK_INCOMING to state machine - it will emit on the task object
118
+ const eventPayload = TaskManager.mapEventToTaskStateMachineEvent(_types2.CC_EVENTS.AGENT_CONTACT_RESERVED, currentTask.data);
119
+ if (eventPayload && currentTask) {
120
+ currentTask.sendStateMachineEvent(eventPayload);
121
+ }
96
122
  }
97
123
  this.call = call;
98
124
  };
@@ -102,468 +128,478 @@ class TaskManager extends _events.default {
102
128
  unregisterIncomingCallEvent() {
103
129
  this.webCallingService.off(_calling.LINE_EVENTS.INCOMING_CALL, this.handleIncomingWebCall);
104
130
  }
131
+
132
+ /**
133
+ * Map WebSocket CC_EVENTS to state machine TaskEvent
134
+ * @param ccEvent - The CC_EVENT type from WebSocket
135
+ * @param payload - The event payload
136
+ * @param agentId - Optional agent ID for state detection (needed for HYDRATE)
137
+ * @returns TaskEventPayload for state machine or null if no mapping
138
+ */
139
+ static mapEventToTaskStateMachineEvent(ccEvent, payload, agentId) {
140
+ const mediaResourceId = payload.mediaResourceId || payload.interaction?.media?.[payload.interactionId]?.mediaResourceId;
141
+ switch (ccEvent) {
142
+ // CC -> TaskEvent mappings (see TaskStateMachine comment for quick reference)
143
+ case _types2.CC_EVENTS.AGENT_CONTACT_RESERVED:
144
+ // AgentContactReserved -> TASK_INCOMING
145
+ return {
146
+ type: _stateMachine.TaskEvent.TASK_INCOMING,
147
+ taskData: payload
148
+ };
149
+ case _types2.CC_EVENTS.AGENT_OFFER_CONTACT:
150
+ // AgentOfferContact -> TASK_OFFERED
151
+ return {
152
+ type: _stateMachine.TaskEvent.TASK_OFFERED,
153
+ taskData: payload
154
+ };
155
+ case _types2.CC_EVENTS.AGENT_CONTACT:
156
+ // AgentContact -> HYDRATE
157
+ // Include agentId for state detection (e.g., checking isWrapUp in participant data)
158
+ return {
159
+ type: _stateMachine.TaskEvent.HYDRATE,
160
+ taskData: payload,
161
+ agentId
162
+ };
163
+ case _types2.CC_EVENTS.CONTACT_UPDATED:
164
+ return {
165
+ type: _stateMachine.TaskEvent.CONTACT_UPDATED,
166
+ taskData: payload
167
+ };
168
+ case _types2.CC_EVENTS.CONTACT_OWNER_CHANGED:
169
+ return {
170
+ type: _stateMachine.TaskEvent.CONTACT_OWNER_CHANGED,
171
+ taskData: payload
172
+ };
173
+ case _types2.CC_EVENTS.AGENT_OFFER_CONSULT:
174
+ // AgentOfferConsult -> OFFER_CONSULT
175
+ return {
176
+ type: _stateMachine.TaskEvent.OFFER_CONSULT,
177
+ taskData: {
178
+ ...payload,
179
+ isConsulted: true
180
+ }
181
+ };
182
+ case _types2.CC_EVENTS.AGENT_CONTACT_ASSIGNED:
183
+ // AgentContactAssigned -> ASSIGN
184
+ return {
185
+ type: _stateMachine.TaskEvent.ASSIGN,
186
+ taskData: payload
187
+ };
188
+ case _types2.CC_EVENTS.AGENT_CONTACT_HELD:
189
+ return {
190
+ type: _stateMachine.TaskEvent.HOLD_SUCCESS,
191
+ mediaResourceId: mediaResourceId || '',
192
+ taskData: payload
193
+ };
194
+ case _types2.CC_EVENTS.AGENT_CONTACT_UNHELD:
195
+ return {
196
+ type: _stateMachine.TaskEvent.UNHOLD_SUCCESS,
197
+ mediaResourceId: mediaResourceId || '',
198
+ taskData: payload
199
+ };
200
+ case _types2.CC_EVENTS.AGENT_CONSULT_CREATED:
201
+ return {
202
+ type: _stateMachine.TaskEvent.CONSULT_CREATED,
203
+ taskData: {
204
+ ...payload,
205
+ isConsulted: false
206
+ }
207
+ };
208
+ case _types2.CC_EVENTS.AGENT_CONSULTING:
209
+ // AgentConsulting -> CONSULTING_ACTIVE
210
+ // use context to figure out if it's the initiator or receiver using consultInitiator from context
211
+ return {
212
+ type: _stateMachine.TaskEvent.CONSULTING_ACTIVE,
213
+ consultDestinationAgentJoined: true,
214
+ taskData: payload
215
+ };
216
+ case _types2.CC_EVENTS.AGENT_CONSULT_ENDED:
217
+ // AgentConsultEnded -> CONSULT_END
218
+ return {
219
+ type: _stateMachine.TaskEvent.CONSULT_END,
220
+ taskData: payload
221
+ };
222
+ case _types2.CC_EVENTS.AGENT_CONSULT_FAILED:
223
+ case _types2.CC_EVENTS.AGENT_CTQ_FAILED:
224
+ return {
225
+ type: _stateMachine.TaskEvent.CONSULT_FAILED,
226
+ reason: payload.reason,
227
+ taskData: payload
228
+ };
229
+ case _types2.CC_EVENTS.AGENT_CTQ_CANCELLED:
230
+ return {
231
+ type: _stateMachine.TaskEvent.CTQ_CANCEL,
232
+ taskData: payload
233
+ };
234
+ case _types2.CC_EVENTS.AGENT_CTQ_CANCEL_FAILED:
235
+ return {
236
+ type: _stateMachine.TaskEvent.CTQ_CANCEL_FAILED,
237
+ taskData: payload
238
+ };
239
+ case _types2.CC_EVENTS.AGENT_BLIND_TRANSFERRED: // AgentBlindTransferred -> TRANSFER_SUCCESS
240
+ case _types2.CC_EVENTS.AGENT_CONSULT_TRANSFERRED: // AgentConsultTransferred -> TRANSFER_SUCCESS
241
+ case _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED:
242
+ // AgentVTeamTransferred -> TRANSFER_SUCCESS
243
+ return {
244
+ type: _stateMachine.TaskEvent.TRANSFER_SUCCESS,
245
+ taskData: payload
246
+ };
247
+ case _types2.CC_EVENTS.AGENT_WRAPUP:
248
+ return {
249
+ type: _stateMachine.TaskEvent.TASK_WRAPUP,
250
+ taskData: {
251
+ ...payload,
252
+ wrapUpRequired: true
253
+ }
254
+ };
255
+ case _types2.CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
256
+ return null;
257
+ // Add WRAPUP if needed
258
+
259
+ case _types2.CC_EVENTS.AGENT_BLIND_TRANSFER_FAILED:
260
+ case _types2.CC_EVENTS.AGENT_VTEAM_TRANSFER_FAILED:
261
+ case _types2.CC_EVENTS.AGENT_CONSULT_TRANSFER_FAILED:
262
+ case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFER_FAILED:
263
+ return {
264
+ type: _stateMachine.TaskEvent.TRANSFER_FAILED,
265
+ taskData: payload
266
+ };
267
+ case _types2.CC_EVENTS.CONTACT_ENDED:
268
+ return {
269
+ type: _stateMachine.TaskEvent.CONTACT_ENDED,
270
+ taskData: {
271
+ ...payload,
272
+ wrapUpRequired: payload.interaction?.state !== 'new'
273
+ }
274
+ };
275
+ case _types2.CC_EVENTS.AGENT_INVITE_FAILED:
276
+ return {
277
+ type: _stateMachine.TaskEvent.INVITE_FAILED,
278
+ reason: payload.reason
279
+ };
280
+ case _types2.CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED:
281
+ return {
282
+ type: _stateMachine.TaskEvent.ASSIGN_FAILED,
283
+ reason: payload.reason
284
+ };
285
+ case _types2.CC_EVENTS.AGENT_CONTACT_OFFER_RONA:
286
+ return {
287
+ type: _stateMachine.TaskEvent.RONA,
288
+ taskData: payload,
289
+ reason: payload.reason
290
+ };
291
+ case _types2.CC_EVENTS.AGENT_OUTBOUND_FAILED:
292
+ return {
293
+ type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
294
+ reason: payload.reason
295
+ };
296
+ case _types2.CC_EVENTS.CONTACT_RECORDING_STARTED:
297
+ return {
298
+ type: _stateMachine.TaskEvent.RECORDING_STARTED,
299
+ taskData: payload
300
+ };
301
+ case _types2.CC_EVENTS.CONTACT_RECORDING_PAUSED:
302
+ return {
303
+ type: _stateMachine.TaskEvent.PAUSE_RECORDING,
304
+ taskData: payload
305
+ };
306
+ case _types2.CC_EVENTS.CONTACT_RECORDING_RESUMED:
307
+ return {
308
+ type: _stateMachine.TaskEvent.RESUME_RECORDING,
309
+ taskData: payload
310
+ };
311
+ case _types2.CC_EVENTS.AGENT_WRAPPEDUP:
312
+ return {
313
+ type: _stateMachine.TaskEvent.WRAPUP_COMPLETE,
314
+ taskData: payload
315
+ };
316
+
317
+ // Conference events - these trigger state machine transition to CONFERENCING
318
+ case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCED:
319
+ case _types2.CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE:
320
+ return {
321
+ type: _stateMachine.TaskEvent.CONFERENCE_START,
322
+ taskData: payload
323
+ };
324
+ case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED:
325
+ return {
326
+ type: _stateMachine.TaskEvent.CONFERENCE_FAILED,
327
+ reason: payload.reason,
328
+ taskData: payload
329
+ };
330
+ case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED:
331
+ return {
332
+ type: _stateMachine.TaskEvent.CONFERENCE_END,
333
+ taskData: payload
334
+ };
335
+ case _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE:
336
+ return {
337
+ type: _stateMachine.TaskEvent.PARTICIPANT_LEAVE,
338
+ taskData: payload,
339
+ participantId: payload?.participantId
340
+ };
341
+ case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED:
342
+ return {
343
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE_SUCCESS,
344
+ taskData: payload
345
+ };
346
+ default:
347
+ // Not all events need state machine mapping
348
+ return null;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Register WebSocket message listeners for task events
354
+ *
355
+ * Main entry point that orchestrates event processing through a clear pipeline:
356
+ * 1. Parse and validate incoming WebSocket messages
357
+ * 2. Prepare event context with task and state machine mappings
358
+ * 3. Handle task lifecycle (creation, updates, collection management)
359
+ * 4. Send events to state machine (task-level transitions/emissions)
360
+ * 5. Cleanup is triggered via task events emitted by the state machine
361
+ *
362
+ * This architecture separates concerns:
363
+ * - TaskManager: Manages task collection lifecycle and operational concerns
364
+ * - State Machine: Manages individual task state and event emissions
365
+ */
105
366
  registerTaskListeners() {
106
367
  this.webSocketManager.on('message', event => {
107
- const payload = JSON.parse(event);
108
- // Re-emit the task events to the task object
109
- let task;
110
- if (payload.data?.type || payload.type) {
111
- if (Object.values(_types2.CC_TASK_EVENTS).includes(payload.data.type || payload.type)) {
112
- task = this.taskCollection[payload.data?.interactionId] || this.taskCollection[payload.data?.data?.conversationId];
113
- }
114
- _loggerProxy.default.info(`Handling task event ${payload.data?.type}`, {
115
- module: _constants.TASK_MANAGER_FILE,
116
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
117
- interactionId: payload.data?.interactionId
118
- });
119
- switch (payload.data.type) {
120
- case _types2.CC_EVENTS.AGENT_CONTACT:
121
- // Case1 : Task is already present in taskCollection
122
- if (this.taskCollection[payload.data.interactionId]) {
123
- _loggerProxy.default.log(`Got AGENT_CONTACT: Task already exists in collection`, {
124
- module: _constants.TASK_MANAGER_FILE,
125
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
126
- interactionId: payload.data.interactionId
127
- });
128
- break;
129
- } else if (!this.taskCollection[payload.data.interactionId]) {
130
- // Case2 : Task is not present in taskCollection
131
- _loggerProxy.default.log(`Got AGENT_CONTACT : Creating new task in taskManager`, {
132
- module: _constants.TASK_MANAGER_FILE,
133
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
134
- interactionId: payload.data.interactionId
135
- });
136
-
137
- // Check if auto-answer should happen for this task
138
- const shouldAutoAnswer = (0, _TaskUtils.shouldAutoAnswerTask)(payload.data, this.agentId, this.webCallingService.loginOption, this.webRtcEnabled);
139
- task = new _.default(this.contact, this.webCallingService, {
140
- ...payload.data,
141
- wrapUpRequired: payload.data.interaction?.participants?.[this.agentId]?.isWrapUp || false,
142
- isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload.data),
143
- isAutoAnswering: shouldAutoAnswer // Set flag before emitting
144
- }, this.wrapupData, this.agentId);
145
- this.taskCollection[payload.data.interactionId] = task;
146
- // Condition 1: The state is=new i.e it is a incoming task
147
- if (payload.data.interaction.state === 'new') {
148
- _loggerProxy.default.log(`Got AGENT_CONTACT for a task with state=new, sending TASK_INCOMING event`, {
149
- module: _constants.TASK_MANAGER_FILE,
150
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
151
- interactionId: payload.data.interactionId
152
- });
153
- this.emit(_types.TASK_EVENTS.TASK_INCOMING, task);
154
- } else {
155
- // Condition 2: The state is anything else i.e the task was connected
156
- _loggerProxy.default.log(`Got AGENT_CONTACT for a task with state=${payload.data.interaction.state}, sending TASK_HYDRATE event`, {
157
- module: _constants.TASK_MANAGER_FILE,
158
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
159
- interactionId: payload.data.interactionId
160
- });
161
- this.emit(_types.TASK_EVENTS.TASK_HYDRATE, task);
162
- }
163
- }
164
- break;
165
- case _types2.CC_EVENTS.AGENT_CONTACT_RESERVED:
166
- {
167
- // Check if auto-answer should happen for this task
168
- const shouldAutoAnswerReserved = (0, _TaskUtils.shouldAutoAnswerTask)(payload.data, this.agentId, this.webCallingService.loginOption, this.webRtcEnabled);
169
- task = new _.default(this.contact, this.webCallingService, {
170
- ...payload.data,
171
- isConsulted: false,
172
- isAutoAnswering: shouldAutoAnswerReserved // Set flag before emitting
173
- }, this.wrapupData, this.agentId);
174
- this.taskCollection[payload.data.interactionId] = task;
175
- if (this.webCallingService.loginOption !== _types3.LoginOption.BROWSER || task.data.interaction.mediaType !== _types.MEDIA_CHANNEL.TELEPHONY // for digital channels
176
- ) {
177
- this.emit(_types.TASK_EVENTS.TASK_INCOMING, task);
178
- } else if (this.call) {
179
- this.emit(_types.TASK_EVENTS.TASK_INCOMING, task);
180
- }
181
- break;
182
- }
183
- case _types2.CC_EVENTS.AGENT_OFFER_CONTACT:
184
- // We don't have to emit any event here since this will be result of promise.
185
- task = this.updateTaskData(task, payload.data);
186
- _loggerProxy.default.log(`Agent offer contact received for task`, {
187
- module: _constants.TASK_MANAGER_FILE,
188
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
189
- interactionId: payload.data?.interactionId
190
- });
191
- this.emit(_types.TASK_EVENTS.TASK_OFFER_CONTACT, task);
192
-
193
- // Handle auto-answer for offer contact
194
- this.handleAutoAnswer(task);
195
- break;
196
- case _types2.CC_EVENTS.AGENT_OUTBOUND_FAILED:
197
- if (task) {
198
- task = this.updateTaskData(task, payload.data);
199
- this.metricsManager.trackEvent(_constants3.METRIC_EVENT_NAMES.TASK_OUTDIAL_FAILED, {
200
- ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(payload.data),
201
- taskId: payload.data.interactionId,
202
- reason: payload.data.reasonCode || payload.data.reason
203
- }, ['behavioral', 'operational']);
204
- _loggerProxy.default.log(`Agent outbound failed for task`, {
205
- module: _constants.TASK_MANAGER_FILE,
206
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
207
- interactionId: payload.data.interactionId
208
- });
209
- task.emit(_types.TASK_EVENTS.TASK_OUTDIAL_FAILED, payload.data.reason ?? 'UNKNOWN_REASON');
210
- }
211
- break;
212
- case _types2.CC_EVENTS.AGENT_CONTACT_ASSIGNED:
213
- // When a campaign preview contact is accepted, the assigned event may arrive
214
- // with a new interactionId while the task is stored under the original
215
- // reservationInteractionId. Fall back to that key so the task is found.
216
- if (!task && payload.data.reservationInteractionId) {
217
- task = this.taskCollection[payload.data.reservationInteractionId];
218
- if (task) {
219
- // Re-key the task under the new interaction ID and remove the old entry
220
- delete this.taskCollection[payload.data.reservationInteractionId];
221
- this.taskCollection[payload.data.interactionId] = task;
222
- }
223
- }
224
- if (task) {
225
- task = this.updateTaskData(task, payload.data);
226
- task.emit(_types.TASK_EVENTS.TASK_ASSIGNED, task);
227
- }
228
- break;
229
- case _types2.CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
230
- task = this.updateTaskData(task, {
231
- ...payload.data,
232
- wrapUpRequired: true
233
- });
234
- task.emit(_types.TASK_EVENTS.TASK_END, task);
235
- break;
236
- case _types2.CC_EVENTS.AGENT_CONTACT_OFFER_RONA:
237
- case _types2.CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED:
238
- case _types2.CC_EVENTS.AGENT_INVITE_FAILED:
239
- {
240
- _loggerProxy.default.warn(`[DEBUG-CAMPAIGN-CLEAR] Task removal triggered by ${payload.data.type}, interactionId=${payload.data.interactionId}, taskType=${task?.data?.type}`, {
241
- module: _constants.TASK_MANAGER_FILE,
242
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
243
- interactionId: payload.data.interactionId
244
- });
245
- task = this.updateTaskData(task, payload.data);
246
- const eventTypeToMetricMap = {
247
- [_types2.CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED]: 'AGENT_CONTACT_ASSIGN_FAILED',
248
- [_types2.CC_EVENTS.AGENT_INVITE_FAILED]: 'AGENT_INVITE_FAILED'
249
- };
250
- const metricEventName = eventTypeToMetricMap[payload.data.type] || 'AGENT_RONA';
251
- this.metricsManager.trackEvent(_constants3.METRIC_EVENT_NAMES[metricEventName], {
252
- ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(payload.data),
253
- taskId: payload.data.interactionId,
254
- reason: payload.data.reason
255
- }, ['behavioral', 'operational']);
256
- this.handleTaskCleanup(task);
257
- task.emit(_types.TASK_EVENTS.TASK_REJECT, payload.data.reason);
258
- break;
259
- }
260
- case _types2.CC_EVENTS.CONTACT_ENDED:
261
- // Update task data
262
- if (task) {
263
- _loggerProxy.default.warn(`[DEBUG-CAMPAIGN-CLEAR] CONTACT_ENDED, interactionId=${payload.data.interactionId}, taskType=${task?.data?.type}, state=${task?.data?.interaction?.state}`, {
264
- module: _constants.TASK_MANAGER_FILE,
265
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
266
- interactionId: payload.data.interactionId
267
- });
268
- task = this.updateTaskData(task, {
269
- ...payload.data,
270
- wrapUpRequired: payload.data.agentsPendingWrapUp?.includes(this.agentId) || false
271
- });
272
-
273
- // Handle cleanup based on whether task should be deleted
274
- this.handleTaskCleanup(task);
275
- task?.emit(_types.TASK_EVENTS.TASK_END, task);
276
- }
277
- break;
278
- case _types2.CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
279
- // CampaignContactUpdated is a non-terminal event (intermediate update during accept).
280
- // Only update the task data — do NOT remove the task or emit TASK_END.
281
- // Task cleanup is handled by CONTACT_ENDED or other terminal events.
282
- if (task) {
283
- task = this.updateTaskData(task, payload.data);
284
- }
285
- break;
286
- case _types2.CC_EVENTS.CONTACT_MERGED:
287
- task = this.handleContactMerged(task, payload.data);
288
- break;
289
- case _types2.CC_EVENTS.AGENT_CONTACT_HELD:
290
- // As soon as the main interaction is held, we need to emit TASK_HOLD
291
- task = this.updateTaskData(task, payload.data);
292
- task.emit(_types.TASK_EVENTS.TASK_HOLD, task);
293
- break;
294
- case _types2.CC_EVENTS.AGENT_CONTACT_UNHELD:
295
- // As soon as the main interaction is unheld, we need to emit TASK_RESUME
296
- task = this.updateTaskData(task, payload.data);
297
- task.emit(_types.TASK_EVENTS.TASK_RESUME, task);
298
- break;
299
- case _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED:
300
- task = this.updateTaskData(task, {
301
- ...payload.data,
302
- wrapUpRequired: true
303
- });
304
- task.emit(_types.TASK_EVENTS.TASK_END, task);
305
- break;
306
- case _types2.CC_EVENTS.AGENT_CTQ_CANCEL_FAILED:
307
- task = this.updateTaskData(task, payload.data);
308
- task.emit(_types.TASK_EVENTS.TASK_CONSULT_QUEUE_FAILED, task);
309
- break;
310
- case _types2.CC_EVENTS.AGENT_CONSULT_CREATED:
311
- // Received when self agent initiates a consult
312
- task = this.updateTaskData(task, {
313
- ...payload.data,
314
- isConsulted: false // This ensures that the task consult status is always reset
315
- });
316
- task.emit(_types.TASK_EVENTS.TASK_CONSULT_CREATED, task);
317
- break;
318
- case _types2.CC_EVENTS.AGENT_OFFER_CONSULT:
319
- // Received when other agent sends us a consult offer
320
- task = this.updateTaskData(task, {
321
- ...payload.data,
322
- isConsulted: true // This ensures that the task is marked as us being requested for a consult
323
- });
324
- task.emit(_types.TASK_EVENTS.TASK_OFFER_CONSULT, task);
325
-
326
- // Handle auto-answer for consult offer
327
- this.handleAutoAnswer(task);
328
- break;
329
- case _types2.CC_EVENTS.AGENT_CONSULTING:
330
- // Received when agent is in an active consult state
331
- // TODO: Check if we can use backend consult state instead of isConsulted
332
- task = this.updateTaskData(task, payload.data);
333
- if (task.data.isConsulted) {
334
- // Fire only if you are the agent who received the consult request
335
- task.emit(_types.TASK_EVENTS.TASK_CONSULT_ACCEPTED, task);
336
- } else {
337
- // Fire only if you are the agent who initiated the consult
338
- task.emit(_types.TASK_EVENTS.TASK_CONSULTING, task);
339
- }
340
- break;
341
- case _types2.CC_EVENTS.AGENT_CONSULT_FAILED:
342
- // This can only be received by the agent who initiated the consult.
343
- // We need not emit any event here since this will be result of promise
344
- task = this.updateTaskData(task, payload.data);
345
- break;
346
- case _types2.CC_EVENTS.AGENT_CONSULT_ENDED:
347
- task = this.updateTaskData(task, payload.data);
348
- if (task.data.isConsulted) {
349
- // This will be the end state of the task as soon as we end the consult in case of
350
- // us being offered a consult
351
- this.removeTaskFromCollection(task);
352
- }
353
- task.emit(_types.TASK_EVENTS.TASK_CONSULT_END, task);
354
- break;
355
- case _types2.CC_EVENTS.AGENT_CTQ_CANCELLED:
356
- // This event is received when the consult using queue is cancelled using API
357
- task = this.updateTaskData(task, payload.data);
358
- task.emit(_types.TASK_EVENTS.TASK_CONSULT_QUEUE_CANCELLED, task);
359
- break;
360
- case _types2.CC_EVENTS.AGENT_WRAPUP:
361
- task = this.updateTaskData(task, {
362
- ...payload.data,
363
- wrapUpRequired: true
364
- });
365
- task.emit(_types.TASK_EVENTS.TASK_END, task);
366
- break;
367
- case _types2.CC_EVENTS.AGENT_WRAPPEDUP:
368
- task.cancelAutoWrapupTimer();
369
- this.removeTaskFromCollection(task);
370
- task.emit(_types.TASK_EVENTS.TASK_WRAPPEDUP, task);
371
- break;
372
- case _types2.CC_EVENTS.CONTACT_RECORDING_PAUSED:
373
- task = this.updateTaskData(task, payload.data);
374
- task.emit(_types.TASK_EVENTS.TASK_RECORDING_PAUSED, task);
375
- break;
376
- case _types2.CC_EVENTS.CONTACT_RECORDING_PAUSE_FAILED:
377
- task = this.updateTaskData(task, payload.data);
378
- task.emit(_types.TASK_EVENTS.TASK_RECORDING_PAUSE_FAILED, task);
379
- break;
380
- case _types2.CC_EVENTS.CONTACT_RECORDING_RESUMED:
381
- task = this.updateTaskData(task, payload.data);
382
- task.emit(_types.TASK_EVENTS.TASK_RECORDING_RESUMED, task);
383
- break;
384
- case _types2.CC_EVENTS.CONTACT_RECORDING_RESUME_FAILED:
385
- task = this.updateTaskData(task, payload.data);
386
- task.emit(_types.TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, task);
387
- break;
388
- case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCING:
389
- // Conference is being established - update task state and emit establishing event
390
- task = this.updateTaskData(task, payload.data);
391
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_ESTABLISHING, task);
392
- break;
393
- case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCED:
394
- // Conference started successfully - update task state and emit event
395
- task = this.updateTaskData(task, payload.data);
396
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
397
- break;
398
- case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED:
399
- // Conference failed - update task state and emit failure event
400
- task = this.updateTaskData(task, payload.data);
401
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_FAILED, task);
402
- break;
403
- case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED:
404
- // Conference ended - update task state and emit event
405
- task = this.updateTaskData(task, payload.data);
406
- if (!task || (0, _TaskUtils.isPrimary)(task, this.agentId) || (0, _TaskUtils.isParticipantInMainInteraction)(task, this.agentId)) {
407
- _loggerProxy.default.log('Primary or main interaction participant leaving conference');
408
- } else {
409
- this.removeTaskFromCollection(task);
410
- }
411
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_ENDED, task);
412
- break;
413
- case _types2.CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE:
414
- {
415
- task = this.updateTaskData(task, {
416
- ...payload.data,
417
- isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload.data)
418
- });
419
- task.emit(_types.TASK_EVENTS.TASK_PARTICIPANT_JOINED, task);
420
- break;
421
- }
422
- case _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE:
423
- {
424
- // Conference ended - update task state and emit event
425
-
426
- task = this.updateTaskData(task, {
427
- ...payload.data,
428
- isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload.data)
429
- });
430
- if ((0, _TaskUtils.checkParticipantNotInInteraction)(task, this.agentId)) {
431
- if ((0, _TaskUtils.isParticipantInMainInteraction)(task, this.agentId) || (0, _TaskUtils.isPrimary)(task, this.agentId)) {
432
- _loggerProxy.default.log('Primary or main interaction participant leaving conference');
433
- } else {
434
- this.removeTaskFromCollection(task);
435
- }
436
- }
437
- task.emit(_types.TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
438
- break;
439
- }
440
- case _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE_FAILED:
441
- // Conference exit failed - update task state and emit failure event
442
- task = this.updateTaskData(task, payload.data);
443
- task.emit(_types.TASK_EVENTS.TASK_PARTICIPANT_LEFT_FAILED, task);
444
- break;
445
- case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_END_FAILED:
446
- // Conference end failed - update task state with error details and emit failure event
447
- task = this.updateTaskData(task, payload.data);
448
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_END_FAILED, task);
449
- break;
450
- case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED:
451
- // Conference was transferred - update task state and emit transfer success event
452
- // Note: Backend should provide hasLeft and wrapUpRequired status
453
- task = this.updateTaskData(task, payload.data);
454
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_TRANSFERRED, task);
455
- break;
456
- case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFER_FAILED:
457
- // Conference transfer failed - update task state with error details and emit failure event
458
- task = this.updateTaskData(task, payload.data);
459
- task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, task);
460
- break;
461
- case _types2.CC_EVENTS.PARTICIPANT_POST_CALL_ACTIVITY:
462
- // Post-call activity for participant - update task state with activity details
463
- task = this.updateTaskData(task, payload.data);
464
- task.emit(_types.TASK_EVENTS.TASK_POST_CALL_ACTIVITY, task);
465
- break;
466
- case _types2.CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION:
467
- {
468
- // Campaign preview contact offered to agent
469
- // Create a task in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED
470
- // after acceptPreviewContact) can find and update it.
471
- // Emit TASK_CAMPAIGN_PREVIEW_RESERVATION instead of TASK_INCOMING so the call
472
- // does not ring out to the customer before the agent explicitly accepts the preview contact.
473
- _loggerProxy.default.log('Campaign preview reservation received', {
474
- module: _constants.TASK_MANAGER_FILE,
475
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
476
- interactionId: payload.data.interactionId
477
- });
478
- if (!this.taskCollection[payload.data.interactionId]) {
479
- task = new _.default(this.contact, this.webCallingService, {
480
- ...payload.data,
481
- wrapUpRequired: false,
482
- isConferenceInProgress: false,
483
- isAutoAnswering: false
484
- }, this.wrapupData, this.agentId);
485
- this.taskCollection[payload.data.interactionId] = task;
486
- } else {
487
- task = this.updateTaskData(task, payload.data);
488
- }
489
- this.emit(_types.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
490
- break;
491
- }
492
- default:
493
- break;
494
- }
495
- if (task) {
496
- task.emit(payload.data.type, payload.data);
497
- }
498
- const transcriptInteractionId = payload.data?.interactionId || payload.data?.data?.conversationId || task?.data?.interactionId;
499
- if (_constants2.TRANSCRIPT_EVENT_MAP[payload.data.type] && transcriptInteractionId) {
500
- this.requestRealTimeTranscripts(payload.data.type, transcriptInteractionId);
501
- }
368
+ // Step 1: Parse and validate the message
369
+ const message = TaskManager.parseWebSocketMessage(event);
370
+ if (!message) return;
371
+
372
+ // Step 2: Prepare event context
373
+ const eventContext = this.prepareEventContext(message);
374
+ if (!eventContext) return;
375
+ const actions = this.handleTaskLifecycleEvent(eventContext);
376
+ const {
377
+ task
378
+ } = actions;
379
+ if (!task) return;
380
+ const {
381
+ payload,
382
+ stateMachineEvent
383
+ } = eventContext;
384
+
385
+ // Always keep task.data updated (even for mapped events) so consumers relying
386
+ // on TaskManager-managed task instances see the latest payload.
387
+ if (payload) {
388
+ this.updateTaskData(task, payload);
389
+ }
390
+
391
+ // Send event to state machine - this will trigger all TASK_EVENTS emissions
392
+ // including TASK_INCOMING which is now handled via the state machine callbacks
393
+ if (stateMachineEvent) {
394
+ task.sendStateMachineEvent(stateMachineEvent);
502
395
  }
396
+
397
+ // Send transcript start/stop events for relevant CC events
398
+ this.requestRealTimeTranscripts(eventContext.eventType, payload.interactionId);
503
399
  });
504
400
  }
505
- updateTaskData(task, taskData) {
506
- if (!task) {
507
- return undefined;
508
- }
509
- if (!taskData?.interactionId) {
510
- _loggerProxy.default.warn('Received task update with missing interactionId', {
511
- module: _constants.TASK_MANAGER_FILE,
512
- method: _constants2.METHODS.UPDATE_TASK_DATA
513
- });
514
- }
401
+
402
+ /**
403
+ * Parse and validate WebSocket message
404
+ * @returns Parsed message or null if invalid/keepalive
405
+ */
406
+ static parseWebSocketMessage(event) {
515
407
  try {
516
- const currentTask = task.updateTaskData(taskData);
517
- this.taskCollection[taskData.interactionId] = currentTask;
518
- return currentTask;
408
+ const payload = JSON.parse(event);
409
+
410
+ // Filter out keepalive messages
411
+ if (payload?.keepalive === 'true' || payload?.keepalive === true) {
412
+ return null;
413
+ }
414
+
415
+ // Normalize task data if present
416
+ if (payload?.data?.interaction) {
417
+ payload.data = (0, _taskDataNormalizer.normalizeTaskData)(payload.data);
418
+ }
419
+ return payload;
519
420
  } catch (error) {
520
- _loggerProxy.default.error(`Failed to update task`, {
421
+ _loggerProxy.default.error('Failed to parse WebSocket message', {
521
422
  module: _constants.TASK_MANAGER_FILE,
522
- method: _constants2.METHODS.UPDATE_TASK_DATA,
523
- interactionId: taskData.interactionId
423
+ method: 'parseWebSocketMessage',
424
+ error
524
425
  });
525
- return task;
426
+ return null;
526
427
  }
527
428
  }
528
429
 
529
430
  /**
530
- * Handles CONTACT_MERGED event logic
531
- * @param task - The task to process
532
- * @param taskData - The task data from the event payload
533
- * @returns Updated or newly created task
534
- * @private
431
+ * Prepare context for event processing
432
+ * @returns Event context or null if event type is invalid
535
433
  */
536
- handleContactMerged(task, taskData) {
537
- if (taskData.childInteractionId) {
538
- // remove the child task from collection
539
- this.removeTaskFromCollection(this.taskCollection[taskData.childInteractionId]);
434
+ prepareEventContext(message) {
435
+ const eventType = message.data?.type || message.type;
436
+ if (!eventType || !isCcEvent(eventType)) {
437
+ return null;
540
438
  }
541
- if (this.taskCollection[taskData.interactionId]) {
542
- _loggerProxy.default.log(`Got CONTACT_MERGED: Task already exists in collection`, {
543
- module: _constants.TASK_MANAGER_FILE,
544
- method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
545
- interactionId: taskData.interactionId
546
- });
547
- // update the task data
548
- task = this.updateTaskData(task, taskData);
549
- } else {
550
- // Case2 : Task is not present in taskCollection
551
- _loggerProxy.default.log(`Got CONTACT_MERGED : Creating new task in taskManager`, {
439
+ const interactionId = message.data.interactionId;
440
+ const task = this.taskCollection[interactionId];
441
+ const wasConsultedTask = Boolean(task?.data?.isConsulted);
442
+ const computeWrapUpRequired = () => {
443
+ if (message.data.wrapUpRequired !== undefined) {
444
+ return message.data.wrapUpRequired;
445
+ }
446
+ if (message.data.isConsulted !== undefined) {
447
+ return !message.data.isConsulted;
448
+ }
449
+ return !wasConsultedTask;
450
+ };
451
+ const adjustedPayload = eventType === _types2.CC_EVENTS.AGENT_CONSULT_TRANSFERRED || eventType === _types2.CC_EVENTS.AGENT_BLIND_TRANSFERRED || eventType === _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED ? {
452
+ ...message.data,
453
+ wrapUpRequired: computeWrapUpRequired()
454
+ } : message.data;
455
+ const stateMachineEvent = TaskManager.mapEventToTaskStateMachineEvent(eventType, adjustedPayload, this.agentId);
456
+ _loggerProxy.default.info(`Handling task event ${eventType}`, {
457
+ module: _constants.TASK_MANAGER_FILE,
458
+ method: 'prepareEventContext',
459
+ interactionId
460
+ });
461
+ return {
462
+ eventType,
463
+ payload: adjustedPayload,
464
+ task,
465
+ stateMachineEvent
466
+ };
467
+ }
468
+
469
+ /**
470
+ * Handle task lifecycle events and determine required actions
471
+ *
472
+ * Delegates to specific event handlers based on event type. Each handler
473
+ * is responsible for TaskManager-level concerns:
474
+ * - Task creation and collection management
475
+ * - Metrics tracking
476
+ * - Resource cleanup decisions
477
+ *
478
+ * Note: Task-level state transitions and event emissions are handled by
479
+ * the task state machine via sendStateMachineEvent()
480
+ */
481
+ handleTaskLifecycleEvent(context) {
482
+ const {
483
+ eventType
484
+ } = context;
485
+ switch (eventType) {
486
+ case _types2.CC_EVENTS.AGENT_CONTACT_RESERVED:
487
+ return this.handleContactReserved(context);
488
+ case _types2.CC_EVENTS.AGENT_CONTACT:
489
+ return this.handleAgentContact(context);
490
+ case _types2.CC_EVENTS.CONTACT_MERGED:
491
+ return this.handleContactMergedEvent(context);
492
+ default:
493
+ return {
494
+ task: context.task
495
+ };
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Handle AGENT_CONTACT_RESERVED event
501
+ * Creates a new task; state machine event is sent during processing
502
+ */
503
+ handleContactReserved(context) {
504
+ const {
505
+ payload
506
+ } = context;
507
+ const isConsultedTask = payload.isConsulted === true || (0, _TaskUtils.isSecondaryEpDnAgent)(payload.interaction);
508
+ const shouldAutoAnswer = (0, _TaskUtils.shouldAutoAnswerTask)(payload, this.agentId, this.webCallingService.loginOption, this.webRtcEnabled);
509
+ const taskData = {
510
+ ...payload,
511
+ isConsulted: isConsultedTask,
512
+ isAutoAnswering: shouldAutoAnswer
513
+ };
514
+ const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
515
+ this.setupTaskListeners(task);
516
+ this.taskCollection[payload.interactionId] = task;
517
+ return {
518
+ task
519
+ };
520
+ }
521
+
522
+ /**
523
+ * Handle AGENT_CONTACT event
524
+ * Re-creates task if missing (multi-session scenario)
525
+ */
526
+ handleAgentContact(context) {
527
+ let {
528
+ task
529
+ } = context;
530
+ const {
531
+ payload
532
+ } = context;
533
+ if (!task) {
534
+ const isConsultedTask = payload.isConsulted === true || (0, _TaskUtils.isSecondaryEpDnAgent)(payload.interaction);
535
+ const shouldAutoAnswer = (0, _TaskUtils.shouldAutoAnswerTask)(payload, this.agentId, this.webCallingService.loginOption, this.webRtcEnabled);
536
+ const taskData = {
537
+ ...payload,
538
+ isConsulted: isConsultedTask,
539
+ wrapUpRequired: payload.interaction?.participants?.[this.agentId]?.isWrapUp || false,
540
+ isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
541
+ isAutoAnswering: shouldAutoAnswer
542
+ };
543
+ task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
544
+ this.setupTaskListeners(task);
545
+ this.taskCollection[payload.interactionId] = task;
546
+ }
547
+ return {
548
+ task
549
+ };
550
+ }
551
+ updateTaskData(task, taskData) {
552
+ if (!task) {
553
+ throw new Error('Task not found for update');
554
+ }
555
+ const snapshot = task.stateMachineService?.getSnapshot?.();
556
+ const isConsultingFlow = snapshot?.value === 'CONSULTING' || taskData.interaction?.state === 'consulting';
557
+ const updateTaskData = isConsultingFlow ? {
558
+ ...taskData,
559
+ destAgentId: taskData.destAgentId ?? snapshot?.context?.consultDestinationAgentId ?? null,
560
+ destinationType: taskData.destinationType ?? snapshot?.context?.consultDestinationType ?? null
561
+ } : taskData;
562
+ task.updateTaskData(updateTaskData);
563
+ this.taskCollection[taskData.interactionId] = task;
564
+ return task;
565
+ }
566
+
567
+ /**
568
+ * Setup listeners for task events that need to be bubbled up to TaskManager
569
+ * This replaces the previous callback injection pattern
570
+ */
571
+ setupTaskListeners(task) {
572
+ // Listen for TASK_INCOMING and re-emit so webex.cc can notify consumers
573
+ task.on(_types.TASK_EVENTS.TASK_INCOMING, t => {
574
+ _loggerProxy.default.log(`Task incoming event received`, {
552
575
  module: _constants.TASK_MANAGER_FILE,
553
576
  method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
554
- interactionId: taskData.interactionId
577
+ interactionId: t.data?.interactionId
555
578
  });
556
- task = new _.default(this.contact, this.webCallingService, {
557
- ...taskData,
558
- wrapUpRequired: taskData.interaction?.participants?.[this.agentId]?.isWrapUp || false,
559
- isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(taskData)
560
- }, this.wrapupData, this.agentId);
561
- this.taskCollection[taskData.interactionId] = task;
562
- }
563
- this.emit(_types.TASK_EVENTS.TASK_MERGED, task);
564
- return task;
579
+ this.emit(_types.TASK_EVENTS.TASK_INCOMING, t);
580
+ });
581
+
582
+ // Listen for TASK_HYDRATE on the task and re-emit on TaskManager
583
+ task.on(_types.TASK_EVENTS.TASK_HYDRATE, t => {
584
+ // Task data is already updated by the task itself before emitting
585
+ this.emit(_types.TASK_EVENTS.TASK_HYDRATE, t);
586
+ });
587
+
588
+ // Listen for internal cleanup signal emitted by the state machine
589
+ task.on(_types.TASK_EVENTS.TASK_CLEANUP, (t, options) => {
590
+ this.handleTaskCleanup(t);
591
+ if (options?.removeFromCollection) {
592
+ const interactionId = t?.data?.interactionId;
593
+ if (interactionId && this.taskCollection[interactionId]) {
594
+ this.removeTaskFromCollection(t);
595
+ }
596
+ }
597
+ });
565
598
  }
566
599
  removeTaskFromCollection(task) {
600
+ if (typeof task.cancelAutoWrapupTimer === 'function') {
601
+ task.cancelAutoWrapupTimer();
602
+ }
567
603
  if (task?.data?.interactionId) {
568
604
  delete this.taskCollection[task.data.interactionId];
569
605
  _loggerProxy.default.info(`Task removed from collection`, {
@@ -575,62 +611,63 @@ class TaskManager extends _events.default {
575
611
  }
576
612
 
577
613
  /**
578
- * Handles auto-answer logic for incoming tasks
579
- * Automatically accepts tasks when isAutoAnswering flag is set
580
- * The flag is set during task creation based on:
581
- * 1. WebRTC calls with auto-answer enabled in agent profile
582
- * 2. Agent-initiated WebRTC outdial calls
583
- * 3. Agent-initiated digital outbound (Email/SMS) without previous transfers
584
- *
585
- * @param task - The task to auto-answer
614
+ * Handles CONTACT_MERGED event logic
615
+ * @param task - The task to process
616
+ * @param taskData - The task data from the event payload
617
+ * @returns Updated or newly created task
586
618
  * @private
587
619
  */
588
- async handleAutoAnswer(task) {
589
- if (!task || !task.data || !task.data.isAutoAnswering) {
590
- return;
620
+ handleContactMergedEvent(context) {
621
+ const {
622
+ payload
623
+ } = context;
624
+ let task = context.task;
625
+ if (payload.childInteractionId) {
626
+ // remove the child task from collection
627
+ this.removeTaskFromCollection(this.taskCollection[payload.childInteractionId]);
591
628
  }
592
- _loggerProxy.default.info(`Auto-answering task`, {
593
- module: _constants.TASK_MANAGER_FILE,
594
- method: 'handleAutoAnswer',
595
- interactionId: task.data.interactionId
596
- });
597
- try {
598
- await task.accept();
599
- _loggerProxy.default.info(`Task auto-answered successfully`, {
629
+ if (task) {
630
+ _loggerProxy.default.log(`Got CONTACT_MERGED: Task already exists in collection`, {
600
631
  module: _constants.TASK_MANAGER_FILE,
601
- method: 'handleAutoAnswer',
602
- interactionId: task.data.interactionId
603
- });
604
-
605
- // Track successful auto-answer
606
- this.metricsManager.trackEvent(_constants3.METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_SUCCESS, {
607
- taskId: task.data.interactionId,
608
- mediaType: task.data.interaction.mediaType,
609
- isAutoAnswered: true
610
- }, ['behavioral', 'operational']);
611
- // Emit task:autoAnswered event for widgets/UI to react
612
- task.emit(_types.TASK_EVENTS.TASK_AUTO_ANSWERED, task);
613
- } catch (error) {
614
- // Reset isAutoAnswering flag on failure
615
- task.updateTaskData({
616
- ...task.data,
617
- isAutoAnswering: false
632
+ method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
633
+ interactionId: payload.interactionId
618
634
  });
619
- _loggerProxy.default.error(`Failed to auto-answer task`, {
635
+ // update the task data
636
+ this.updateTaskData(task, payload);
637
+ } else {
638
+ // Case2 : Task is not present in taskCollection
639
+ _loggerProxy.default.log(`Got CONTACT_MERGED : Creating new task in taskManager`, {
620
640
  module: _constants.TASK_MANAGER_FILE,
621
- method: 'handleAutoAnswer',
622
- interactionId: task.data.interactionId,
623
- error
641
+ method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
642
+ interactionId: payload.interactionId
624
643
  });
644
+ const taskData = {
645
+ ...payload,
646
+ wrapUpRequired: payload.interaction?.participants?.[this.agentId]?.isWrapUp || false,
647
+ isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
648
+ isConsulted: false
649
+ };
650
+ task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
651
+ this.taskCollection[payload.interactionId] = task;
625
652
 
626
- // Track auto-answer failure
627
- this.metricsManager.trackEvent(_constants3.METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_FAILED, {
628
- taskId: task.data.interactionId,
629
- mediaType: task.data.interaction.mediaType,
630
- error: error?.message || 'Unknown error',
631
- isAutoAnswered: false
632
- }, ['behavioral', 'operational']);
653
+ // Transition the new task out of IDLE immediately so UI controls are
654
+ // computed before TASK_MERGED is emitted. This handles the race where
655
+ // AgentContactAssigned arrives before ContactMerged and gets dropped.
656
+ // Send HYDRATE before setupTaskListeners so the emitTaskHydrate action
657
+ // doesn't bubble up to the Widget (avoids duplicate listener registration).
658
+ task.sendStateMachineEvent({
659
+ type: _stateMachine.TaskEvent.HYDRATE,
660
+ taskData,
661
+ agentId: this.agentId
662
+ });
663
+ this.setupTaskListeners(task);
664
+ }
665
+ if (task) {
666
+ this.emit(_types.TASK_EVENTS.TASK_MERGED, task);
633
667
  }
668
+ return {
669
+ task
670
+ };
634
671
  }
635
672
 
636
673
  /**
@@ -639,20 +676,19 @@ class TaskManager extends _events.default {
639
676
  * @private
640
677
  */
641
678
  handleTaskCleanup(task) {
642
- // Clean up Desktop/WebRTC calling resources for browser-based telephony tasks
643
- if (this.webCallingService.loginOption === _types3.LoginOption.BROWSER && task.data.interaction.mediaType === 'telephony') {
679
+ if (this.webCallingService.loginOption === _types3.LoginOption.BROWSER && task.data.interaction.mediaType === _types.MEDIA_CHANNEL.TELEPHONY && task instanceof _WebRTC.default) {
644
680
  task.unregisterWebCallListeners();
645
681
  this.webCallingService.cleanUpCall();
646
682
  }
647
683
  const isOutdial = task.data.interaction.outboundType === 'OUTDIAL';
648
684
  const isNew = task.data.interaction.state === 'new';
649
- const needsWrapUp = task.data.agentsPendingWrapUp?.includes(this.agentId) ?? false;
685
+ const needsWrapUp = task.data.agentsPendingWrapUp?.length > 0;
650
686
 
651
687
  // For OUTDIAL: only remove if NOT terminated (user-declined, no wrap-up follows)
688
+ // If terminated, keep task for wrap-up flow (CONTACT_ENDED → AGENT_WRAPUP)
652
689
  // For non-OUTDIAL: remove if state is 'new'
653
690
  // Always remove if secondary EpDn agent
654
- if (isNew && !(isOutdial && needsWrapUp) || (0, _TaskUtils.isSecondaryEpDnAgent)(task.data.interaction) || !needsWrapUp && isOutdial // For outdial tasks, needs wrap-up is false and state is "WRAPUP". We need to just remove the task.
655
- ) {
691
+ if (isNew && !(isOutdial && needsWrapUp) || (0, _TaskUtils.isSecondaryEpDnAgent)(task.data.interaction)) {
656
692
  this.removeTaskFromCollection(task);
657
693
  }
658
694
  }
@@ -663,11 +699,12 @@ class TaskManager extends _events.default {
663
699
  */
664
700
  requestRealTimeTranscripts(eventType, interactionId) {
665
701
  const action = _constants2.TRANSCRIPT_EVENT_MAP[eventType];
666
- if (!action || !this.apiAIAssistant || this.apiAIAssistant.aiFeature?.realtimeTranscripts?.enable === false) return;
702
+ if (!action || !this.apiAIAssistant) return;
703
+ if (this.configFlags?.aiFeature?.realtimeTranscripts?.enable === false) return;
667
704
  this.apiAIAssistant.sendEvent(this.agentId, interactionId, _types3.AIAssistantEventType.CUSTOM_EVENT, _types3.AIAssistantEventName.GET_TRANSCRIPTS, action).catch(error => {
668
705
  _loggerProxy.default.error(`Failed to send transcript ${action} event`, {
669
706
  module: _constants.TASK_MANAGER_FILE,
670
- method: 'requestRealTimeTranscripts',
707
+ method: _constants2.METHODS.REQUEST_REAL_TIME_TRANSCRIPTS,
671
708
  interactionId,
672
709
  error
673
710
  });
@@ -676,18 +713,16 @@ class TaskManager extends _events.default {
676
713
  getTask(taskId) {
677
714
  return this.taskCollection[taskId];
678
715
  }
679
-
680
- /**
681
- * @param taskId - Unique identifier for each task
682
- */
683
- getAllTasks = () => {
684
- return this.taskCollection;
685
- };
686
- static getTaskManager(apiAIAssistant, contact, webCallingService, webSocketManager) {
716
+ getAllTasks() {
717
+ return {
718
+ ...this.taskCollection
719
+ };
720
+ }
721
+ static getTaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
687
722
  if (!TaskManager.taskManager) {
688
- TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager);
723
+ TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager);
689
724
  }
690
- return this.taskManager;
725
+ return TaskManager.taskManager;
691
726
  }
692
727
  }
693
728
  exports.default = TaskManager;