@webex/contact-center 3.11.0 → 3.12.0-mobius-socket.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +121 -28
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +13 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +9 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +173 -0
- package/dist/services/ApiAiAssistant.js.map +1 -0
- package/dist/services/agent/types.js.map +1 -1
- package/dist/services/config/Util.js +6 -2
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +12 -0
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +41 -2
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +19 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +27 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +28 -6
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +20 -5
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/connection-service.js +3 -1
- package/dist/services/core/websocket/connection-service.js.map +1 -1
- package/dist/services/index.js +6 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/TaskManager.js +117 -24
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +16 -3
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +15 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +51 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/types.js +15 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +801 -0
- package/dist/types/config.d.ts +66 -0
- package/dist/types/constants.d.ts +50 -0
- package/dist/types/index.d.ts +184 -0
- package/dist/types/logger-proxy.d.ts +71 -0
- package/dist/types/metrics/MetricsManager.d.ts +223 -0
- package/dist/types/metrics/behavioral-events.d.ts +29 -0
- package/dist/types/metrics/constants.d.ts +161 -0
- package/dist/types/services/AddressBook.d.ts +74 -0
- package/dist/types/services/ApiAiAssistant.d.ts +31 -0
- package/dist/types/services/EntryPoint.d.ts +67 -0
- package/dist/types/services/Queue.d.ts +76 -0
- package/dist/types/services/WebCallingService.d.ts +1 -0
- package/dist/types/services/agent/index.d.ts +46 -0
- package/dist/types/services/agent/types.d.ts +413 -0
- package/dist/types/services/config/Util.d.ts +20 -0
- package/dist/types/services/config/constants.d.ts +249 -0
- package/dist/types/services/config/index.d.ts +177 -0
- package/dist/types/services/config/types.d.ts +1207 -0
- package/dist/types/services/constants.d.ts +110 -0
- package/dist/types/services/core/Err.d.ts +121 -0
- package/dist/types/services/core/GlobalTypes.d.ts +58 -0
- package/dist/types/services/core/Utils.d.ts +101 -0
- package/dist/types/services/core/WebexRequest.d.ts +22 -0
- package/dist/types/services/core/aqm-reqs.d.ts +65 -0
- package/dist/types/services/core/constants.d.ts +99 -0
- package/dist/types/services/core/types.d.ts +47 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +35 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
- package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
- package/dist/types/services/core/websocket/types.d.ts +37 -0
- package/dist/types/services/index.d.ts +54 -0
- package/dist/types/services/task/AutoWrapup.d.ts +40 -0
- package/dist/types/services/task/TaskManager.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +92 -0
- package/dist/types/services/task/constants.d.ts +84 -0
- package/dist/types/services/task/contact.d.ts +69 -0
- package/dist/types/services/task/dialer.d.ts +43 -0
- package/dist/types/services/task/index.d.ts +650 -0
- package/dist/types/services/task/types.d.ts +1319 -0
- package/dist/types/types.d.ts +643 -0
- package/dist/types/utils/PageCache.d.ts +173 -0
- package/dist/types/webex-config.d.ts +53 -0
- package/dist/types/webex.d.ts +7 -0
- package/dist/types.js +14 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +9 -9
- package/src/cc.ts +157 -30
- package/src/constants.ts +4 -0
- package/src/index.ts +1 -0
- package/src/metrics/behavioral-events.ts +14 -0
- package/src/metrics/constants.ts +11 -0
- package/src/services/ApiAiAssistant.ts +217 -0
- package/src/services/agent/types.ts +1 -1
- package/src/services/config/Util.ts +8 -0
- package/src/services/config/constants.ts +12 -0
- package/src/services/config/index.ts +45 -1
- package/src/services/config/types.ts +67 -0
- package/src/services/constants.ts +29 -0
- package/src/services/core/Err.ts +1 -0
- package/src/services/core/Utils.ts +32 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +21 -6
- package/src/services/core/websocket/connection-service.ts +5 -1
- package/src/services/index.ts +4 -0
- package/src/services/task/TaskManager.ts +174 -27
- package/src/services/task/TaskUtils.ts +12 -0
- package/src/services/task/constants.ts +16 -0
- package/src/services/task/dialer.ts +56 -1
- package/src/services/task/types.ts +24 -0
- package/src/types.ts +40 -1
- package/test/unit/spec/cc.ts +163 -23
- package/test/unit/spec/services/ApiAiAssistant.ts +115 -0
- package/test/unit/spec/services/config/index.ts +56 -0
- package/test/unit/spec/services/core/Utils.ts +63 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +82 -12
- package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
- package/test/unit/spec/services/task/TaskManager.ts +1119 -251
- package/test/unit/spec/services/task/dialer.ts +198 -112
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/src/cc.ts
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
METHODS,
|
|
40
40
|
} from './constants';
|
|
41
41
|
import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
|
|
42
|
-
import {AGENT, WEB_RTC_PREFIX} from './services/constants';
|
|
42
|
+
import {AGENT, RTD_SUBSCRIBE_API, SUBSCRIBE_API, WEB_RTC_PREFIX} from './services/constants';
|
|
43
43
|
import Services from './services';
|
|
44
44
|
import WebexRequest from './services/core/WebexRequest';
|
|
45
45
|
import LoggerProxy from './logger-proxy';
|
|
@@ -55,13 +55,20 @@ import {
|
|
|
55
55
|
import {ConnectionLostDetails} from './services/core/websocket/types';
|
|
56
56
|
import TaskManager from './services/task/TaskManager';
|
|
57
57
|
import WebCallingService from './services/WebCallingService';
|
|
58
|
-
import {
|
|
58
|
+
import {
|
|
59
|
+
ITask,
|
|
60
|
+
TASK_EVENTS,
|
|
61
|
+
TaskResponse,
|
|
62
|
+
DialerPayload,
|
|
63
|
+
PreviewContactPayload,
|
|
64
|
+
} from './services/task/types';
|
|
59
65
|
import MetricsManager from './metrics/MetricsManager';
|
|
60
66
|
import {METRIC_EVENT_NAMES} from './metrics/constants';
|
|
61
67
|
import {Failure} from './services/core/GlobalTypes';
|
|
62
|
-
import EntryPoint from './services/EntryPoint';
|
|
63
|
-
import AddressBook from './services/AddressBook';
|
|
64
|
-
import Queue from './services/Queue';
|
|
68
|
+
import {EntryPoint} from './services/EntryPoint';
|
|
69
|
+
import {AddressBook} from './services/AddressBook';
|
|
70
|
+
import {Queue} from './services/Queue';
|
|
71
|
+
import {ApiAIAssistant} from './services/ApiAiAssistant';
|
|
65
72
|
import type {
|
|
66
73
|
EntryPointListResponse,
|
|
67
74
|
EntryPointSearchParams,
|
|
@@ -119,6 +126,7 @@ import type {
|
|
|
119
126
|
* - `task:established` - Task/call has been connected
|
|
120
127
|
* - `task:ended` - Task/call has ended
|
|
121
128
|
* - `task:error` - An error occurred during task handling
|
|
129
|
+
* - `task:campaignPreviewReservation` - Campaign preview contact offered to agent
|
|
122
130
|
*
|
|
123
131
|
* @public
|
|
124
132
|
*
|
|
@@ -319,6 +327,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
319
327
|
*/
|
|
320
328
|
private queue: Queue;
|
|
321
329
|
|
|
330
|
+
/**
|
|
331
|
+
* API instance for AI Assistant operations such as transcript controls.
|
|
332
|
+
* @type {ApiAIAssistant}
|
|
333
|
+
* @public
|
|
334
|
+
*/
|
|
335
|
+
public apiAIAssistant: ApiAIAssistant;
|
|
336
|
+
|
|
322
337
|
/**
|
|
323
338
|
* Logger utility for Contact Center plugin
|
|
324
339
|
* Provides consistent logging across the plugin
|
|
@@ -357,8 +372,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
357
372
|
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
358
373
|
|
|
359
374
|
this.webCallingService = new WebCallingService(this.$webex);
|
|
375
|
+
this.apiAIAssistant = new ApiAIAssistant(this.$webex);
|
|
360
376
|
this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
|
|
361
377
|
this.taskManager = TaskManager.getTaskManager(
|
|
378
|
+
this.apiAIAssistant,
|
|
362
379
|
this.services.contact,
|
|
363
380
|
this.webCallingService,
|
|
364
381
|
this.services.webSocketManager
|
|
@@ -370,8 +387,6 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
370
387
|
this.entryPoint = new EntryPoint(this.$webex);
|
|
371
388
|
this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
|
|
372
389
|
this.queue = new Queue(this.$webex);
|
|
373
|
-
|
|
374
|
-
// Initialize logger
|
|
375
390
|
LoggerProxy.initialize(this.$webex.logger);
|
|
376
391
|
});
|
|
377
392
|
}
|
|
@@ -406,6 +421,20 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
406
421
|
this.trigger(TASK_EVENTS.TASK_MERGED, task);
|
|
407
422
|
};
|
|
408
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Handles campaign preview reservation events when a contact is offered to the agent
|
|
426
|
+
* @private
|
|
427
|
+
* @param {ITask} task The campaign reservation task
|
|
428
|
+
*/
|
|
429
|
+
private handleCampaignPreviewReservation = (task: ITask) => {
|
|
430
|
+
// @ts-ignore
|
|
431
|
+
this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
private handleRTDWebsocketMessage = (payload: string) => {
|
|
435
|
+
this.taskManager.handleRealtimeWebsocketEvent(payload);
|
|
436
|
+
};
|
|
437
|
+
|
|
409
438
|
/**
|
|
410
439
|
* Sets up event listeners for incoming tasks and task hydration
|
|
411
440
|
* Subscribes to task events from the task manager
|
|
@@ -415,6 +444,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
415
444
|
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
416
445
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
417
446
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
447
|
+
this.taskManager.on(
|
|
448
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
449
|
+
this.handleCampaignPreviewReservation
|
|
450
|
+
);
|
|
418
451
|
}
|
|
419
452
|
|
|
420
453
|
/**
|
|
@@ -543,9 +576,14 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
543
576
|
|
|
544
577
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
545
578
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
579
|
+
this.taskManager.off(
|
|
580
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
581
|
+
this.handleCampaignPreviewReservation
|
|
582
|
+
);
|
|
546
583
|
this.taskManager.unregisterIncomingCallEvent();
|
|
547
584
|
|
|
548
585
|
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
586
|
+
this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
|
|
549
587
|
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
550
588
|
|
|
551
589
|
if (
|
|
@@ -569,6 +607,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
569
607
|
this.services.webSocketManager.close(false, 'Unregistering the SDK');
|
|
570
608
|
}
|
|
571
609
|
|
|
610
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
611
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
612
|
+
}
|
|
613
|
+
|
|
572
614
|
// Clear any cached agent configuration
|
|
573
615
|
this.agentConfig = null;
|
|
574
616
|
|
|
@@ -697,6 +739,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
697
739
|
try {
|
|
698
740
|
const data = (await this.services.webSocketManager.initWebSocket({
|
|
699
741
|
body: this.getConnectionConfig(),
|
|
742
|
+
resource: SUBSCRIBE_API,
|
|
700
743
|
})) as WelcomeEvent;
|
|
701
744
|
|
|
702
745
|
const agentId = data.agentId;
|
|
@@ -712,6 +755,33 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
712
755
|
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
713
756
|
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
714
757
|
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
758
|
+
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
759
|
+
|
|
760
|
+
if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable) {
|
|
761
|
+
LoggerProxy.info('Connecting to RTD websocket', {
|
|
762
|
+
module: CC_FILE,
|
|
763
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
await this.services.rtdWebSocketManager
|
|
767
|
+
.initWebSocket({
|
|
768
|
+
body: this.getConnectionConfig(),
|
|
769
|
+
resource: RTD_SUBSCRIBE_API,
|
|
770
|
+
})
|
|
771
|
+
.then(() => {
|
|
772
|
+
LoggerProxy.log('RTD websocket connected successfully', {
|
|
773
|
+
module: CC_FILE,
|
|
774
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
775
|
+
});
|
|
776
|
+
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
777
|
+
})
|
|
778
|
+
.catch((error) => {
|
|
779
|
+
LoggerProxy.error(`Error during RTD websocket setup: ${error}`, {
|
|
780
|
+
module: CC_FILE,
|
|
781
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
}
|
|
715
785
|
|
|
716
786
|
if (
|
|
717
787
|
this.agentConfig.webRtcEnabled &&
|
|
@@ -785,7 +855,11 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
785
855
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
786
856
|
]);
|
|
787
857
|
|
|
788
|
-
|
|
858
|
+
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
859
|
+
if (
|
|
860
|
+
data.loginOption === LoginOption.AGENT_DN &&
|
|
861
|
+
!isValidDialNumber(data.dialNumber, dialPlanEntries)
|
|
862
|
+
) {
|
|
789
863
|
const error = new Error('INVALID_DIAL_NUMBER');
|
|
790
864
|
// @ts-ignore - adding custom key to the error object
|
|
791
865
|
error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
|
|
@@ -1509,6 +1583,79 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1509
1583
|
}
|
|
1510
1584
|
}
|
|
1511
1585
|
|
|
1586
|
+
/**
|
|
1587
|
+
* Accepts a campaign preview contact, initiating the outbound call.
|
|
1588
|
+
*
|
|
1589
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1590
|
+
* `AgentOfferCampaignReservation` event. The agent can then accept the preview contact
|
|
1591
|
+
* to initiate the outbound call.
|
|
1592
|
+
*
|
|
1593
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1594
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1595
|
+
* @throws {Error} If the operation fails (network error, customer unavailable, etc.)
|
|
1596
|
+
*
|
|
1597
|
+
* @example
|
|
1598
|
+
* ```typescript
|
|
1599
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1600
|
+
* const { interactionId } = task.data;
|
|
1601
|
+
* // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID
|
|
1602
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1603
|
+
*
|
|
1604
|
+
* const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId });
|
|
1605
|
+
* });
|
|
1606
|
+
* ```
|
|
1607
|
+
*/
|
|
1608
|
+
public async acceptPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
1609
|
+
LoggerProxy.info('Accepting campaign preview contact', {
|
|
1610
|
+
module: CC_FILE,
|
|
1611
|
+
method: METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1612
|
+
});
|
|
1613
|
+
try {
|
|
1614
|
+
this.metricsManager.timeEvent([
|
|
1615
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
|
|
1616
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
1617
|
+
]);
|
|
1618
|
+
|
|
1619
|
+
const result = await this.services.dialer.acceptPreviewContact({data: payload});
|
|
1620
|
+
|
|
1621
|
+
this.metricsManager.trackEvent(
|
|
1622
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
|
|
1623
|
+
{
|
|
1624
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
1625
|
+
interactionId: payload.interactionId,
|
|
1626
|
+
campaignId: payload.campaignId,
|
|
1627
|
+
},
|
|
1628
|
+
['behavioral', 'business', 'operational']
|
|
1629
|
+
);
|
|
1630
|
+
|
|
1631
|
+
LoggerProxy.log('Campaign preview contact accepted successfully', {
|
|
1632
|
+
module: CC_FILE,
|
|
1633
|
+
method: METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1634
|
+
trackingId: result.trackingId,
|
|
1635
|
+
interactionId: payload.interactionId,
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
return result;
|
|
1639
|
+
} catch (error) {
|
|
1640
|
+
const failure = error.details as Failure;
|
|
1641
|
+
this.metricsManager.trackEvent(
|
|
1642
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
1643
|
+
{
|
|
1644
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1645
|
+
interactionId: payload.interactionId,
|
|
1646
|
+
campaignId: payload.campaignId,
|
|
1647
|
+
},
|
|
1648
|
+
['behavioral', 'business', 'operational']
|
|
1649
|
+
);
|
|
1650
|
+
const {error: detailedError} = getErrorDetails(
|
|
1651
|
+
error,
|
|
1652
|
+
METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1653
|
+
CC_FILE
|
|
1654
|
+
);
|
|
1655
|
+
throw detailedError;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1512
1659
|
/**
|
|
1513
1660
|
* Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
|
|
1514
1661
|
*
|
|
@@ -1656,6 +1803,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1656
1803
|
/**
|
|
1657
1804
|
* Updates the agent device type and login configuration.
|
|
1658
1805
|
* Use this method to change how an agent connects to the contact center system (e.g., switching from browser-based calling to a desk phone extension).
|
|
1806
|
+
* Change to any field of the profile is allowed;
|
|
1659
1807
|
*
|
|
1660
1808
|
* @param {AgentDeviceUpdate} data Configuration containing:
|
|
1661
1809
|
* - loginOption: New device type ('BROWSER', 'EXTENSION', 'AGENT_DN')
|
|
@@ -1695,29 +1843,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1695
1843
|
});
|
|
1696
1844
|
|
|
1697
1845
|
try {
|
|
1698
|
-
// Only block if both loginOption AND teamId remain unchanged
|
|
1699
|
-
if (
|
|
1700
|
-
this.webCallingService?.loginOption === data.loginOption &&
|
|
1701
|
-
data.teamId === this.agentConfig.currentTeamId
|
|
1702
|
-
) {
|
|
1703
|
-
const message =
|
|
1704
|
-
'Will not proceed with device update as new Device type is same as current device type and teamId is same as current teamId';
|
|
1705
|
-
const err = new Error(message) as GenericError;
|
|
1706
|
-
err.details = {
|
|
1707
|
-
type: 'Identical Device Change Failure',
|
|
1708
|
-
orgId: this.$webex.credentials.getOrgId(),
|
|
1709
|
-
trackingId,
|
|
1710
|
-
data: {
|
|
1711
|
-
agentId: this.agentConfig.agentId,
|
|
1712
|
-
reasonCode: 'R002',
|
|
1713
|
-
reason: message,
|
|
1714
|
-
},
|
|
1715
|
-
};
|
|
1716
|
-
throw err;
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
1846
|
await this.stationLogout({
|
|
1720
|
-
logoutReason: 'User requested agent
|
|
1847
|
+
logoutReason: 'User requested agent profile update',
|
|
1721
1848
|
});
|
|
1722
1849
|
|
|
1723
1850
|
const loginPayload: AgentLogin = {
|
package/src/constants.ts
CHANGED
|
@@ -49,4 +49,8 @@ export const METHODS = {
|
|
|
49
49
|
HANDLE_INCOMING_TASK: 'handleIncomingTask',
|
|
50
50
|
HANDLE_TASK_HYDRATE: 'handleTaskHydrate',
|
|
51
51
|
INCOMING_TASK_LISTENER: 'incomingTaskListener',
|
|
52
|
+
ACCEPT_PREVIEW_CONTACT: 'acceptPreviewContact',
|
|
53
|
+
GET_BASE_URL: 'getBaseUrl',
|
|
54
|
+
SEND_EVENT: 'sendEvent',
|
|
55
|
+
FETCH_HISTORIC_TRANSCRIPTS: 'fetchHistoricTranscripts',
|
|
52
56
|
};
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export {default as routingAgent} from './services/agent';
|
|
|
28
28
|
|
|
29
29
|
// API exports (AddressBook is public, EntryPoint and Queue are accessed via cc wrappers)
|
|
30
30
|
export {default as AddressBook} from './services/AddressBook';
|
|
31
|
+
export {default as ApiAIAssistant} from './services/ApiAiAssistant';
|
|
31
32
|
|
|
32
33
|
/** EntryPoint API types */
|
|
33
34
|
export type {
|
|
@@ -435,6 +435,20 @@ const eventTaxonomyMap: Record<string, BehavioralEventTaxonomy> = {
|
|
|
435
435
|
target: 'outdial_ani_ep_fetch',
|
|
436
436
|
verb: 'fail',
|
|
437
437
|
},
|
|
438
|
+
|
|
439
|
+
// Campaign Preview API Events
|
|
440
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS]: {
|
|
441
|
+
product,
|
|
442
|
+
agent: 'user',
|
|
443
|
+
target: 'campaign_preview_accept',
|
|
444
|
+
verb: 'complete',
|
|
445
|
+
},
|
|
446
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED]: {
|
|
447
|
+
product,
|
|
448
|
+
agent: 'user',
|
|
449
|
+
target: 'campaign_preview_accept',
|
|
450
|
+
verb: 'fail',
|
|
451
|
+
},
|
|
438
452
|
};
|
|
439
453
|
|
|
440
454
|
/**
|
package/src/metrics/constants.ts
CHANGED
|
@@ -159,6 +159,17 @@ export const METRIC_EVENT_NAMES = {
|
|
|
159
159
|
// Outdial ANI Entries API Events
|
|
160
160
|
OUTDIAL_ANI_EP_FETCH_SUCCESS: 'Outdial ANI Entries Fetch Success',
|
|
161
161
|
OUTDIAL_ANI_EP_FETCH_FAILED: 'Outdial ANI Entries Fetch Failed',
|
|
162
|
+
|
|
163
|
+
// Campaign Preview API Events
|
|
164
|
+
CAMPAIGN_PREVIEW_ACCEPT_SUCCESS: 'Campaign Preview Accept Success',
|
|
165
|
+
CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'Campaign Preview Accept Failed',
|
|
166
|
+
|
|
167
|
+
// AI Assistant transcript events
|
|
168
|
+
AI_ASSISTANT_SEND_EVENT_SUCCESS: 'AI Assistant Send Event Success',
|
|
169
|
+
AI_ASSISTANT_SEND_EVENT_FAILED: 'AI Assistant Send Event Failed',
|
|
170
|
+
AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS:
|
|
171
|
+
'AI Assistant Fetch Historic Transcripts Success',
|
|
172
|
+
AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED: 'AI Assistant Fetch Historic Transcripts Failed',
|
|
162
173
|
} as const;
|
|
163
174
|
|
|
164
175
|
/**
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import LoggerProxy from '../logger-proxy';
|
|
2
|
+
import MetricsManager from '../metrics/MetricsManager';
|
|
3
|
+
import {METRIC_EVENT_NAMES} from '../metrics/constants';
|
|
4
|
+
import {CC_FILE, METHODS} from '../constants';
|
|
5
|
+
import {
|
|
6
|
+
HTTP_METHODS,
|
|
7
|
+
WebexSDK,
|
|
8
|
+
IHttpResponse,
|
|
9
|
+
TranscriptAction,
|
|
10
|
+
AIAssistantEventType,
|
|
11
|
+
AIAssistantEventName,
|
|
12
|
+
HistoricTranscriptsResponse,
|
|
13
|
+
} from '../types';
|
|
14
|
+
import {getErrorDetails} from './core/Utils';
|
|
15
|
+
import {
|
|
16
|
+
AI_ASSISTANT_BASE_URL_TEMPLATE,
|
|
17
|
+
AI_ASSISTANT_ENV_MAP,
|
|
18
|
+
AI_ASSISTANT_API_URLS,
|
|
19
|
+
WCC_API_GATEWAY,
|
|
20
|
+
} from './constants';
|
|
21
|
+
import {AIFeatureFlags} from './config/types';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* ApiAIAssistant provides AI Assistant APIs for transcript controls.
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export class ApiAIAssistant {
|
|
28
|
+
private webex: WebexSDK;
|
|
29
|
+
private metricsManager: MetricsManager;
|
|
30
|
+
public aiFeature: AIFeatureFlags;
|
|
31
|
+
|
|
32
|
+
constructor(webex: WebexSDK) {
|
|
33
|
+
this.webex = webex;
|
|
34
|
+
this.metricsManager = MetricsManager.getInstance({webex});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public setAIFeatureFlags(aiFeature: AIFeatureFlags): void {
|
|
38
|
+
this.aiFeature = aiFeature;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private getBaseUrl(): string {
|
|
42
|
+
const wccApiGatewayUrl = this.webex.internal.services.get(WCC_API_GATEWAY) || '';
|
|
43
|
+
|
|
44
|
+
if (!wccApiGatewayUrl) {
|
|
45
|
+
const {error: detailedError} = getErrorDetails(
|
|
46
|
+
new Error('AI_ASSISTANT_BASE_URL_NOT_AVAILABLE'),
|
|
47
|
+
METHODS.GET_BASE_URL,
|
|
48
|
+
CC_FILE
|
|
49
|
+
);
|
|
50
|
+
throw detailedError;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let hostname = '';
|
|
54
|
+
try {
|
|
55
|
+
hostname = new URL(wccApiGatewayUrl).hostname.toLowerCase();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
hostname = wccApiGatewayUrl.toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const resolvedEnv = AI_ASSISTANT_ENV_MAP[hostname];
|
|
61
|
+
if (!resolvedEnv) {
|
|
62
|
+
const {error: detailedError} = getErrorDetails(
|
|
63
|
+
new Error('AI_ASSISTANT_BASE_URL_NOT_AVAILABLE'),
|
|
64
|
+
METHODS.GET_BASE_URL,
|
|
65
|
+
CC_FILE
|
|
66
|
+
);
|
|
67
|
+
throw detailedError;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return AI_ASSISTANT_BASE_URL_TEMPLATE.replace('%s', resolvedEnv);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Sends an event to the AI Assistant service.
|
|
75
|
+
* @param agentId - agent identifier
|
|
76
|
+
* @param interactionId - interaction/conversation identifier
|
|
77
|
+
* @param eventType - the type of event (e.g. 'CUSTOM_EVENT')
|
|
78
|
+
* @param eventName - the name of the event (e.g. 'GET_TRANSCRIPTS')
|
|
79
|
+
* @param action - action within eventDetails (e.g. 'START' or 'STOP')
|
|
80
|
+
*/
|
|
81
|
+
public async sendEvent(
|
|
82
|
+
agentId: string,
|
|
83
|
+
interactionId: string,
|
|
84
|
+
eventType: AIAssistantEventType,
|
|
85
|
+
eventName: AIAssistantEventName,
|
|
86
|
+
action: TranscriptAction
|
|
87
|
+
): Promise<Record<string, unknown>> {
|
|
88
|
+
LoggerProxy.info('Sending event', {
|
|
89
|
+
module: CC_FILE,
|
|
90
|
+
method: METHODS.SEND_EVENT,
|
|
91
|
+
interactionId,
|
|
92
|
+
data: {eventType, eventName, action},
|
|
93
|
+
});
|
|
94
|
+
this.metricsManager.timeEvent([
|
|
95
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_SUCCESS,
|
|
96
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_FAILED,
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const baseUrl = this.getBaseUrl();
|
|
101
|
+
const orgId = this.webex.credentials.getOrgId();
|
|
102
|
+
const response = (await this.webex.request({
|
|
103
|
+
uri: `${baseUrl}${AI_ASSISTANT_API_URLS.EVENT}`,
|
|
104
|
+
method: HTTP_METHODS.POST,
|
|
105
|
+
addAuthHeader: true,
|
|
106
|
+
body: {
|
|
107
|
+
agentId,
|
|
108
|
+
orgId,
|
|
109
|
+
eventType,
|
|
110
|
+
eventName,
|
|
111
|
+
eventDetails: {
|
|
112
|
+
data: {
|
|
113
|
+
interactionId,
|
|
114
|
+
action,
|
|
115
|
+
actionTimeStamp: String(Date.now()),
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
})) as IHttpResponse;
|
|
120
|
+
|
|
121
|
+
this.metricsManager.trackEvent(
|
|
122
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_SUCCESS,
|
|
123
|
+
{agentId, orgId, interactionId, eventType, eventName, action},
|
|
124
|
+
['operational']
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return response?.body || {};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
this.metricsManager.trackEvent(
|
|
130
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_FAILED,
|
|
131
|
+
{
|
|
132
|
+
interactionId,
|
|
133
|
+
eventType,
|
|
134
|
+
eventName,
|
|
135
|
+
action,
|
|
136
|
+
error: error instanceof Error ? error.message : String(error),
|
|
137
|
+
},
|
|
138
|
+
['operational']
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const {error: detailedError} = getErrorDetails(error, METHODS.SEND_EVENT, CC_FILE);
|
|
142
|
+
throw detailedError;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Fetches historic transcripts for an interaction.
|
|
148
|
+
* This API is allowed only when real-time transcription feature is enabled.
|
|
149
|
+
*
|
|
150
|
+
* @param interactionId - interaction/conversation identifier
|
|
151
|
+
*/
|
|
152
|
+
public async fetchHistoricTranscripts(
|
|
153
|
+
agentId: string,
|
|
154
|
+
interactionId: string
|
|
155
|
+
): Promise<HistoricTranscriptsResponse> {
|
|
156
|
+
LoggerProxy.info('Fetching historic transcripts', {
|
|
157
|
+
module: CC_FILE,
|
|
158
|
+
method: METHODS.FETCH_HISTORIC_TRANSCRIPTS,
|
|
159
|
+
interactionId,
|
|
160
|
+
});
|
|
161
|
+
this.metricsManager.timeEvent([
|
|
162
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS,
|
|
163
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED,
|
|
164
|
+
]);
|
|
165
|
+
if (!this.aiFeature?.realtimeTranscripts?.enable) {
|
|
166
|
+
const {error: detailedError} = getErrorDetails(
|
|
167
|
+
new Error('REAL_TIME_TRANSCRIPTION_NOT_ENABLED'),
|
|
168
|
+
METHODS.FETCH_HISTORIC_TRANSCRIPTS,
|
|
169
|
+
CC_FILE
|
|
170
|
+
);
|
|
171
|
+
throw detailedError;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const baseUrl = this.getBaseUrl();
|
|
176
|
+
const orgId = this.webex.credentials.getOrgId();
|
|
177
|
+
const response = (await this.webex.request({
|
|
178
|
+
uri: `${baseUrl}${AI_ASSISTANT_API_URLS.TRANSCRIPTS_LIST}`,
|
|
179
|
+
method: HTTP_METHODS.POST,
|
|
180
|
+
addAuthHeader: true,
|
|
181
|
+
body: {
|
|
182
|
+
agentId,
|
|
183
|
+
orgId,
|
|
184
|
+
interactionId,
|
|
185
|
+
},
|
|
186
|
+
})) as IHttpResponse;
|
|
187
|
+
|
|
188
|
+
this.metricsManager.trackEvent(
|
|
189
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS,
|
|
190
|
+
{agentId, orgId, interactionId},
|
|
191
|
+
['operational']
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return response.body as HistoricTranscriptsResponse;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
this.metricsManager.trackEvent(
|
|
197
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED,
|
|
198
|
+
{
|
|
199
|
+
interactionId,
|
|
200
|
+
error: error instanceof Error ? error.message : String(error),
|
|
201
|
+
},
|
|
202
|
+
['operational']
|
|
203
|
+
);
|
|
204
|
+
if (error instanceof Error) {
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
const {error: detailedError} = getErrorDetails(
|
|
208
|
+
error,
|
|
209
|
+
METHODS.FETCH_HISTORIC_TRANSCRIPTS,
|
|
210
|
+
CC_FILE
|
|
211
|
+
);
|
|
212
|
+
throw detailedError;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export default ApiAIAssistant;
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
TenantData,
|
|
15
15
|
URLMapping,
|
|
16
16
|
WRAP_UP_CODE,
|
|
17
|
+
AIFeatureFlagsResponse,
|
|
18
|
+
AIFeatureFlags,
|
|
17
19
|
} from './types';
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -140,6 +142,7 @@ function parseAgentConfigs(profileData: {
|
|
|
140
142
|
dialPlanData: DialPlanEntity[];
|
|
141
143
|
urlMapping: URLMapping[];
|
|
142
144
|
multimediaProfileId: string;
|
|
145
|
+
aiFeatureFlags: AIFeatureFlagsResponse;
|
|
143
146
|
}): Profile {
|
|
144
147
|
const {
|
|
145
148
|
userData,
|
|
@@ -151,6 +154,7 @@ function parseAgentConfigs(profileData: {
|
|
|
151
154
|
agentProfileData,
|
|
152
155
|
dialPlanData,
|
|
153
156
|
urlMapping,
|
|
157
|
+
aiFeatureFlags,
|
|
154
158
|
} = profileData;
|
|
155
159
|
|
|
156
160
|
const tenantDataTimeout = tenantData.timeoutDesktopInactivityEnabled
|
|
@@ -180,6 +184,8 @@ function parseAgentConfigs(profileData: {
|
|
|
180
184
|
}); // pushing available state to idle codes
|
|
181
185
|
|
|
182
186
|
const defaultWrapUpData = getDefaultWrapUpCode(wrapupCodes);
|
|
187
|
+
const aiFeature: AIFeatureFlags | undefined =
|
|
188
|
+
aiFeatureFlags?.data && aiFeatureFlags.data.length > 0 ? aiFeatureFlags.data[0] : undefined;
|
|
183
189
|
|
|
184
190
|
const finalData = {
|
|
185
191
|
teams: teamData,
|
|
@@ -204,6 +210,7 @@ function parseAgentConfigs(profileData: {
|
|
|
204
210
|
siteId: userData.siteId,
|
|
205
211
|
enterpriseId: orgInfoData.tenantId,
|
|
206
212
|
tenantTimezone: orgInfoData.timezone,
|
|
213
|
+
environment: orgInfoData.environment,
|
|
207
214
|
privacyShieldVisible: tenantData.privacyShieldVisible,
|
|
208
215
|
organizationIdleCodes: [], // TODO: for supervisor, getOrgFilteredIdleCodes(auxCodes, false),
|
|
209
216
|
idleCodesAccess: agentProfileData.accessIdleCode as 'ALL' | 'SPECIFIC',
|
|
@@ -253,6 +260,7 @@ function parseAgentConfigs(profileData: {
|
|
|
253
260
|
webexConfig: getWebexConfig(agentProfileData),
|
|
254
261
|
lostConnectionRecoveryTimeout:
|
|
255
262
|
tenantData.lostConnectionRecoveryTimeout || LOST_CONNECTION_RECOVERY_TIMEOUT,
|
|
263
|
+
aiFeature,
|
|
256
264
|
};
|
|
257
265
|
|
|
258
266
|
return finalData;
|
|
@@ -67,6 +67,8 @@ export const METHODS = {
|
|
|
67
67
|
GET_TENANT_DATA: 'getTenantData',
|
|
68
68
|
GET_URL_MAPPING: 'getURLMapping',
|
|
69
69
|
GET_DIAL_PLAN_DATA: 'getDialPlanData',
|
|
70
|
+
GET_AI_FEATURE_FLAGS: 'getAIFeatureFlags',
|
|
71
|
+
GET_QUEUES: 'getQueues',
|
|
70
72
|
|
|
71
73
|
// Util methods
|
|
72
74
|
PARSE_AGENT_CONFIGS: 'parseAgentConfigs',
|
|
@@ -232,6 +234,16 @@ export const endPointMap = {
|
|
|
232
234
|
* @ignore
|
|
233
235
|
*/
|
|
234
236
|
dialPlan: (orgId: string) => `organization/${orgId}/dial-plan?agentView=true`,
|
|
237
|
+
/**
|
|
238
|
+
* Gets the endpoint for listing AI feature flags.
|
|
239
|
+
* @param orgId - Organization ID.
|
|
240
|
+
* @returns The endpoint URL string.
|
|
241
|
+
* @public
|
|
242
|
+
* @example
|
|
243
|
+
* const url = endPointMap.aiFeatureFlags('org123');
|
|
244
|
+
* @ignore
|
|
245
|
+
*/
|
|
246
|
+
aiFeature: (orgId: string) => `organization/${orgId}/v2/ai-feature?page=0&pageSize=100`,
|
|
235
247
|
|
|
236
248
|
/**
|
|
237
249
|
* Gets the endpoint for the queue list with custom query parameters.
|