@webex/contact-center 3.12.0-llmrefactor.13 → 3.12.0-llmrefactor.15
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 +7 -1
- package/ai-docs/contact-center-spec.md +7 -7
- package/ai-docs/features/contact-owner-change-refactor.md +78 -0
- package/dist/cc.js +39 -7
- package/dist/cc.js.map +1 -1
- package/dist/metrics/behavioral-events.js +6 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +1 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +4 -4
- package/dist/services/AnswerCallOnWebexService.js.map +1 -1
- package/dist/services/WebexCrossClientService.js +5 -2
- package/dist/services/WebexCrossClientService.js.map +1 -1
- package/dist/services/WxAppTelephonyMercurySync.js +6 -3
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/task/TaskFactory.js +1 -0
- package/dist/services/task/TaskFactory.js.map +1 -1
- package/dist/services/task/TaskManager.js +50 -58
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +63 -44
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
- package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js +52 -11
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
- package/dist/services/wxAppDiagnosticLogging.js +50 -13
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -1
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/services/config/types.d.ts +2 -0
- package/dist/types/services/task/types.d.ts +2 -0
- package/dist/types/services/task/voice/Voice.d.ts +15 -2
- package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +19 -6
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +12 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +9 -9
- package/src/cc.ts +43 -7
- package/src/metrics/ai-docs/metrics-spec.md +44 -2
- package/src/metrics/behavioral-events.ts +6 -0
- package/src/metrics/constants.ts +1 -0
- package/src/services/AnswerCallOnWebexService.ts +4 -4
- package/src/services/WebexCrossClientService.ts +7 -2
- package/src/services/WxAppTelephonyMercurySync.ts +3 -3
- package/src/services/config/types.ts +2 -0
- package/src/services/task/TaskFactory.ts +1 -0
- package/src/services/task/TaskManager.ts +85 -87
- package/src/services/task/ai-docs/task-spec.md +7 -3
- package/src/services/task/types.ts +16 -0
- package/src/services/task/voice/Voice.ts +74 -52
- package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +83 -24
- package/src/services/wxAppDiagnosticLogging.ts +135 -23
- package/src/types.ts +2 -0
- package/test/unit/spec/cc.ts +201 -19
- package/test/unit/spec/services/WebexCrossClientService.ts +45 -3
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +1 -1
- package/test/unit/spec/services/task/TaskManager.ts +214 -3
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +33 -0
- package/test/unit/spec/services/task/voice/Voice.ts +10 -51
- package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +121 -5
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +44 -8
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -2927,7 +2927,7 @@ describe('TaskManager', () => {
|
|
|
2927
2927
|
expect(hydrateHandler).toHaveBeenCalledTimes(1);
|
|
2928
2928
|
});
|
|
2929
2929
|
|
|
2930
|
-
it('reuses a task
|
|
2930
|
+
it('reuses a task identified by the incoming main-call media key', () => {
|
|
2931
2931
|
const mainMediaInteractionId = 'main-media-interaction-id';
|
|
2932
2932
|
const promotedChildId = 'promoted-child-without-main-id';
|
|
2933
2933
|
const task = installTask(
|
|
@@ -2958,6 +2958,41 @@ describe('TaskManager', () => {
|
|
|
2958
2958
|
expect(hydrateHandler).toHaveBeenCalledWith(task);
|
|
2959
2959
|
});
|
|
2960
2960
|
|
|
2961
|
+
it('reuses a stale task through its collection key and the incoming main-call media key', () => {
|
|
2962
|
+
const mainMediaInteractionId = 'collection-main-interaction-id';
|
|
2963
|
+
const staleChildId = 'stale-collection-child-id';
|
|
2964
|
+
const promotedChildId = 'promoted-collection-child-id';
|
|
2965
|
+
const staleTaskData = createConferenceTaskData(
|
|
2966
|
+
staleChildId,
|
|
2967
|
+
previousOwnerId,
|
|
2968
|
+
staleChildId
|
|
2969
|
+
);
|
|
2970
|
+
staleTaskData.interaction.media[staleChildId].mType = 'consult';
|
|
2971
|
+
const task = createMockTask(staleTaskData);
|
|
2972
|
+
taskManager.taskCollection = {[mainMediaInteractionId]: task};
|
|
2973
|
+
(taskManager as any).setupTaskListeners(task);
|
|
2974
|
+
const hydrateHandler = jest.fn();
|
|
2975
|
+
taskManager.on(TASK_EVENTS.TASK_HYDRATE, hydrateHandler);
|
|
2976
|
+
const ownerChangeData = createPromotedAgentOwnerChangeData(
|
|
2977
|
+
promotedChildId,
|
|
2978
|
+
mainMediaInteractionId
|
|
2979
|
+
);
|
|
2980
|
+
delete ownerChangeData.interaction.mainInteractionId;
|
|
2981
|
+
|
|
2982
|
+
webSocketManagerMock.emit('message', JSON.stringify({data: ownerChangeData}));
|
|
2983
|
+
|
|
2984
|
+
expectLastStateMachineEvent(task.sendStateMachineEvent, TaskEvent.CONTACT_OWNER_CHANGED);
|
|
2985
|
+
expect(task.data.interactionId).toBe(mainMediaInteractionId);
|
|
2986
|
+
expect(task.data.interaction.owner).toBe(currentAgentId);
|
|
2987
|
+
expect(TaskFactory.createTask).not.toHaveBeenCalled();
|
|
2988
|
+
expect(taskManager.taskCollection[mainMediaInteractionId]).toBe(task);
|
|
2989
|
+
expect(taskManager.taskCollection[staleChildId]).toBeUndefined();
|
|
2990
|
+
expect(taskManager.taskCollection[promotedChildId]).toBeUndefined();
|
|
2991
|
+
expect(Object.values(taskManager.taskCollection)).toEqual([task]);
|
|
2992
|
+
expect(task.listenerCount(TASK_EVENTS.TASK_HYDRATE)).toBe(1);
|
|
2993
|
+
expect(hydrateHandler).toHaveBeenCalledTimes(1);
|
|
2994
|
+
});
|
|
2995
|
+
|
|
2961
2996
|
it('prefers the exact task for ContactOwnerChanged over related child tasks', () => {
|
|
2962
2997
|
const exactTask = installTask(createConferenceTaskData(taskId, previousOwnerId));
|
|
2963
2998
|
const childTaskId = 'related-child-id';
|
|
@@ -2980,6 +3015,102 @@ describe('TaskManager', () => {
|
|
|
2980
3015
|
expect(childTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
2981
3016
|
});
|
|
2982
3017
|
|
|
3018
|
+
it('does not replace a distinct main task when an exact child task also exists', () => {
|
|
3019
|
+
const childTaskId = 'exact-child-id';
|
|
3020
|
+
const mainTask = installTask(createConferenceTaskData(taskId, previousOwnerId));
|
|
3021
|
+
const childTask = createMockTask(
|
|
3022
|
+
createConferenceTaskData(childTaskId, previousOwnerId, taskId)
|
|
3023
|
+
);
|
|
3024
|
+
taskManager.taskCollection[childTaskId] = childTask;
|
|
3025
|
+
|
|
3026
|
+
webSocketManagerMock.emit(
|
|
3027
|
+
'message',
|
|
3028
|
+
JSON.stringify({
|
|
3029
|
+
data: {
|
|
3030
|
+
...createConferenceTaskData(childTaskId, promotedOwnerId, taskId),
|
|
3031
|
+
type: CC_EVENTS.CONTACT_OWNER_CHANGED,
|
|
3032
|
+
},
|
|
3033
|
+
})
|
|
3034
|
+
);
|
|
3035
|
+
|
|
3036
|
+
expect(childTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3037
|
+
expect(mainTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3038
|
+
expect(taskManager.taskCollection[taskId]).toBe(mainTask);
|
|
3039
|
+
expect(taskManager.taskCollection[childTaskId]).toBe(childTask);
|
|
3040
|
+
});
|
|
3041
|
+
|
|
3042
|
+
it('ignores a related owner change with conflicting explicit main interaction IDs', () => {
|
|
3043
|
+
const childTaskId = 'conflicting-main-child-id';
|
|
3044
|
+
const existingMainId = 'existing-main-id';
|
|
3045
|
+
const incomingMainId = 'incoming-main-id';
|
|
3046
|
+
const sharedParentId = 'shared-parent-id';
|
|
3047
|
+
const taskData = createConferenceTaskData(
|
|
3048
|
+
childTaskId,
|
|
3049
|
+
previousOwnerId,
|
|
3050
|
+
existingMainId
|
|
3051
|
+
);
|
|
3052
|
+
taskData.interaction.parentInteractionId = sharedParentId;
|
|
3053
|
+
const task = installTask(taskData);
|
|
3054
|
+
const ownerChangeData = createConferenceTaskData(
|
|
3055
|
+
'different-child-id',
|
|
3056
|
+
promotedOwnerId,
|
|
3057
|
+
incomingMainId
|
|
3058
|
+
);
|
|
3059
|
+
ownerChangeData.interaction.parentInteractionId = sharedParentId;
|
|
3060
|
+
|
|
3061
|
+
webSocketManagerMock.emit(
|
|
3062
|
+
'message',
|
|
3063
|
+
JSON.stringify({
|
|
3064
|
+
data: {...ownerChangeData, type: CC_EVENTS.CONTACT_OWNER_CHANGED},
|
|
3065
|
+
})
|
|
3066
|
+
);
|
|
3067
|
+
|
|
3068
|
+
expect(task.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3069
|
+
expect(taskManager.taskCollection[childTaskId]).toBe(task);
|
|
3070
|
+
expect(taskManager.taskCollection[existingMainId]).toBeUndefined();
|
|
3071
|
+
expect(taskManager.taskCollection[incomingMainId]).toBeUndefined();
|
|
3072
|
+
expect(TaskFactory.createTask).not.toHaveBeenCalled();
|
|
3073
|
+
});
|
|
3074
|
+
|
|
3075
|
+
it('does not replace a distinct task already stored at the canonical main key', () => {
|
|
3076
|
+
const childTaskId = 'related-active-child-id';
|
|
3077
|
+
const canonicalMainId = 'occupied-canonical-main-id';
|
|
3078
|
+
const candidate = createMockTask(
|
|
3079
|
+
createConferenceTaskData(childTaskId, previousOwnerId, canonicalMainId)
|
|
3080
|
+
);
|
|
3081
|
+
const occupiedData = createConferenceTaskData(
|
|
3082
|
+
'occupied-task-child-id',
|
|
3083
|
+
previousOwnerId,
|
|
3084
|
+
canonicalMainId
|
|
3085
|
+
);
|
|
3086
|
+
occupiedData.interaction.media[canonicalMainId].mType = 'consult';
|
|
3087
|
+
const occupiedTask = createMockTask(occupiedData);
|
|
3088
|
+
taskManager.taskCollection = {
|
|
3089
|
+
[childTaskId]: candidate,
|
|
3090
|
+
[canonicalMainId]: occupiedTask,
|
|
3091
|
+
};
|
|
3092
|
+
|
|
3093
|
+
webSocketManagerMock.emit(
|
|
3094
|
+
'message',
|
|
3095
|
+
JSON.stringify({
|
|
3096
|
+
data: {
|
|
3097
|
+
...createConferenceTaskData(
|
|
3098
|
+
'incoming-related-child-id',
|
|
3099
|
+
promotedOwnerId,
|
|
3100
|
+
canonicalMainId
|
|
3101
|
+
),
|
|
3102
|
+
type: CC_EVENTS.CONTACT_OWNER_CHANGED,
|
|
3103
|
+
},
|
|
3104
|
+
})
|
|
3105
|
+
);
|
|
3106
|
+
|
|
3107
|
+
expect(candidate.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3108
|
+
expect(occupiedTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3109
|
+
expect(taskManager.taskCollection[childTaskId]).toBe(candidate);
|
|
3110
|
+
expect(taskManager.taskCollection[canonicalMainId]).toBe(occupiedTask);
|
|
3111
|
+
expect(TaskFactory.createTask).not.toHaveBeenCalled();
|
|
3112
|
+
});
|
|
3113
|
+
|
|
2983
3114
|
it('ignores a related ContactOwnerChanged task whose current agent is consult-only', () => {
|
|
2984
3115
|
const childTaskId = 'consult-only-child-id';
|
|
2985
3116
|
const data = createConferenceTaskData(childTaskId, previousOwnerId, taskId);
|
|
@@ -3066,7 +3197,43 @@ describe('TaskManager', () => {
|
|
|
3066
3197
|
expect(secondTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3067
3198
|
});
|
|
3068
3199
|
|
|
3069
|
-
it('
|
|
3200
|
+
it('ignores ambiguous main-call media-key matches without creating a task', () => {
|
|
3201
|
+
const mainMediaInteractionId = 'ambiguous-main-media-id';
|
|
3202
|
+
const createMediaRelatedTask = (interactionId: string) => {
|
|
3203
|
+
const data = createConferenceTaskData(interactionId, previousOwnerId, interactionId);
|
|
3204
|
+
data.interaction.media = {
|
|
3205
|
+
[mainMediaInteractionId]: {
|
|
3206
|
+
mediaResourceId: mainMediaInteractionId,
|
|
3207
|
+
mediaType: 'telephony',
|
|
3208
|
+
mType: 'mainCall',
|
|
3209
|
+
participants: [currentAgentId, previousOwnerId, promotedOwnerId],
|
|
3210
|
+
},
|
|
3211
|
+
};
|
|
3212
|
+
|
|
3213
|
+
return createMockTask(data);
|
|
3214
|
+
};
|
|
3215
|
+
const firstTask = createMediaRelatedTask('first-media-child-id');
|
|
3216
|
+
const secondTask = createMediaRelatedTask('second-media-child-id');
|
|
3217
|
+
taskManager.taskCollection = {
|
|
3218
|
+
'first-media-child-id': firstTask,
|
|
3219
|
+
'second-media-child-id': secondTask,
|
|
3220
|
+
};
|
|
3221
|
+
(TaskFactory.createTask as jest.Mock).mockClear();
|
|
3222
|
+
const ownerChangeData = createPromotedAgentOwnerChangeData(
|
|
3223
|
+
'promoted-media-child-id',
|
|
3224
|
+
mainMediaInteractionId
|
|
3225
|
+
);
|
|
3226
|
+
delete ownerChangeData.interaction.mainInteractionId;
|
|
3227
|
+
|
|
3228
|
+
webSocketManagerMock.emit('message', JSON.stringify({data: ownerChangeData}));
|
|
3229
|
+
|
|
3230
|
+
expect(firstTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3231
|
+
expect(secondTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
3232
|
+
expect(TaskFactory.createTask).not.toHaveBeenCalled();
|
|
3233
|
+
expect(Object.values(taskManager.taskCollection)).toEqual([firstTask, secondTask]);
|
|
3234
|
+
});
|
|
3235
|
+
|
|
3236
|
+
it('does not recover a new task when multiple stale related tasks exist', () => {
|
|
3070
3237
|
const createStaleRelatedTask = (interactionId: string) => {
|
|
3071
3238
|
const data = createConferenceTaskData(interactionId, previousOwnerId, taskId);
|
|
3072
3239
|
data.interaction.media[taskId].participants = [previousOwnerId, promotedOwnerId];
|
|
@@ -3178,7 +3345,10 @@ describe('TaskManager', () => {
|
|
|
3178
3345
|
expect.objectContaining({
|
|
3179
3346
|
type: TaskEvent.HYDRATE,
|
|
3180
3347
|
agentId: currentAgentId,
|
|
3181
|
-
taskData: expect.objectContaining({
|
|
3348
|
+
taskData: expect.objectContaining({
|
|
3349
|
+
interactionId: taskId,
|
|
3350
|
+
interaction: expect.objectContaining({state: 'conference'}),
|
|
3351
|
+
}),
|
|
3182
3352
|
})
|
|
3183
3353
|
);
|
|
3184
3354
|
expect(recoveredTask.sendStateMachineEvent).toHaveBeenNthCalledWith(
|
|
@@ -3192,6 +3362,7 @@ describe('TaskManager', () => {
|
|
|
3192
3362
|
expect(taskManager.taskCollection['promoted-child-interaction-id']).toBeUndefined();
|
|
3193
3363
|
expect(Object.values(taskManager.taskCollection)).toEqual([recoveredTask]);
|
|
3194
3364
|
expect(recoveredTask.data.interaction.owner).toBe(currentAgentId);
|
|
3365
|
+
expect(recoveredTask.listenerCount(TASK_EVENTS.TASK_HYDRATE)).toBe(1);
|
|
3195
3366
|
expect(hydrateHandler).toHaveBeenCalledTimes(1);
|
|
3196
3367
|
expect(hydrateHandler).toHaveBeenCalledWith(recoveredTask);
|
|
3197
3368
|
expect(incomingHandler).not.toHaveBeenCalled();
|
|
@@ -3217,6 +3388,21 @@ describe('TaskManager', () => {
|
|
|
3217
3388
|
data.interaction.isTerminated = true;
|
|
3218
3389
|
},
|
|
3219
3390
|
],
|
|
3391
|
+
[
|
|
3392
|
+
'the interaction is not telephony',
|
|
3393
|
+
(data) => {
|
|
3394
|
+
data.interaction.mediaType = 'chat';
|
|
3395
|
+
},
|
|
3396
|
+
],
|
|
3397
|
+
[
|
|
3398
|
+
'the payload has no stable interaction identifier',
|
|
3399
|
+
(data) => {
|
|
3400
|
+
data.interactionId = undefined;
|
|
3401
|
+
data.interaction.interactionId = undefined;
|
|
3402
|
+
delete data.interaction.mainInteractionId;
|
|
3403
|
+
data.interaction.media = {};
|
|
3404
|
+
},
|
|
3405
|
+
],
|
|
3220
3406
|
])('does not create a missing task when %s', (_description, mutatePayload) => {
|
|
3221
3407
|
clearTasksAndFactoryHistory();
|
|
3222
3408
|
const ownerChangeData = createPromotedAgentOwnerChangeData();
|
|
@@ -3626,6 +3812,15 @@ describe('TaskManager', () => {
|
|
|
3626
3812
|
expect(taskManager.taskCollection['task-alias']).toBeUndefined();
|
|
3627
3813
|
});
|
|
3628
3814
|
|
|
3815
|
+
it('disposes wxApp offer observability when removing a task from the collection', () => {
|
|
3816
|
+
const disposeWxAppOfferObservability = jest.fn();
|
|
3817
|
+
task.disposeWxAppOfferObservability = disposeWxAppOfferObservability;
|
|
3818
|
+
|
|
3819
|
+
(taskManager as any).removeTaskFromCollection(task);
|
|
3820
|
+
|
|
3821
|
+
expect(disposeWxAppOfferObservability).toHaveBeenCalledTimes(1);
|
|
3822
|
+
});
|
|
3823
|
+
|
|
3629
3824
|
it('replaces an EP-DN child task with a hydrated main task on CONTACT_MERGED', () => {
|
|
3630
3825
|
const childTaskId = 'ep-dn-child-interaction-id';
|
|
3631
3826
|
const childTask = createMockTask({
|
|
@@ -4186,4 +4381,20 @@ describe('TaskManager', () => {
|
|
|
4186
4381
|
expect(taskTwo.applyWxAppMuteStateFromSync).toHaveBeenCalledWith('call-1', true);
|
|
4187
4382
|
});
|
|
4188
4383
|
});
|
|
4384
|
+
|
|
4385
|
+
describe('refreshWxAppOfferObservabilityForAllTasks', () => {
|
|
4386
|
+
it('invokes refresh on collected tasks that implement it', () => {
|
|
4387
|
+
const taskOne = {refreshWxAppOfferObservability: jest.fn()};
|
|
4388
|
+
const taskTwo = {};
|
|
4389
|
+
|
|
4390
|
+
taskManager['taskCollection'] = {
|
|
4391
|
+
[taskId]: taskOne,
|
|
4392
|
+
'task-2': taskTwo,
|
|
4393
|
+
};
|
|
4394
|
+
|
|
4395
|
+
taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
4396
|
+
|
|
4397
|
+
expect(taskOne.refreshWxAppOfferObservability).toHaveBeenCalledTimes(1);
|
|
4398
|
+
});
|
|
4399
|
+
});
|
|
4189
4400
|
});
|
|
@@ -539,6 +539,39 @@ describe('Task state machine', () => {
|
|
|
539
539
|
} as any,
|
|
540
540
|
});
|
|
541
541
|
|
|
542
|
+
it('hydrates a recovered active conference from IDLE to CONFERENCING', () => {
|
|
543
|
+
const service = startMachine();
|
|
544
|
+
const taskData = createTaskData({
|
|
545
|
+
agentId: 'agent-1',
|
|
546
|
+
interactionId: 'interaction-1',
|
|
547
|
+
mediaResourceId: 'interaction-1',
|
|
548
|
+
interaction: {
|
|
549
|
+
state: 'conference',
|
|
550
|
+
interactionId: 'interaction-1',
|
|
551
|
+
mainInteractionId: 'interaction-1',
|
|
552
|
+
owner: 'agent-1',
|
|
553
|
+
participants: {
|
|
554
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', hasLeft: false},
|
|
555
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', hasLeft: false},
|
|
556
|
+
'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
|
|
557
|
+
},
|
|
558
|
+
media: {
|
|
559
|
+
'interaction-1': {
|
|
560
|
+
mediaResourceId: 'interaction-1',
|
|
561
|
+
mType: 'mainCall',
|
|
562
|
+
participants: ['agent-1', 'agent-2', 'customer-1'],
|
|
563
|
+
isHold: false,
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
} as any,
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
service.send({type: TaskEvent.HYDRATE, taskData});
|
|
570
|
+
|
|
571
|
+
expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
|
|
572
|
+
expect(service.getSnapshot().context.taskData).toEqual(taskData);
|
|
573
|
+
});
|
|
574
|
+
|
|
542
575
|
it('boots from IDLE to CONSULTING on CONSULTING_ACTIVE for split-leg ordering', () => {
|
|
543
576
|
const service = startMachine();
|
|
544
577
|
const taskData = createTaskData({
|
|
@@ -1006,69 +1006,28 @@ describe('Voice Task', () => {
|
|
|
1006
1006
|
});
|
|
1007
1007
|
});
|
|
1008
1008
|
|
|
1009
|
-
describe('wxApp offer
|
|
1010
|
-
|
|
1011
|
-
deviceType: 'wxApp',
|
|
1012
|
-
deviceId: 'device-id-1',
|
|
1013
|
-
deviceCallId: 'call-id-1',
|
|
1014
|
-
};
|
|
1015
|
-
|
|
1016
|
-
const makeWxAppOfferTaskData = () =>
|
|
1017
|
-
createBaseData({
|
|
1018
|
-
agentId: 'agent-1',
|
|
1019
|
-
interaction: {
|
|
1020
|
-
participants: {
|
|
1021
|
-
'agent-1': {id: 'agent-1', ...wxAppParticipant},
|
|
1022
|
-
},
|
|
1023
|
-
} as any,
|
|
1024
|
-
});
|
|
1025
|
-
|
|
1026
|
-
it('logs evolving accept reasons through the normal wxApp accept flow', () => {
|
|
1027
|
-
const logSpy = jest.spyOn(wxAppDiagnosticLogging, 'logWxAppOfferDecision');
|
|
1028
|
-
const taskData = makeWxAppOfferTaskData();
|
|
1029
|
-
const voice = new Voice(dummyContact, taskData, {enableWxBetterTogether: true});
|
|
1030
|
-
|
|
1031
|
-
voice.stateMachineService?.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
1032
|
-
|
|
1033
|
-
expect(logSpy).toHaveBeenCalledWith(
|
|
1034
|
-
expect.objectContaining({acceptReason: 'wxApp_offer_ready'})
|
|
1035
|
-
);
|
|
1036
|
-
|
|
1037
|
-
logSpy.mockClear();
|
|
1038
|
-
voice['setWxAppAcceptInFlight'](true);
|
|
1039
|
-
|
|
1040
|
-
expect(logSpy).toHaveBeenCalledWith(
|
|
1041
|
-
expect.objectContaining({acceptReason: 'wxApp_accept_in_flight'})
|
|
1042
|
-
);
|
|
1043
|
-
|
|
1044
|
-
logSpy.mockClear();
|
|
1045
|
-
voice['setWxAppAcceptInFlight'](false);
|
|
1046
|
-
voice['setWxAppAnswerPending'](true);
|
|
1047
|
-
|
|
1048
|
-
expect(logSpy).toHaveBeenCalledWith(
|
|
1049
|
-
expect.objectContaining({acceptReason: 'wxApp_answer_pending'})
|
|
1050
|
-
);
|
|
1051
|
-
});
|
|
1052
|
-
|
|
1053
|
-
it('logs browser_webrtc_offer when voice variant is WebRTC on a non-wxApp inbound offer', () => {
|
|
1009
|
+
describe('wxApp offer observability integration', () => {
|
|
1010
|
+
it('delegates OFFERED ui control updates to wxApp offer observability', () => {
|
|
1054
1011
|
const logSpy = jest.spyOn(wxAppDiagnosticLogging, 'logWxAppOfferDecision');
|
|
1055
1012
|
const taskData = createBaseData({
|
|
1056
1013
|
agentId: 'agent-1',
|
|
1057
1014
|
interaction: {
|
|
1058
1015
|
participants: {
|
|
1059
|
-
'agent-1': {
|
|
1016
|
+
'agent-1': {
|
|
1017
|
+
id: 'agent-1',
|
|
1018
|
+
deviceType: 'wxApp',
|
|
1019
|
+
deviceId: 'device-id-1',
|
|
1020
|
+
deviceCallId: 'call-id-1',
|
|
1021
|
+
},
|
|
1060
1022
|
},
|
|
1061
1023
|
} as any,
|
|
1062
1024
|
});
|
|
1063
|
-
const voice = new Voice(dummyContact, taskData, {
|
|
1064
|
-
enableWxBetterTogether: true,
|
|
1065
|
-
voiceVariant: VOICE_VARIANT.WEBRTC,
|
|
1066
|
-
});
|
|
1025
|
+
const voice = new Voice(dummyContact, taskData, {enableWxBetterTogether: true});
|
|
1067
1026
|
|
|
1068
1027
|
voice.stateMachineService?.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
1069
1028
|
|
|
1070
1029
|
expect(logSpy).toHaveBeenCalledWith(
|
|
1071
|
-
expect.objectContaining({acceptReason: '
|
|
1030
|
+
expect.objectContaining({acceptReason: 'wxApp_offer_ready'})
|
|
1072
1031
|
);
|
|
1073
1032
|
});
|
|
1074
1033
|
});
|