@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.
Files changed (198) hide show
  1. package/.sdd/manifest.json +7 -5
  2. package/ai-docs/ARCHITECTURE.md +1 -1
  3. package/ai-docs/CONTRACTS.md +8 -3
  4. package/ai-docs/SECURITY.md +1 -1
  5. package/ai-docs/contact-center-spec.md +22 -4
  6. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  7. package/dist/cc.js +544 -12
  8. package/dist/cc.js.map +1 -1
  9. package/dist/config.js +7 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/constants.js +10 -2
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/metrics/MetricsManager.js +16 -0
  15. package/dist/metrics/MetricsManager.js.map +1 -1
  16. package/dist/metrics/behavioral-events.js +104 -0
  17. package/dist/metrics/behavioral-events.js.map +1 -1
  18. package/dist/metrics/constants.js +18 -0
  19. package/dist/metrics/constants.js.map +1 -1
  20. package/dist/services/AddressBook.js +18 -15
  21. package/dist/services/AddressBook.js.map +1 -1
  22. package/dist/services/AnswerCallOnWebexService.js +202 -0
  23. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  24. package/dist/services/EntryPoint.js +46 -68
  25. package/dist/services/EntryPoint.js.map +1 -1
  26. package/dist/services/Queue.js +27 -22
  27. package/dist/services/Queue.js.map +1 -1
  28. package/dist/services/WebexCrossClientService.js +222 -0
  29. package/dist/services/WebexCrossClientService.js.map +1 -0
  30. package/dist/services/WxAppTelephonyMercurySync.js +108 -0
  31. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  32. package/dist/services/config/Util.js +3 -0
  33. package/dist/services/config/Util.js.map +1 -1
  34. package/dist/services/config/constants.js +10 -6
  35. package/dist/services/config/constants.js.map +1 -1
  36. package/dist/services/config/types.js +4 -0
  37. package/dist/services/config/types.js.map +1 -1
  38. package/dist/services/core/Err.js.map +1 -1
  39. package/dist/services/core/WebexRequest.js +6 -2
  40. package/dist/services/core/WebexRequest.js.map +1 -1
  41. package/dist/services/core/aqm-reqs.js +28 -14
  42. package/dist/services/core/aqm-reqs.js.map +1 -1
  43. package/dist/services/core/types.js.map +1 -1
  44. package/dist/services/task/Task.js +73 -7
  45. package/dist/services/task/Task.js.map +1 -1
  46. package/dist/services/task/TaskFactory.js +8 -4
  47. package/dist/services/task/TaskFactory.js.map +1 -1
  48. package/dist/services/task/TaskManager.js +490 -21
  49. package/dist/services/task/TaskManager.js.map +1 -1
  50. package/dist/services/task/WebexCallingUtils.js +70 -0
  51. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  52. package/dist/services/task/constants.js +4 -1
  53. package/dist/services/task/constants.js.map +1 -1
  54. package/dist/services/task/contact.js +29 -0
  55. package/dist/services/task/contact.js.map +1 -1
  56. package/dist/services/task/digital/Digital.js +3 -2
  57. package/dist/services/task/digital/Digital.js.map +1 -1
  58. package/dist/services/task/state-machine/TaskStateMachine.js +162 -53
  59. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  60. package/dist/services/task/state-machine/actions.js +6 -1
  61. package/dist/services/task/state-machine/actions.js.map +1 -1
  62. package/dist/services/task/state-machine/guards.js +7 -10
  63. package/dist/services/task/state-machine/guards.js.map +1 -1
  64. package/dist/services/task/state-machine/types.js.map +1 -1
  65. package/dist/services/task/state-machine/uiControlsComputer.js +118 -18
  66. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
  67. package/dist/services/task/types.js +16 -1
  68. package/dist/services/task/types.js.map +1 -1
  69. package/dist/services/task/voice/Voice.js +456 -15
  70. package/dist/services/task/voice/Voice.js.map +1 -1
  71. package/dist/services/task/voice/WebRTC.js +4 -1
  72. package/dist/services/task/voice/WebRTC.js.map +1 -1
  73. package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
  74. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  75. package/dist/services/wxAppDiagnosticLogging.js +122 -0
  76. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  77. package/dist/services/wxAppTelephonyUtils.js +19 -0
  78. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  79. package/dist/types/cc.d.ts +78 -4
  80. package/dist/types/config.d.ts +7 -0
  81. package/dist/types/constants.d.ts +8 -0
  82. package/dist/types/index.d.ts +3 -1
  83. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  84. package/dist/types/metrics/constants.d.ts +17 -0
  85. package/dist/types/services/AddressBook.d.ts +2 -1
  86. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  87. package/dist/types/services/EntryPoint.d.ts +7 -5
  88. package/dist/types/services/Queue.d.ts +5 -3
  89. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  90. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  91. package/dist/types/services/config/constants.d.ts +8 -5
  92. package/dist/types/services/config/types.d.ts +17 -4
  93. package/dist/types/services/core/Err.d.ts +2 -0
  94. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  95. package/dist/types/services/core/types.d.ts +2 -0
  96. package/dist/types/services/task/Task.d.ts +20 -2
  97. package/dist/types/services/task/TaskFactory.d.ts +2 -1
  98. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  99. package/dist/types/services/task/constants.d.ts +3 -0
  100. package/dist/types/services/task/contact.d.ts +4 -0
  101. package/dist/types/services/task/digital/Digital.d.ts +2 -2
  102. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +304 -56
  103. package/dist/types/services/task/state-machine/guards.d.ts +6 -5
  104. package/dist/types/services/task/state-machine/types.d.ts +9 -1
  105. package/dist/types/services/task/types.d.ts +81 -4
  106. package/dist/types/services/task/voice/Voice.d.ts +52 -9
  107. package/dist/types/services/task/voice/WebRTC.d.ts +2 -2
  108. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
  109. package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
  110. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  111. package/dist/types/types.d.ts +14 -8
  112. package/dist/types/utils/PageCache.d.ts +20 -3
  113. package/dist/types.js +7 -2
  114. package/dist/types.js.map +1 -1
  115. package/dist/utils/PageCache.js +19 -5
  116. package/dist/utils/PageCache.js.map +1 -1
  117. package/dist/webex.js +1 -1
  118. package/package.json +8 -8
  119. package/src/cc.ts +657 -12
  120. package/src/config.ts +7 -0
  121. package/src/constants.ts +8 -0
  122. package/src/index.ts +2 -0
  123. package/src/metrics/MetricsManager.ts +16 -0
  124. package/src/metrics/ai-docs/metrics-spec.md +78 -10
  125. package/src/metrics/behavioral-events.ts +106 -0
  126. package/src/metrics/constants.ts +20 -0
  127. package/src/services/AddressBook.ts +17 -6
  128. package/src/services/AnswerCallOnWebexService.ts +239 -0
  129. package/src/services/EntryPoint.ts +59 -60
  130. package/src/services/Queue.ts +29 -12
  131. package/src/services/WebexCrossClientService.ts +293 -0
  132. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  133. package/src/services/agent/ai-docs/agent-spec.md +1 -0
  134. package/src/services/ai-docs/AGENTS.md +10 -10
  135. package/src/services/ai-docs/services-spec.md +6 -1
  136. package/src/services/config/Util.ts +3 -0
  137. package/src/services/config/ai-docs/AGENTS.md +1 -1
  138. package/src/services/config/ai-docs/ARCHITECTURE.md +2 -2
  139. package/src/services/config/ai-docs/config-spec.md +6 -0
  140. package/src/services/config/constants.ts +10 -6
  141. package/src/services/config/types.ts +16 -4
  142. package/src/services/core/Err.ts +1 -0
  143. package/src/services/core/WebexRequest.ts +3 -1
  144. package/src/services/core/ai-docs/core-spec.md +5 -1
  145. package/src/services/core/aqm-reqs.ts +30 -15
  146. package/src/services/core/types.ts +2 -0
  147. package/src/services/task/Task.ts +79 -8
  148. package/src/services/task/TaskFactory.ts +8 -3
  149. package/src/services/task/TaskManager.ts +716 -17
  150. package/src/services/task/WebexCallingUtils.ts +136 -0
  151. package/src/services/task/ai-docs/ARCHITECTURE.md +1 -0
  152. package/src/services/task/ai-docs/task-spec.md +156 -3
  153. package/src/services/task/constants.ts +3 -0
  154. package/src/services/task/contact.ts +30 -0
  155. package/src/services/task/digital/Digital.ts +4 -2
  156. package/src/services/task/state-machine/TaskStateMachine.ts +235 -81
  157. package/src/services/task/state-machine/actions.ts +5 -2
  158. package/src/services/task/state-machine/ai-docs/AGENTS.md +6 -4
  159. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +35 -16
  160. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +103 -33
  161. package/src/services/task/state-machine/guards.ts +7 -10
  162. package/src/services/task/state-machine/types.ts +16 -1
  163. package/src/services/task/state-machine/uiControlsComputer.ts +206 -32
  164. package/src/services/task/types.ts +114 -4
  165. package/src/services/task/voice/Voice.ts +595 -14
  166. package/src/services/task/voice/WebRTC.ts +5 -1
  167. package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
  168. package/src/services/wxAppDiagnosticLogging.ts +174 -0
  169. package/src/services/wxAppTelephonyUtils.ts +14 -0
  170. package/src/types.ts +33 -9
  171. package/src/utils/AGENTS.md +21 -10
  172. package/src/utils/PageCache.ts +38 -5
  173. package/src/utils/ai-docs/utils-spec.md +21 -11
  174. package/test/unit/spec/cc.ts +1522 -19
  175. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  176. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  177. package/test/unit/spec/services/AddressBook.ts +37 -6
  178. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  179. package/test/unit/spec/services/EntryPoint.ts +87 -40
  180. package/test/unit/spec/services/Queue.ts +123 -12
  181. package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
  182. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  183. package/test/unit/spec/services/config/Util.ts +85 -0
  184. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  185. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  186. package/test/unit/spec/services/task/Task.ts +274 -6
  187. package/test/unit/spec/services/task/TaskFactory.ts +39 -2
  188. package/test/unit/spec/services/task/TaskManager.ts +1717 -9
  189. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  190. package/test/unit/spec/services/task/contact.ts +33 -0
  191. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +1485 -109
  192. package/test/unit/spec/services/task/state-machine/guards.ts +210 -8
  193. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +445 -7
  194. package/test/unit/spec/services/task/voice/Voice.ts +1412 -0
  195. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
  196. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
  197. package/umd/contact-center.min.js +2 -2
  198. package/umd/contact-center.min.js.map +1 -1
