@webex/contact-center 3.12.0-llmrefactor.1 → 3.12.0-llmrefactor.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.
- package/.sdd/manifest.json +7 -5
- package/ai-docs/ARCHITECTURE.md +1 -1
- package/ai-docs/CONTRACTS.md +8 -3
- package/ai-docs/SECURITY.md +1 -1
- package/ai-docs/contact-center-spec.md +22 -4
- package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
- package/dist/cc.js +544 -12
- package/dist/cc.js.map +1 -1
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +10 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics/MetricsManager.js +16 -0
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +104 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +18 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +18 -15
- package/dist/services/AddressBook.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +202 -0
- package/dist/services/AnswerCallOnWebexService.js.map +1 -0
- package/dist/services/EntryPoint.js +46 -68
- package/dist/services/EntryPoint.js.map +1 -1
- package/dist/services/Queue.js +27 -22
- package/dist/services/Queue.js.map +1 -1
- package/dist/services/WebexCrossClientService.js +222 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +108 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/config/Util.js +3 -0
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +10 -6
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/types.js +4 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/WebexRequest.js +6 -2
- package/dist/services/core/WebexRequest.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +28 -14
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/task/Task.js +73 -7
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskFactory.js +8 -4
- package/dist/services/task/TaskFactory.js.map +1 -1
- package/dist/services/task/TaskManager.js +490 -21
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/WebexCallingUtils.js +70 -0
- package/dist/services/task/WebexCallingUtils.js.map +1 -0
- package/dist/services/task/constants.js +4 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +29 -0
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/digital/Digital.js +3 -2
- package/dist/services/task/digital/Digital.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +162 -53
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +6 -1
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +7 -10
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +118 -18
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/types.js +16 -1
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +456 -15
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/services/task/voice/WebRTC.js +4 -1
- package/dist/services/task/voice/WebRTC.js.map +1 -1
- package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
- package/dist/services/wxAppDiagnosticLogging.js +122 -0
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
- package/dist/services/wxAppTelephonyUtils.js +19 -0
- package/dist/services/wxAppTelephonyUtils.js.map +1 -0
- package/dist/types/cc.d.ts +78 -4
- package/dist/types/config.d.ts +7 -0
- package/dist/types/constants.d.ts +8 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +17 -0
- package/dist/types/services/AddressBook.d.ts +2 -1
- package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
- package/dist/types/services/EntryPoint.d.ts +7 -5
- package/dist/types/services/Queue.d.ts +5 -3
- package/dist/types/services/WebexCrossClientService.d.ts +32 -0
- package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
- package/dist/types/services/config/constants.d.ts +8 -5
- package/dist/types/services/config/types.d.ts +17 -4
- package/dist/types/services/core/Err.d.ts +2 -0
- package/dist/types/services/core/WebexRequest.d.ts +1 -0
- package/dist/types/services/core/types.d.ts +2 -0
- package/dist/types/services/task/Task.d.ts +20 -2
- package/dist/types/services/task/TaskFactory.d.ts +2 -1
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +3 -0
- package/dist/types/services/task/contact.d.ts +4 -0
- package/dist/types/services/task/digital/Digital.d.ts +2 -2
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +304 -56
- package/dist/types/services/task/state-machine/guards.d.ts +6 -5
- package/dist/types/services/task/state-machine/types.d.ts +9 -1
- package/dist/types/services/task/types.d.ts +81 -4
- package/dist/types/services/task/voice/Voice.d.ts +52 -9
- package/dist/types/services/task/voice/WebRTC.d.ts +2 -2
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +14 -8
- package/dist/types/utils/PageCache.d.ts +20 -3
- package/dist/types.js +7 -2
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +19 -5
- package/dist/utils/PageCache.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +8 -8
- package/src/cc.ts +657 -12
- package/src/config.ts +7 -0
- package/src/constants.ts +8 -0
- package/src/index.ts +2 -0
- package/src/metrics/MetricsManager.ts +16 -0
- package/src/metrics/ai-docs/metrics-spec.md +78 -10
- package/src/metrics/behavioral-events.ts +106 -0
- package/src/metrics/constants.ts +20 -0
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/EntryPoint.ts +59 -60
- package/src/services/Queue.ts +29 -12
- package/src/services/WebexCrossClientService.ts +293 -0
- package/src/services/WxAppTelephonyMercurySync.ts +142 -0
- package/src/services/agent/ai-docs/agent-spec.md +1 -0
- package/src/services/ai-docs/AGENTS.md +10 -10
- package/src/services/ai-docs/services-spec.md +6 -1
- package/src/services/config/Util.ts +3 -0
- package/src/services/config/ai-docs/AGENTS.md +1 -1
- package/src/services/config/ai-docs/ARCHITECTURE.md +2 -2
- package/src/services/config/ai-docs/config-spec.md +6 -0
- package/src/services/config/constants.ts +10 -6
- package/src/services/config/types.ts +16 -4
- package/src/services/core/Err.ts +1 -0
- package/src/services/core/WebexRequest.ts +3 -1
- package/src/services/core/ai-docs/core-spec.md +5 -1
- package/src/services/core/aqm-reqs.ts +30 -15
- package/src/services/core/types.ts +2 -0
- package/src/services/task/Task.ts +79 -8
- package/src/services/task/TaskFactory.ts +8 -3
- package/src/services/task/TaskManager.ts +716 -17
- package/src/services/task/WebexCallingUtils.ts +136 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +1 -0
- package/src/services/task/ai-docs/task-spec.md +156 -3
- package/src/services/task/constants.ts +3 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/digital/Digital.ts +4 -2
- package/src/services/task/state-machine/TaskStateMachine.ts +235 -81
- package/src/services/task/state-machine/actions.ts +5 -2
- package/src/services/task/state-machine/ai-docs/AGENTS.md +6 -4
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +35 -16
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +103 -33
- package/src/services/task/state-machine/guards.ts +7 -10
- package/src/services/task/state-machine/types.ts +16 -1
- package/src/services/task/state-machine/uiControlsComputer.ts +206 -32
- package/src/services/task/types.ts +114 -4
- package/src/services/task/voice/Voice.ts +595 -14
- package/src/services/task/voice/WebRTC.ts +5 -1
- package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
- package/src/services/wxAppDiagnosticLogging.ts +174 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +33 -9
- package/src/utils/AGENTS.md +21 -10
- package/src/utils/PageCache.ts +38 -5
- package/src/utils/ai-docs/utils-spec.md +21 -11
- package/test/unit/spec/cc.ts +1522 -19
- package/test/unit/spec/metrics/MetricsManager.ts +25 -0
- package/test/unit/spec/metrics/behavioral-events.ts +60 -0
- package/test/unit/spec/services/AddressBook.ts +37 -6
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
- package/test/unit/spec/services/EntryPoint.ts +87 -40
- package/test/unit/spec/services/Queue.ts +123 -12
- package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
- package/test/unit/spec/services/config/Util.ts +85 -0
- package/test/unit/spec/services/core/WebexRequest.ts +3 -1
- package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
- package/test/unit/spec/services/task/Task.ts +274 -6
- package/test/unit/spec/services/task/TaskFactory.ts +39 -2
- package/test/unit/spec/services/task/TaskManager.ts +1717 -9
- package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
- package/test/unit/spec/services/task/contact.ts +33 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +1485 -109
- package/test/unit/spec/services/task/state-machine/guards.ts +210 -8
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +445 -7
- package/test/unit/spec/services/task/voice/Voice.ts +1412 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -14,12 +14,17 @@ var _types3 = require("../../types");
|
|
|
14
14
|
var _loggerProxy = _interopRequireDefault(require("../../logger-proxy"));
|
|
15
15
|
var _TaskUtils = require("./TaskUtils");
|
|
16
16
|
var _TaskFactory = _interopRequireDefault(require("./TaskFactory"));
|
|
17
|
+
var _WebexCallingUtils = require("./WebexCallingUtils");
|
|
17
18
|
var _WebRTC = _interopRequireDefault(require("./voice/WebRTC"));
|
|
18
19
|
var _stateMachine = require("./state-machine");
|
|
20
|
+
var _constants3 = require("./state-machine/constants");
|
|
19
21
|
var _taskDataNormalizer = require("./taskDataNormalizer");
|
|
20
22
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
23
|
const CC_EVENT_SET = new Set(Object.values(_types2.CC_EVENTS));
|
|
24
|
+
const MAIN_INTERACTION_CORRELATED_EVENTS = new Set([_types2.CC_EVENTS.AGENT_CONSULT_ENDED, _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE]);
|
|
22
25
|
const isCcEvent = value => CC_EVENT_SET.has(value);
|
|
26
|
+
const WRAP_UP_STAMP_EVENTS = new Set([_types2.CC_EVENTS.AGENT_WRAPUP, _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED, _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE, _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED]);
|
|
27
|
+
const PARTICIPANT_LEAVE_WRAP_STATES = new Set([_stateMachine.TaskState.HELD, _stateMachine.TaskState.RESUME_INITIATING, _stateMachine.TaskState.CONSULTING, _stateMachine.TaskState.CONSULT_INITIATING, _stateMachine.TaskState.CONFERENCING]);
|
|
23
28
|
|
|
24
29
|
/** @internal */
|
|
25
30
|
class TaskManager extends _events.default {
|
|
@@ -109,6 +114,9 @@ class TaskManager extends _events.default {
|
|
|
109
114
|
setWebRtcEnabled(webRtcEnabled) {
|
|
110
115
|
this.webRtcEnabled = webRtcEnabled;
|
|
111
116
|
}
|
|
117
|
+
setAnswerCallOnWebexService(answerCallOnWebexService) {
|
|
118
|
+
this.answerCallOnWebexService = answerCallOnWebexService;
|
|
119
|
+
}
|
|
112
120
|
handleIncomingWebCall = call => {
|
|
113
121
|
const currentTask = Object.values(this.taskCollection).find(task => task.data.interaction.mediaType === 'telephony' && !(0, _TaskUtils.isCampaignPreviewReservation)(task));
|
|
114
122
|
if (currentTask) {
|
|
@@ -134,14 +142,147 @@ class TaskManager extends _events.default {
|
|
|
134
142
|
this.webCallingService.off(_calling.LINE_EVENTS.INCOMING_CALL, this.handleIncomingWebCall);
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
/**
|
|
146
|
+
* True when this agent left on the current payload.
|
|
147
|
+
* A named other participantId is never self-leave (even if the participants
|
|
148
|
+
* map is partial). Missing-map fallback is EP-DN only: participantId absent
|
|
149
|
+
* and self omitted now, with previous-task evidence when a prior map exists.
|
|
150
|
+
*/
|
|
151
|
+
static didSelfLeaveConference(payload, agentId, task) {
|
|
152
|
+
if (!agentId) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (payload.participantId === agentId) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
if (payload.participantId && payload.participantId !== agentId) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const participants = payload.interaction?.participants;
|
|
162
|
+
if (!participants || agentId in participants) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
const previousParticipants = task?.data?.interaction?.participants;
|
|
166
|
+
if (previousParticipants && !(agentId in previousParticipants)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Consulted non-owner: Voice exitConference / shouldWrapUpForThisAgent stays
|
|
174
|
+
* false unless an explicit wrap-up signal includes this agent.
|
|
175
|
+
*/
|
|
176
|
+
static isConsultedNonOwner(payload, agentId, task) {
|
|
177
|
+
const isConsulted = payload.isConsulted ?? task?.data?.isConsulted;
|
|
178
|
+
if (isConsulted !== true) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const owner = payload.interaction?.owner ?? task?.data?.interaction?.owner;
|
|
182
|
+
return Boolean(agentId && owner && owner !== agentId);
|
|
183
|
+
}
|
|
184
|
+
static hasNonemptyPendingWrapUp(payload) {
|
|
185
|
+
return Array.isArray(payload.agentsPendingWrapUp) && payload.agentsPendingWrapUp.length > 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Transfer initiator fallback: widgets set transferConferenceRequested on the
|
|
190
|
+
* actor; desktop success payloads may include consultingAgentId. Only used
|
|
191
|
+
* when agentsPendingWrapUp is empty or absent.
|
|
192
|
+
*/
|
|
193
|
+
static isSelfConferenceTransferInitiator(payload, agentId, task) {
|
|
194
|
+
const snapshotContext = task?.stateMachineService?.getSnapshot?.()?.context;
|
|
195
|
+
if (snapshotContext?.transferConferenceRequested === true) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return Boolean(agentId && payload.consultingAgentId === agentId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Explicit wrap-up signals only (no owner / isConsulted fallback).
|
|
203
|
+
* Used for events that remaining conference agents also receive.
|
|
204
|
+
* nonempty agentsPendingWrapUp is authoritative, then wrapUpRequired / isWrapUp.
|
|
205
|
+
*/
|
|
206
|
+
static wrapUpRequiredFromExplicitSignals(payload, agentId) {
|
|
207
|
+
const pending = payload.agentsPendingWrapUp;
|
|
208
|
+
if (Array.isArray(pending) && pending.length > 0) {
|
|
209
|
+
return Boolean(agentId && pending.includes(agentId));
|
|
210
|
+
}
|
|
211
|
+
if (payload.wrapUpRequired === true) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (!agentId) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return payload.interaction?.participants?.[agentId]?.isWrapUp === true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Stamp wrapUpRequired once at ingress (prepareEventContext) so updateTaskData
|
|
222
|
+
* and the mapper see the same flag. Remaining agents stay false.
|
|
223
|
+
*/
|
|
224
|
+
static stampWrapUpRequiredForEvent(eventType, payload, agentId, task) {
|
|
225
|
+
if (!WRAP_UP_STAMP_EVENTS.has(eventType)) {
|
|
226
|
+
return payload;
|
|
227
|
+
}
|
|
228
|
+
if (eventType === _types2.CC_EVENTS.AGENT_WRAPUP) {
|
|
229
|
+
return {
|
|
230
|
+
...payload,
|
|
231
|
+
wrapUpRequired: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (eventType === _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED) {
|
|
235
|
+
return {
|
|
236
|
+
...payload,
|
|
237
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
if (eventType === _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE) {
|
|
241
|
+
const actorState = task?.stateMachineService?.getSnapshot?.()?.value;
|
|
242
|
+
if (actorState && !PARTICIPANT_LEAVE_WRAP_STATES.has(actorState)) {
|
|
243
|
+
return payload;
|
|
244
|
+
}
|
|
245
|
+
const left = TaskManager.didSelfLeaveConference(payload, agentId, task);
|
|
246
|
+
if (!left) {
|
|
247
|
+
return {
|
|
248
|
+
...payload,
|
|
249
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (TaskManager.hasNonemptyPendingWrapUp(payload)) {
|
|
253
|
+
return {
|
|
254
|
+
...payload,
|
|
255
|
+
wrapUpRequired: Boolean(agentId && payload.agentsPendingWrapUp?.includes(agentId))
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...payload,
|
|
260
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId) || !TaskManager.isConsultedNonOwner(payload, agentId, task)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// AGENT_CONFERENCE_TRANSFERRED — pending list wins; initiator only if empty/absent
|
|
265
|
+
if (TaskManager.hasNonemptyPendingWrapUp(payload)) {
|
|
266
|
+
return {
|
|
267
|
+
...payload,
|
|
268
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
...payload,
|
|
273
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId) || TaskManager.isSelfConferenceTransferInitiator(payload, agentId, task)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
137
277
|
/**
|
|
138
278
|
* Map WebSocket CC_EVENTS to state machine TaskEvent
|
|
139
279
|
* @param ccEvent - The CC_EVENT type from WebSocket
|
|
140
280
|
* @param payload - The event payload
|
|
141
281
|
* @param agentId - Optional agent ID for state detection (needed for HYDRATE)
|
|
282
|
+
* @param task - Optional task instance for state-aware CONTACT_ENDED mapping
|
|
142
283
|
* @returns TaskEventPayload for state machine or null if no mapping
|
|
143
284
|
*/
|
|
144
|
-
static mapEventToTaskStateMachineEvent(ccEvent, payload, agentId) {
|
|
285
|
+
static mapEventToTaskStateMachineEvent(ccEvent, payload, agentId, task, enableWxBetterTogether) {
|
|
145
286
|
const mediaResourceId = payload.mediaResourceId || payload.interaction?.media?.[payload.interactionId]?.mediaResourceId;
|
|
146
287
|
switch (ccEvent) {
|
|
147
288
|
// CC -> TaskEvent mappings (see TaskStateMachine comment for quick reference)
|
|
@@ -180,6 +321,12 @@ class TaskManager extends _events.default {
|
|
|
180
321
|
agentId
|
|
181
322
|
};
|
|
182
323
|
case _types2.CC_EVENTS.CONTACT_UPDATED:
|
|
324
|
+
if (task && typeof payload.interaction?.owner === 'string' && payload.interaction.owner.trim().length > 0 && payload.interaction.owner !== task.data?.interaction?.owner) {
|
|
325
|
+
return {
|
|
326
|
+
type: _stateMachine.TaskEvent.CONTACT_OWNER_CHANGED,
|
|
327
|
+
taskData: payload
|
|
328
|
+
};
|
|
329
|
+
}
|
|
183
330
|
return {
|
|
184
331
|
type: _stateMachine.TaskEvent.CONTACT_UPDATED,
|
|
185
332
|
taskData: payload
|
|
@@ -284,13 +431,31 @@ class TaskManager extends _events.default {
|
|
|
284
431
|
taskData: payload
|
|
285
432
|
};
|
|
286
433
|
case _types2.CC_EVENTS.CONTACT_ENDED:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
434
|
+
{
|
|
435
|
+
const wrapUpRequired = (0, _TaskUtils.isCampaignPreviewTask)(payload) ? false : payload.agentsPendingWrapUp?.includes(agentId || '') || false;
|
|
436
|
+
|
|
437
|
+
// Post-accept wxApp outdial agent-end delivers ContactEnded instead of
|
|
438
|
+
// AgentOutboundFailed. Normalize to OUTBOUND_FAILED so state machine
|
|
439
|
+
// enters WRAPPING_UP and emits TASK_OUTDIAL_FAILED (AGENT_ENDS).
|
|
440
|
+
// Pre-accept cancelTask decline stays CONTACT_ENDED → TERMINATED (no wrapup).
|
|
441
|
+
if (TaskManager.isAgentTerminatedOutdialWrapup(payload, wrapUpRequired, task, agentId, enableWxBetterTogether)) {
|
|
442
|
+
return {
|
|
443
|
+
type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
|
|
444
|
+
reason: 'AGENT_ENDS',
|
|
445
|
+
taskData: {
|
|
446
|
+
...payload,
|
|
447
|
+
wrapUpRequired: true
|
|
448
|
+
}
|
|
449
|
+
};
|
|
292
450
|
}
|
|
293
|
-
|
|
451
|
+
return {
|
|
452
|
+
type: _stateMachine.TaskEvent.CONTACT_ENDED,
|
|
453
|
+
taskData: {
|
|
454
|
+
...payload,
|
|
455
|
+
wrapUpRequired
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
294
459
|
case _types2.CC_EVENTS.AGENT_INVITE_FAILED:
|
|
295
460
|
return {
|
|
296
461
|
type: _stateMachine.TaskEvent.INVITE_FAILED,
|
|
@@ -308,11 +473,18 @@ class TaskManager extends _events.default {
|
|
|
308
473
|
reason: payload.reason
|
|
309
474
|
};
|
|
310
475
|
case _types2.CC_EVENTS.AGENT_OUTBOUND_FAILED:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
476
|
+
{
|
|
477
|
+
const isAgentTerminatedOutdial = payload.interaction?.outboundType === 'OUTDIAL' && (payload.terminatingParty === 'Agent' || payload.reason === 'AGENT_ENDS');
|
|
478
|
+
const suppressOutdialFailedPopup = isAgentTerminatedOutdial && !TaskManager.isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether);
|
|
479
|
+
return {
|
|
480
|
+
type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
|
|
481
|
+
taskData: suppressOutdialFailedPopup ? {
|
|
482
|
+
...payload,
|
|
483
|
+
suppressOutdialFailedPopup: true
|
|
484
|
+
} : payload,
|
|
485
|
+
reason: payload.reason
|
|
486
|
+
};
|
|
487
|
+
}
|
|
316
488
|
case _types2.CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED:
|
|
317
489
|
return {
|
|
318
490
|
type: _stateMachine.TaskEvent.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
@@ -385,6 +557,43 @@ class TaskManager extends _events.default {
|
|
|
385
557
|
}
|
|
386
558
|
}
|
|
387
559
|
|
|
560
|
+
/**
|
|
561
|
+
* True when the current agent's task is a wxApp-managed outdial (thick-client telephony).
|
|
562
|
+
*/
|
|
563
|
+
static isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether) {
|
|
564
|
+
const wxAppEnabled = task?.uiControlConfig?.enableWxBetterTogether ?? enableWxBetterTogether ?? false;
|
|
565
|
+
if (!wxAppEnabled) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
const effectiveAgentId = agentId ?? task?.data?.agentId;
|
|
569
|
+
const participants = payload.interaction?.participants ?? task?.data?.interaction?.participants;
|
|
570
|
+
const details = (0, _WebexCallingUtils.getWebexCallingDeviceDetailsForAgent)(effectiveAgentId, participants);
|
|
571
|
+
return details?.deviceType === 'wxApp';
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* True when ContactEnded represents an agent-terminated wxApp outdial that should
|
|
576
|
+
* enter wrapup with an outdial-failed popup (legacy AgentOutboundFailed parity).
|
|
577
|
+
*/
|
|
578
|
+
static isAgentTerminatedOutdialWrapup(payload, wrapUpRequired, task, agentId, enableWxBetterTogether) {
|
|
579
|
+
const isOutdial = payload.interaction?.outboundType === 'OUTDIAL';
|
|
580
|
+
const agentTerminated = payload.terminatingParty === 'Agent';
|
|
581
|
+
if (!isOutdial || !agentTerminated) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
if (!TaskManager.isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether)) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
const wxAppAnswerPending = task?.uiControlConfig?.wxAppAnswerPending ?? false;
|
|
588
|
+
|
|
589
|
+
// Pre-accept decline: decline() clears wxAppAnswerPending; backend may still send state wrapUp.
|
|
590
|
+
if (!wxAppAnswerPending && !wrapUpRequired) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
const interactionWrapUp = payload.interaction?.state === 'wrapUp';
|
|
594
|
+
return wrapUpRequired || interactionWrapUp || wxAppAnswerPending;
|
|
595
|
+
}
|
|
596
|
+
|
|
388
597
|
/**
|
|
389
598
|
* Register WebSocket message listeners for task events
|
|
390
599
|
*
|
|
@@ -493,6 +702,12 @@ class TaskManager extends _events.default {
|
|
|
493
702
|
this.taskCollection[interactionId] = task;
|
|
494
703
|
}
|
|
495
704
|
}
|
|
705
|
+
if (!task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
|
|
706
|
+
task = this.findTaskForContactOwnerChange(message.data);
|
|
707
|
+
}
|
|
708
|
+
if (!task && MAIN_INTERACTION_CORRELATED_EVENTS.has(eventType)) {
|
|
709
|
+
task = this.findUniqueTaskByRelatedInteraction(message.data);
|
|
710
|
+
}
|
|
496
711
|
const wasConsultedTask = Boolean(task?.data?.isConsulted);
|
|
497
712
|
const computeWrapUpRequired = () => {
|
|
498
713
|
if (message.data.wrapUpRequired !== undefined) {
|
|
@@ -503,11 +718,20 @@ class TaskManager extends _events.default {
|
|
|
503
718
|
}
|
|
504
719
|
return !wasConsultedTask;
|
|
505
720
|
};
|
|
506
|
-
|
|
721
|
+
let adjustedPayload = eventType === _types2.CC_EVENTS.AGENT_CONSULT_TRANSFERRED || eventType === _types2.CC_EVENTS.AGENT_BLIND_TRANSFERRED || eventType === _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED ? {
|
|
507
722
|
...message.data,
|
|
508
723
|
wrapUpRequired: computeWrapUpRequired()
|
|
509
724
|
} : message.data;
|
|
510
|
-
|
|
725
|
+
if (task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
|
|
726
|
+
adjustedPayload = TaskManager.preserveMainTaskIdentity(task, adjustedPayload);
|
|
727
|
+
}
|
|
728
|
+
if (task && eventType === _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE) {
|
|
729
|
+
adjustedPayload = TaskManager.preserveConfirmedOwner(task, adjustedPayload);
|
|
730
|
+
}
|
|
731
|
+
if (WRAP_UP_STAMP_EVENTS.has(eventType)) {
|
|
732
|
+
adjustedPayload = TaskManager.stampWrapUpRequiredForEvent(eventType, adjustedPayload, this.agentId, task);
|
|
733
|
+
}
|
|
734
|
+
const stateMachineEvent = TaskManager.mapEventToTaskStateMachineEvent(eventType, adjustedPayload, this.agentId, task, this.configFlags?.enableWxBetterTogether);
|
|
511
735
|
_loggerProxy.default.info(`Handling task event ${eventType}`, {
|
|
512
736
|
module: _constants.TASK_MANAGER_FILE,
|
|
513
737
|
method: 'prepareEventContext',
|
|
@@ -521,6 +745,134 @@ class TaskManager extends _events.default {
|
|
|
521
745
|
};
|
|
522
746
|
}
|
|
523
747
|
|
|
748
|
+
/**
|
|
749
|
+
* ContactOwnerChanged can identify the promoted agent's child interaction while
|
|
750
|
+
* the surviving task is stored under the main interaction. Accept one related
|
|
751
|
+
* task when its existing snapshot or the authoritative incoming promotion proves
|
|
752
|
+
* current-agent main-call membership, while excluding non-promoted and ambiguous matches.
|
|
753
|
+
*/
|
|
754
|
+
findTaskForContactOwnerChange(payload) {
|
|
755
|
+
const relatedCandidates = this.findRelatedTasks(payload, {
|
|
756
|
+
includeCollectionKeys: true,
|
|
757
|
+
includeMainCallMediaKeys: true
|
|
758
|
+
});
|
|
759
|
+
const incomingShowsCurrentAgentPromotion = this.isCurrentAgentPromotedOnIncomingMainCall(payload);
|
|
760
|
+
const eligibleCandidates = relatedCandidates.filter(candidate => this.isCurrentAgentActiveOnMainInteraction(candidate.data) || incomingShowsCurrentAgentPromotion);
|
|
761
|
+
if (eligibleCandidates.length === 1) {
|
|
762
|
+
return eligibleCandidates[0];
|
|
763
|
+
}
|
|
764
|
+
if (eligibleCandidates.length > 1) {
|
|
765
|
+
_loggerProxy.default.warn('Unable to correlate task event to a unique main interaction task', {
|
|
766
|
+
module: _constants.TASK_MANAGER_FILE,
|
|
767
|
+
method: 'findTaskForContactOwnerChange',
|
|
768
|
+
interactionId: payload.interaction?.mainInteractionId || payload.interaction?.parentInteractionId || payload.interaction?.callProcessingDetails?.parentInteractionId || payload.interactionId
|
|
769
|
+
});
|
|
770
|
+
return undefined;
|
|
771
|
+
}
|
|
772
|
+
return undefined;
|
|
773
|
+
}
|
|
774
|
+
isCurrentAgentActiveOnMainInteraction(taskData) {
|
|
775
|
+
const currentAgentId = this.agentId || taskData?.agentId;
|
|
776
|
+
if (!currentAgentId) return false;
|
|
777
|
+
return TaskManager.isParticipantActiveOnMainInteraction(taskData, currentAgentId);
|
|
778
|
+
}
|
|
779
|
+
static isParticipantActiveOnMainInteraction(taskData, participantId) {
|
|
780
|
+
const interaction = taskData?.interaction;
|
|
781
|
+
const participant = interaction?.participants?.[participantId];
|
|
782
|
+
if (!participant || participant.hasLeft === true) return false;
|
|
783
|
+
return Object.values(interaction?.media ?? {}).some(media => media?.mType === _constants3.MEDIA_TYPE_MAIN_CALL && media.participants?.includes(participantId));
|
|
784
|
+
}
|
|
785
|
+
isCurrentAgentPromotedOnIncomingMainCall(payload) {
|
|
786
|
+
return Boolean(this.agentId && payload.interaction?.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(payload, this.agentId));
|
|
787
|
+
}
|
|
788
|
+
static getMainCallMediaId(interaction) {
|
|
789
|
+
return Object.entries(interaction?.media ?? {}).find(([, media]) => media?.mType === _constants3.MEDIA_TYPE_MAIN_CALL)?.[0];
|
|
790
|
+
}
|
|
791
|
+
static getStableMainInteractionId(payload) {
|
|
792
|
+
return payload.interaction?.mainInteractionId || TaskManager.getMainCallMediaId(payload.interaction) || payload.interaction?.interactionId || payload.interactionId;
|
|
793
|
+
}
|
|
794
|
+
canRecoverTaskFromContactOwnerChanged(payload) {
|
|
795
|
+
const interaction = payload.interaction;
|
|
796
|
+
const stableInteractionId = TaskManager.getStableMainInteractionId(payload);
|
|
797
|
+
return Boolean(this.agentId && stableInteractionId && interaction?.mediaType === _types.MEDIA_CHANNEL.TELEPHONY && interaction.isTerminated !== true && interaction.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(payload, this.agentId));
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** Keep a related owner-change event from re-keying the surviving main task to a child ID. */
|
|
801
|
+
static preserveMainTaskIdentity(task, payload) {
|
|
802
|
+
const currentMainInteractionId = task.data?.interaction?.mainInteractionId;
|
|
803
|
+
const payloadMainInteractionId = payload.interaction?.mainInteractionId;
|
|
804
|
+
const currentMainMediaId = TaskManager.getMainCallMediaId(task.data?.interaction);
|
|
805
|
+
const payloadMainMediaId = TaskManager.getMainCallMediaId(payload.interaction);
|
|
806
|
+
const stableInteractionId = currentMainInteractionId || payloadMainInteractionId || currentMainMediaId || payloadMainMediaId || payload.interaction?.interactionId || task.data?.interaction?.interactionId || task.data?.interactionId;
|
|
807
|
+
if (!stableInteractionId || stableInteractionId === payload.interactionId) {
|
|
808
|
+
return payload;
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
...payload,
|
|
812
|
+
interactionId: stableInteractionId
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* ParticipantLeftConference may arrive after an owner update while still carrying
|
|
818
|
+
* the departed owner. Keep the confirmed owner only when the new roster proves that
|
|
819
|
+
* owner is active on mainCall and identifies the incoming owner as departed.
|
|
820
|
+
*/
|
|
821
|
+
static preserveConfirmedOwner(task, payload) {
|
|
822
|
+
const confirmedOwner = task.data?.interaction?.owner;
|
|
823
|
+
const incomingOwner = payload.interaction?.owner;
|
|
824
|
+
if (!confirmedOwner || !incomingOwner || confirmedOwner === incomingOwner) {
|
|
825
|
+
return payload;
|
|
826
|
+
}
|
|
827
|
+
const confirmedOwnerIsActive = TaskManager.isParticipantActiveOnMainInteraction(payload, confirmedOwner);
|
|
828
|
+
const incomingOwnerIsExplicitlyDeparted = payload.participantId === incomingOwner || payload.interaction?.participants?.[incomingOwner]?.hasLeft === true;
|
|
829
|
+
if (!confirmedOwnerIsActive || !incomingOwnerIsExplicitlyDeparted) {
|
|
830
|
+
return payload;
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
...payload,
|
|
834
|
+
owner: confirmedOwner,
|
|
835
|
+
interaction: {
|
|
836
|
+
...payload.interaction,
|
|
837
|
+
owner: confirmedOwner
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Resolve lifecycle events that are emitted for the main interaction while the
|
|
844
|
+
* local task can still be indexed by a child consult interaction. Exact task
|
|
845
|
+
* keys are handled before this fallback. Multiple aliases of the same task are
|
|
846
|
+
* treated as one candidate; genuinely ambiguous matches are intentionally ignored.
|
|
847
|
+
*/
|
|
848
|
+
findUniqueTaskByRelatedInteraction(payload, candidateFilter = () => true) {
|
|
849
|
+
const candidates = this.findRelatedTasks(payload).filter(candidateFilter);
|
|
850
|
+
if (candidates.length === 1) {
|
|
851
|
+
return candidates[0];
|
|
852
|
+
}
|
|
853
|
+
if (candidates.length > 1) {
|
|
854
|
+
_loggerProxy.default.warn('Unable to correlate task event to a unique main interaction task', {
|
|
855
|
+
module: _constants.TASK_MANAGER_FILE,
|
|
856
|
+
method: 'findUniqueTaskByRelatedInteraction',
|
|
857
|
+
interactionId: payload.interaction?.mainInteractionId || payload.interaction?.parentInteractionId || payload.interaction?.callProcessingDetails?.parentInteractionId || payload.interactionId
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
return undefined;
|
|
861
|
+
}
|
|
862
|
+
findRelatedTasks(payload, options = {}) {
|
|
863
|
+
const {
|
|
864
|
+
includeCollectionKeys = false,
|
|
865
|
+
includeMainCallMediaKeys = false
|
|
866
|
+
} = options;
|
|
867
|
+
const payloadInteractionIds = new Set([payload.interactionId, payload.interaction?.interactionId, payload.interaction?.mainInteractionId, payload.interaction?.parentInteractionId, payload.interaction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(payload.interaction)] : [])].filter(interactionId => Boolean(interactionId)));
|
|
868
|
+
if (payloadInteractionIds.size === 0) return [];
|
|
869
|
+
return [...new Set(Object.entries(this.taskCollection).filter(([taskId, candidate]) => {
|
|
870
|
+
const taskInteraction = candidate?.data?.interaction;
|
|
871
|
+
const candidateInteractionIds = [...(includeCollectionKeys ? [taskId] : []), candidate?.data?.interactionId, taskInteraction?.interactionId, taskInteraction?.mainInteractionId, taskInteraction?.parentInteractionId, taskInteraction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(taskInteraction)] : [])];
|
|
872
|
+
return candidateInteractionIds.some(interactionId => Boolean(interactionId) && payloadInteractionIds.has(interactionId));
|
|
873
|
+
}).map(([, candidate]) => candidate))];
|
|
874
|
+
}
|
|
875
|
+
|
|
524
876
|
/**
|
|
525
877
|
* Handle task lifecycle events and determine required actions
|
|
526
878
|
*
|
|
@@ -544,6 +896,8 @@ class TaskManager extends _events.default {
|
|
|
544
896
|
return this.handleAgentContact(context);
|
|
545
897
|
case _types2.CC_EVENTS.CONTACT_MERGED:
|
|
546
898
|
return this.handleContactMergedEvent(context);
|
|
899
|
+
case _types2.CC_EVENTS.CONTACT_OWNER_CHANGED:
|
|
900
|
+
return this.handleContactOwnerChanged(context);
|
|
547
901
|
case _types2.CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION:
|
|
548
902
|
return this.handleCampaignPreviewReservation(context);
|
|
549
903
|
case _types2.CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
|
|
@@ -554,6 +908,63 @@ class TaskManager extends _events.default {
|
|
|
554
908
|
};
|
|
555
909
|
}
|
|
556
910
|
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Recover the promoted agent's active voice task when local task state was lost.
|
|
914
|
+
* Existing related tasks are never replaced: an unresolved or ambiguous relation
|
|
915
|
+
* is safer to ignore than to create a duplicate task.
|
|
916
|
+
*/
|
|
917
|
+
handleContactOwnerChanged(context) {
|
|
918
|
+
if (context.task) {
|
|
919
|
+
return {
|
|
920
|
+
task: context.task
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
const {
|
|
924
|
+
payload
|
|
925
|
+
} = context;
|
|
926
|
+
if (this.findRelatedTasks(payload, {
|
|
927
|
+
includeCollectionKeys: true,
|
|
928
|
+
includeMainCallMediaKeys: true
|
|
929
|
+
}).length > 0 || !this.canRecoverTaskFromContactOwnerChanged(payload)) {
|
|
930
|
+
return {};
|
|
931
|
+
}
|
|
932
|
+
const stableInteractionId = TaskManager.getStableMainInteractionId(payload);
|
|
933
|
+
if (!stableInteractionId || this.taskCollection[stableInteractionId]) {
|
|
934
|
+
return {};
|
|
935
|
+
}
|
|
936
|
+
const normalizedPayload = payload.interactionId === stableInteractionId ? payload : {
|
|
937
|
+
...payload,
|
|
938
|
+
interactionId: stableInteractionId
|
|
939
|
+
};
|
|
940
|
+
const taskData = {
|
|
941
|
+
...normalizedPayload,
|
|
942
|
+
owner: normalizedPayload.interaction.owner,
|
|
943
|
+
wrapUpRequired: normalizedPayload.interaction.participants?.[this.agentId]?.isWrapUp || false,
|
|
944
|
+
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(normalizedPayload),
|
|
945
|
+
isConsulted: false,
|
|
946
|
+
isAutoAnswering: false
|
|
947
|
+
};
|
|
948
|
+
const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
949
|
+
this.taskCollection[stableInteractionId] = task;
|
|
950
|
+
|
|
951
|
+
// Restore the actor before installing listeners so the internal hydrate does
|
|
952
|
+
// not leak as a second public hydrate or as an incoming-task notification.
|
|
953
|
+
task.sendStateMachineEvent({
|
|
954
|
+
type: _stateMachine.TaskEvent.HYDRATE,
|
|
955
|
+
taskData,
|
|
956
|
+
agentId: this.agentId
|
|
957
|
+
});
|
|
958
|
+
this.setupTaskListeners(task);
|
|
959
|
+
context.payload = taskData;
|
|
960
|
+
context.stateMachineEvent = {
|
|
961
|
+
type: _stateMachine.TaskEvent.CONTACT_OWNER_CHANGED,
|
|
962
|
+
taskData
|
|
963
|
+
};
|
|
964
|
+
return {
|
|
965
|
+
task
|
|
966
|
+
};
|
|
967
|
+
}
|
|
557
968
|
handleCampaignContactUpdated(context) {
|
|
558
969
|
const {
|
|
559
970
|
payload
|
|
@@ -638,7 +1049,7 @@ class TaskManager extends _events.default {
|
|
|
638
1049
|
wrapUpRequired: false,
|
|
639
1050
|
isConferenceInProgress: false,
|
|
640
1051
|
isAutoAnswering: false
|
|
641
|
-
}, this.configFlags, this.wrapupData, this.agentId);
|
|
1052
|
+
}, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
642
1053
|
this.setupTaskListeners(task);
|
|
643
1054
|
this.taskCollection[payload.interactionId] = task;
|
|
644
1055
|
} else {
|
|
@@ -664,7 +1075,7 @@ class TaskManager extends _events.default {
|
|
|
664
1075
|
isConsulted: isConsultedTask,
|
|
665
1076
|
isAutoAnswering: shouldAutoAnswer
|
|
666
1077
|
};
|
|
667
|
-
const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
|
|
1078
|
+
const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
668
1079
|
this.setupTaskListeners(task);
|
|
669
1080
|
this.taskCollection[payload.interactionId] = task;
|
|
670
1081
|
return {
|
|
@@ -693,7 +1104,7 @@ class TaskManager extends _events.default {
|
|
|
693
1104
|
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
|
|
694
1105
|
isAutoAnswering: shouldAutoAnswer
|
|
695
1106
|
};
|
|
696
|
-
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
|
|
1107
|
+
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
697
1108
|
this.setupTaskListeners(task);
|
|
698
1109
|
this.taskCollection[payload.interactionId] = task;
|
|
699
1110
|
}
|
|
@@ -713,6 +1124,14 @@ class TaskManager extends _events.default {
|
|
|
713
1124
|
destinationType: taskData.destinationType ?? snapshot?.context?.consultDestinationType ?? null
|
|
714
1125
|
} : taskData;
|
|
715
1126
|
task.updateTaskData(updateTaskData);
|
|
1127
|
+
|
|
1128
|
+
// A main-interaction lifecycle event can re-key a child consult task. Remove
|
|
1129
|
+
// any aliases first so consumers never observe duplicate task entries.
|
|
1130
|
+
Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
|
|
1131
|
+
if (candidate === task && taskId !== taskData.interactionId) {
|
|
1132
|
+
delete this.taskCollection[taskId];
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
716
1135
|
this.taskCollection[taskData.interactionId] = task;
|
|
717
1136
|
return task;
|
|
718
1137
|
}
|
|
@@ -753,8 +1172,8 @@ class TaskManager extends _events.default {
|
|
|
753
1172
|
task.on(_types.TASK_EVENTS.TASK_CLEANUP, (t, options) => {
|
|
754
1173
|
this.handleTaskCleanup(t);
|
|
755
1174
|
if (options?.removeFromCollection) {
|
|
756
|
-
const
|
|
757
|
-
if (
|
|
1175
|
+
const taskIsStillCollected = Object.values(this.taskCollection).some(candidate => candidate === t);
|
|
1176
|
+
if (taskIsStillCollected) {
|
|
758
1177
|
this.removeTaskFromCollection(t);
|
|
759
1178
|
}
|
|
760
1179
|
}
|
|
@@ -765,7 +1184,11 @@ class TaskManager extends _events.default {
|
|
|
765
1184
|
task.cancelAutoWrapupTimer();
|
|
766
1185
|
}
|
|
767
1186
|
if (task?.data?.interactionId) {
|
|
768
|
-
|
|
1187
|
+
Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
|
|
1188
|
+
if (candidate === task) {
|
|
1189
|
+
delete this.taskCollection[taskId];
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
769
1192
|
_loggerProxy.default.info(`Task removed from collection`, {
|
|
770
1193
|
module: _constants.TASK_MANAGER_FILE,
|
|
771
1194
|
method: _constants2.METHODS.REMOVE_TASK_FROM_COLLECTION,
|
|
@@ -810,7 +1233,7 @@ class TaskManager extends _events.default {
|
|
|
810
1233
|
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
|
|
811
1234
|
isConsulted: false
|
|
812
1235
|
};
|
|
813
|
-
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
|
|
1236
|
+
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
814
1237
|
this.taskCollection[payload.interactionId] = task;
|
|
815
1238
|
|
|
816
1239
|
// Transition the new task out of IDLE immediately so UI controls are
|
|
@@ -884,6 +1307,52 @@ class TaskManager extends _events.default {
|
|
|
884
1307
|
...this.taskCollection
|
|
885
1308
|
};
|
|
886
1309
|
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Propagate runtime enableWxBetterTogether changes to config flags and active tasks.
|
|
1313
|
+
*/
|
|
1314
|
+
applyEnableWxBetterTogether(enabled) {
|
|
1315
|
+
if (this.configFlags) {
|
|
1316
|
+
this.configFlags = {
|
|
1317
|
+
...this.configFlags,
|
|
1318
|
+
enableWxBetterTogether: enabled
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
Object.values(this.taskCollection).forEach(task => {
|
|
1322
|
+
task.setEnableWxBetterTogether(enabled);
|
|
1323
|
+
if (enabled) {
|
|
1324
|
+
TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* Propagate wxApp mute state from Mercury sync to active voice tasks.
|
|
1331
|
+
*/
|
|
1332
|
+
applyWxAppMuteStateFromSync(callId, muted) {
|
|
1333
|
+
Object.values(this.taskCollection).forEach(task => {
|
|
1334
|
+
task.applyWxAppMuteStateFromSync(callId, muted);
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Re-seeds wxApp mute state from GET telephony/calls/{callId} for one task.
|
|
1340
|
+
*/
|
|
1341
|
+
static syncWxAppMuteFromCallDetailsForTask(task) {
|
|
1342
|
+
const sync = task.syncWxAppMuteFromCallDetails?.bind(task);
|
|
1343
|
+
if (typeof sync === 'function') {
|
|
1344
|
+
sync().catch(() => undefined);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Re-seeds wxApp mute for all engaged voice tasks (reload / runtime enable backfill).
|
|
1350
|
+
*/
|
|
1351
|
+
syncWxAppMuteFromCallDetailsForAllTasks() {
|
|
1352
|
+
Object.values(this.taskCollection).forEach(task => {
|
|
1353
|
+
TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
887
1356
|
static getTaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
|
|
888
1357
|
if (!TaskManager.taskManager) {
|
|
889
1358
|
TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager);
|