@webex/contact-center 3.12.0-llmrefactor.7 → 3.12.0-llmrefactor.9
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 +3 -2
- package/ai-docs/CONTRACTS.md +3 -2
- package/dist/cc.js +108 -6
- package/dist/cc.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 +91 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +16 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +34 -6
- package/dist/services/AnswerCallOnWebexService.js.map +1 -1
- package/dist/services/WebexCrossClientService.js +54 -3
- package/dist/services/WebexCrossClientService.js.map +1 -1
- package/dist/services/WxAppTelephonyMercurySync.js +21 -6
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +0 -17
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +7 -37
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/voice/Voice.js +81 -14
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/services/task/voice/wxAppVoiceMethods.js +154 -1
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
- package/dist/services/wxAppDiagnosticLogging.js +122 -0
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
- package/dist/types/cc.d.ts +1 -0
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +15 -0
- package/dist/types/services/AnswerCallOnWebexService.d.ts +1 -0
- package/dist/types/services/WebexCrossClientService.d.ts +4 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +0 -42
- package/dist/types/services/task/state-machine/guards.d.ts +4 -4
- package/dist/types/services/task/voice/Voice.d.ts +5 -1
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +3 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
- package/dist/webex.js +1 -1
- package/package.json +8 -8
- package/src/cc.ts +155 -7
- package/src/metrics/MetricsManager.ts +16 -0
- package/src/metrics/ai-docs/metrics-spec.md +71 -9
- package/src/metrics/behavioral-events.ts +92 -0
- package/src/metrics/constants.ts +18 -0
- package/src/services/AnswerCallOnWebexService.ts +41 -8
- package/src/services/WebexCrossClientService.ts +85 -4
- package/src/services/WxAppTelephonyMercurySync.ts +31 -4
- package/src/services/agent/ai-docs/agent-spec.md +1 -0
- package/src/services/task/ai-docs/task-spec.md +17 -16
- package/src/services/task/state-machine/TaskStateMachine.ts +0 -20
- package/src/services/task/state-machine/ai-docs/AGENTS.md +2 -4
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +8 -9
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +5 -7
- package/src/services/task/state-machine/guards.ts +7 -63
- package/src/services/task/voice/Voice.ts +115 -26
- package/src/services/task/voice/wxAppVoiceMethods.ts +211 -2
- package/src/services/wxAppDiagnosticLogging.ts +174 -0
- package/test/unit/spec/cc.ts +251 -22
- package/test/unit/spec/metrics/MetricsManager.ts +25 -0
- package/test/unit/spec/metrics/behavioral-events.ts +42 -0
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +41 -0
- package/test/unit/spec/services/WebexCrossClientService.ts +207 -1
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +23 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +72 -46
- package/test/unit/spec/services/task/state-machine/guards.ts +6 -6
- package/test/unit/spec/services/task/voice/Voice.ts +380 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +145 -1
- 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
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {EventPayload} from '@webex/internal-plugin-metrics/src/metrics.types';
|
|
2
|
+
|
|
3
|
+
import AnswerCallOnWebexService, {WxAppTelephonyError} from '../../AnswerCallOnWebexService';
|
|
2
4
|
import LoggerProxy from '../../../logger-proxy';
|
|
3
5
|
import {METHODS} from '../../../constants';
|
|
4
6
|
import {getErrorDetails} from '../../core/Utils';
|
|
7
|
+
import {Failure} from '../../core/GlobalTypes';
|
|
5
8
|
import {TaskData, TaskToggleMuteOptions, TaskTransmitDtmfOptions} from '../types';
|
|
6
9
|
import {TaskState} from '../state-machine';
|
|
10
|
+
import MetricsManager from '../../../metrics/MetricsManager';
|
|
11
|
+
import {METRIC_EVENT_NAMES} from '../../../metrics/constants';
|
|
7
12
|
import {
|
|
8
13
|
decodedLineOwnerId,
|
|
9
14
|
getWebexCallingDeviceDetailsForAgent,
|
|
10
15
|
WebexCallingDeviceDetails,
|
|
11
16
|
} from '../WebexCallingUtils';
|
|
17
|
+
import {
|
|
18
|
+
logWxAppTelephonyAction,
|
|
19
|
+
logWxAppValidationFailure,
|
|
20
|
+
type WxAppTelephonyAction,
|
|
21
|
+
} from '../../wxAppDiagnosticLogging';
|
|
12
22
|
|
|
13
23
|
type WxAppParticipant = {
|
|
14
24
|
deviceType?: string;
|
|
@@ -20,6 +30,7 @@ export type WxAppVoiceDependencies = {
|
|
|
20
30
|
enableWxBetterTogether: boolean;
|
|
21
31
|
answerCallOnWebexService?: AnswerCallOnWebexService;
|
|
22
32
|
agentId?: string;
|
|
33
|
+
metricsManager: MetricsManager;
|
|
23
34
|
getTaskData: () => TaskData;
|
|
24
35
|
getTaskState: () => TaskState | undefined;
|
|
25
36
|
getWxAppMuted: () => boolean;
|
|
@@ -135,12 +146,86 @@ export function resolveWxAppLineOwnerId(
|
|
|
135
146
|
return decodedLineOwnerId(getWxAppLineOwnerId(deps));
|
|
136
147
|
}
|
|
137
148
|
|
|
149
|
+
function getInteractionId(deps: WxAppVoiceDependencies): string | undefined {
|
|
150
|
+
return deps.getTaskData()?.interactionId;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function logTelephonyFailure(
|
|
154
|
+
action: WxAppTelephonyAction,
|
|
155
|
+
deps: WxAppVoiceDependencies,
|
|
156
|
+
error: unknown
|
|
157
|
+
): void {
|
|
158
|
+
const wxError = error as WxAppTelephonyError;
|
|
159
|
+
logWxAppTelephonyAction({
|
|
160
|
+
action,
|
|
161
|
+
phase: 'failed',
|
|
162
|
+
interactionId: getInteractionId(deps),
|
|
163
|
+
trackingId: wxError.trackingId,
|
|
164
|
+
httpStatus: wxError.status,
|
|
165
|
+
failureReason: wxError.message,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function getWxAppTelephonyMetricFailurePayload(
|
|
170
|
+
deps: WxAppVoiceDependencies,
|
|
171
|
+
error: unknown
|
|
172
|
+
): Record<string, string> {
|
|
173
|
+
const wxError = error as WxAppTelephonyError;
|
|
174
|
+
const payload: Record<string, string> = {
|
|
175
|
+
taskId: getInteractionId(deps) ?? '',
|
|
176
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (wxError.trackingId) {
|
|
180
|
+
payload.trackingId = wxError.trackingId;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return payload;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
type AqmWrappedError = {
|
|
187
|
+
details?: Failure;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
function getWxAppOutdialDeclineFailurePayload(
|
|
191
|
+
deps: WxAppVoiceDependencies,
|
|
192
|
+
error: unknown
|
|
193
|
+
): EventPayload {
|
|
194
|
+
const base: EventPayload = {
|
|
195
|
+
taskId: getInteractionId(deps) ?? '',
|
|
196
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
197
|
+
};
|
|
198
|
+
const aqmDetails = (error as AqmWrappedError).details;
|
|
199
|
+
|
|
200
|
+
if (aqmDetails) {
|
|
201
|
+
return {...base, ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(aqmDetails)};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return getWxAppTelephonyMetricFailurePayload(deps, error);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function logWxAppOutdialDeclineFailure(deps: WxAppVoiceDependencies, error: unknown): void {
|
|
208
|
+
const aqmDetails = (error as AqmWrappedError).details;
|
|
209
|
+
const wxError = error as WxAppTelephonyError;
|
|
210
|
+
|
|
211
|
+
logWxAppTelephonyAction({
|
|
212
|
+
action: 'decline',
|
|
213
|
+
phase: 'failed',
|
|
214
|
+
interactionId: getInteractionId(deps),
|
|
215
|
+
trackingId: aqmDetails?.trackingId ?? wxError.trackingId,
|
|
216
|
+
httpStatus: wxError.status,
|
|
217
|
+
failureReason: aqmDetails?.data?.reason ?? wxError.message,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
138
221
|
function assertWxAppEnabled(deps: WxAppVoiceDependencies): void {
|
|
139
222
|
if (!deps.enableWxBetterTogether) {
|
|
223
|
+
logWxAppValidationFailure('enableWxBetterTogether_disabled', getInteractionId(deps));
|
|
140
224
|
throw new Error('enableWxBetterTogether is disabled');
|
|
141
225
|
}
|
|
142
226
|
|
|
143
227
|
if (!deps.answerCallOnWebexService) {
|
|
228
|
+
logWxAppValidationFailure('answer_call_on_webex_service_unavailable', getInteractionId(deps));
|
|
144
229
|
throw new Error('AnswerCallOnWebexService is unavailable');
|
|
145
230
|
}
|
|
146
231
|
}
|
|
@@ -149,14 +234,18 @@ export async function acceptOnWebex(
|
|
|
149
234
|
deps: WxAppVoiceDependencies,
|
|
150
235
|
options?: {lineOwnerId?: string}
|
|
151
236
|
): Promise<void> {
|
|
237
|
+
const interactionId = getInteractionId(deps);
|
|
238
|
+
logWxAppTelephonyAction({action: 'accept', phase: 'started', interactionId});
|
|
152
239
|
assertWxAppEnabled(deps);
|
|
153
240
|
|
|
154
241
|
if (!isWebexAppCallingOffer(deps)) {
|
|
242
|
+
logWxAppValidationFailure('not_wxApp_calling_offer', interactionId);
|
|
155
243
|
throw new Error('Task is not a wxApp calling offer');
|
|
156
244
|
}
|
|
157
245
|
|
|
158
246
|
const details = getCallingDeviceDetails(deps);
|
|
159
247
|
if (!details) {
|
|
248
|
+
logWxAppValidationFailure('wxApp_device_details_unavailable', interactionId);
|
|
160
249
|
throw new Error('WxApp calling device details are unavailable');
|
|
161
250
|
}
|
|
162
251
|
|
|
@@ -165,20 +254,25 @@ export async function acceptOnWebex(
|
|
|
165
254
|
endpointId: details.deviceId,
|
|
166
255
|
lineOwnerId: resolveWxAppLineOwnerId(deps, options?.lineOwnerId),
|
|
167
256
|
});
|
|
257
|
+
logWxAppTelephonyAction({action: 'accept', phase: 'success', interactionId});
|
|
168
258
|
}
|
|
169
259
|
|
|
170
260
|
export async function rejectOnWebex(
|
|
171
261
|
deps: WxAppVoiceDependencies,
|
|
172
262
|
options?: {lineOwnerId?: string}
|
|
173
263
|
): Promise<void> {
|
|
264
|
+
const interactionId = getInteractionId(deps);
|
|
265
|
+
logWxAppTelephonyAction({action: 'decline', phase: 'started', interactionId});
|
|
174
266
|
assertWxAppEnabled(deps);
|
|
175
267
|
|
|
176
268
|
if (!isWebexAppInboundCallingOffer(deps)) {
|
|
269
|
+
logWxAppValidationFailure('not_wxApp_inbound_calling_offer', interactionId);
|
|
177
270
|
throw new Error('Task is not a wxApp inbound calling offer');
|
|
178
271
|
}
|
|
179
272
|
|
|
180
273
|
const details = getCallingDeviceDetails(deps);
|
|
181
274
|
if (!details) {
|
|
275
|
+
logWxAppValidationFailure('wxApp_device_details_unavailable', interactionId);
|
|
182
276
|
throw new Error('WxApp calling device details are unavailable');
|
|
183
277
|
}
|
|
184
278
|
|
|
@@ -186,16 +280,20 @@ export async function rejectOnWebex(
|
|
|
186
280
|
callId: details.deviceCallId,
|
|
187
281
|
lineOwnerId: resolveWxAppLineOwnerId(deps, options?.lineOwnerId),
|
|
188
282
|
});
|
|
283
|
+
logWxAppTelephonyAction({action: 'decline', phase: 'success', interactionId});
|
|
189
284
|
}
|
|
190
285
|
|
|
191
286
|
export async function toggleMuteOnWebex(
|
|
192
287
|
deps: WxAppVoiceDependencies,
|
|
193
288
|
options?: {lineOwnerId?: string; muted?: boolean}
|
|
194
289
|
): Promise<void> {
|
|
290
|
+
const interactionId = getInteractionId(deps);
|
|
291
|
+
logWxAppTelephonyAction({action: 'mute', phase: 'started', interactionId});
|
|
195
292
|
assertWxAppEnabled(deps);
|
|
196
293
|
|
|
197
294
|
const callId = getWebexCallingCallId(deps);
|
|
198
295
|
if (!callId) {
|
|
296
|
+
logWxAppValidationFailure('wxApp_call_id_unavailable', interactionId);
|
|
199
297
|
throw new Error('WxApp call ID is unavailable');
|
|
200
298
|
}
|
|
201
299
|
|
|
@@ -209,16 +307,20 @@ export async function toggleMuteOnWebex(
|
|
|
209
307
|
lineOwnerId: resolveWxAppLineOwnerId(deps, options?.lineOwnerId),
|
|
210
308
|
});
|
|
211
309
|
deps.setWxAppMuted(targetMuted);
|
|
310
|
+
logWxAppTelephonyAction({action: 'mute', phase: 'success', interactionId});
|
|
212
311
|
}
|
|
213
312
|
|
|
214
313
|
export async function transmitDtmfOnWebex(
|
|
215
314
|
deps: WxAppVoiceDependencies,
|
|
216
315
|
options: {dtmf: string; lineOwnerId?: string}
|
|
217
316
|
): Promise<void> {
|
|
317
|
+
const interactionId = getInteractionId(deps);
|
|
318
|
+
logWxAppTelephonyAction({action: 'dtmf', phase: 'started', interactionId});
|
|
218
319
|
assertWxAppEnabled(deps);
|
|
219
320
|
|
|
220
321
|
const callId = getWebexCallingCallId(deps);
|
|
221
322
|
if (!callId) {
|
|
323
|
+
logWxAppValidationFailure('wxApp_call_id_unavailable', interactionId);
|
|
222
324
|
throw new Error('WxApp call ID is unavailable');
|
|
223
325
|
}
|
|
224
326
|
|
|
@@ -226,7 +328,8 @@ export async function transmitDtmfOnWebex(
|
|
|
226
328
|
module: 'wxAppVoiceMethods',
|
|
227
329
|
method: METHODS.TRANSMIT_DTMF,
|
|
228
330
|
data: {
|
|
229
|
-
|
|
331
|
+
interactionId,
|
|
332
|
+
callIdSuffix: callId.length <= 8 ? callId : callId.slice(-8),
|
|
230
333
|
dtmfLength: options.dtmf.length,
|
|
231
334
|
hasLineOwnerId: Boolean(options.lineOwnerId),
|
|
232
335
|
},
|
|
@@ -237,6 +340,7 @@ export async function transmitDtmfOnWebex(
|
|
|
237
340
|
dtmf: options.dtmf,
|
|
238
341
|
lineOwnerId: resolveWxAppLineOwnerId(deps, options.lineOwnerId),
|
|
239
342
|
});
|
|
343
|
+
logWxAppTelephonyAction({action: 'dtmf', phase: 'success', interactionId});
|
|
240
344
|
}
|
|
241
345
|
|
|
242
346
|
export async function runWxAppAccept(
|
|
@@ -244,14 +348,32 @@ export async function runWxAppAccept(
|
|
|
244
348
|
lifecycle: WxAppVoiceLifecycle,
|
|
245
349
|
options?: {lineOwnerId?: string}
|
|
246
350
|
): Promise<void> {
|
|
351
|
+
const taskId = getInteractionId(deps);
|
|
352
|
+
|
|
353
|
+
deps.metricsManager.timeEvent([
|
|
354
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_ACCEPT_SUCCESS,
|
|
355
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_ACCEPT_FAILED,
|
|
356
|
+
]);
|
|
357
|
+
|
|
247
358
|
try {
|
|
248
359
|
lifecycle.setWxAppAcceptInFlight(true);
|
|
249
360
|
lifecycle.setWxAppAnswerPending(true);
|
|
250
361
|
await acceptOnWebex(deps, options);
|
|
251
362
|
lifecycle.resetWxAppMuted();
|
|
252
363
|
await lifecycle.syncWxAppMuteFromCallDetails();
|
|
364
|
+
|
|
365
|
+
deps.metricsManager.trackEvent(METRIC_EVENT_NAMES.WXAPP_TASK_ACCEPT_SUCCESS, {taskId}, [
|
|
366
|
+
'operational',
|
|
367
|
+
'behavioral',
|
|
368
|
+
]);
|
|
253
369
|
} catch (error) {
|
|
254
370
|
lifecycle.setWxAppAnswerPending(false);
|
|
371
|
+
logTelephonyFailure('accept', deps, error);
|
|
372
|
+
deps.metricsManager.trackEvent(
|
|
373
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_ACCEPT_FAILED,
|
|
374
|
+
getWxAppTelephonyMetricFailurePayload(deps, error),
|
|
375
|
+
['operational', 'behavioral']
|
|
376
|
+
);
|
|
255
377
|
lifecycle.mapWxAppVoiceError(error, METHODS.ACCEPT);
|
|
256
378
|
} finally {
|
|
257
379
|
lifecycle.setWxAppAcceptInFlight(false);
|
|
@@ -263,21 +385,89 @@ export async function runWxAppReject(
|
|
|
263
385
|
lifecycle: WxAppVoiceLifecycle,
|
|
264
386
|
options?: {lineOwnerId?: string}
|
|
265
387
|
): Promise<void> {
|
|
388
|
+
const taskId = getInteractionId(deps);
|
|
389
|
+
|
|
390
|
+
deps.metricsManager.timeEvent([
|
|
391
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_SUCCESS,
|
|
392
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_FAILED,
|
|
393
|
+
]);
|
|
394
|
+
|
|
266
395
|
try {
|
|
267
396
|
await rejectOnWebex(deps, options);
|
|
397
|
+
|
|
398
|
+
deps.metricsManager.trackEvent(METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_SUCCESS, {taskId}, [
|
|
399
|
+
'operational',
|
|
400
|
+
'behavioral',
|
|
401
|
+
]);
|
|
268
402
|
} catch (error) {
|
|
403
|
+
logTelephonyFailure('decline', deps, error);
|
|
404
|
+
deps.metricsManager.trackEvent(
|
|
405
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_FAILED,
|
|
406
|
+
getWxAppTelephonyMetricFailurePayload(deps, error),
|
|
407
|
+
['operational', 'behavioral']
|
|
408
|
+
);
|
|
269
409
|
lifecycle.mapWxAppVoiceError(error, METHODS.REJECT);
|
|
270
410
|
}
|
|
271
411
|
}
|
|
272
412
|
|
|
413
|
+
export async function runWxAppOutdialDecline<T>(
|
|
414
|
+
deps: WxAppVoiceDependencies,
|
|
415
|
+
executeCancel: () => Promise<T>
|
|
416
|
+
): Promise<T> {
|
|
417
|
+
const taskId = getInteractionId(deps);
|
|
418
|
+
|
|
419
|
+
deps.metricsManager.timeEvent([
|
|
420
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_SUCCESS,
|
|
421
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_FAILED,
|
|
422
|
+
]);
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
const result = await executeCancel();
|
|
426
|
+
|
|
427
|
+
deps.metricsManager.trackEvent(METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_SUCCESS, {taskId}, [
|
|
428
|
+
'operational',
|
|
429
|
+
'behavioral',
|
|
430
|
+
]);
|
|
431
|
+
|
|
432
|
+
return result;
|
|
433
|
+
} catch (error) {
|
|
434
|
+
logWxAppOutdialDeclineFailure(deps, error);
|
|
435
|
+
deps.metricsManager.trackEvent(
|
|
436
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DECLINE_FAILED,
|
|
437
|
+
getWxAppOutdialDeclineFailurePayload(deps, error),
|
|
438
|
+
['operational', 'behavioral']
|
|
439
|
+
);
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
273
444
|
export async function runWxAppToggleMute(
|
|
274
445
|
deps: WxAppVoiceDependencies,
|
|
275
446
|
lifecycle: WxAppVoiceLifecycle,
|
|
276
447
|
options?: TaskToggleMuteOptions
|
|
277
448
|
): Promise<void> {
|
|
449
|
+
const taskId = getInteractionId(deps);
|
|
450
|
+
|
|
451
|
+
deps.metricsManager.timeEvent([
|
|
452
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_MUTE_SUCCESS,
|
|
453
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_MUTE_FAILED,
|
|
454
|
+
]);
|
|
455
|
+
|
|
278
456
|
try {
|
|
279
457
|
await toggleMuteOnWebex(deps, options);
|
|
458
|
+
|
|
459
|
+
deps.metricsManager.trackEvent(
|
|
460
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_MUTE_SUCCESS,
|
|
461
|
+
{taskId, targetMuted: deps.getWxAppMuted()},
|
|
462
|
+
['operational', 'behavioral']
|
|
463
|
+
);
|
|
280
464
|
} catch (error) {
|
|
465
|
+
logTelephonyFailure('mute', deps, error);
|
|
466
|
+
deps.metricsManager.trackEvent(
|
|
467
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_MUTE_FAILED,
|
|
468
|
+
getWxAppTelephonyMetricFailurePayload(deps, error),
|
|
469
|
+
['operational', 'behavioral']
|
|
470
|
+
);
|
|
281
471
|
lifecycle.mapWxAppVoiceError(error, METHODS.TOGGLE_MUTE);
|
|
282
472
|
}
|
|
283
473
|
}
|
|
@@ -287,9 +477,28 @@ export async function runWxAppTransmitDtmf(
|
|
|
287
477
|
lifecycle: WxAppVoiceLifecycle,
|
|
288
478
|
options: TaskTransmitDtmfOptions
|
|
289
479
|
): Promise<void> {
|
|
480
|
+
const taskId = getInteractionId(deps);
|
|
481
|
+
|
|
482
|
+
deps.metricsManager.timeEvent([
|
|
483
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DTMF_SUCCESS,
|
|
484
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DTMF_FAILED,
|
|
485
|
+
]);
|
|
486
|
+
|
|
290
487
|
try {
|
|
291
488
|
await transmitDtmfOnWebex(deps, options);
|
|
489
|
+
|
|
490
|
+
deps.metricsManager.trackEvent(
|
|
491
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DTMF_SUCCESS,
|
|
492
|
+
{taskId, dtmfLength: options.dtmf.length},
|
|
493
|
+
['operational', 'behavioral']
|
|
494
|
+
);
|
|
292
495
|
} catch (error) {
|
|
496
|
+
logTelephonyFailure('dtmf', deps, error);
|
|
497
|
+
deps.metricsManager.trackEvent(
|
|
498
|
+
METRIC_EVENT_NAMES.WXAPP_TASK_DTMF_FAILED,
|
|
499
|
+
getWxAppTelephonyMetricFailurePayload(deps, error),
|
|
500
|
+
['operational', 'behavioral']
|
|
501
|
+
);
|
|
293
502
|
lifecycle.mapWxAppVoiceError(error, METHODS.TRANSMIT_DTMF);
|
|
294
503
|
}
|
|
295
504
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import LoggerProxy from '../logger-proxy';
|
|
2
|
+
|
|
3
|
+
export const WXAPP_LOG_FEATURE = 'wxApp';
|
|
4
|
+
export const WXAPP_LOG_PREFIX = '[CC wxApp]';
|
|
5
|
+
|
|
6
|
+
export type WxAppAcceptReason =
|
|
7
|
+
| 'wxApp_offer_ready'
|
|
8
|
+
| 'wxApp_answer_pending'
|
|
9
|
+
| 'wxApp_accept_in_flight'
|
|
10
|
+
| 'extension_non_wxApp_offer'
|
|
11
|
+
| 'browser_webrtc_offer'
|
|
12
|
+
| 'outdial_auto_answer';
|
|
13
|
+
|
|
14
|
+
export type WxAppSessionSkipReason =
|
|
15
|
+
| 'unsupported_browser_login'
|
|
16
|
+
| 'flag_disabled'
|
|
17
|
+
| 'mercury_subscribe_failed'
|
|
18
|
+
| 'publish_failed';
|
|
19
|
+
|
|
20
|
+
export type WxAppTelephonyAction = 'accept' | 'decline' | 'mute' | 'dtmf';
|
|
21
|
+
export type WxAppTelephonyPhase = 'started' | 'success' | 'failed';
|
|
22
|
+
|
|
23
|
+
type WxAppLogData = Record<string, string | boolean | number | undefined>;
|
|
24
|
+
|
|
25
|
+
/** Last 8 chars of callId for correlation without logging full id. */
|
|
26
|
+
export function callIdSuffix(callId: string | null | undefined): string | undefined {
|
|
27
|
+
if (!callId) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return callId.length <= 8 ? callId : callId.slice(-8);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function logWxApp(message: string, method: string, data: WxAppLogData): void {
|
|
35
|
+
LoggerProxy.info(`${WXAPP_LOG_PREFIX} ${message}`, {
|
|
36
|
+
module: 'wxAppDiagnosticLogging',
|
|
37
|
+
method,
|
|
38
|
+
data: {feature: WXAPP_LOG_FEATURE, ...data},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function logWxAppError(message: string, method: string, data: WxAppLogData): void {
|
|
43
|
+
LoggerProxy.error(`${WXAPP_LOG_PREFIX} ${message}`, {
|
|
44
|
+
module: 'wxAppDiagnosticLogging',
|
|
45
|
+
method,
|
|
46
|
+
data: {feature: WXAPP_LOG_FEATURE, ...data},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function logWxAppSessionReadiness(params: {
|
|
51
|
+
enableWxBetterTogether: boolean;
|
|
52
|
+
loginOption: string | undefined;
|
|
53
|
+
wxAppHooksApplied: boolean;
|
|
54
|
+
usersubPublished: boolean;
|
|
55
|
+
mercurySubscribed: boolean;
|
|
56
|
+
telephonyTaskType: 'Voice' | 'WebRTC' | 'unknown';
|
|
57
|
+
skipReason?: WxAppSessionSkipReason;
|
|
58
|
+
}): void {
|
|
59
|
+
logWxApp('session readiness', 'logWxAppSessionReadiness', {
|
|
60
|
+
event: 'session_readiness',
|
|
61
|
+
enableWxBetterTogether: params.enableWxBetterTogether,
|
|
62
|
+
loginOption: params.loginOption,
|
|
63
|
+
wxAppHooksApplied: params.wxAppHooksApplied,
|
|
64
|
+
usersubPublished: params.usersubPublished,
|
|
65
|
+
mercurySubscribed: params.mercurySubscribed,
|
|
66
|
+
telephonyTaskType: params.telephonyTaskType,
|
|
67
|
+
skipReason: params.skipReason,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function deriveWxAppAcceptReason(params: {
|
|
72
|
+
isWxAppInboundOffer: boolean;
|
|
73
|
+
isWxAppOutdialOffer: boolean;
|
|
74
|
+
isWebrtc: boolean;
|
|
75
|
+
isOutdial: boolean;
|
|
76
|
+
wxAppAcceptInFlight: boolean;
|
|
77
|
+
wxAppAnswerPending: boolean;
|
|
78
|
+
enableWxBetterTogether: boolean;
|
|
79
|
+
hasDeviceCallId: boolean;
|
|
80
|
+
}): WxAppAcceptReason {
|
|
81
|
+
if (params.isWxAppInboundOffer || params.isWxAppOutdialOffer) {
|
|
82
|
+
if (params.wxAppAcceptInFlight) {
|
|
83
|
+
return 'wxApp_accept_in_flight';
|
|
84
|
+
}
|
|
85
|
+
if (params.wxAppAnswerPending) {
|
|
86
|
+
return 'wxApp_answer_pending';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return 'wxApp_offer_ready';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (params.isWebrtc && !params.isOutdial) {
|
|
93
|
+
return 'browser_webrtc_offer';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (params.isOutdial) {
|
|
97
|
+
return 'outdial_auto_answer';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (params.enableWxBetterTogether && !params.hasDeviceCallId) {
|
|
101
|
+
return 'extension_non_wxApp_offer';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return 'extension_non_wxApp_offer';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function logWxAppOfferDecision(params: {
|
|
108
|
+
interactionId: string;
|
|
109
|
+
acceptVisible: boolean;
|
|
110
|
+
acceptEnabled: boolean;
|
|
111
|
+
acceptReason: WxAppAcceptReason;
|
|
112
|
+
wxAppParticipantDeviceType?: string;
|
|
113
|
+
hasDeviceCallId: boolean;
|
|
114
|
+
}): void {
|
|
115
|
+
logWxApp('offer decision', 'logWxAppOfferDecision', {
|
|
116
|
+
event: 'offer_decision',
|
|
117
|
+
interactionId: params.interactionId,
|
|
118
|
+
acceptVisible: params.acceptVisible,
|
|
119
|
+
acceptEnabled: params.acceptEnabled,
|
|
120
|
+
acceptReason: params.acceptReason,
|
|
121
|
+
wxAppParticipantDeviceType: params.wxAppParticipantDeviceType,
|
|
122
|
+
hasDeviceCallId: params.hasDeviceCallId,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function logWxAppTelephonyAction(params: {
|
|
127
|
+
action: WxAppTelephonyAction;
|
|
128
|
+
phase: WxAppTelephonyPhase;
|
|
129
|
+
interactionId?: string;
|
|
130
|
+
trackingId?: string;
|
|
131
|
+
httpStatus?: number | string;
|
|
132
|
+
failureReason?: string;
|
|
133
|
+
}): void {
|
|
134
|
+
const payload: WxAppLogData = {
|
|
135
|
+
event: 'telephony_action',
|
|
136
|
+
action: params.action,
|
|
137
|
+
phase: params.phase,
|
|
138
|
+
interactionId: params.interactionId,
|
|
139
|
+
trackingId: params.trackingId,
|
|
140
|
+
httpStatus: params.httpStatus,
|
|
141
|
+
failureReason: params.failureReason,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (params.phase === 'failed') {
|
|
145
|
+
logWxAppError(`telephony ${params.action} ${params.phase}`, 'logWxAppTelephonyAction', payload);
|
|
146
|
+
} else {
|
|
147
|
+
logWxApp(`telephony ${params.action} ${params.phase}`, 'logWxAppTelephonyAction', payload);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function logWxAppValidationFailure(reason: string, interactionId?: string): void {
|
|
152
|
+
logWxAppError('validation failed', 'logWxAppValidationFailure', {
|
|
153
|
+
event: 'validation_failed',
|
|
154
|
+
reason,
|
|
155
|
+
interactionId,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function logWxAppMercuryMuteSync(params: {
|
|
160
|
+
phase: 'received' | 'applied' | 'dropped';
|
|
161
|
+
muted?: boolean;
|
|
162
|
+
callIdSuffix?: string;
|
|
163
|
+
interactionId?: string;
|
|
164
|
+
dropReason?: string;
|
|
165
|
+
}): void {
|
|
166
|
+
logWxApp(`mercury mute sync ${params.phase}`, 'logWxAppMercuryMuteSync', {
|
|
167
|
+
event: 'mercury_mute_sync',
|
|
168
|
+
phase: params.phase,
|
|
169
|
+
muted: params.muted,
|
|
170
|
+
callIdSuffix: params.callIdSuffix,
|
|
171
|
+
interactionId: params.interactionId,
|
|
172
|
+
dropReason: params.dropReason,
|
|
173
|
+
});
|
|
174
|
+
}
|