@@ -12,42 +12,389 @@ var _loggerProxy = _interopRequireDefault(require("../../../logger-proxy"));
12
12
  var _MetricsManager = _interopRequireDefault(require("../../../metrics/MetricsManager"));
13
13
  var _constants2 = require("../../../metrics/constants");
14
14
  var _stateMachine = require("../state-machine");
15
+ var _guards = require("../state-machine/guards");
15
16
  var _TaskUtils = require("../TaskUtils");
17
+ var _wxAppTelephonyUtils = require("../../wxAppTelephonyUtils");
18
+ var _wxAppVoiceMethods = require("./wxAppVoiceMethods");
19
+ var _wxAppDiagnosticLogging = require("../../wxAppDiagnosticLogging");
16
20
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
+ const hasNonemptyPendingWrapUp = taskData => Array.isArray(taskData?.agentsPendingWrapUp) && taskData.agentsPendingWrapUp.length > 0;
22
+
23
+ /**
24
+ * Same consulted-non-owner rule as TaskManager leave stamp. Used only for the
25
+ * HTTP exitConference self-exit fallback (shouldWrapUpForThisAgent stays unchanged).
26
+ */
27
+ const isConsultedNonOwner = (taskData, fallback) => {
28
+ const isConsulted = taskData.isConsulted ?? fallback?.isConsulted;
29
+ if (isConsulted !== true) {
30
+ return false;
31
+ }
32
+ const owner = taskData.interaction?.owner ?? fallback?.interaction?.owner;
33
+ const selfAgentId = taskData.agentId ?? fallback?.agentId;
34
+ return Boolean(selfAgentId && owner && owner !== selfAgentId);
35
+ };
17
36
  class Voice extends _Task.default {
37
+ static WXAPP_MUTE_SYNC_RETRY_DELAY_MS = 50;
38
+ static WXAPP_MUTE_SYNC_MAX_RETRIES = 3;
39
+ enableWxBetterTogether = false;
40
+ wxAppMuted = false;
41
+ wxAppAnswerPending = false;
42
+ wxAppAcceptInFlight = false;
43
+ wxAppMuteToggleTail = Promise.resolve();
44
+ wxAppDtmfTail = Promise.resolve();
18
45
  constructor(contact, data, callOptions, wrapupData, agentId) {
19
46
  const resolvedOptions = {
20
47
  isEndTaskEnabled: callOptions?.isEndTaskEnabled ?? true,
21
48
  isEndConsultEnabled: callOptions?.isEndConsultEnabled ?? true,
22
49
  voiceVariant: callOptions?.voiceVariant ?? _types.VOICE_VARIANT.PSTN,
23
- isRecordingEnabled: callOptions?.isRecordingEnabled ?? true
50
+ isRecordingEnabled: callOptions?.isRecordingEnabled ?? true,
51
+ enableWxBetterTogether: callOptions?.enableWxBetterTogether ?? false
24
52
  };
25
53
  super(contact, data, {
26
- ...resolvedOptions
54
+ ...resolvedOptions,
55
+ consultTransferConfig: callOptions?.consultTransferConfig
27
56
  }, wrapupData, agentId);
57
+ this.enableWxBetterTogether = resolvedOptions.enableWxBetterTogether;
58
+ this.answerCallOnWebexService = callOptions?.answerCallOnWebexService;
59
+ }
60
+ getWxAppVoiceDependencies() {
61
+ return {
62
+ enableWxBetterTogether: this.enableWxBetterTogether,
63
+ answerCallOnWebexService: this.answerCallOnWebexService,
64
+ agentId: this.agentId,
65
+ metricsManager: this.metricsManager,
66
+ getTaskData: () => this.data,
67
+ getTaskState: () => this.stateMachineService?.getSnapshot?.()?.value,
68
+ getWxAppMuted: () => this.wxAppMuted,
69
+ setWxAppMuted: muted => {
70
+ this.wxAppMuted = muted;
71
+ }
72
+ };
73
+ }
74
+
75
+ /** @deprecated Use {@link Voice.getWxAppVoiceDependencies} */
76
+ getWxAppVoiceDeps() {
77
+ return this.getWxAppVoiceDependencies();
78
+ }
79
+ isWebexAppCallingOffer() {
80
+ return (0, _wxAppVoiceMethods.isWebexAppCallingOffer)(this.getWxAppVoiceDeps());
81
+ }
82
+ isWebexAppInboundCallingOffer() {
83
+ return (0, _wxAppVoiceMethods.isWebexAppInboundCallingOffer)(this.getWxAppVoiceDeps());
84
+ }
85
+ setWxAppAnswerPending(pending) {
86
+ this.wxAppAnswerPending = pending;
87
+ this.uiControlConfig = {
88
+ ...this.uiControlConfig,
89
+ wxAppAnswerPending: pending
90
+ };
91
+ this.updateUiControls(true);
92
+ }
93
+ setWxAppAcceptInFlight(inFlight) {
94
+ this.wxAppAcceptInFlight = inFlight;
95
+ this.uiControlConfig = {
96
+ ...this.uiControlConfig,
97
+ wxAppAcceptInFlight: inFlight
98
+ };
99
+ this.updateUiControls(true);
100
+ }
101
+ setEnableWxBetterTogether(enabled) {
102
+ this.enableWxBetterTogether = enabled;
103
+ this.uiControlConfig = {
104
+ ...this.uiControlConfig,
105
+ enableWxBetterTogether: enabled
106
+ };
107
+ this.updateUiControls(true);
108
+ }
109
+ updateUiControls(forceEmit = false) {
110
+ super.updateUiControls(forceEmit);
111
+ this.logWxAppOfferDecisionIfNeeded();
112
+ }
113
+ logWxAppOfferDecisionIfNeeded() {
114
+ if (!this.enableWxBetterTogether) {
115
+ return;
116
+ }
117
+ const state = this.stateMachineService?.getSnapshot?.()?.value;
118
+ if (state !== _stateMachine.TaskState.OFFERED) {
119
+ return;
120
+ }
121
+ const accept = this.currentUiControls?.main?.accept;
122
+ if (!accept?.isVisible) {
123
+ return;
124
+ }
125
+ const deps = this.getWxAppVoiceDependencies();
126
+ const isOutdial = this.data?.interaction?.outboundType === 'OUTDIAL';
127
+ const isWxAppInboundOffer = this.isWebexAppInboundCallingOffer();
128
+ const isWxAppOutdialOffer = this.isWebexAppCallingOffer() && isOutdial;
129
+ const deviceDetails = (0, _wxAppVoiceMethods.getCallingDeviceDetails)(deps);
130
+ const acceptReason = (0, _wxAppDiagnosticLogging.deriveWxAppAcceptReason)({
131
+ isWxAppInboundOffer,
132
+ isWxAppOutdialOffer,
133
+ isWebrtc: this.uiControlConfig.voiceVariant === _types.VOICE_VARIANT.WEBRTC,
134
+ isOutdial,
135
+ wxAppAcceptInFlight: this.wxAppAcceptInFlight,
136
+ wxAppAnswerPending: this.wxAppAnswerPending,
137
+ enableWxBetterTogether: this.enableWxBetterTogether,
138
+ hasDeviceCallId: Boolean(deviceDetails?.deviceCallId)
139
+ });
140
+ if (acceptReason === this.lastLoggedWxAppAcceptReason) {
141
+ return;
142
+ }
143
+ this.lastLoggedWxAppAcceptReason = acceptReason;
144
+ (0, _wxAppDiagnosticLogging.logWxAppOfferDecision)({
145
+ interactionId: this.data.interactionId,
146
+ acceptVisible: accept.isVisible,
147
+ acceptEnabled: accept.isEnabled,
148
+ acceptReason,
149
+ wxAppParticipantDeviceType: deviceDetails?.deviceType,
150
+ hasDeviceCallId: Boolean(deviceDetails?.deviceCallId)
151
+ });
152
+ }
153
+ applyWxAppMuteStateFromSync(incomingCallId, muted) {
154
+ if (!this.enableWxBetterTogether) {
155
+ return;
156
+ }
157
+ const activeCallId = this.getWebexCallingCallId();
158
+ const incomingSuffix = (0, _wxAppDiagnosticLogging.callIdSuffix)(incomingCallId);
159
+ const activeSuffix = (0, _wxAppDiagnosticLogging.callIdSuffix)(activeCallId);
160
+ if (!activeCallId || !incomingCallId.endsWith(activeCallId)) {
161
+ (0, _wxAppDiagnosticLogging.logWxAppMercuryMuteSync)({
162
+ phase: 'dropped',
163
+ muted,
164
+ callIdSuffix: incomingSuffix,
165
+ interactionId: this.data.interactionId,
166
+ dropReason: !activeCallId ? 'no_active_wxApp_call' : 'call_id_mismatch'
167
+ });
168
+ return;
169
+ }
170
+ if (this.wxAppMuted === muted) {
171
+ (0, _wxAppDiagnosticLogging.logWxAppMercuryMuteSync)({
172
+ phase: 'dropped',
173
+ muted,
174
+ callIdSuffix: incomingSuffix,
175
+ interactionId: this.data.interactionId,
176
+ dropReason: 'mute_state_unchanged'
177
+ });
178
+ return;
179
+ }
180
+ this.wxAppMuted = muted;
181
+ (0, _wxAppDiagnosticLogging.logWxAppMercuryMuteSync)({
182
+ phase: 'applied',
183
+ muted,
184
+ callIdSuffix: activeSuffix,
185
+ interactionId: this.data.interactionId
186
+ });
187
+ this.emit(_types.TASK_EVENTS.TASK_WXAPP_MUTE_STATE_UPDATED, {
188
+ muted
189
+ });
190
+ }
191
+ getWxAppMuted() {
192
+ return this.wxAppMuted;
193
+ }
194
+ static delay(ms) {
195
+ return new Promise(resolve => {
196
+ setTimeout(resolve, ms);
197
+ });
198
+ }
199
+ canRetryWxAppMuteSync() {
200
+ if (!this.enableWxBetterTogether) {
201
+ return false;
202
+ }
203
+ return Boolean((0, _wxAppVoiceMethods.getCallingDeviceDetails)(this.getWxAppVoiceDeps())?.deviceCallId) && !this.getWebexCallingCallId();
204
+ }
205
+ shouldSkipWxAppMuteSync() {
206
+ if (this.data?.interaction?.isTerminated) {
207
+ return true;
208
+ }
209
+ return this.isWebexAppCallingOffer() && !this.uiControlConfig?.wxAppAnswerPending;
210
+ }
211
+ applyWxAppMuteFromBackfill(callId, muted) {
212
+ if (!this.enableWxBetterTogether || this.wxAppMuted === muted) {
213
+ return;
214
+ }
215
+ const activeCallId = this.getWebexCallingCallId();
216
+ if (activeCallId && !callId.endsWith(activeCallId) && !activeCallId.endsWith(callId)) {
217
+ return;
218
+ }
219
+ this.wxAppMuted = muted;
220
+ this.emit(_types.TASK_EVENTS.TASK_WXAPP_MUTE_STATE_UPDATED, {
221
+ muted
222
+ });
223
+ }
224
+ async syncWxAppMuteFromCallDetails(retryAttempt = 0) {
225
+ if (!this.enableWxBetterTogether || !this.answerCallOnWebexService) {
226
+ return undefined;
227
+ }
228
+ if (retryAttempt === 0 && this.shouldSkipWxAppMuteSync()) {
229
+ return undefined;
230
+ }
231
+ if (retryAttempt === 0 && this.wxAppMuteSyncInFlight) {
232
+ return this.wxAppMuteSyncInFlight;
233
+ }
234
+ const syncPromise = this.performWxAppMuteSync(retryAttempt);
235
+ if (retryAttempt === 0) {
236
+ this.wxAppMuteSyncInFlight = syncPromise;
237
+ syncPromise.finally(() => {
238
+ if (this.wxAppMuteSyncInFlight === syncPromise) {
239
+ this.wxAppMuteSyncInFlight = undefined;
240
+ }
241
+ }).catch(() => undefined);
242
+ return syncPromise;
243
+ }
244
+ return syncPromise;
245
+ }
246
+ async performWxAppMuteSync(retryAttempt) {
247
+ if (this.shouldSkipWxAppMuteSync()) {
248
+ return undefined;
249
+ }
250
+ let callId = this.getWebexCallingCallId();
251
+ if (!callId) {
252
+ if (retryAttempt < Voice.WXAPP_MUTE_SYNC_MAX_RETRIES && this.canRetryWxAppMuteSync()) {
253
+ await Voice.delay(Voice.WXAPP_MUTE_SYNC_RETRY_DELAY_MS);
254
+ return this.performWxAppMuteSync(retryAttempt + 1);
255
+ }
256
+ callId = (0, _wxAppVoiceMethods.getCallingDeviceDetails)(this.getWxAppVoiceDeps())?.deviceCallId ?? null;
257
+ if (!callId) {
258
+ return undefined;
259
+ }
260
+ }
261
+ try {
262
+ const lineOwnerId = (0, _wxAppVoiceMethods.resolveWxAppLineOwnerId)(this.getWxAppVoiceDeps());
263
+ const details = await this.answerCallOnWebexService.getCallDetails({
264
+ callId,
265
+ lineOwnerId
266
+ });
267
+ if (details.muted !== undefined) {
268
+ this.applyWxAppMuteStateFromSync(callId, details.muted);
269
+ if (this.wxAppMuted !== details.muted) {
270
+ this.applyWxAppMuteFromBackfill(callId, details.muted);
271
+ }
272
+ return details.muted;
273
+ }
274
+ return this.wxAppMuted;
275
+ } catch (error) {
276
+ if (!(0, _wxAppTelephonyUtils.isWxAppCallNotFoundError)(error)) {
277
+ _loggerProxy.default.error(`Failed to sync wxApp mute from call details: ${error}`, {
278
+ module: _constants.CC_FILE,
279
+ method: _constants.METHODS.GET_CALL_DETAILS_ON_WEBEX,
280
+ interactionId: this.data?.interactionId
281
+ });
282
+ }
283
+ return undefined;
284
+ }
285
+ }
286
+ onTaskAssigned() {
287
+ this.setWxAppAnswerPending(false);
288
+ queueMicrotask(() => {
289
+ this.syncWxAppMuteFromCallDetails().catch(() => undefined);
290
+ });
291
+ }
292
+ onTaskHydrated() {
293
+ const state = this.stateMachineService?.getSnapshot?.()?.value;
294
+ const answerStillPending = state === _stateMachine.TaskState.OFFERED && (this.uiControlConfig?.wxAppAcceptInFlight || this.uiControlConfig?.wxAppAnswerPending);
295
+ if (!answerStillPending) {
296
+ this.setWxAppAnswerPending(false);
297
+ }
298
+ this.syncWxAppMuteFromCallDetails().catch(() => undefined);
299
+ }
300
+ getCallingDeviceDetails() {
301
+ return (0, _wxAppVoiceMethods.getCallingDeviceDetails)(this.getWxAppVoiceDeps());
302
+ }
303
+ getWebexCallingCallId() {
304
+ return (0, _wxAppVoiceMethods.getWebexCallingCallId)(this.getWxAppVoiceDeps());
305
+ }
306
+ createWxAppLifecycle() {
307
+ return {
308
+ setWxAppAcceptInFlight: inFlight => this.setWxAppAcceptInFlight(inFlight),
309
+ setWxAppAnswerPending: pending => this.setWxAppAnswerPending(pending),
310
+ resetWxAppMuted: () => {
311
+ this.wxAppMuted = false;
312
+ },
313
+ syncWxAppMuteFromCallDetails: () => this.syncWxAppMuteFromCallDetails(),
314
+ mapWxAppVoiceError: (error, method) => (0, _wxAppVoiceMethods.mapWxAppVoiceError)(error, method, _constants.CC_FILE)
315
+ };
28
316
  }
29
317
  getStateMachineSnapshot() {
30
318
  return this.stateMachineService?.getSnapshot?.();
31
319
  }
32
320
 
33
321
  /**
34
- * This method is used to accept the task.
35
- * It is expected to be overridden by child classes.
36
- * @returns Promise<TaskResponse>
37
- * @throws Error
322
+ * Accepts the task. Routes to wxApp telephony answer when `enableWxBetterTogether` and wxApp offer.
38
323
  */
39
324
  async accept() {
40
- super.unsupportedMethodError(_constants.METHODS.ACCEPT);
325
+ if (this.enableWxBetterTogether && this.isWebexAppCallingOffer()) {
326
+ await (0, _wxAppVoiceMethods.runWxAppAccept)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle());
327
+ return Promise.resolve();
328
+ }
329
+ return super.unsupportedMethodError(_constants.METHODS.ACCEPT);
41
330
  }
