@webex/contact-center 3.12.0-next.84 → 3.12.0-next.85
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 +876 -0
- package/AGENTS.md +65 -409
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +46 -0
- package/ai-docs/GETTING_STARTED.md +168 -0
- package/ai-docs/GLOSSARY.md +43 -0
- package/ai-docs/README.md +25 -18
- package/ai-docs/REVIEW_CHECKLIST.md +41 -0
- package/ai-docs/RULES.md +187 -198
- package/ai-docs/SECURITY.md +52 -0
- package/ai-docs/SERVICE_STATE.md +48 -0
- package/ai-docs/SPEC_INDEX.md +65 -0
- package/ai-docs/adr/0001-spec-source-policy.md +55 -0
- package/ai-docs/adr/README.md +8 -0
- package/ai-docs/adr/_adr-template.md +31 -0
- package/ai-docs/contact-center-spec.md +341 -0
- package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
- package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
- package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
- package/ai-docs/templates/README.md +1 -1
- package/ai-docs/templates/new-method/04-validation.md +1 -1
- package/ai-docs/templates/new-service/05-validation.md +1 -1
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/metrics/ai-docs/AGENTS.md +2 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +2 -0
- package/src/metrics/ai-docs/metrics-spec.md +854 -0
- package/src/services/agent/ai-docs/AGENTS.md +3 -1
- package/src/services/agent/ai-docs/ARCHITECTURE.md +2 -0
- package/src/services/agent/ai-docs/agent-spec.md +504 -0
- package/src/services/ai-docs/AGENTS.md +4 -2
- package/src/services/ai-docs/services-spec.md +492 -0
- package/src/services/config/ai-docs/AGENTS.md +3 -1
- package/src/services/config/ai-docs/ARCHITECTURE.md +2 -0
- package/src/services/config/ai-docs/config-spec.md +669 -0
- package/src/services/core/ai-docs/AGENTS.md +4 -2
- package/src/services/core/ai-docs/ARCHITECTURE.md +2 -0
- package/src/services/core/ai-docs/core-spec.md +783 -0
- package/src/services/task/ai-docs/AGENTS.md +2 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +11 -2
- package/src/services/task/ai-docs/task-spec.md +1319 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +38 -75
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +6 -4
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2115 -0
- package/src/utils/AGENTS.md +3 -1
- package/src/utils/ai-docs/utils-spec.md +381 -0
- package/umd/contact-center.min.js +2 -2
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
# Metrics — SPEC
|
|
2
|
+
|
|
3
|
+
> Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). This is the module's canonical specification.
|
|
4
|
+
|
|
5
|
+
## Metadata
|
|
6
|
+
|
|
7
|
+
| Field | Value |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Module id | `metrics` |
|
|
10
|
+
| Source path(s) | `src/metrics` |
|
|
11
|
+
| Doc kind | Module spec |
|
|
12
|
+
| Coverage score | Partial (manifest-authoritative); 15/15 required document fields present |
|
|
13
|
+
| Generated from | `module-spec` @ SDLC template library `0.2.1` |
|
|
14
|
+
| generated_by / approved_by / updated_at | Codex generator / developer-approved follow-up review remediation / 2026-07-21 |
|
|
15
|
+
| Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); coverage remains Partial |
|
|
16
|
+
|
|
17
|
+
## Evidence Rules
|
|
18
|
+
Every requirement cites stable source and test file paths. Code/tests are the behavioral referee; routed source text supplies explicit intent and rationale. Missing or contradictory evidence blocks promotion.
|
|
19
|
+
|
|
20
|
+
## Source Material Register
|
|
21
|
+
| Source material | Scope | Decision | Detail location or disposition |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| Reviewed prior module guides and architecture material | overview / architecture / API / tests | used and code-checked | Content is placed by meaning throughout this specification; exact routing remains in the manifest. |
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
Metrics is one of nine confirmed Contact Center SDK modules. Own timing, taxonomy, queuing, payload preparation, and submission for Contact Center behavioral, operational, and business telemetry. Existing reviewed documentation is migrated by meaning and code/tests remain the behavioral referee.
|
|
27
|
+
|
|
28
|
+
- **Singleton Pattern**: Single `MetricsManager` instance shared across the entire SDK
|
|
29
|
+
|
|
30
|
+
- **Three Metric Types**: Behavioral (user actions), operational (system events), business (business-level analytics)
|
|
31
|
+
|
|
32
|
+
- **Event Timing**: `timeEvent` + `trackEvent` pattern automatically calculates `duration_ms`
|
|
33
|
+
|
|
34
|
+
- **Queued Submission**: Events are queued until the Webex SDK is ready, then submitted in order
|
|
35
|
+
|
|
36
|
+
- **Behavioral Taxonomy**: Structured `product.agent.target.verb` naming convention for behavioral events
|
|
37
|
+
|
|
38
|
+
- **Payload Preparation**: Automatic cleanup of empty fields, space-to-underscore conversion, and `tabHidden` metadata
|
|
39
|
+
|
|
40
|
+
- **AQM Response Helpers**: Static methods to extract common tracking fields from AQM responses
|
|
41
|
+
|
|
42
|
+
## Purpose / Responsibility
|
|
43
|
+
Own timing, taxonomy, queuing, payload preparation, and submission for Contact Center behavioral, operational, and business telemetry.
|
|
44
|
+
|
|
45
|
+
## Stack
|
|
46
|
+
TypeScript 5.4 singleton service, Webex internal metrics APIs, LoggerProxy, Jest 27.
|
|
47
|
+
|
|
48
|
+
## Folder / Package Structure
|
|
49
|
+
```text
|
|
50
|
+
src/metrics/
|
|
51
|
+
├── MetricsManager.ts
|
|
52
|
+
├── behavioral-events.ts
|
|
53
|
+
├── constants.ts
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
src/metrics/
|
|
58
|
+
├── MetricsManager.ts # Singleton metrics manager
|
|
59
|
+
├── behavioral-events.ts # Behavioral event taxonomy mapping
|
|
60
|
+
├── constants.ts # METRIC_EVENT_NAMES constants
|
|
61
|
+
└── ai-docs/
|
|
62
|
+
├── AGENTS.md # Usage documentation (see PR #4762)
|
|
63
|
+
└── ARCHITECTURE.md # Preserved legacy, noncanonical architecture guide
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Key Files (source of truth)
|
|
67
|
+
| File | Holds |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `src/metrics/MetricsManager.ts` | Authoritative Metrics implementation or contract source. |
|
|
70
|
+
| `src/metrics/behavioral-events.ts` | Authoritative Metrics implementation or contract source. |
|
|
71
|
+
| `src/metrics/constants.ts` | Authoritative Metrics implementation or contract source. |
|
|
72
|
+
|
|
73
|
+
## Public Surface
|
|
74
|
+
| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index |
|
|
75
|
+
|---|---|---|---|---|---|---|
|
|
76
|
+
| `metrics.surface` | SDK / event / internal API | Internal `MetricsManager` singleton, metric event constants, taxonomy lookup, timing, and tracking helpers. | Stable module consumption boundary. | Additive changes by default; breaking package exports require a major-version transition. | `src/metrics/MetricsManager.ts` | `../../../ai-docs/CONTRACTS.md` |
|
|
77
|
+
|
|
78
|
+
Compatibility notes:
|
|
79
|
+
- Do not remove or reinterpret exported symbols/events without a documented consumer migration.
|
|
80
|
+
|
|
81
|
+
Returns the singleton instance. On first call with `{webex}`, binds to the Webex SDK and begins listening for the `ready` event.
|
|
82
|
+
|
|
83
|
+
**Parameters**:
|
|
84
|
+
|
|
85
|
+
- `options` (object, optional): `{webex: WebexSDK}` - The Webex SDK instance
|
|
86
|
+
|
|
87
|
+
**Returns**: `MetricsManager`
|
|
88
|
+
|
|
89
|
+
**Example**:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// During initialization (called internally by cc.register())
|
|
93
|
+
const metrics = MetricsManager.getInstance({webex});
|
|
94
|
+
|
|
95
|
+
// Subsequent calls (no webex needed)
|
|
96
|
+
const metrics = MetricsManager.getInstance();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Starts a timer for one or more event keys. When a matching `trackEvent` / `trackBehavioralEvent` / `trackOperationalEvent` / `trackBusinessEvent` is called, `duration_ms` is automatically added to the payload.
|
|
100
|
+
|
|
101
|
+
**Parameters**:
|
|
102
|
+
|
|
103
|
+
- `keys` (string | string[]): One or more `METRIC_EVENT_NAMES` values. The first key is the tracking key; all keys in the array will resolve the same timer.
|
|
104
|
+
|
|
105
|
+
**Returns**: `void`
|
|
106
|
+
|
|
107
|
+
**Example**:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Single key
|
|
111
|
+
metrics.timeEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS);
|
|
112
|
+
|
|
113
|
+
// Multiple keys (success/failure share one timer)
|
|
114
|
+
metrics.timeEvent([
|
|
115
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
116
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
117
|
+
]);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Tracks an event across one or more metric services.
|
|
121
|
+
|
|
122
|
+
**Parameters**:
|
|
123
|
+
|
|
124
|
+
- `name` (METRIC_EVENT_NAMES): The event name constant
|
|
125
|
+
|
|
126
|
+
- `payload` (EventPayload, optional): Key-value pairs of event data
|
|
127
|
+
|
|
128
|
+
- `metricServices` (MetricsType[], optional): Array of `'behavioral'` | `'operational'` | `'business'` (default: `['behavioral']`)
|
|
129
|
+
|
|
130
|
+
**Returns**: `void`
|
|
131
|
+
|
|
132
|
+
**Example**:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Behavioral only (default)
|
|
136
|
+
metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {agentId: '123'});
|
|
137
|
+
|
|
138
|
+
// Multiple services
|
|
139
|
+
metrics.trackEvent(
|
|
140
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
141
|
+
{interactionId: 'abc'},
|
|
142
|
+
['behavioral', 'operational']
|
|
143
|
+
);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Tracks a single behavioral event. Looks up the event taxonomy from `behavioral-events.ts` and submits via `webex.internal.newMetrics.submitBehavioralEvent`.
|
|
147
|
+
|
|
148
|
+
**Parameters**:
|
|
149
|
+
|
|
150
|
+
- `name` (METRIC_EVENT_NAMES): The event name
|
|
151
|
+
|
|
152
|
+
- `options` (EventPayload, optional): Additional payload data
|
|
153
|
+
|
|
154
|
+
**Returns**: `void`
|
|
155
|
+
|
|
156
|
+
Tracks a single operational event. Prefixes the event name with `WXCC_SDK_` and submits via `webex.internal.newMetrics.submitOperationalEvent`.
|
|
157
|
+
|
|
158
|
+
**Parameters**:
|
|
159
|
+
|
|
160
|
+
- `name` (METRIC_EVENT_NAMES): The event name
|
|
161
|
+
|
|
162
|
+
- `options` (EventPayload, optional): Additional payload data
|
|
163
|
+
|
|
164
|
+
**Returns**: `void`
|
|
165
|
+
|
|
166
|
+
Tracks a single business event. Prefixes the event name with `WXCC_SDK_` and submits via `webex.internal.newMetrics.submitBusinessEvent` with `appType: 'wxcc_sdk'`.
|
|
167
|
+
|
|
168
|
+
**Parameters**:
|
|
169
|
+
|
|
170
|
+
- `name` (METRIC_EVENT_NAMES): The event name
|
|
171
|
+
|
|
172
|
+
- `options` (EventPayload, optional): Additional payload data
|
|
173
|
+
|
|
174
|
+
**Returns**: `void`
|
|
175
|
+
|
|
176
|
+
Enables or disables metrics collection. When disabled, all pending events are cleared and new events are dropped.
|
|
177
|
+
|
|
178
|
+
**Parameters**:
|
|
179
|
+
|
|
180
|
+
- `disabled` (boolean): `true` to disable, `false` to enable
|
|
181
|
+
|
|
182
|
+
**Returns**: `void`
|
|
183
|
+
|
|
184
|
+
Static helper that extracts common tracking fields from an AQM success response.
|
|
185
|
+
|
|
186
|
+
**Parameters**:
|
|
187
|
+
|
|
188
|
+
- `response` (any): The AQM response object
|
|
189
|
+
|
|
190
|
+
**Returns**: `Record<string, any>` with fields: `agentId`, `agentSessionId`, `teamId`, `siteId`, `orgId`, `eventType`, `trackingId`, `notifTrackingId`
|
|
191
|
+
|
|
192
|
+
**Example**:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const fields = MetricsManager.getCommonTrackingFieldForAQMResponse(aqmResponse);
|
|
196
|
+
metrics.trackEvent(METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS, {
|
|
197
|
+
...fields,
|
|
198
|
+
interactionId: task.interactionId,
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Resets the singleton instance. Used for testing only.
|
|
203
|
+
|
|
204
|
+
**Returns**: `void`
|
|
205
|
+
|
|
206
|
+
All event names are defined in `METRIC_EVENT_NAMES` (`constants.ts`). Events follow a `<Category> <Action> <Result>` pattern.
|
|
207
|
+
|
|
208
|
+
| Constant | Value | Description |
|
|
209
|
+
|---|---|---|
|
|
210
|
+
| `STATION_LOGIN_SUCCESS` | `'Station Login Success'` | Agent station login succeeded |
|
|
211
|
+
| `STATION_LOGIN_FAILED` | `'Station Login Failed'` | Agent station login failed |
|
|
212
|
+
| `STATION_LOGOUT_SUCCESS` | `'Station Logout Success'` | Agent station logout succeeded |
|
|
213
|
+
| `STATION_LOGOUT_FAILED` | `'Station Logout Failed'` | Agent station logout failed |
|
|
214
|
+
| `STATION_RELOGIN_SUCCESS` | `'Station Relogin Success'` | Silent relogin succeeded |
|
|
215
|
+
| `STATION_RELOGIN_FAILED` | `'Station Relogin Failed'` | Silent relogin failed |
|
|
216
|
+
| `AGENT_STATE_CHANGE_SUCCESS` | `'Agent State Change Success'` | State change succeeded |
|
|
217
|
+
| `AGENT_STATE_CHANGE_FAILED` | `'Agent State Change Failed'` | State change failed |
|
|
218
|
+
| `FETCH_BUDDY_AGENTS_SUCCESS` | `'Fetch Buddy Agents Success'` | Buddy agents fetch succeeded |
|
|
219
|
+
| `FETCH_BUDDY_AGENTS_FAILED` | `'Fetch Buddy Agents Failed'` | Buddy agents fetch failed |
|
|
220
|
+
| `AGENT_RONA` | `'Agent RONA'` | Agent Ring-On-No-Answer triggered |
|
|
221
|
+
| `AGENT_CONTACT_ASSIGN_FAILED` | `'Agent Contact Assign Failed'` | Contact assignment failed |
|
|
222
|
+
| `AGENT_INVITE_FAILED` | `'Agent Invite Failed'` | Agent invite failed |
|
|
223
|
+
| `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `'Agent Device Type Update Success'` | Device type update succeeded |
|
|
224
|
+
| `AGENT_DEVICE_TYPE_UPDATE_FAILED` | `'Agent Device Type Update Failed'` | Device type update failed |
|
|
225
|
+
| `TASK_ACCEPT_SUCCESS` / `FAILED` | `'Task Accept ...'` | Task accept result |
|
|
226
|
+
| `TASK_DECLINE_SUCCESS` / `FAILED` | `'Task Decline ...'` | Task decline result |
|
|
227
|
+
| `TASK_END_SUCCESS` / `FAILED` | `'Task End ...'` | Task end result |
|
|
228
|
+
| `TASK_WRAPUP_SUCCESS` / `FAILED` | `'Task Wrapup ...'` | Task wrapup result |
|
|
229
|
+
| `TASK_HOLD_SUCCESS` / `FAILED` | `'Task Hold ...'` | Task hold result |
|
|
230
|
+
| `TASK_RESUME_SUCCESS` / `FAILED` | `'Task Resume ...'` | Task resume result |
|
|
231
|
+
| `TASK_CONSULT_START_SUCCESS` / `FAILED` | `'Task Consult Start ...'` | Consult start result |
|
|
232
|
+
| `TASK_CONSULT_END_SUCCESS` / `FAILED` | `'Task Consult End ...'` | Consult end result |
|
|
233
|
+
| `TASK_TRANSFER_SUCCESS` / `FAILED` | `'Task Transfer ...'` | Transfer result |
|
|
234
|
+
| `TASK_PAUSE_RECORDING_SUCCESS` / `FAILED` | `'Task Pause Recording ...'` | Pause recording result |
|
|
235
|
+
| `TASK_RESUME_RECORDING_SUCCESS` / `FAILED` | `'Task Resume Recording ...'` | Resume recording result |
|
|
236
|
+
| `TASK_ACCEPT_CONSULT_SUCCESS` / `FAILED` | `'Task Accept Consult ...'` | Accept consult result |
|
|
237
|
+
| `TASK_AUTO_ANSWER_SUCCESS` / `FAILED` | `'Task Auto Answer ...'` | Auto-answer result |
|
|
238
|
+
| `TASK_OUTDIAL_SUCCESS` / `FAILED` | `'Task Outdial ...'` | Outdial result |
|
|
239
|
+
| `TASK_CONFERENCE_START_SUCCESS` / `FAILED` | `'Task Conference Start ...'` | Conference start result |
|
|
240
|
+
| `TASK_CONFERENCE_END_SUCCESS` / `FAILED` | `'Task Conference End ...'` | Conference end result |
|
|
241
|
+
| `TASK_CONFERENCE_TRANSFER_SUCCESS` / `FAILED` | `'Task Conference Transfer ...'` | Conference transfer result |
|
|
242
|
+
| `TASK_CONFERENCE_EXIT_SUCCESS` / `FAILED` | `'Task Conference Exit ...'` | Conference exit result |
|
|
243
|
+
| `TASK_SWITCH_CALL_SUCCESS` / `FAILED` | `'Task Switch Call ...'` | Switch call result |
|
|
244
|
+
| `WEBSOCKET_REGISTER_SUCCESS` / `FAILED` | `'Websocket Register ...'` | WebSocket registration result |
|
|
245
|
+
| `WEBSOCKET_DEREGISTER_SUCCESS` / `FAIL` | `'Websocket Deregister ...'` | WebSocket deregistration result |
|
|
246
|
+
| `WEBSOCKET_EVENT_RECEIVED` | `'Websocket Event Received'` | WebSocket event received |
|
|
247
|
+
| `UPLOAD_LOGS_SUCCESS` / `FAILED` | `'Upload Logs ...'` | Log upload result |
|
|
248
|
+
| `ENTRYPOINT_FETCH_SUCCESS` / `FAILED` | `'Entrypoint Fetch ...'` | Entry point fetch result |
|
|
249
|
+
| `ADDRESSBOOK_FETCH_SUCCESS` / `FAILED` | `'AddressBook Fetch ...'` | Address book fetch result |
|
|
250
|
+
| `QUEUE_FETCH_SUCCESS` / `FAILED` | `'Queue Fetch ...'` | Queue fetch result |
|
|
251
|
+
| `OUTDIAL_ANI_EP_FETCH_SUCCESS` / `FAILED` | `'Outdial ANI Entries Fetch ...'` | Outdial ANI entries fetch result |
|
|
252
|
+
|
|
253
|
+
All event names are defined in `constants.ts` as `METRIC_EVENT_NAMES`. Events follow a `{Domain} {Action} {Success|Failed}` naming convention:
|
|
254
|
+
|
|
255
|
+
| Category | Success Event | Failure Event |
|
|
256
|
+
|---|---|---|
|
|
257
|
+
| Station Login | `STATION_LOGIN_SUCCESS` | `STATION_LOGIN_FAILED` |
|
|
258
|
+
| Station Logout | `STATION_LOGOUT_SUCCESS` | `STATION_LOGOUT_FAILED` |
|
|
259
|
+
| Station Relogin | `STATION_RELOGIN_SUCCESS` | `STATION_RELOGIN_FAILED` |
|
|
260
|
+
| State Change | `AGENT_STATE_CHANGE_SUCCESS` | `AGENT_STATE_CHANGE_FAILED` |
|
|
261
|
+
| Buddy Agents | `FETCH_BUDDY_AGENTS_SUCCESS` | `FETCH_BUDDY_AGENTS_FAILED` |
|
|
262
|
+
| WebSocket Register | `WEBSOCKET_REGISTER_SUCCESS` | `WEBSOCKET_REGISTER_FAILED` |
|
|
263
|
+
| Task Accept | `TASK_ACCEPT_SUCCESS` | `TASK_ACCEPT_FAILED` |
|
|
264
|
+
| Task Decline | `TASK_DECLINE_SUCCESS` | `TASK_DECLINE_FAILED` |
|
|
265
|
+
| Task End | `TASK_END_SUCCESS` | `TASK_END_FAILED` |
|
|
266
|
+
| Task Wrapup | `TASK_WRAPUP_SUCCESS` | `TASK_WRAPUP_FAILED` |
|
|
267
|
+
| Task Hold | `TASK_HOLD_SUCCESS` | `TASK_HOLD_FAILED` |
|
|
268
|
+
| Task Resume | `TASK_RESUME_SUCCESS` | `TASK_RESUME_FAILED` |
|
|
269
|
+
| Task Consult Start | `TASK_CONSULT_START_SUCCESS` | `TASK_CONSULT_START_FAILED` |
|
|
270
|
+
| Task Consult End | `TASK_CONSULT_END_SUCCESS` | `TASK_CONSULT_END_FAILED` |
|
|
271
|
+
| Task Transfer | `TASK_TRANSFER_SUCCESS` | `TASK_TRANSFER_FAILED` |
|
|
272
|
+
| Task Resume Recording | `TASK_RESUME_RECORDING_SUCCESS` | `TASK_RESUME_RECORDING_FAILED` |
|
|
273
|
+
| Task Pause Recording | `TASK_PAUSE_RECORDING_SUCCESS` | `TASK_PAUSE_RECORDING_FAILED` |
|
|
274
|
+
| Task Accept Consult | `TASK_ACCEPT_CONSULT_SUCCESS` | `TASK_ACCEPT_CONSULT_FAILED` |
|
|
275
|
+
| Task Auto Answer | `TASK_AUTO_ANSWER_SUCCESS` | `TASK_AUTO_ANSWER_FAILED` |
|
|
276
|
+
| Conference Start | `TASK_CONFERENCE_START_SUCCESS` | `TASK_CONFERENCE_START_FAILED` |
|
|
277
|
+
| Conference End | `TASK_CONFERENCE_END_SUCCESS` | `TASK_CONFERENCE_END_FAILED` |
|
|
278
|
+
| Conference Transfer | `TASK_CONFERENCE_TRANSFER_SUCCESS` | `TASK_CONFERENCE_TRANSFER_FAILED` |
|
|
279
|
+
| Conference Exit | `TASK_CONFERENCE_EXIT_SUCCESS` | `TASK_CONFERENCE_EXIT_FAILED` |
|
|
280
|
+
| Switch Call | `TASK_SWITCH_CALL_SUCCESS` | `TASK_SWITCH_CALL_FAILED` |
|
|
281
|
+
| Outdial | `TASK_OUTDIAL_SUCCESS` | `TASK_OUTDIAL_FAILED` |
|
|
282
|
+
| Upload Logs | `UPLOAD_LOGS_SUCCESS` | `UPLOAD_LOGS_FAILED` |
|
|
283
|
+
| WebSocket Deregister | `WEBSOCKET_DEREGISTER_SUCCESS` | `WEBSOCKET_DEREGISTER_FAIL` |
|
|
284
|
+
| Device Type Update | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `AGENT_DEVICE_TYPE_UPDATE_FAILED` |
|
|
285
|
+
| EntryPoint | `ENTRYPOINT_FETCH_SUCCESS` | `ENTRYPOINT_FETCH_FAILED` |
|
|
286
|
+
| AddressBook | `ADDRESSBOOK_FETCH_SUCCESS` | `ADDRESSBOOK_FETCH_FAILED` |
|
|
287
|
+
| Queue | `QUEUE_FETCH_SUCCESS` | `QUEUE_FETCH_FAILED` |
|
|
288
|
+
| Outdial ANI Entries | `OUTDIAL_ANI_EP_FETCH_SUCCESS` | `OUTDIAL_ANI_EP_FETCH_FAILED` |
|
|
289
|
+
|
|
290
|
+
Special events (no success/failure pair):
|
|
291
|
+
|
|
292
|
+
- `AGENT_RONA` — has behavioral taxonomy (`service.agent_rona.set`)
|
|
293
|
+
|
|
294
|
+
- `AGENT_CONTACT_ASSIGN_FAILED` — has behavioral taxonomy (`service.agent_contact_assign.fail`)
|
|
295
|
+
|
|
296
|
+
- `AGENT_INVITE_FAILED` — has behavioral taxonomy (`service.agent_invite.fail`)
|
|
297
|
+
|
|
298
|
+
- `WEBSOCKET_EVENT_RECEIVED` — **no** behavioral taxonomy (not in `eventTaxonomyMap`)
|
|
299
|
+
|
|
300
|
+
Of the 80 defined metric names, 71 have behavioral taxonomy and 9 do not. Events **without** an `eventTaxonomyMap` entry are the six `AI_ASSISTANT_*` names plus `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, and `WEBSOCKET_EVENT_RECEIVED`.
|
|
301
|
+
|
|
302
|
+
### Complete METRIC_EVENT_NAMES catalog
|
|
303
|
+
|
|
304
|
+
This table contains all 80 names from `src/metrics/constants.ts`; taxonomy presence is checked against `src/metrics/behavioral-events.ts`: 71 mapped and 9 unmapped.
|
|
305
|
+
|
|
306
|
+
| Constant | Emitted name | Behavioral taxonomy? |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| `STATION_LOGIN_SUCCESS` | `Station Login Success` | yes |
|
|
309
|
+
| `STATION_LOGIN_FAILED` | `Station Login Failed` | yes |
|
|
310
|
+
| `STATION_LOGOUT_SUCCESS` | `Station Logout Success` | yes |
|
|
311
|
+
| `STATION_LOGOUT_FAILED` | `Station Logout Failed` | yes |
|
|
312
|
+
| `STATION_RELOGIN_SUCCESS` | `Station Relogin Success` | yes |
|
|
313
|
+
| `STATION_RELOGIN_FAILED` | `Station Relogin Failed` | yes |
|
|
314
|
+
| `AGENT_STATE_CHANGE_SUCCESS` | `Agent State Change Success` | yes |
|
|
315
|
+
| `AGENT_STATE_CHANGE_FAILED` | `Agent State Change Failed` | yes |
|
|
316
|
+
| `FETCH_BUDDY_AGENTS_SUCCESS` | `Fetch Buddy Agents Success` | yes |
|
|
317
|
+
| `FETCH_BUDDY_AGENTS_FAILED` | `Fetch Buddy Agents Failed` | yes |
|
|
318
|
+
| `WEBSOCKET_REGISTER_SUCCESS` | `Websocket Register Success` | yes |
|
|
319
|
+
| `WEBSOCKET_REGISTER_FAILED` | `Websocket Register Failed` | yes |
|
|
320
|
+
| `AGENT_RONA` | `Agent RONA` | yes |
|
|
321
|
+
| `AGENT_CONTACT_ASSIGN_FAILED` | `Agent Contact Assign Failed` | yes |
|
|
322
|
+
| `AGENT_INVITE_FAILED` | `Agent Invite Failed` | yes |
|
|
323
|
+
| `TASK_ACCEPT_SUCCESS` | `Task Accept Success` | yes |
|
|
324
|
+
| `TASK_ACCEPT_FAILED` | `Task Accept Failed` | yes |
|
|
325
|
+
| `TASK_DECLINE_SUCCESS` | `Task Decline Success` | yes |
|
|
326
|
+
| `TASK_DECLINE_FAILED` | `Task Decline Failed` | yes |
|
|
327
|
+
| `TASK_END_SUCCESS` | `Task End Success` | yes |
|
|
328
|
+
| `TASK_END_FAILED` | `Task End Failed` | yes |
|
|
329
|
+
| `TASK_WRAPUP_SUCCESS` | `Task Wrapup Success` | yes |
|
|
330
|
+
| `TASK_WRAPUP_FAILED` | `Task Wrapup Failed` | yes |
|
|
331
|
+
| `TASK_HOLD_SUCCESS` | `Task Hold Success` | yes |
|
|
332
|
+
| `TASK_HOLD_FAILED` | `Task Hold Failed` | yes |
|
|
333
|
+
| `TASK_RESUME_SUCCESS` | `Task Resume Success` | yes |
|
|
334
|
+
| `TASK_RESUME_FAILED` | `Task Resume Failed` | yes |
|
|
335
|
+
| `TASK_CONSULT_START_SUCCESS` | `Task Consult Start Success` | yes |
|
|
336
|
+
| `TASK_CONSULT_START_FAILED` | `Task Consult Start Failed` | yes |
|
|
337
|
+
| `TASK_CONSULT_END_SUCCESS` | `Task Consult End Success` | yes |
|
|
338
|
+
| `TASK_CONSULT_END_FAILED` | `Task Consult End Failed` | yes |
|
|
339
|
+
| `TASK_TRANSFER_SUCCESS` | `Task Transfer Success` | yes |
|
|
340
|
+
| `TASK_TRANSFER_FAILED` | `Task Transfer Failed` | yes |
|
|
341
|
+
| `TASK_RESUME_RECORDING_SUCCESS` | `Task Resume Recording Success` | yes |
|
|
342
|
+
| `TASK_RESUME_RECORDING_FAILED` | `Task Resume Recording Failed` | yes |
|
|
343
|
+
| `TASK_PAUSE_RECORDING_SUCCESS` | `Task Pause Recording Success` | yes |
|
|
344
|
+
| `TASK_PAUSE_RECORDING_FAILED` | `Task Pause Recording Failed` | yes |
|
|
345
|
+
| `TASK_ACCEPT_CONSULT_SUCCESS` | `Task Accept Consult Success` | yes |
|
|
346
|
+
| `TASK_ACCEPT_CONSULT_FAILED` | `Task Accept Consult Failed` | yes |
|
|
347
|
+
| `TASK_AUTO_ANSWER_SUCCESS` | `Task Auto Answer Success` | yes |
|
|
348
|
+
| `TASK_AUTO_ANSWER_FAILED` | `Task Auto Answer Failed` | yes |
|
|
349
|
+
| `TASK_CONFERENCE_START_SUCCESS` | `Task Conference Start Success` | yes |
|
|
350
|
+
| `TASK_CONFERENCE_START_FAILED` | `Task Conference Start Failed` | yes |
|
|
351
|
+
| `TASK_CONFERENCE_END_SUCCESS` | `Task Conference End Success` | yes |
|
|
352
|
+
| `TASK_CONFERENCE_END_FAILED` | `Task Conference End Failed` | yes |
|
|
353
|
+
| `TASK_CONFERENCE_TRANSFER_SUCCESS` | `Task Conference Transfer Success` | yes |
|
|
354
|
+
| `TASK_CONFERENCE_TRANSFER_FAILED` | `Task Conference Transfer Failed` | yes |
|
|
355
|
+
| `TASK_CONFERENCE_EXIT_SUCCESS` | `Task Conference Exit Success` | yes |
|
|
356
|
+
| `TASK_CONFERENCE_EXIT_FAILED` | `Task Conference Exit Failed` | yes |
|
|
357
|
+
| `TASK_SWITCH_CALL_SUCCESS` | `Task Switch Call Success` | yes |
|
|
358
|
+
| `TASK_SWITCH_CALL_FAILED` | `Task Switch Call Failed` | yes |
|
|
359
|
+
| `TASK_OUTDIAL_SUCCESS` | `Task Outdial Success` | yes |
|
|
360
|
+
| `TASK_OUTDIAL_FAILED` | `Task Outdial Failed` | yes |
|
|
361
|
+
| `UPLOAD_LOGS_SUCCESS` | `Upload Logs Success` | yes |
|
|
362
|
+
| `UPLOAD_LOGS_FAILED` | `Upload Logs Failed` | yes |
|
|
363
|
+
| `WEBSOCKET_DEREGISTER_SUCCESS` | `Websocket Deregister Success` | no |
|
|
364
|
+
| `WEBSOCKET_DEREGISTER_FAIL` | `Websocket Deregister Failed` | no |
|
|
365
|
+
| `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `Agent Device Type Update Success` | yes |
|
|
366
|
+
| `AGENT_DEVICE_TYPE_UPDATE_FAILED` | `Agent Device Type Update Failed` | yes |
|
|
367
|
+
| `WEBSOCKET_EVENT_RECEIVED` | `Websocket Event Received` | no |
|
|
368
|
+
| `ENTRYPOINT_FETCH_SUCCESS` | `Entrypoint Fetch Success` | yes |
|
|
369
|
+
| `ENTRYPOINT_FETCH_FAILED` | `Entrypoint Fetch Failed` | yes |
|
|
370
|
+
| `ADDRESSBOOK_FETCH_SUCCESS` | `AddressBook Fetch Success` | yes |
|
|
371
|
+
| `ADDRESSBOOK_FETCH_FAILED` | `AddressBook Fetch Failed` | yes |
|
|
372
|
+
| `QUEUE_FETCH_SUCCESS` | `Queue Fetch Success` | yes |
|
|
373
|
+
| `QUEUE_FETCH_FAILED` | `Queue Fetch Failed` | yes |
|
|
374
|
+
| `OUTDIAL_ANI_EP_FETCH_SUCCESS` | `Outdial ANI Entries Fetch Success` | yes |
|
|
375
|
+
| `OUTDIAL_ANI_EP_FETCH_FAILED` | `Outdial ANI Entries Fetch Failed` | yes |
|
|
376
|
+
| `CAMPAIGN_PREVIEW_ACCEPT_SUCCESS` | `Campaign Preview Accept Success` | yes |
|
|
377
|
+
| `CAMPAIGN_PREVIEW_ACCEPT_FAILED` | `Campaign Preview Accept Failed` | yes |
|
|
378
|
+
| `CAMPAIGN_PREVIEW_SKIP_SUCCESS` | `Campaign Preview Skip Success` | yes |
|
|
379
|
+
| `CAMPAIGN_PREVIEW_SKIP_FAILED` | `Campaign Preview Skip Failed` | yes |
|
|
380
|
+
| `CAMPAIGN_PREVIEW_REMOVE_SUCCESS` | `Campaign Preview Remove Success` | yes |
|
|
381
|
+
| `CAMPAIGN_PREVIEW_REMOVE_FAILED` | `Campaign Preview Remove Failed` | yes |
|
|
382
|
+
| `AI_ASSISTANT_SEND_EVENT_SUCCESS` | `AI Assistant Send Event Success` | no |
|
|
383
|
+
| `AI_ASSISTANT_SEND_EVENT_FAILED` | `AI Assistant Send Event Failed` | no |
|
|
384
|
+
| `AI_ASSISTANT_GET_SUGGESTED_RESPONSE_SUCCESS` | `AI Assistant Get Suggested Response Success` | no |
|
|
385
|
+
| `AI_ASSISTANT_GET_SUGGESTED_RESPONSE_FAILED` | `AI Assistant Get Suggested Response Failed` | no |
|
|
386
|
+
| `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS` | `AI Assistant Fetch Historic Transcripts Success` | no |
|
|
387
|
+
| `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED` | `AI Assistant Fetch Historic Transcripts Failed` | no |
|
|
388
|
+
|
|
389
|
+
Defined names without an `eventTaxonomyMap` entry: `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED`, `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS`, `AI_ASSISTANT_GET_SUGGESTED_RESPONSE_FAILED`, `AI_ASSISTANT_GET_SUGGESTED_RESPONSE_SUCCESS`, `AI_ASSISTANT_SEND_EVENT_FAILED`, `AI_ASSISTANT_SEND_EVENT_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_EVENT_RECEIVED`.
|
|
390
|
+
|
|
391
|
+
## Requires (dependencies)
|
|
392
|
+
- `webex.internal.newMetrics` submission APIs
|
|
393
|
+
- Webex ready lifecycle
|
|
394
|
+
- LoggerProxy and browser visibility metadata
|
|
395
|
+
|
|
396
|
+
- **`@webex/internal-plugin-metrics`**: Provides `webex.internal.newMetrics` for actual metric submission (`submitBehavioralEvent`, `submitOperationalEvent`, `submitBusinessEvent`)
|
|
397
|
+
|
|
398
|
+
- **`LoggerProxy`**: Used for error logging within the metrics module
|
|
399
|
+
|
|
400
|
+
- **`Failure` type** (from `services/core/GlobalTypes`): Used in `getCommonTrackingFieldForAQMResponseFailed`
|
|
401
|
+
|
|
402
|
+
- **`PRODUCT_NAME`** (from `constants.ts`): Set to `'wxcc_sdk'`, used as the product identifier in behavioral taxonomy and as prefix for operational/business event names
|
|
403
|
+
|
|
404
|
+
## Requirements
|
|
405
|
+
| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence |
|
|
406
|
+
|---|---|---|---|---|---|---|
|
|
407
|
+
| METRICS-R-001 | Track only names defined by METRIC_EVENT_NAMES and keep the canonical catalog synchronized with the const object. | Telemetry queries and dashboards depend on exact stable event names. | `src/metrics/constants.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
408
|
+
| METRICS-R-002 | Map behavioral events through eventTaxonomyMap and explicitly identify defined names without taxonomy. | Undefined taxonomy must not be mistaken for an emitted or classified behavioral event. | `src/metrics/behavioral-events.ts` | `test/unit/spec/metrics/behavioral-events.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
409
|
+
| METRICS-R-003 | When metrics are disabled, clear pending events and make `timeEvent` plus all tracking methods return without recording/submitting. | Telemetry must never block or alter product behavior and disablement must be comprehensive. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
410
|
+
| METRICS-R-004 | Queue submissions until the host SDK is ready and flush through the correct behavioral/operational/business service. | Early lifecycle telemetry must not be lost solely because the host is not ready. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
411
|
+
| METRICS-R-005 | Submit through the host SDK's `webex.internal.newMetrics` client without storing credentials or implementing authorization policy in MetricsManager. | Host-owned authentication keeps telemetry credential handling outside the Contact Center metrics module. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; authentication is inherited and credential ownership is explicitly N/A. | PRESENT |
|
|
412
|
+
|
|
413
|
+
## Design Overview
|
|
414
|
+
Metrics separates its stable consumption boundary from collaborators so ownership and failure behavior stay explicit. Telemetry is deliberately non-blocking and queue-backed so product behavior never waits for metrics; failures are logged rather than propagated.
|
|
415
|
+
|
|
416
|
+
> **Purpose**: Track behavioral, operational, and business metrics for Contact Center SDK operations using a singleton `MetricsManager`. Provides event timing, payload preparation, batching, and submission to the Webex metrics backend.
|
|
417
|
+
|
|
418
|
+
Each behavioral event maps to a structured taxonomy in `behavioral-events.ts`:
|
|
419
|
+
|
|
420
|
+
```text
|
|
421
|
+
{product}.{agent}.{target}.{verb}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
- **product**: Always `'wxcc_sdk'` (from `PRODUCT_NAME`)
|
|
425
|
+
|
|
426
|
+
- **agent**: `'user'` for user-initiated actions, `'service'` for system-generated events
|
|
427
|
+
|
|
428
|
+
- **target**: Snake_case description of the action (e.g., `'station_login'`, `'task_accept'`)
|
|
429
|
+
|
|
430
|
+
- **verb**: `'complete'` for success, `'fail'` for failure, `'set'` for RONA events
|
|
431
|
+
|
|
432
|
+
**Example**: `STATION_LOGIN_SUCCESS` maps to `wxcc_sdk.user.station_login.complete`
|
|
433
|
+
|
|
434
|
+
> **Note**: The following events do **not** have behavioral taxonomy mappings in `behavioral-events.ts`:
|
|
435
|
+
> - `AI_ASSISTANT_SEND_EVENT_SUCCESS`
|
|
436
|
+
> - `AI_ASSISTANT_SEND_EVENT_FAILED`
|
|
437
|
+
> - `AI_ASSISTANT_GET_SUGGESTED_RESPONSE_SUCCESS`
|
|
438
|
+
> - `AI_ASSISTANT_GET_SUGGESTED_RESPONSE_FAILED`
|
|
439
|
+
> - `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS`
|
|
440
|
+
> - `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED`
|
|
441
|
+
> - `WEBSOCKET_DEREGISTER_SUCCESS`
|
|
442
|
+
> - `WEBSOCKET_DEREGISTER_FAIL`
|
|
443
|
+
> - `WEBSOCKET_EVENT_RECEIVED`
|
|
444
|
+
>
|
|
445
|
+
> Calling `trackBehavioralEvent` with these event names will push an event with an `undefined` taxonomy.
|
|
446
|
+
|
|
447
|
+
> **Purpose**: Technical documentation for the metrics collection, batching, and submission system within the Contact Center SDK.
|
|
448
|
+
|
|
449
|
+
Each metric event name maps to a `BehavioralEventTaxonomy` with four fields:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
type BehavioralEventTaxonomy = {
|
|
453
|
+
product: MetricEventProduct; // Always PRODUCT_NAME ('wxcc_sdk')
|
|
454
|
+
agent: MetricEventAgent; // 'user' or 'service'
|
|
455
|
+
target: string; // e.g., 'station_login', 'task_accept'
|
|
456
|
+
verb: MetricEventVerb; // 'complete' for success, 'fail' for failure
|
|
457
|
+
};
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
The final behavioral event name is constructed as: `{product}.{agent}.{target}.{verb}`
|
|
461
|
+
|
|
462
|
+
Example: `wxcc_sdk.user.station_login.complete`
|
|
463
|
+
|
|
464
|
+
The mapping is defined in `behavioral-events.ts` via `eventTaxonomyMap` and accessed through `getEventTaxonomy(name)`.
|
|
465
|
+
|
|
466
|
+
```mermaid
|
|
467
|
+
flowchart LR
|
|
468
|
+
A[timeEvent keys] --> B[runningEvents stores startTime + key Set]
|
|
469
|
+
B --> C[trackEvent called with one of the keys]
|
|
470
|
+
C --> D[addDurationIfTimed matches key]
|
|
471
|
+
D --> E[Calculates duration_ms = now - startTime]
|
|
472
|
+
E --> F[Removes all keys for that operation]
|
|
473
|
+
F --> G[Attaches duration_ms to payload]
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Usage pattern from `cc.ts`:
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// Before operation
|
|
480
|
+
this.metricsManager.timeEvent([
|
|
481
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
482
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
483
|
+
]);
|
|
484
|
+
|
|
485
|
+
// On success
|
|
486
|
+
this.metricsManager.trackEvent(
|
|
487
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
488
|
+
{ ...MetricsManager.getCommonTrackingFieldForAQMResponse(resp) },
|
|
489
|
+
['behavioral', 'operational', 'business']
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
// On failure
|
|
493
|
+
this.metricsManager.trackEvent(
|
|
494
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
495
|
+
{ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure) },
|
|
496
|
+
['behavioral', 'operational', 'business']
|
|
497
|
+
);
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Two static helpers extract standardized fields from AQM responses for metric payloads:
|
|
501
|
+
|
|
502
|
+
Extracts: `agentId`, `agentSessionId`, `teamId`, `siteId`, `orgId`, `eventType`, `trackingId`, `notifTrackingId`
|
|
503
|
+
|
|
504
|
+
## Data Flow
|
|
505
|
+
```mermaid
|
|
506
|
+
flowchart TD
|
|
507
|
+
A[cc.ts calls metricsManager.timeEvent] --> B[Store startTime + keys in runningEvents]
|
|
508
|
+
B --> C[Operation executes]
|
|
509
|
+
C --> D{Success or Failure?}
|
|
510
|
+
D -->|Success| E[cc.ts calls metricsManager.trackEvent with success name]
|
|
511
|
+
D -->|Failure| F[cc.ts calls metricsManager.trackEvent with failure name]
|
|
512
|
+
E --> G[addDurationIfTimed attaches duration_ms]
|
|
513
|
+
F --> G
|
|
514
|
+
G --> H[preparePayload cleans and enriches]
|
|
515
|
+
H --> I{Metric type?}
|
|
516
|
+
I -->|behavioral| J[Push to pendingBehavioralEvents]
|
|
517
|
+
I -->|operational| K[Push to pendingOperationalEvents]
|
|
518
|
+
I -->|business| L[Push to pendingBusinessEvents]
|
|
519
|
+
J --> M[submitPendingBehavioralEvents]
|
|
520
|
+
K --> N[submitPendingOperationalEvents]
|
|
521
|
+
L --> O[submitPendingBusinessEvents]
|
|
522
|
+
M --> P[webex.internal.newMetrics.submitBehavioralEvent]
|
|
523
|
+
N --> Q[webex.internal.newMetrics.submitOperationalEvent]
|
|
524
|
+
O --> R[webex.internal.newMetrics.submitBusinessEvent]
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## Sequence Diagram(s)
|
|
528
|
+
Sequence coverage:
|
|
529
|
+
|
|
530
|
+
| Operation group | Diagram | Failure / recovery coverage |
|
|
531
|
+
|---|---|---|
|
|
532
|
+
| Time and track an event | Timing and tracking | Success/failure keys share one timer; missing timing data leaves the payload untimed. |
|
|
533
|
+
| Queue until SDK readiness | SDK readiness | A not-ready SDK retains events; READY drains them through the configured metrics service. |
|
|
534
|
+
| Submit behavioral/operational/business events | Category submission | The module does not retry or requeue after handing an event to `newMetrics`. |
|
|
535
|
+
| Disable metrics | Disablement | Pending queues are cleared and future timing/tracking calls return immediately. |
|
|
536
|
+
|
|
537
|
+
### Timing and tracking
|
|
538
|
+
|
|
539
|
+
```mermaid
|
|
540
|
+
sequenceDiagram
|
|
541
|
+
participant CC as ContactCenter (cc.ts)
|
|
542
|
+
participant MM as MetricsManager
|
|
543
|
+
participant NM as webex.internal.newMetrics
|
|
544
|
+
|
|
545
|
+
CC->>MM: timeEvent([SUCCESS_KEY, FAILURE_KEY])
|
|
546
|
+
Note over MM: Store startTime + key set in runningEvents
|
|
547
|
+
CC->>CC: Execute operation (e.g., stationLogin)
|
|
548
|
+
Note over MM: trackEvent defaults to ['behavioral'] only if no metricServices specified
|
|
549
|
+
alt Success
|
|
550
|
+
CC->>MM: trackEvent(SUCCESS_KEY, payload, ['behavioral', 'operational', 'business'])
|
|
551
|
+
else Failure
|
|
552
|
+
CC->>MM: trackEvent(FAILURE_KEY, payload, ['behavioral', 'operational', 'business'])
|
|
553
|
+
end
|
|
554
|
+
MM->>MM: addDurationIfTimed → attach duration_ms
|
|
555
|
+
MM->>MM: preparePayload → clean empty fields, add tabHidden
|
|
556
|
+
loop For each metric type
|
|
557
|
+
MM->>MM: Push to pending queue
|
|
558
|
+
alt readyToSubmitEvents
|
|
559
|
+
MM->>NM: submit[Behavioral|Operational|Business]Event
|
|
560
|
+
else not ready
|
|
561
|
+
Note over MM: Events stay queued until SDK ready
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### SDK readiness
|
|
567
|
+
|
|
568
|
+
```mermaid
|
|
569
|
+
sequenceDiagram
|
|
570
|
+
participant CC as ContactCenter
|
|
571
|
+
participant MM as MetricsManager
|
|
572
|
+
participant Webex as WebexSDK
|
|
573
|
+
|
|
574
|
+
CC->>MM: getInstance({webex})
|
|
575
|
+
MM->>MM: Create singleton (if needed)
|
|
576
|
+
MM->>MM: setWebex(webex)
|
|
577
|
+
opt webex.ready === true
|
|
578
|
+
MM->>MM: setReadyToSubmitEvents()
|
|
579
|
+
MM->>MM: submitPendingEvents()
|
|
580
|
+
end
|
|
581
|
+
MM->>Webex: webex.once('ready', callback)
|
|
582
|
+
Note over MM: 'ready' listener is always registered
|
|
583
|
+
Webex-->>MM: 'ready' event fires
|
|
584
|
+
MM->>MM: setReadyToSubmitEvents()
|
|
585
|
+
MM->>MM: submitPendingEvents()
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Category submission
|
|
589
|
+
|
|
590
|
+
```mermaid
|
|
591
|
+
sequenceDiagram
|
|
592
|
+
participant Caller
|
|
593
|
+
participant MM as MetricsManager
|
|
594
|
+
participant NM as webex.internal.newMetrics
|
|
595
|
+
Caller->>MM: trackEvent(name, payload, metricServices)
|
|
596
|
+
loop each requested metric service
|
|
597
|
+
alt behavioral
|
|
598
|
+
MM->>NM: submitBehavioralEvent(taxonomy + payload)
|
|
599
|
+
else operational
|
|
600
|
+
MM->>NM: submitOperationalEvent(WXCC_SDK_* + payload)
|
|
601
|
+
else business
|
|
602
|
+
MM->>NM: submitBusinessEvent(WXCC_SDK_* + appType)
|
|
603
|
+
else invalid metric service
|
|
604
|
+
MM->>MM: LoggerProxy.error(invalid type)
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
Note over MM,NM: No module-level retry or requeue after submission handoff
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Disablement
|
|
611
|
+
|
|
612
|
+
```mermaid
|
|
613
|
+
sequenceDiagram
|
|
614
|
+
participant Caller
|
|
615
|
+
participant MM as MetricsManager
|
|
616
|
+
Caller->>MM: setMetricsDisabled(true)
|
|
617
|
+
MM->>MM: clear pending behavioral/operational/business queues
|
|
618
|
+
Caller->>MM: timeEvent(...) / trackEvent(...)
|
|
619
|
+
alt metricsDisabled
|
|
620
|
+
MM-->>Caller: return without timing, queueing, or submission
|
|
621
|
+
else re-enabled
|
|
622
|
+
MM->>MM: use normal timing/queue flow
|
|
623
|
+
end
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## Class / Component Relationships
|
|
627
|
+
```mermaid
|
|
628
|
+
classDiagram
|
|
629
|
+
class ContactCenter
|
|
630
|
+
class MetricsManager
|
|
631
|
+
class METRIC_EVENT_NAMES
|
|
632
|
+
class eventTaxonomyMap
|
|
633
|
+
class NewMetrics
|
|
634
|
+
ContactCenter --> MetricsManager : timeEvent / trackEvent
|
|
635
|
+
MetricsManager --> METRIC_EVENT_NAMES : canonical names
|
|
636
|
+
MetricsManager --> eventTaxonomyMap : behavioral taxonomy lookup
|
|
637
|
+
MetricsManager --> NewMetrics : submit queued event types
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
| Component | File | Responsibility |
|
|
641
|
+
|---|---|---|
|
|
642
|
+
| `MetricsManager` | `MetricsManager.ts` | Singleton that manages event queuing, timing, payload preparation, and submission |
|
|
643
|
+
| `BehavioralEventTaxonomy`| `behavioral-events.ts` | Maps metric event names to structured taxonomy for behavioral analytics |
|
|
644
|
+
| `METRIC_EVENT_NAMES` | `constants.ts` | Canonical constant object of all tracked metric event names |
|
|
645
|
+
|
|
646
|
+
`MetricsManager` uses a private constructor with a static `getInstance` factory:
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
// MetricsManager.ts
|
|
650
|
+
export default class MetricsManager {
|
|
651
|
+
private static instance: MetricsManager;
|
|
652
|
+
private constructor() {}
|
|
653
|
+
|
|
654
|
+
public static getInstance(options?: {webex: WebexSDK}): MetricsManager {
|
|
655
|
+
if (!MetricsManager.instance) {
|
|
656
|
+
MetricsManager.instance = new MetricsManager();
|
|
657
|
+
}
|
|
658
|
+
if (!MetricsManager.instance.webex && options?.webex) {
|
|
659
|
+
MetricsManager.instance.setWebex(options.webex);
|
|
660
|
+
}
|
|
661
|
+
return MetricsManager.instance;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
public static resetInstance() {
|
|
665
|
+
MetricsManager.instance = undefined;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
- The Webex SDK instance is set once via `setWebex()`, which listens for the `ready` event before flushing pending queues.
|
|
671
|
+
|
|
672
|
+
- `resetInstance()` sets the singleton to `undefined`, allowing a fresh instance to be created. Primarily used in tests.
|
|
673
|
+
|
|
674
|
+
## Use Cases
|
|
675
|
+
- **UC-1 Time and track an event:** `timeEvent` records the start against success/failure keys and `trackEvent` attaches `duration_ms` before preparing and queuing the payload. Evidence: `src/metrics/MetricsManager.ts`, `test/unit/spec/metrics/MetricsManager.ts`.
|
|
676
|
+
- **UC-2 Queue until SDK readiness:** events remain in the behavioral/operational/business pending queues until the host is ready, then the ready callback drains them. Evidence: `src/metrics/MetricsManager.ts`, `test/unit/spec/metrics/MetricsManager.ts`.
|
|
677
|
+
- **UC-3 Submit event categories:** each requested metric service uses its matching `webex.internal.newMetrics` submission method; taxonomy is applied only where `eventTaxonomyMap` contains the name. Evidence: `src/metrics/MetricsManager.ts`, `src/metrics/behavioral-events.ts`, `test/unit/spec/metrics/MetricsManager.ts`.
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
import MetricsManager from '../metrics/MetricsManager';
|
|
681
|
+
import {METRIC_EVENT_NAMES} from '../metrics/constants';
|
|
682
|
+
|
|
683
|
+
// Get the singleton instance (webex is set during the ContactCenter READY callback)
|
|
684
|
+
const metrics = MetricsManager.getInstance();
|
|
685
|
+
|
|
686
|
+
// Time an operation, then track its result
|
|
687
|
+
metrics.timeEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS);
|
|
688
|
+
// ... perform the operation ...
|
|
689
|
+
metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {agentId: '123'});
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
The standard pattern used throughout the Contact Center SDK:
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
const metrics = MetricsManager.getInstance();
|
|
696
|
+
|
|
697
|
+
// 1. Start timing before the operation
|
|
698
|
+
metrics.timeEvent([
|
|
699
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
700
|
+
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
701
|
+
]);
|
|
702
|
+
|
|
703
|
+
try {
|
|
704
|
+
const response = await performLogin(params);
|
|
705
|
+
|
|
706
|
+
// 2a. Track success (duration_ms auto-added)
|
|
707
|
+
metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {
|
|
708
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
|
|
709
|
+
});
|
|
710
|
+
} catch (error) {
|
|
711
|
+
// 2b. Track failure (duration_ms auto-added)
|
|
712
|
+
metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_FAILED, {
|
|
713
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error),
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
## State Model
|
|
719
|
+
MetricsManager holds three pending queues, running timing entries, a readiness flag, a submission lock, and an independent disabled flag. Host READY enables draining; disabling metrics prevents both timing and tracking and clears pending events through the configured disable path.
|
|
720
|
+
|
|
721
|
+
`preparePayload()` processes every event payload before submission:
|
|
722
|
+
|
|
723
|
+
1. **Removes empty/null/undefined fields** — strips keys with `undefined`, `null`, `''`, any arrays, or empty objects
|
|
724
|
+
|
|
725
|
+
2. **Converts spaces to underscores** — `spacesToUnderscore()` applied to all key names
|
|
726
|
+
|
|
727
|
+
3. **Adds common metadata** — appends `tabHidden: document.hidden` in browser environments
|
|
728
|
+
|
|
729
|
+
## Business Rules & Invariants
|
|
730
|
+
- `timeEvent` and every tracking method return immediately while metrics are disabled.
|
|
731
|
+
- Only names present in `eventTaxonomyMap` receive behavioral taxonomy; 71 of 80 names are mapped and the six AI Assistant plus three WebSocket names are intentionally catalogued as unmapped.
|
|
732
|
+
- `submittingEvents` prevents overlapping queue-drain executions.
|
|
733
|
+
- Authentication is inherited from the host Webex SDK's metrics client; MetricsManager owns no credentials, tokens, or authorization policy.
|
|
734
|
+
|
|
735
|
+
## Concurrency & Reactive Flow
|
|
736
|
+
- Queue insertion is synchronous. Submission is gated by host readiness and the submission lock; each metric type drains through its own host method without propagating telemetry failures to product callers.
|
|
737
|
+
|
|
738
|
+
MetricsManager maintains three independent pending event queues:
|
|
739
|
+
|
|
740
|
+
| Queue | Type | Submitted Via | Name Transform | Extra Metadata |
|
|
741
|
+
|---|---|---|---|---|
|
|
742
|
+
| `pendingBehavioralEvents` | behavioral | `webex.internal.newMetrics.submitBehavioralEvent` | Taxonomy-based (`{product}.{agent}.{target}.{verb}`) | None |
|
|
743
|
+
| `pendingOperationalEvents` | operational | `webex.internal.newMetrics.submitOperationalEvent` | `WXCC_SDK_` prefix + uppercase (e.g. `WXCC_SDK_STATION_LOGIN_SUCCESS`) | None |
|
|
744
|
+
| `pendingBusinessEvents` | business | `webex.internal.newMetrics.submitBusinessEvent` | `WXCC_SDK_` prefix + uppercase (same as operational) | `metadata: {appType: 'wxcc_sdk'}` |
|
|
745
|
+
|
|
746
|
+
## State Machine
|
|
747
|
+
```mermaid
|
|
748
|
+
stateDiagram-v2
|
|
749
|
+
[*] --> Buffering
|
|
750
|
+
Buffering --> ReadyToSubmit: host already ready / ready event
|
|
751
|
+
ReadyToSubmit --> Submitting: pending events and lock acquired
|
|
752
|
+
Submitting --> ReadyToSubmit: queues drained and lock released
|
|
753
|
+
Buffering --> Disabled: setMetricsDisabled(true)
|
|
754
|
+
ReadyToSubmit --> Disabled: setMetricsDisabled(true)
|
|
755
|
+
Disabled --> Buffering: enabled before host ready
|
|
756
|
+
Disabled --> ReadyToSubmit: enabled after host ready
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
- **readyToSubmitEvents**: Set to `true` only after `webex.once('ready')` fires. Events queue until then.
|
|
760
|
+
|
|
761
|
+
- **submittingEvents**: Lock flag to prevent concurrent `submitPendingEvents()` calls.
|
|
762
|
+
|
|
763
|
+
- **metricsDisabled**: When `true`, `timeEvent` and all `track*` methods return early, and `clearPendingEvents()` empties all queues.
|
|
764
|
+
|
|
765
|
+
## Pitfalls
|
|
766
|
+
- `METRIC_EVENT_NAMES` and `eventTaxonomyMap` are different inventories: nine defined names intentionally have no behavioral taxonomy.
|
|
767
|
+
- `setMetricsDisabled(true)` clears pending queues but does not create a delivery receipt; callers must not infer that previously submitted events were accepted.
|
|
768
|
+
- Submission helpers hand events to `webex.internal.newMetrics` without a module-level retry/requeue policy, so telemetry must remain non-blocking and non-authoritative.
|
|
769
|
+
|
|
770
|
+
Static helper that extracts common tracking fields from an AQM failure response.
|
|
771
|
+
|
|
772
|
+
**Parameters**:
|
|
773
|
+
|
|
774
|
+
- `failureResponse` (Failure): The AQM failure response object
|
|
775
|
+
|
|
776
|
+
**Returns**: `Record<string, any>` with fields: `agentId`, `trackingId`, `notifTrackingId`, `orgId`, `failureType`, `failureReason`, `reasonCode`
|
|
777
|
+
|
|
778
|
+
MetricsManager is designed to be non-blocking. Metric failures do not propagate to callers:
|
|
779
|
+
|
|
780
|
+
- If `webex` is not yet ready, events are queued in `pendingBehavioralEvents`, `pendingOperationalEvents`, or `pendingBusinessEvents`
|
|
781
|
+
|
|
782
|
+
- Once `webex.ready` fires, all pending events are flushed
|
|
783
|
+
|
|
784
|
+
- If metrics are disabled via `setMetricsDisabled(true)`, all track methods silently return
|
|
785
|
+
|
|
786
|
+
- Invalid metric types log an error via `LoggerProxy` but do not throw
|
|
787
|
+
|
|
788
|
+
MetricsManager does not throw errors to callers. Instead:
|
|
789
|
+
|
|
790
|
+
- Invalid metric types are logged via `LoggerProxy.error`
|
|
791
|
+
|
|
792
|
+
- Empty `timeEvent` key arrays are logged and ignored
|
|
793
|
+
|
|
794
|
+
- Disabled state (`metricsDisabled`) prevents both timing and tracking and silently drops new events
|
|
795
|
+
|
|
796
|
+
- The `submittingEvents` lock prevents race conditions during concurrent submissions
|
|
797
|
+
|
|
798
|
+
Extracts: `agentId`, `trackingId`, `notifTrackingId`, `orgId`, `failureType`, `failureReason`, `reasonCode`
|
|
799
|
+
|
|
800
|
+
**Cause**: Webex SDK not yet ready when `trackEvent` is called
|
|
801
|
+
|
|
802
|
+
**Solution**: Events are automatically queued in `pending*Events` arrays and flushed once `webex.once('ready')` fires. Verify the SDK is initializing correctly.
|
|
803
|
+
|
|
804
|
+
**Cause**: `timeEvent` was not called before `trackEvent`, or the event name does not match any key in `runningEvents`
|
|
805
|
+
|
|
806
|
+
**Solution**: Ensure `timeEvent([SUCCESS_KEY, FAILURE_KEY])` is called before the operation, and that the exact `METRIC_EVENT_NAMES` constant is used in both calls.
|
|
807
|
+
|
|
808
|
+
**Cause**: `metricsDisabled` is set to `true`
|
|
809
|
+
|
|
810
|
+
**Solution**: Check if `setMetricsDisabled(true)` was called. This clears all pending queues and causes all `track*` methods to return early.
|
|
811
|
+
|
|
812
|
+
## Module Do's / Don'ts
|
|
813
|
+
- DO use `METRIC_EVENT_NAMES` and the matching category submitter instead of constructing event names manually.
|
|
814
|
+
- DO keep timing success/failure keys together so either outcome clears the same running timer.
|
|
815
|
+
- DON'T add a behavioral taxonomy entry unless the backend taxonomy contract exists.
|
|
816
|
+
- DON'T make product behavior depend on metrics delivery.
|
|
817
|
+
|
|
818
|
+
## Key Design Trade-off
|
|
819
|
+
- Telemetry is deliberately non-blocking and queue-backed so product behavior never waits for metrics; failures are logged rather than propagated.
|
|
820
|
+
|
|
821
|
+
## Test-Case Strategy (module)
|
|
822
|
+
Use `test/unit/spec/metrics/MetricsManager.ts` for readiness queues, timing/tracking, disabled behavior, submission categories, and failures. Use `test/unit/spec/metrics/behavioral-events.ts` to reconcile every taxonomy-backed name. Mechanically compare the complete constant catalog with taxonomy keys so newly defined events cannot disappear from the spec.
|
|
823
|
+
|
|
824
|
+
| Behavior / Requirement | Existing test evidence | Gap |
|
|
825
|
+
|---|---|---|
|
|
826
|
+
| `METRICS-R-001` | `test/unit/spec/metrics/MetricsManager.ts` | Add a catalog parity assertion if constants change. |
|
|
827
|
+
| `METRICS-R-002` | `test/unit/spec/metrics/behavioral-events.ts` | Keep explicit coverage for all nine unmapped names. |
|
|
828
|
+
| `METRICS-R-003` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
|
|
829
|
+
| `METRICS-R-004` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
|
|
830
|
+
| `METRICS-R-005` | `test/unit/spec/metrics/MetricsManager.ts` | Authentication ownership is verified indirectly through the host metrics client. |
|
|
831
|
+
|
|
832
|
+
## Traceability
|
|
833
|
+
- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../ai-docs/SPEC_INDEX.md`
|
|
834
|
+
- Coverage state and contracts baseline: `../../../.sdd/manifest.json`
|
|
835
|
+
|
|
836
|
+
- [`MetricsManager.ts`](../MetricsManager.ts) - Singleton metrics manager implementation
|
|
837
|
+
|
|
838
|
+
- [`behavioral-events.ts`](../behavioral-events.ts) - Event taxonomy mapping
|
|
839
|
+
|
|
840
|
+
- [`constants.ts`](../constants.ts) - `METRIC_EVENT_NAMES` definitions
|
|
841
|
+
|
|
842
|
+
- [`../../constants.ts`](../../constants.ts) - `PRODUCT_NAME` constant
|
|
843
|
+
|
|
844
|
+
- [`services/core/GlobalTypes.ts`](../../services/core/GlobalTypes.ts) - `Failure` type definition
|
|
845
|
+
|
|
846
|
+
- [MetricsManager.ts](../MetricsManager.ts) — Singleton metrics manager
|
|
847
|
+
|
|
848
|
+
- [behavioral-events.ts](../behavioral-events.ts) — Event taxonomy mapping
|
|
849
|
+
|
|
850
|
+
- [constants.ts](../constants.ts) — METRIC_EVENT_NAMES definitions
|
|
851
|
+
|
|
852
|
+
- [cc.ts](../../cc.ts) — Main plugin class (primary consumer)
|
|
853
|
+
|
|
854
|
+
- [constants.ts](../../constants.ts) — PRODUCT_NAME used in event prefixing
|