@webex/contact-center 3.12.0-next.8 → 3.12.0-task-refactor.1
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/AGENTS.md +438 -0
- package/ai-docs/README.md +131 -0
- package/ai-docs/RULES.md +455 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +65 -123
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +13 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -13
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +7 -6
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +0 -3
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +2 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +16 -14
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +0 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +79 -55
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +17 -92
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +5 -25
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +644 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +45 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +556 -532
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +132 -28
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -6
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +0 -51
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +77 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +366 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +139 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +256 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +369 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +157 -18
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1031 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +149 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/types/cc.d.ts +4 -33
- package/dist/types/constants.d.ts +13 -2
- package/dist/types/index.d.ts +11 -5
- package/dist/types/metrics/constants.d.ts +5 -3
- package/dist/types/services/ApiAiAssistant.d.ts +1 -1
- package/dist/types/services/config/types.d.ts +97 -25
- package/dist/types/services/core/Err.d.ts +0 -2
- package/dist/types/services/core/Utils.d.ts +25 -23
- package/dist/types/services/core/aqm-reqs.d.ts +0 -49
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
- package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +146 -0
- package/dist/types/services/task/TaskFactory.d.ts +12 -0
- package/dist/types/services/task/TaskUtils.d.ts +39 -8
- package/dist/types/services/task/constants.d.ts +5 -4
- package/dist/types/services/task/dialer.d.ts +0 -15
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
- package/dist/types/services/task/state-machine/actions.d.ts +8 -0
- package/dist/types/services/task/state-machine/constants.d.ts +91 -0
- package/dist/types/services/task/state-machine/guards.d.ts +78 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +256 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +539 -88
- package/dist/types/services/task/voice/Voice.d.ts +183 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/types.d.ts +68 -0
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +70 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +14 -11
- package/src/cc.ts +91 -177
- package/src/constants.ts +13 -2
- package/src/index.ts +14 -5
- package/src/metrics/ai-docs/AGENTS.md +348 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
- package/src/metrics/behavioral-events.ts +28 -14
- package/src/metrics/constants.ts +7 -8
- package/src/services/ApiAiAssistant.ts +2 -4
- package/src/services/agent/ai-docs/AGENTS.md +238 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
- package/src/services/ai-docs/AGENTS.md +384 -0
- package/src/services/config/Util.ts +2 -3
- package/src/services/config/ai-docs/AGENTS.md +253 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
- package/src/services/config/types.ts +108 -20
- package/src/services/constants.ts +0 -1
- package/src/services/core/Err.ts +0 -1
- package/src/services/core/Utils.ts +90 -67
- package/src/services/core/ai-docs/AGENTS.md +379 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
- package/src/services/core/aqm-reqs.ts +22 -100
- package/src/services/core/websocket/WebSocketManager.ts +4 -23
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -2
- package/src/services/task/Task.ts +785 -0
- package/src/services/task/TaskFactory.ts +55 -0
- package/src/services/task/TaskManager.ts +567 -633
- package/src/services/task/TaskUtils.ts +175 -31
- package/src/services/task/ai-docs/AGENTS.md +448 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
- package/src/services/task/constants.ts +5 -4
- package/src/services/task/dialer.ts +1 -56
- package/src/services/task/digital/Digital.ts +95 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
- package/src/services/task/state-machine/actions.ts +409 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
- package/src/services/task/state-machine/constants.ts +150 -0
- package/src/services/task/state-machine/guards.ts +295 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +228 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +529 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +641 -95
- package/src/services/task/voice/Voice.ts +1255 -0
- package/src/services/task/voice/WebRTC.ts +187 -0
- package/src/types.ts +88 -5
- package/src/utils/AGENTS.md +276 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +59 -142
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
- package/test/unit/spec/services/config/index.ts +26 -55
- package/test/unit/spec/services/core/Utils.ts +103 -52
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
- package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +416 -0
- package/test/unit/spec/services/task/TaskFactory.ts +62 -0
- package/test/unit/spec/services/task/TaskManager.ts +781 -1735
- package/test/unit/spec/services/task/TaskUtils.ts +125 -0
- package/test/unit/spec/services/task/dialer.ts +112 -198
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +587 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Event-Driven Patterns - Contact Center SDK
|
|
2
|
+
|
|
3
|
+
> **Purpose**: Event handling patterns for the Contact Center SDK, including WebSocket events and EventEmitter usage.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Event Constants
|
|
8
|
+
|
|
9
|
+
### Definition Pattern
|
|
10
|
+
|
|
11
|
+
Events are defined as const objects for type safety:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// services/config/types.ts
|
|
15
|
+
|
|
16
|
+
// Agent-related events
|
|
17
|
+
export const CC_AGENT_EVENTS = {
|
|
18
|
+
WELCOME: 'Welcome',
|
|
19
|
+
AGENT_RELOGIN_SUCCESS: 'AgentReloginSuccess',
|
|
20
|
+
AGENT_RELOGIN_FAILED: 'AgentReloginFailed',
|
|
21
|
+
AGENT_DN_REGISTERED: 'AgentDNRegistered',
|
|
22
|
+
AGENT_LOGOUT: 'Logout',
|
|
23
|
+
AGENT_LOGOUT_SUCCESS: 'AgentLogoutSuccess',
|
|
24
|
+
AGENT_LOGOUT_FAILED: 'AgentLogoutFailed',
|
|
25
|
+
AGENT_STATION_LOGIN: 'StationLogin',
|
|
26
|
+
AGENT_STATION_LOGIN_SUCCESS: 'AgentStationLoginSuccess',
|
|
27
|
+
AGENT_STATION_LOGIN_FAILED: 'AgentStationLoginFailed',
|
|
28
|
+
AGENT_STATE_CHANGE: 'AgentStateChange',
|
|
29
|
+
AGENT_MULTI_LOGIN: 'AGENT_MULTI_LOGIN',
|
|
30
|
+
AGENT_STATE_CHANGE_SUCCESS: 'AgentStateChangeSuccess',
|
|
31
|
+
AGENT_STATE_CHANGE_FAILED: 'AgentStateChangeFailed',
|
|
32
|
+
AGENT_BUDDY_AGENTS: 'BuddyAgents',
|
|
33
|
+
AGENT_BUDDY_AGENTS_SUCCESS: 'BuddyAgents',
|
|
34
|
+
AGENT_BUDDY_AGENTS_RETRIEVE_FAILED: 'BuddyAgentsRetrieveFailed',
|
|
35
|
+
AGENT_CONTACT_RESERVED: 'AgentContactReserved',
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
// Task-related events
|
|
39
|
+
export const CC_TASK_EVENTS = {
|
|
40
|
+
AGENT_CONTACT_HELD: 'AgentContactHeld',
|
|
41
|
+
AGENT_CONTACT_UNHELD: 'AgentContactUnheld',
|
|
42
|
+
AGENT_CONSULT_CREATED: 'AgentConsultCreated',
|
|
43
|
+
AGENT_BLIND_TRANSFERRED: 'AgentBlindTransferred',
|
|
44
|
+
CONTACT_ENDED: 'ContactEnded',
|
|
45
|
+
AGENT_WRAPUP: 'AgentWrapup',
|
|
46
|
+
AGENT_WRAPPEDUP: 'AgentWrappedUp',
|
|
47
|
+
// ... more events
|
|
48
|
+
} as const;
|
|
49
|
+
|
|
50
|
+
// Combined events
|
|
51
|
+
export const CC_EVENTS = {
|
|
52
|
+
...CC_AGENT_EVENTS,
|
|
53
|
+
...CC_TASK_EVENTS,
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
// Type extraction
|
|
57
|
+
type Enum<T extends Record<string, unknown>> = T[keyof T];
|
|
58
|
+
export type CC_EVENTS = Enum<typeof CC_EVENTS>;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Agent Events (AGENT_EVENTS)
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// services/agent/types.ts
|
|
65
|
+
export enum AGENT_EVENTS {
|
|
66
|
+
AGENT_STATE_CHANGE = 'agent:stateChange',
|
|
67
|
+
AGENT_MULTI_LOGIN = 'agent:multiLogin',
|
|
68
|
+
AGENT_STATION_LOGIN_SUCCESS = 'agent:stationLoginSuccess',
|
|
69
|
+
AGENT_STATION_LOGIN_FAILED = 'agent:stationLoginFailed',
|
|
70
|
+
AGENT_LOGOUT_SUCCESS = 'agent:logoutSuccess',
|
|
71
|
+
AGENT_LOGOUT_FAILED = 'agent:logoutFailed',
|
|
72
|
+
AGENT_DN_REGISTERED = 'agent:dnRegistered',
|
|
73
|
+
AGENT_RELOGIN_SUCCESS = 'agent:reloginSuccess',
|
|
74
|
+
AGENT_STATE_CHANGE_SUCCESS = 'agent:stateChangeSuccess',
|
|
75
|
+
AGENT_STATE_CHANGE_FAILED = 'agent:stateChangeFailed',
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Task Events (TASK_EVENTS)
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// services/task/types.ts
|
|
83
|
+
export enum TASK_EVENTS {
|
|
84
|
+
TASK_INCOMING = 'task:incoming',
|
|
85
|
+
TASK_ASSIGNED = 'task:assigned',
|
|
86
|
+
TASK_MEDIA = 'task:media',
|
|
87
|
+
TASK_UNASSIGNED = 'task:unassigned',
|
|
88
|
+
TASK_HOLD = 'task:hold',
|
|
89
|
+
TASK_RESUME = 'task:resume',
|
|
90
|
+
TASK_HYDRATE = 'task:hydrate',
|
|
91
|
+
TASK_MERGED = 'task:merged',
|
|
92
|
+
TASK_END = 'task:end',
|
|
93
|
+
TASK_WRAPUP = 'task:wrapup',
|
|
94
|
+
TASK_CLEANUP = 'task:cleanup',
|
|
95
|
+
// ... more events (consult, recording, etc.)
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Event Flow
|
|
102
|
+
|
|
103
|
+
### Generic Event Reception
|
|
104
|
+
|
|
105
|
+
Events flow through a standard pipeline: receive from transport, parse, log, route, and emit to listeners.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Transport (WebSocket/EventEmitter)
|
|
109
|
+
│
|
|
110
|
+
▼
|
|
111
|
+
Handler receives raw event
|
|
112
|
+
│
|
|
113
|
+
├── Parse / validate
|
|
114
|
+
├── Log reception
|
|
115
|
+
├── Route by event type
|
|
116
|
+
└── Emit to subscribers
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Handlers use one of two patterns to preserve `this` binding: **arrow function properties** (e.g., `handleWebsocketMessage`) or **regular methods with `.bind(this)`** (e.g., `handleConnectionLost`):
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Pattern 1: Arrow function property (used by handleWebsocketMessage)
|
|
123
|
+
private handleEvent = (event: string) => {
|
|
124
|
+
const eventData = JSON.parse(event);
|
|
125
|
+
|
|
126
|
+
// Skip non-actionable events (e.g., keepalives)
|
|
127
|
+
if (eventData.keepalive) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Log reception
|
|
132
|
+
LoggerProxy.log(`Received event: ${eventData?.data?.type ?? eventData.type}`, {
|
|
133
|
+
module: CC_FILE,
|
|
134
|
+
method: 'handleEvent',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Route by type and emit to subscribers
|
|
138
|
+
switch (eventData.type) {
|
|
139
|
+
case CC_EVENTS.AGENT_STATE_CHANGE:
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
this.trigger(AGENT_EVENTS.AGENT_STATE_CHANGE, eventData.data);
|
|
142
|
+
break;
|
|
143
|
+
// ... more cases
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Event Emission
|
|
151
|
+
|
|
152
|
+
There are **two methods** for emitting events: `trigger` and `emit`. They are **not interchangeable** — which one to use depends on what the class extends.
|
|
153
|
+
|
|
154
|
+
### `trigger` — for classes extending `WebexPlugin` (e.g., cc.ts)
|
|
155
|
+
|
|
156
|
+
`cc.ts` (`ContactCenter`) extends `WebexPlugin`, **not** `EventEmitter`. The `emit` method does not exist on `WebexPlugin`. The correct method for emitting events from `cc` is `trigger`, which comes from the Ampersand event system that `WebexPlugin` is built on.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// cc.ts — ContactCenter extends WebexPlugin
|
|
160
|
+
// @ts-ignore
|
|
161
|
+
this.trigger(TASK_EVENTS.TASK_INCOMING, task);
|
|
162
|
+
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
> **Note**: `trigger` requires `// @ts-ignore` because WebexPlugin's TypeScript type definitions don't expose it.
|
|
168
|
+
|
|
169
|
+
### `emit` — for classes extending `EventEmitter` (e.g., Task, TaskManager, WebCallingService)
|
|
170
|
+
|
|
171
|
+
`emit` is the standard Node.js `EventEmitter` method. It works natively on classes that extend `EventEmitter` — no `@ts-ignore` needed.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// Task extends EventEmitter — emit works natively
|
|
175
|
+
export default abstract class Task extends EventEmitter implements ITask {
|
|
176
|
+
private autoAnswerIfNeeded() {
|
|
177
|
+
// ...
|
|
178
|
+
this.emit(TASK_EVENTS.TASK_AUTO_ANSWERED, this);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// TaskManager extends EventEmitter — emit works natively
|
|
183
|
+
export default class TaskManager extends EventEmitter {
|
|
184
|
+
private handleIncomingTask(taskData: TaskData) {
|
|
185
|
+
const task = this.createTask(taskData);
|
|
186
|
+
this.emit(TASK_EVENTS.TASK_INCOMING, task);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
> **Note on existing code**: The `cc` object currently uses `this.emit()` in `handleWebsocketMessage` for agent events with `// @ts-ignore`. This works at runtime but is not type-safe. For new code on the `cc` object, always use `trigger`.
|
|
192
|
+
|
|
193
|
+
### When to Use Which
|
|
194
|
+
|
|
195
|
+
| Class extends | Method | `@ts-ignore` needed? | Example classes |
|
|
196
|
+
|---------------|--------|----------------------|-----------------|
|
|
197
|
+
| `WebexPlugin` | `trigger` | Yes | `cc.ts` (ContactCenter) |
|
|
198
|
+
| `EventEmitter` | `emit` | No | Task, TaskManager, WebCallingService, WebSocketManager |
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Event Subscription
|
|
203
|
+
|
|
204
|
+
### How to Add / Remove a Listener
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// Add a listener
|
|
208
|
+
source.on(EVENT_CONSTANT, handler);
|
|
209
|
+
|
|
210
|
+
// Remove a listener (must pass the same function reference)
|
|
211
|
+
source.off(EVENT_CONSTANT, handler);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> **Important**: Always store the callback as a named function reference. Using inline anonymous functions makes it impossible to call `.off()` because you can't pass the same reference back.
|
|
215
|
+
|
|
216
|
+
#### Example
|
|
217
|
+
|
|
218
|
+
Store handler references so you can remove them later. Use **arrow function properties** or **`.bind(this)`** depending on the pattern:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Pattern 1: Arrow function property (preserves `this` automatically)
|
|
222
|
+
private handleIncomingTask = (task: ITask) => {
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
this.trigger(TASK_EVENTS.TASK_INCOMING, task);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Register
|
|
228
|
+
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
229
|
+
|
|
230
|
+
// Cleanup
|
|
231
|
+
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
232
|
+
|
|
233
|
+
// Pattern 2: Regular method with .bind(this) (used by handleConnectionLost in cc.ts)
|
|
234
|
+
private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
|
|
235
|
+
// handle connection lost
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Register with .bind(this)
|
|
239
|
+
this.services.connectionService.on('connectionLost', this.handleConnectionLost.bind(this));
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Internal Event Listening (between services)
|
|
243
|
+
|
|
244
|
+
Internal services listen to each other during initialization, and clean up during deregistration:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// Register listeners after SDK is ready
|
|
248
|
+
this.$webex.once(READY, () => {
|
|
249
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
250
|
+
this.services.connectionService.on('connectionLost', this.handleConnectionLost);
|
|
251
|
+
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
252
|
+
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Remove all listeners on deregister
|
|
256
|
+
public async deregister() {
|
|
257
|
+
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
258
|
+
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
259
|
+
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
260
|
+
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Events Sent to Application (from cc, task)
|
|
265
|
+
|
|
266
|
+
Application consumers subscribe to events on the `cc` object or on task instances. Always use named callbacks so `.off()` can reference the same function:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
const cc = webex.cc;
|
|
270
|
+
|
|
271
|
+
// Define named callbacks (required for .off() to work)
|
|
272
|
+
const handleStateChange = (event) => {
|
|
273
|
+
// handle agent state change
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const handleLoginSuccess = (event) => {
|
|
277
|
+
// handle login success
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const handleIncomingTask = (task) => {
|
|
281
|
+
// handle incoming task
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Subscribe — agent events (from cc)
|
|
285
|
+
cc.on(AGENT_EVENTS.AGENT_STATE_CHANGE, handleStateChange);
|
|
286
|
+
cc.on(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, handleLoginSuccess);
|
|
287
|
+
|
|
288
|
+
// Subscribe — task events (from cc)
|
|
289
|
+
cc.on(TASK_EVENTS.TASK_INCOMING, handleIncomingTask);
|
|
290
|
+
|
|
291
|
+
// Unsubscribe — pass the same function reference
|
|
292
|
+
cc.off(AGENT_EVENTS.AGENT_STATE_CHANGE, handleStateChange);
|
|
293
|
+
cc.off(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, handleLoginSuccess);
|
|
294
|
+
cc.off(TASK_EVENTS.TASK_INCOMING, handleIncomingTask);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Event Data Transformation
|
|
300
|
+
|
|
301
|
+
### Login Success Transformation
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
case CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS: {
|
|
305
|
+
// Transform channelsMap to mmProfile
|
|
306
|
+
const {channelsMap, ...loginData} = eventData.data;
|
|
307
|
+
const stationLoginData = {
|
|
308
|
+
...loginData,
|
|
309
|
+
mmProfile: {
|
|
310
|
+
chat: channelsMap.chat?.length,
|
|
311
|
+
email: channelsMap.email?.length,
|
|
312
|
+
social: channelsMap.social?.length,
|
|
313
|
+
telephony: channelsMap.telephony?.length,
|
|
314
|
+
},
|
|
315
|
+
notifsTrackingId: eventData.trackingId,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// @ts-ignore
|
|
319
|
+
this.trigger(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, stationLoginData);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Adding New Events
|
|
327
|
+
|
|
328
|
+
### Step 1: Define Event Constant
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// In appropriate types file
|
|
332
|
+
export const CC_MY_EVENTS = {
|
|
333
|
+
MY_NEW_EVENT: 'MyNewEvent',
|
|
334
|
+
MY_NEW_EVENT_SUCCESS: 'MyNewEventSuccess',
|
|
335
|
+
MY_NEW_EVENT_FAILED: 'MyNewEventFailed',
|
|
336
|
+
} as const;
|
|
337
|
+
|
|
338
|
+
// Add to CC_EVENTS if needed
|
|
339
|
+
export const CC_EVENTS = {
|
|
340
|
+
...CC_AGENT_EVENTS,
|
|
341
|
+
...CC_TASK_EVENTS,
|
|
342
|
+
...CC_MY_EVENTS,
|
|
343
|
+
} as const;
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Step 2: Define External Event Name
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
export enum AGENT_EVENTS {
|
|
350
|
+
// ... existing
|
|
351
|
+
MY_NEW_EVENT = 'agent:myNewEvent',
|
|
352
|
+
MY_NEW_EVENT_SUCCESS = 'agent:myNewEventSuccess',
|
|
353
|
+
MY_NEW_EVENT_FAILED = 'agent:myNewEventFailed',
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Step 3: Handle in WebSocket Handler
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
case CC_EVENTS.MY_NEW_EVENT_SUCCESS:
|
|
361
|
+
// @ts-ignore
|
|
362
|
+
this.trigger(AGENT_EVENTS.MY_NEW_EVENT_SUCCESS, eventData.data);
|
|
363
|
+
break;
|
|
364
|
+
case CC_EVENTS.MY_NEW_EVENT_FAILED:
|
|
365
|
+
// @ts-ignore
|
|
366
|
+
this.trigger(AGENT_EVENTS.MY_NEW_EVENT_FAILED, eventData.data);
|
|
367
|
+
break;
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Step 4: Document the Event
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
/**
|
|
374
|
+
* @fires agent:myNewEvent When event occurs
|
|
375
|
+
* @fires agent:myNewEventSuccess When event succeeds
|
|
376
|
+
* @fires agent:myNewEventFailed When event fails
|
|
377
|
+
*/
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Event Type Safety
|
|
383
|
+
|
|
384
|
+
### Typed Event Handlers
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// Define event data types
|
|
388
|
+
type AgentStateChangeEvent = {
|
|
389
|
+
agentId: string;
|
|
390
|
+
state: string;
|
|
391
|
+
auxCodeId: string;
|
|
392
|
+
timestamp: number;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
type TaskIncomingEvent = {
|
|
396
|
+
interactionId: string;
|
|
397
|
+
taskId: string;
|
|
398
|
+
channelType: string;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// Type-safe event handler
|
|
402
|
+
function onAgentStateChange(handler: (event: AgentStateChangeEvent) => void) {
|
|
403
|
+
cc.on(AGENT_EVENTS.AGENT_STATE_CHANGE, handler);
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## WebSocket Lifecycle
|
|
410
|
+
|
|
411
|
+
### Registration (subscribing to messages)
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// Subscribe to WebSocket messages during initialization
|
|
415
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
416
|
+
|
|
417
|
+
// Subscribe to connection state changes
|
|
418
|
+
this.services.connectionService.on('connectionLost', this.handleConnectionLost);
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Connection
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// Establish WebSocket connection via initWebSocket
|
|
425
|
+
const welcomeData = await this.services.webSocketManager.initWebSocket({
|
|
426
|
+
body: this.getConnectionConfig(),
|
|
427
|
+
});
|
|
428
|
+
// welcomeData contains the Welcome event with agentId
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Reconnection
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// ConnectionService emits 'connectionLost' with connection state details
|
|
435
|
+
// The handler checks whether the socket was lost or reconnected
|
|
436
|
+
this.services.connectionService.on('connectionLost', this.handleConnectionLost);
|
|
437
|
+
|
|
438
|
+
// On reconnection, perform a silent relogin to restore agent state
|
|
439
|
+
private async silentRelogin(): Promise<void> {
|
|
440
|
+
await this.services.agent.reload();
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Disconnection
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// 1. Remove all listeners first
|
|
448
|
+
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
449
|
+
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
450
|
+
|
|
451
|
+
// 2. Check socket state before closing
|
|
452
|
+
if (!this.services.webSocketManager.isSocketClosed) {
|
|
453
|
+
this.services.webSocketManager.close(false, 'Unregistering the SDK');
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Best Practices
|
|
460
|
+
|
|
461
|
+
### Always Use Constants
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
// CORRECT — use event constants, not raw strings
|
|
465
|
+
this.trigger(AGENT_EVENTS.AGENT_STATE_CHANGE, data); // WebexPlugin (cc.ts)
|
|
466
|
+
this.emit(TASK_EVENTS.TASK_INCOMING, task); // EventEmitter (Task, TaskManager)
|
|
467
|
+
cc.on(TASK_EVENTS.TASK_INCOMING, handler);
|
|
468
|
+
|
|
469
|
+
// WRONG — never use raw string event names
|
|
470
|
+
this.trigger('stateChange', data);
|
|
471
|
+
cc.on('task:incoming', handler);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Always Clean Up Listeners
|
|
475
|
+
|
|
476
|
+
Every `on()` must have a corresponding `off()` in the deregister/cleanup path. See the [Event Subscription](#event-subscription) section for the full pattern.
|
|
477
|
+
|
|
478
|
+
### Log Event Reception
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
LoggerProxy.log(`Received event: ${eventType}`, {
|
|
482
|
+
module: CC_FILE,
|
|
483
|
+
method: 'handleEvent',
|
|
484
|
+
});
|
|
485
|
+
```
|