42
331
 
43
332
  /**
44
- * This method is used to decline the task.
45
- * It is expected to be overridden by child classes.
46
- * @returns Promise<TaskResponse>
47
- * @throws Error
333
+ * Declines the task. Routes wxApp inbound to telephony reject; wxApp outdial offer to cancelTask.
48
334
  */
49
335
  async decline() {
50
- super.unsupportedMethodError(_constants.METHODS.REJECT);
336
+ if (this.enableWxBetterTogether && this.isWebexAppInboundCallingOffer()) {
337
+ await (0, _wxAppVoiceMethods.runWxAppReject)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle());
338
+ return Promise.resolve();
339
+ }
340
+ const isOutdial = this.data?.interaction?.outboundType === 'OUTDIAL';
341
+ if (!this.enableWxBetterTogether || !isOutdial || !this.isWebexAppCallingOffer()) {
342
+ return super.unsupportedMethodError(_constants.METHODS.REJECT);
343
+ }
344
+ const interactionId = this.data.interactionId;
345
+ _loggerProxy.default.log(`Declining wxApp outdial task for taskId:${interactionId}`, {
346
+ module: _constants.CC_FILE,
347
+ method: _constants.METHODS.REJECT,
348
+ interactionId
349
+ });
350
+ try {
351
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_DECLINE_FAILED]);
352
+ this.setWxAppAnswerPending(false);
353
+ const response = await (0, _wxAppVoiceMethods.runWxAppOutdialDecline)(this.getWxAppVoiceDependencies(), () => this.contact.cancelTask({
354
+ interactionId
355
+ }));
356
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS, {
357
+ taskId: interactionId,
358
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
359
+ }, ['operational', 'behavioral']);
360
+ return response;
361
+ } catch (error) {
362
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_DECLINE_FAILED, {
363
+ taskId: interactionId,
364
+ error: error.toString(),
365
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
366
+ }, ['operational', 'behavioral']);
367
+ const {
368
+ error: detailedError
369
+ } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.REJECT, _constants.CC_FILE);
370
+ throw detailedError;
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Toggles mute for wxApp engaged telephony when `enableWxBetterTogether` is active.
376
+ */
377
+ async toggleMute(options) {
378
+ if (this.enableWxBetterTogether && this.getWebexCallingCallId()) {
379
+ const togglePromise = this.wxAppMuteToggleTail.catch(() => undefined).then(() => (0, _wxAppVoiceMethods.runWxAppToggleMute)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle(), options));
380
+ this.wxAppMuteToggleTail = togglePromise.catch(() => undefined);
381
+ await togglePromise;
382
+ return;
383
+ }
384
+ super.unsupportedMethodError(_constants.METHODS.TOGGLE_MUTE);
385
+ }
386
+
387
+ /**
388
+ * Sends in-call DTMF for wxApp engaged telephony when `enableWxBetterTogether` is active.
389
+ */
390
+ async transmitDtmf(options) {
391
+ if (this.enableWxBetterTogether && this.getWebexCallingCallId()) {
392
+ const dtmfPromise = this.wxAppDtmfTail.catch(() => undefined).then(() => (0, _wxAppVoiceMethods.runWxAppTransmitDtmf)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle(), options));
393
+ this.wxAppDtmfTail = dtmfPromise.catch(() => undefined);
394
+ await dtmfPromise;
395
+ return;
396
+ }
397
+ super.unsupportedMethodError(_constants.METHODS.TRANSMIT_DTMF);
51
398
  }
52
399
 
53
400
  /**
@@ -667,6 +1014,74 @@ class Voice extends _Task.default {
667
1014
  }
668
1015
  }
669
1016
 
1017
+ /**
1018
+ * Removes another participant from an active conference.
1019
+ * Completion is correlated with ParticipantLeftConference by the AQM request layer.
1020
+ * @param payload - Participant identifier to remove
1021
+ * @returns Promise<TaskResponse>
1022
+ */
1023
+ async dropConferenceParticipant(payload) {
1024
+ // The TypeScript contract requires payload, but the published SDK is also callable from
1025
+ // untyped JavaScript. Keep an explicit runtime boundary check before direct property access.
1026
+ if (!payload) {
1027
+ throw new Error('participantId must be a non-empty string');
1028
+ }
1029
+ const {
1030
+ participantId
1031
+ } = payload;
1032
+ if (typeof participantId !== 'string' || participantId.trim().length === 0) {
1033
+ throw new Error('participantId must be a non-empty string');
1034
+ }
1035
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
1036
+ // taskId identifies the SDK task instance, which can remain child-interaction keyed after
1037
+ // consult/EP-DN flows. requestInteractionId identifies the main interaction sent to AQM.
1038
+
1039
+ _loggerProxy.default.info('Dropping conference participant', {
1040
+ module: _constants.CC_FILE,
1041
+ method: _constants.METHODS.DROP_CONFERENCE_PARTICIPANT,
1042
+ interactionId: requestInteractionId
1043
+ });
1044
+ try {
1045
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_FAILED]);
1046
+ const response = await this.contact.dropConferenceParticipant({
1047
+ interactionId: requestInteractionId,
1048
+ participantId
1049
+ });
1050
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS, {
1051
+ taskId: this.data.interactionId,
1052
+ requestInteractionId,
1053
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response),
1054
+ agentId: this.agentId
1055
+ }, ['operational', 'behavioral', 'business']);
1056
+ _loggerProxy.default.log('Successfully dropped conference participant', {
1057
+ module: _constants.CC_FILE,
1058
+ method: _constants.METHODS.DROP_CONFERENCE_PARTICIPANT,
1059
+ trackingId: response?.trackingId,
1060
+ interactionId: requestInteractionId
1061
+ });
1062
+ return response;
1063
+ } catch (error) {
1064
+ const failureTrackingFields = _MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error?.details || {});
1065
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_FAILED, {
1066
+ taskId: this.data.interactionId,
1067
+ requestInteractionId,
1068
+ agentId: this.agentId,
1069
+ trackingId: failureTrackingFields.trackingId,
1070
+ notifTrackingId: failureTrackingFields.notifTrackingId,
1071
+ orgId: failureTrackingFields.orgId,
1072
+ failureType: failureTrackingFields.failureType,
1073
+ reasonCode: failureTrackingFields.reasonCode
1074
+ }, ['operational', 'behavioral', 'business']);
1075
+ _loggerProxy.default.error('Failed to drop conference participant', {
1076
+ module: _constants.CC_FILE,
1077
+ method: _constants.METHODS.DROP_CONFERENCE_PARTICIPANT,
1078
+ trackingId: failureTrackingFields.trackingId,
1079
+ interactionId: requestInteractionId
1080
+ });
1081
+ throw error;
1082
+ }
1083
+ }
1084
+
670
1085
  /**
671
1086
  * Exit from a conference call.
672
1087
  * Per conference-spec.md:
@@ -721,9 +1136,31 @@ class Voice extends _Task.default {
721
1136
 
722
1137
  // Send success event to transition state
723
1138
  if (this.stateMachineService) {
1139
+ const responseTaskData = response?.data ?? {};
1140
+ const snapshot = this.stateMachineService.getSnapshot();
1141
+ const wrapUpContext = snapshot?.context ?? {
1142
+ uiControlConfig: this.uiControlConfig,
1143
+ taskData: this.data
1144
+ };
1145
+ const mergedTaskData = {
1146
+ ...this.data,
1147
+ ...responseTaskData
1148
+ };
1149
+ // Pending-list precedence is response-only; do not keep a cached snapshot list.
1150
+ if (!Object.prototype.hasOwnProperty.call(responseTaskData, 'agentsPendingWrapUp')) {
1151
+ delete mergedTaskData.agentsPendingWrapUp;
1152
+ }
1153
+ let wrapUpRequired = (0, _guards.shouldWrapUpForThisAgent)(wrapUpContext, mergedTaskData);
1154
+ // Confirmed self-exit: empty/absent response pending matches TaskManager leave stamp.
1155
+ if (!wrapUpRequired && !hasNonemptyPendingWrapUp(responseTaskData)) {
1156
+ wrapUpRequired = !isConsultedNonOwner(mergedTaskData, this.data);
1157
+ }
724
1158
  this.stateMachineService.send({
725
1159
  type: _stateMachine.TaskEvent.EXIT_CONFERENCE_SUCCESS,
726
- taskData: response.data
1160
+ taskData: {
1161
+ ...responseTaskData,
1162
+ wrapUpRequired
1163
+ }
727
1164
  });
728
1165
  }
729
1166
  this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_SUCCESS, {
@@ -1031,8 +1468,12 @@ class Voice extends _Task.default {
1031
1468
  emitTaskOutdialFailed: ({
1032
1469
  event
1033
1470
  }) => {
1034
- if (event && 'taskData' in event && event.taskData) {
1035
- this.updateTaskData(event.taskData);
1471
+ const taskData = event?.taskData;
1472
+ if (taskData) {
1473
+ this.updateTaskData(taskData);
1474
+ }
1475
+ if (taskData?.suppressOutdialFailedPopup) {
1476
+ return;
1036
1477
  }
1037
1478
  const reason = event?.reason || 'Outdial failed';
1038
1479
  this.emit(_types.TASK_EVENTS.TASK_OUTDIAL_FAILED, reason);