@webex/contact-center 3.12.0-next.9 → 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.
Files changed (200) hide show
  1. package/AGENTS.md +438 -0
  2. package/ai-docs/README.md +131 -0
  3. package/ai-docs/RULES.md +455 -0
  4. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  5. package/ai-docs/patterns/testing-patterns.md +480 -0
  6. package/ai-docs/patterns/typescript-patterns.md +365 -0
  7. package/ai-docs/templates/README.md +102 -0
  8. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  9. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  10. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  11. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  12. package/ai-docs/templates/new-method/00-master.md +80 -0
  13. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  14. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  15. package/ai-docs/templates/new-method/03-tests.md +201 -0
  16. package/ai-docs/templates/new-method/04-validation.md +141 -0
  17. package/ai-docs/templates/new-service/00-master.md +109 -0
  18. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  19. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  20. package/ai-docs/templates/new-service/03-integration.md +178 -0
  21. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  22. package/ai-docs/templates/new-service/05-validation.md +145 -0
  23. package/dist/cc.js +65 -123
  24. package/dist/cc.js.map +1 -1
  25. package/dist/constants.js +13 -2
  26. package/dist/constants.js.map +1 -1
  27. package/dist/index.js +13 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/metrics/behavioral-events.js +26 -13
  30. package/dist/metrics/behavioral-events.js.map +1 -1
  31. package/dist/metrics/constants.js +7 -6
  32. package/dist/metrics/constants.js.map +1 -1
  33. package/dist/services/ApiAiAssistant.js +0 -3
  34. package/dist/services/ApiAiAssistant.js.map +1 -1
  35. package/dist/services/config/Util.js +2 -3
  36. package/dist/services/config/Util.js.map +1 -1
  37. package/dist/services/config/types.js +16 -14
  38. package/dist/services/config/types.js.map +1 -1
  39. package/dist/services/constants.js +0 -1
  40. package/dist/services/constants.js.map +1 -1
  41. package/dist/services/core/Err.js.map +1 -1
  42. package/dist/services/core/Utils.js +79 -55
  43. package/dist/services/core/Utils.js.map +1 -1
  44. package/dist/services/core/aqm-reqs.js +17 -92
  45. package/dist/services/core/aqm-reqs.js.map +1 -1
  46. package/dist/services/core/websocket/WebSocketManager.js +5 -25
  47. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  48. package/dist/services/core/websocket/types.js.map +1 -1
  49. package/dist/services/index.js +1 -2
  50. package/dist/services/index.js.map +1 -1
  51. package/dist/services/task/Task.js +644 -0
  52. package/dist/services/task/Task.js.map +1 -0
  53. package/dist/services/task/TaskFactory.js +45 -0
  54. package/dist/services/task/TaskFactory.js.map +1 -0
  55. package/dist/services/task/TaskManager.js +556 -532
  56. package/dist/services/task/TaskManager.js.map +1 -1
  57. package/dist/services/task/TaskUtils.js +132 -28
  58. package/dist/services/task/TaskUtils.js.map +1 -1
  59. package/dist/services/task/constants.js +7 -6
  60. package/dist/services/task/constants.js.map +1 -1
  61. package/dist/services/task/dialer.js +0 -51
  62. package/dist/services/task/dialer.js.map +1 -1
  63. package/dist/services/task/digital/Digital.js +77 -0
  64. package/dist/services/task/digital/Digital.js.map +1 -0
  65. package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
  66. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  67. package/dist/services/task/state-machine/actions.js +366 -0
  68. package/dist/services/task/state-machine/actions.js.map +1 -0
  69. package/dist/services/task/state-machine/constants.js +139 -0
  70. package/dist/services/task/state-machine/constants.js.map +1 -0
  71. package/dist/services/task/state-machine/guards.js +256 -0
  72. package/dist/services/task/state-machine/guards.js.map +1 -0
  73. package/dist/services/task/state-machine/index.js +53 -0
  74. package/dist/services/task/state-machine/index.js.map +1 -0
  75. package/dist/services/task/state-machine/types.js +54 -0
  76. package/dist/services/task/state-machine/types.js.map +1 -0
  77. package/dist/services/task/state-machine/uiControlsComputer.js +369 -0
  78. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  79. package/dist/services/task/taskDataNormalizer.js +99 -0
  80. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  81. package/dist/services/task/types.js +157 -18
  82. package/dist/services/task/types.js.map +1 -1
  83. package/dist/services/task/voice/Voice.js +1031 -0
  84. package/dist/services/task/voice/Voice.js.map +1 -0
  85. package/dist/services/task/voice/WebRTC.js +149 -0
  86. package/dist/services/task/voice/WebRTC.js.map +1 -0
  87. package/dist/types/cc.d.ts +4 -33
  88. package/dist/types/constants.d.ts +13 -2
  89. package/dist/types/index.d.ts +11 -5
  90. package/dist/types/metrics/constants.d.ts +5 -3
  91. package/dist/types/services/ApiAiAssistant.d.ts +1 -1
  92. package/dist/types/services/config/types.d.ts +97 -25
  93. package/dist/types/services/core/Err.d.ts +0 -2
  94. package/dist/types/services/core/Utils.d.ts +25 -23
  95. package/dist/types/services/core/aqm-reqs.d.ts +0 -49
  96. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
  97. package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
  98. package/dist/types/services/core/websocket/types.d.ts +1 -1
  99. package/dist/types/services/index.d.ts +1 -1
  100. package/dist/types/services/task/Task.d.ts +146 -0
  101. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  102. package/dist/types/services/task/TaskUtils.d.ts +39 -8
  103. package/dist/types/services/task/constants.d.ts +5 -4
  104. package/dist/types/services/task/dialer.d.ts +0 -15
  105. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  106. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
  107. package/dist/types/services/task/state-machine/actions.d.ts +8 -0
  108. package/dist/types/services/task/state-machine/constants.d.ts +91 -0
  109. package/dist/types/services/task/state-machine/guards.d.ts +78 -0
  110. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  111. package/dist/types/services/task/state-machine/types.d.ts +256 -0
  112. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  113. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  114. package/dist/types/services/task/types.d.ts +539 -88
  115. package/dist/types/services/task/voice/Voice.d.ts +183 -0
  116. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  117. package/dist/types/types.d.ts +68 -0
  118. package/dist/types/webex.d.ts +1 -0
  119. package/dist/types.js +70 -0
  120. package/dist/types.js.map +1 -1
  121. package/dist/webex.js +14 -2
  122. package/dist/webex.js.map +1 -1
  123. package/package.json +14 -11
  124. package/src/cc.ts +91 -177
  125. package/src/constants.ts +13 -2
  126. package/src/index.ts +14 -5
  127. package/src/metrics/ai-docs/AGENTS.md +348 -0
  128. package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
  129. package/src/metrics/behavioral-events.ts +28 -14
  130. package/src/metrics/constants.ts +7 -8
  131. package/src/services/ApiAiAssistant.ts +2 -4
  132. package/src/services/agent/ai-docs/AGENTS.md +238 -0
  133. package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
  134. package/src/services/ai-docs/AGENTS.md +384 -0
  135. package/src/services/config/Util.ts +2 -3
  136. package/src/services/config/ai-docs/AGENTS.md +253 -0
  137. package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
  138. package/src/services/config/types.ts +108 -20
  139. package/src/services/constants.ts +0 -1
  140. package/src/services/core/Err.ts +0 -1
  141. package/src/services/core/Utils.ts +90 -67
  142. package/src/services/core/ai-docs/AGENTS.md +379 -0
  143. package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
  144. package/src/services/core/aqm-reqs.ts +22 -100
  145. package/src/services/core/websocket/WebSocketManager.ts +4 -23
  146. package/src/services/core/websocket/types.ts +1 -1
  147. package/src/services/index.ts +1 -2
  148. package/src/services/task/Task.ts +785 -0
  149. package/src/services/task/TaskFactory.ts +55 -0
  150. package/src/services/task/TaskManager.ts +567 -633
  151. package/src/services/task/TaskUtils.ts +175 -31
  152. package/src/services/task/ai-docs/AGENTS.md +448 -0
  153. package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
  154. package/src/services/task/constants.ts +5 -4
  155. package/src/services/task/dialer.ts +1 -56
  156. package/src/services/task/digital/Digital.ts +95 -0
  157. package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
  158. package/src/services/task/state-machine/actions.ts +409 -0
  159. package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
  160. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
  161. package/src/services/task/state-machine/constants.ts +150 -0
  162. package/src/services/task/state-machine/guards.ts +295 -0
  163. package/src/services/task/state-machine/index.ts +28 -0
  164. package/src/services/task/state-machine/types.ts +228 -0
  165. package/src/services/task/state-machine/uiControlsComputer.ts +529 -0
  166. package/src/services/task/taskDataNormalizer.ts +137 -0
  167. package/src/services/task/types.ts +641 -95
  168. package/src/services/task/voice/Voice.ts +1255 -0
  169. package/src/services/task/voice/WebRTC.ts +187 -0
  170. package/src/types.ts +88 -5
  171. package/src/utils/AGENTS.md +276 -0
  172. package/src/webex.js +2 -0
  173. package/test/unit/spec/cc.ts +59 -142
  174. package/test/unit/spec/logger-proxy.ts +70 -0
  175. package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
  176. package/test/unit/spec/services/config/index.ts +26 -55
  177. package/test/unit/spec/services/core/Utils.ts +103 -52
  178. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
  179. package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
  180. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  181. package/test/unit/spec/services/task/Task.ts +416 -0
  182. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  183. package/test/unit/spec/services/task/TaskManager.ts +781 -1735
  184. package/test/unit/spec/services/task/TaskUtils.ts +125 -0
  185. package/test/unit/spec/services/task/dialer.ts +112 -198
  186. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  187. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
  188. package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
  189. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  190. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
  191. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  192. package/test/unit/spec/services/task/voice/Voice.ts +587 -0
  193. package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
  194. package/umd/contact-center.min.js +2 -2
  195. package/umd/contact-center.min.js.map +1 -1
  196. package/dist/services/task/index.js +0 -1525
  197. package/dist/services/task/index.js.map +0 -1
  198. package/dist/types/services/task/index.d.ts +0 -650
  199. package/src/services/task/index.ts +0 -1801
  200. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,348 @@
1
+ # Metrics Module - AI Agent Guide
2
+
3
+ > **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.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ ```typescript
10
+ import MetricsManager from '../metrics/MetricsManager';
11
+ import {METRIC_EVENT_NAMES} from '../metrics/constants';
12
+
13
+ // Get the singleton instance (webex is set during cc.register())
14
+ const metrics = MetricsManager.getInstance();
15
+
16
+ // Time an operation, then track its result
17
+ metrics.timeEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS);
18
+ // ... perform the operation ...
19
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {agentId: '123'});
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Key Capabilities
25
+
26
+ - **Singleton Pattern**: Single `MetricsManager` instance shared across the entire SDK
27
+ - **Three Metric Types**: Behavioral (user actions), operational (system events), business (business-level analytics)
28
+ - **Event Timing**: `timeEvent` + `trackEvent` pattern automatically calculates `duration_ms`
29
+ - **Queued Submission**: Events are queued until the Webex SDK is ready, then submitted in order
30
+ - **Behavioral Taxonomy**: Structured `product.agent.target.verb` naming convention for behavioral events
31
+ - **Payload Preparation**: Automatic cleanup of empty fields, space-to-underscore conversion, and `tabHidden` metadata
32
+ - **AQM Response Helpers**: Static methods to extract common tracking fields from AQM responses
33
+
34
+ ---
35
+
36
+ ## API Reference
37
+
38
+ ### Methods
39
+
40
+ #### `MetricsManager.getInstance(options?)`
41
+
42
+ Returns the singleton instance. On first call with `{webex}`, binds to the Webex SDK and begins listening for the `ready` event.
43
+
44
+ **Parameters**:
45
+ - `options` (object, optional): `{webex: WebexSDK}` - The Webex SDK instance
46
+
47
+ **Returns**: `MetricsManager`
48
+
49
+ **Example**:
50
+ ```typescript
51
+ // During initialization (called internally by cc.register())
52
+ const metrics = MetricsManager.getInstance({webex});
53
+
54
+ // Subsequent calls (no webex needed)
55
+ const metrics = MetricsManager.getInstance();
56
+ ```
57
+
58
+ ---
59
+
60
+ #### `metrics.timeEvent(keys)`
61
+
62
+ 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.
63
+
64
+ **Parameters**:
65
+ - `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.
66
+
67
+ **Returns**: `void`
68
+
69
+ **Example**:
70
+ ```typescript
71
+ // Single key
72
+ metrics.timeEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS);
73
+
74
+ // Multiple keys (success/failure share one timer)
75
+ metrics.timeEvent([
76
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
77
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
78
+ ]);
79
+ ```
80
+
81
+ ---
82
+
83
+ #### `metrics.trackEvent(name, payload?, metricServices?)`
84
+
85
+ Tracks an event across one or more metric services.
86
+
87
+ **Parameters**:
88
+ - `name` (METRIC_EVENT_NAMES): The event name constant
89
+ - `payload` (EventPayload, optional): Key-value pairs of event data
90
+ - `metricServices` (MetricsType[], optional): Array of `'behavioral'` | `'operational'` | `'business'` (default: `['behavioral']`)
91
+
92
+ **Returns**: `void`
93
+
94
+ **Example**:
95
+ ```typescript
96
+ // Behavioral only (default)
97
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {agentId: '123'});
98
+
99
+ // Multiple services
100
+ metrics.trackEvent(
101
+ METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
102
+ {interactionId: 'abc'},
103
+ ['behavioral', 'operational']
104
+ );
105
+ ```
106
+
107
+ ---
108
+
109
+ #### `metrics.trackBehavioralEvent(name, options?)`
110
+
111
+ Tracks a single behavioral event. Looks up the event taxonomy from `behavioral-events.ts` and submits via `webex.internal.newMetrics.submitBehavioralEvent`.
112
+
113
+ **Parameters**:
114
+ - `name` (METRIC_EVENT_NAMES): The event name
115
+ - `options` (EventPayload, optional): Additional payload data
116
+
117
+ **Returns**: `void`
118
+
119
+ ---
120
+
121
+ #### `metrics.trackOperationalEvent(name, options?)`
122
+
123
+ Tracks a single operational event. Prefixes the event name with `WXCC_SDK_` and submits via `webex.internal.newMetrics.submitOperationalEvent`.
124
+
125
+ **Parameters**:
126
+ - `name` (METRIC_EVENT_NAMES): The event name
127
+ - `options` (EventPayload, optional): Additional payload data
128
+
129
+ **Returns**: `void`
130
+
131
+ ---
132
+
133
+ #### `metrics.trackBusinessEvent(name, options?)`
134
+
135
+ Tracks a single business event. Prefixes the event name with `WXCC_SDK_` and submits via `webex.internal.newMetrics.submitBusinessEvent` with `appType: 'wxcc_sdk'`.
136
+
137
+ **Parameters**:
138
+ - `name` (METRIC_EVENT_NAMES): The event name
139
+ - `options` (EventPayload, optional): Additional payload data
140
+
141
+ **Returns**: `void`
142
+
143
+ ---
144
+
145
+ #### `metrics.setMetricsDisabled(disabled)`
146
+
147
+ Enables or disables metrics collection. When disabled, all pending events are cleared and new events are dropped.
148
+
149
+ **Parameters**:
150
+ - `disabled` (boolean): `true` to disable, `false` to enable
151
+
152
+ **Returns**: `void`
153
+
154
+ ---
155
+
156
+ #### `MetricsManager.getCommonTrackingFieldForAQMResponse(response)`
157
+
158
+ Static helper that extracts common tracking fields from an AQM success response.
159
+
160
+ **Parameters**:
161
+ - `response` (any): The AQM response object
162
+
163
+ **Returns**: `Record<string, any>` with fields: `agentId`, `agentSessionId`, `teamId`, `siteId`, `orgId`, `eventType`, `trackingId`, `notifTrackingId`
164
+
165
+ **Example**:
166
+ ```typescript
167
+ const fields = MetricsManager.getCommonTrackingFieldForAQMResponse(aqmResponse);
168
+ metrics.trackEvent(METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS, {
169
+ ...fields,
170
+ interactionId: task.interactionId,
171
+ });
172
+ ```
173
+
174
+ ---
175
+
176
+ #### `MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResponse)`
177
+
178
+ Static helper that extracts common tracking fields from an AQM failure response.
179
+
180
+ **Parameters**:
181
+ - `failureResponse` (Failure): The AQM failure response object
182
+
183
+ **Returns**: `Record<string, any>` with fields: `agentId`, `trackingId`, `notifTrackingId`, `orgId`, `failureType`, `failureReason`, `reasonCode`
184
+
185
+ ---
186
+
187
+ #### `MetricsManager.resetInstance()`
188
+
189
+ Resets the singleton instance. Used for testing only.
190
+
191
+ **Returns**: `void`
192
+
193
+ ---
194
+
195
+ ## Metric Event Names
196
+
197
+ All event names are defined in `METRIC_EVENT_NAMES` (`constants.ts`). Events follow a `<Category> <Action> <Result>` pattern.
198
+
199
+ ### Agent Events
200
+
201
+ | Constant | Value | Description |
202
+ |----------|-------|-------------|
203
+ | `STATION_LOGIN_SUCCESS` | `'Station Login Success'` | Agent station login succeeded |
204
+ | `STATION_LOGIN_FAILED` | `'Station Login Failed'` | Agent station login failed |
205
+ | `STATION_LOGOUT_SUCCESS` | `'Station Logout Success'` | Agent station logout succeeded |
206
+ | `STATION_LOGOUT_FAILED` | `'Station Logout Failed'` | Agent station logout failed |
207
+ | `STATION_RELOGIN_SUCCESS` | `'Station Relogin Success'` | Silent relogin succeeded |
208
+ | `STATION_RELOGIN_FAILED` | `'Station Relogin Failed'` | Silent relogin failed |
209
+ | `AGENT_STATE_CHANGE_SUCCESS` | `'Agent State Change Success'` | State change succeeded |
210
+ | `AGENT_STATE_CHANGE_FAILED` | `'Agent State Change Failed'` | State change failed |
211
+ | `FETCH_BUDDY_AGENTS_SUCCESS` | `'Fetch Buddy Agents Success'` | Buddy agents fetch succeeded |
212
+ | `FETCH_BUDDY_AGENTS_FAILED` | `'Fetch Buddy Agents Failed'` | Buddy agents fetch failed |
213
+ | `AGENT_RONA` | `'Agent RONA'` | Agent Ring-On-No-Answer triggered |
214
+ | `AGENT_CONTACT_ASSIGN_FAILED` | `'Agent Contact Assign Failed'` | Contact assignment failed |
215
+ | `AGENT_INVITE_FAILED` | `'Agent Invite Failed'` | Agent invite failed |
216
+ | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `'Agent Device Type Update Success'` | Device type update succeeded |
217
+ | `AGENT_DEVICE_TYPE_UPDATE_FAILED` | `'Agent Device Type Update Failed'` | Device type update failed |
218
+
219
+ ### Task Events
220
+
221
+ | Constant | Value | Description |
222
+ |----------|-------|-------------|
223
+ | `TASK_ACCEPT_SUCCESS` / `FAILED` | `'Task Accept ...'` | Task accept result |
224
+ | `TASK_DECLINE_SUCCESS` / `FAILED` | `'Task Decline ...'` | Task decline result |
225
+ | `TASK_END_SUCCESS` / `FAILED` | `'Task End ...'` | Task end result |
226
+ | `TASK_WRAPUP_SUCCESS` / `FAILED` | `'Task Wrapup ...'` | Task wrapup result |
227
+ | `TASK_HOLD_SUCCESS` / `FAILED` | `'Task Hold ...'` | Task hold result |
228
+ | `TASK_RESUME_SUCCESS` / `FAILED` | `'Task Resume ...'` | Task resume result |
229
+ | `TASK_CONSULT_START_SUCCESS` / `FAILED` | `'Task Consult Start ...'` | Consult start result |
230
+ | `TASK_CONSULT_END_SUCCESS` / `FAILED` | `'Task Consult End ...'` | Consult end result |
231
+ | `TASK_TRANSFER_SUCCESS` / `FAILED` | `'Task Transfer ...'` | Transfer result |
232
+ | `TASK_PAUSE_RECORDING_SUCCESS` / `FAILED` | `'Task Pause Recording ...'` | Pause recording result |
233
+ | `TASK_RESUME_RECORDING_SUCCESS` / `FAILED` | `'Task Resume Recording ...'` | Resume recording result |
234
+ | `TASK_ACCEPT_CONSULT_SUCCESS` / `FAILED` | `'Task Accept Consult ...'` | Accept consult result |
235
+ | `TASK_AUTO_ANSWER_SUCCESS` / `FAILED` | `'Task Auto Answer ...'` | Auto-answer result |
236
+ | `TASK_OUTDIAL_SUCCESS` / `FAILED` | `'Task Outdial ...'` | Outdial result |
237
+
238
+ ### Conference Events
239
+
240
+ | Constant | Value | Description |
241
+ |----------|-------|-------------|
242
+ | `TASK_CONFERENCE_START_SUCCESS` / `FAILED` | `'Task Conference Start ...'` | Conference start result |
243
+ | `TASK_CONFERENCE_END_SUCCESS` / `FAILED` | `'Task Conference End ...'` | Conference end result |
244
+ | `TASK_CONFERENCE_TRANSFER_SUCCESS` / `FAILED` | `'Task Conference Transfer ...'` | Conference transfer result |
245
+ | `TASK_CONFERENCE_EXIT_SUCCESS` / `FAILED` | `'Task Conference Exit ...'` | Conference exit result |
246
+ | `TASK_SWITCH_CALL_SUCCESS` / `FAILED` | `'Task Switch Call ...'` | Switch call result |
247
+
248
+ ### System Events
249
+
250
+ | Constant | Value | Description |
251
+ |----------|-------|-------------|
252
+ | `WEBSOCKET_REGISTER_SUCCESS` / `FAILED` | `'Websocket Register ...'` | WebSocket registration result |
253
+ | `WEBSOCKET_DEREGISTER_SUCCESS` / `FAIL` | `'Websocket Deregister ...'` | WebSocket deregistration result |
254
+ | `WEBSOCKET_EVENT_RECEIVED` | `'Websocket Event Received'` | WebSocket event received |
255
+ | `UPLOAD_LOGS_SUCCESS` / `FAILED` | `'Upload Logs ...'` | Log upload result |
256
+
257
+ ### Data Fetch Events
258
+
259
+ | Constant | Value | Description |
260
+ |----------|-------|-------------|
261
+ | `ENTRYPOINT_FETCH_SUCCESS` / `FAILED` | `'Entrypoint Fetch ...'` | Entry point fetch result |
262
+ | `ADDRESSBOOK_FETCH_SUCCESS` / `FAILED` | `'AddressBook Fetch ...'` | Address book fetch result |
263
+ | `QUEUE_FETCH_SUCCESS` / `FAILED` | `'Queue Fetch ...'` | Queue fetch result |
264
+ | `OUTDIAL_ANI_EP_FETCH_SUCCESS` / `FAILED` | `'Outdial ANI Entries Fetch ...'` | Outdial ANI entries fetch result |
265
+
266
+ ---
267
+
268
+ ## Behavioral Event Taxonomy
269
+
270
+ Each behavioral event maps to a structured taxonomy in `behavioral-events.ts`:
271
+
272
+ ```
273
+ {product}.{agent}.{target}.{verb}
274
+ ```
275
+
276
+ - **product**: Always `'wxcc_sdk'` (from `PRODUCT_NAME`)
277
+ - **agent**: `'user'` for user-initiated actions, `'service'` for system-generated events
278
+ - **target**: Snake_case description of the action (e.g., `'station_login'`, `'task_accept'`)
279
+ - **verb**: `'complete'` for success, `'fail'` for failure, `'set'` for RONA events
280
+
281
+ **Example**: `STATION_LOGIN_SUCCESS` maps to `wxcc_sdk.user.station_login.complete`
282
+
283
+ > **Note**: The following events do **not** have behavioral taxonomy mappings in `behavioral-events.ts`:
284
+ > - `WEBSOCKET_DEREGISTER_SUCCESS`
285
+ > - `WEBSOCKET_DEREGISTER_FAIL`
286
+ > - `WEBSOCKET_EVENT_RECEIVED`
287
+ >
288
+ > Calling `trackBehavioralEvent` with these event names will push an event with an `undefined` taxonomy.
289
+
290
+ ---
291
+
292
+ ## Usage Pattern (timeEvent + trackEvent)
293
+
294
+ The standard pattern used throughout the Contact Center SDK:
295
+
296
+ ```typescript
297
+ const metrics = MetricsManager.getInstance();
298
+
299
+ // 1. Start timing before the operation
300
+ metrics.timeEvent([
301
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
302
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
303
+ ]);
304
+
305
+ try {
306
+ const response = await performLogin(params);
307
+
308
+ // 2a. Track success (duration_ms auto-added)
309
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {
310
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
311
+ });
312
+ } catch (error) {
313
+ // 2b. Track failure (duration_ms auto-added)
314
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_FAILED, {
315
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error),
316
+ });
317
+ }
318
+ ```
319
+
320
+ ---
321
+
322
+ ## Error Handling
323
+
324
+ MetricsManager is designed to be non-blocking. Metric failures do not propagate to callers:
325
+
326
+ - If `webex` is not yet ready, events are queued in `pendingBehavioralEvents`, `pendingOperationalEvents`, or `pendingBusinessEvents`
327
+ - Once `webex.ready` fires, all pending events are flushed
328
+ - If metrics are disabled via `setMetricsDisabled(true)`, all track methods silently return
329
+ - Invalid metric types log an error via `LoggerProxy` but do not throw
330
+
331
+ ---
332
+
333
+ ## Dependencies
334
+
335
+ - **`@webex/internal-plugin-metrics`**: Provides `webex.internal.newMetrics` for actual metric submission (`submitBehavioralEvent`, `submitOperationalEvent`, `submitBusinessEvent`)
336
+ - **`LoggerProxy`**: Used for error logging within the metrics module
337
+ - **`Failure` type** (from `services/core/GlobalTypes`): Used in `getCommonTrackingFieldForAQMResponseFailed`
338
+ - **`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
339
+
340
+ ---
341
+
342
+ ## Related
343
+
344
+ - [`MetricsManager.ts`](../MetricsManager.ts) - Singleton metrics manager implementation
345
+ - [`behavioral-events.ts`](../behavioral-events.ts) - Event taxonomy mapping
346
+ - [`constants.ts`](../constants.ts) - `METRIC_EVENT_NAMES` definitions
347
+ - [`../../constants.ts`](../../constants.ts) - `PRODUCT_NAME` constant
348
+ - [`services/core/GlobalTypes.ts`](../../services/core/GlobalTypes.ts) - `Failure` type definition
@@ -0,0 +1,336 @@
1
+ # Metrics Module - Architecture
2
+
3
+ > **Purpose**: Technical documentation for the metrics collection, batching, and submission system within the Contact Center SDK.
4
+
5
+ ---
6
+
7
+ ## Component Overview
8
+
9
+ | Component | File | Responsibility |
10
+ | ------------------------ | ----------------------- | ------------------------------------------------------------------------------ |
11
+ | `MetricsManager` | `MetricsManager.ts` | Singleton that manages event queuing, timing, payload preparation, and submission |
12
+ | `BehavioralEventTaxonomy`| `behavioral-events.ts` | Maps metric event names to structured taxonomy for behavioral analytics |
13
+ | `METRIC_EVENT_NAMES` | `constants.ts` | Canonical constant object of all tracked metric event names |
14
+
15
+ ---
16
+
17
+ ## File Structure
18
+
19
+ ```
20
+ src/metrics/
21
+ ├── MetricsManager.ts # Singleton metrics manager
22
+ ├── behavioral-events.ts # Behavioral event taxonomy mapping
23
+ ├── constants.ts # METRIC_EVENT_NAMES constants
24
+ └── ai-docs/
25
+ ├── AGENTS.md # Usage documentation (see PR #4762)
26
+ └── ARCHITECTURE.md # This file
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Singleton Pattern
32
+
33
+ `MetricsManager` uses a private constructor with a static `getInstance` factory:
34
+
35
+ ```typescript
36
+ // MetricsManager.ts
37
+ export default class MetricsManager {
38
+ private static instance: MetricsManager;
39
+ private constructor() {}
40
+
41
+ public static getInstance(options?: {webex: WebexSDK}): MetricsManager {
42
+ if (!MetricsManager.instance) {
43
+ MetricsManager.instance = new MetricsManager();
44
+ }
45
+ if (!MetricsManager.instance.webex && options?.webex) {
46
+ MetricsManager.instance.setWebex(options.webex);
47
+ }
48
+ return MetricsManager.instance;
49
+ }
50
+
51
+ public static resetInstance() {
52
+ MetricsManager.instance = undefined;
53
+ }
54
+ }
55
+ ```
56
+
57
+ - The Webex SDK instance is set once via `setWebex()`, which listens for the `ready` event before flushing pending queues.
58
+ - `resetInstance()` sets the singleton to `undefined`, allowing a fresh instance to be created. Primarily used in tests.
59
+
60
+ ---
61
+
62
+ ## Data Flow
63
+
64
+ ### Event Submission Flow
65
+
66
+ ```mermaid
67
+ flowchart TD
68
+ A[cc.ts calls metricsManager.timeEvent] --> B[Store startTime + keys in runningEvents]
69
+ B --> C[Operation executes]
70
+ C --> D{Success or Failure?}
71
+ D -->|Success| E[cc.ts calls metricsManager.trackEvent with success name]
72
+ D -->|Failure| F[cc.ts calls metricsManager.trackEvent with failure name]
73
+ E --> G[addDurationIfTimed attaches duration_ms]
74
+ F --> G
75
+ G --> H[preparePayload cleans and enriches]
76
+ H --> I{Metric type?}
77
+ I -->|behavioral| J[Push to pendingBehavioralEvents]
78
+ I -->|operational| K[Push to pendingOperationalEvents]
79
+ I -->|business| L[Push to pendingBusinessEvents]
80
+ J --> M[submitPendingBehavioralEvents]
81
+ K --> N[submitPendingOperationalEvents]
82
+ L --> O[submitPendingBusinessEvents]
83
+ M --> P[webex.internal.newMetrics.submitBehavioralEvent]
84
+ N --> Q[webex.internal.newMetrics.submitOperationalEvent]
85
+ O --> R[webex.internal.newMetrics.submitBusinessEvent]
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Sequence Diagrams
91
+
92
+ ### Track Event with Timing
93
+
94
+ ```mermaid
95
+ sequenceDiagram
96
+ participant CC as ContactCenter (cc.ts)
97
+ participant MM as MetricsManager
98
+ participant NM as webex.internal.newMetrics
99
+
100
+ CC->>MM: timeEvent([SUCCESS_KEY, FAILURE_KEY])
101
+ Note over MM: Store startTime + key set in runningEvents
102
+ CC->>CC: Execute operation (e.g., stationLogin)
103
+ Note over MM: trackEvent defaults to ['behavioral'] only if no metricServices specified
104
+ alt Success
105
+ CC->>MM: trackEvent(SUCCESS_KEY, payload, ['behavioral', 'operational', 'business'])
106
+ else Failure
107
+ CC->>MM: trackEvent(FAILURE_KEY, payload, ['behavioral', 'operational', 'business'])
108
+ end
109
+ MM->>MM: addDurationIfTimed → attach duration_ms
110
+ MM->>MM: preparePayload → clean empty fields, add tabHidden
111
+ loop For each metric type
112
+ MM->>MM: Push to pending queue
113
+ alt readyToSubmitEvents
114
+ MM->>NM: submit[Behavioral|Operational|Business]Event
115
+ else not ready
116
+ Note over MM: Events stay queued until SDK ready
117
+ end
118
+ end
119
+ ```
120
+
121
+ ### Initialization and Readiness
122
+
123
+ ```mermaid
124
+ sequenceDiagram
125
+ participant CC as ContactCenter
126
+ participant MM as MetricsManager
127
+ participant Webex as WebexSDK
128
+
129
+ CC->>MM: getInstance({webex})
130
+ MM->>MM: Create singleton (if needed)
131
+ MM->>MM: setWebex(webex)
132
+ opt webex.ready === true
133
+ MM->>MM: setReadyToSubmitEvents()
134
+ MM->>MM: submitPendingEvents()
135
+ end
136
+ MM->>Webex: webex.once('ready', callback)
137
+ Note over MM: 'ready' listener is always registered
138
+ Webex-->>MM: 'ready' event fires
139
+ MM->>MM: setReadyToSubmitEvents()
140
+ MM->>MM: submitPendingEvents()
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Behavioral Event Taxonomy
146
+
147
+ Each metric event name maps to a `BehavioralEventTaxonomy` with four fields:
148
+
149
+ ```typescript
150
+ type BehavioralEventTaxonomy = {
151
+ product: MetricEventProduct; // Always PRODUCT_NAME ('wxcc_sdk')
152
+ agent: MetricEventAgent; // 'user' or 'service'
153
+ target: string; // e.g., 'station_login', 'task_accept'
154
+ verb: MetricEventVerb; // 'complete' for success, 'fail' for failure
155
+ };
156
+ ```
157
+
158
+ The final behavioral event name is constructed as: `{product}.{agent}.{target}.{verb}`
159
+
160
+ Example: `wxcc_sdk.user.station_login.complete`
161
+
162
+ The mapping is defined in `behavioral-events.ts` via `eventTaxonomyMap` and accessed through `getEventTaxonomy(name)`.
163
+
164
+ ---
165
+
166
+ ## Event Queuing and Submission
167
+
168
+ ### Three Parallel Queues
169
+
170
+ MetricsManager maintains three independent pending event queues:
171
+
172
+ | Queue | Type | Submitted Via | Name Transform | Extra Metadata |
173
+ | --------------------------- | ------------ | -------------------------------------------------- | ----------------------------------------------- | -------------------------------- |
174
+ | `pendingBehavioralEvents` | behavioral | `webex.internal.newMetrics.submitBehavioralEvent` | Taxonomy-based (`{product}.{agent}.{target}.{verb}`) | None |
175
+ | `pendingOperationalEvents` | operational | `webex.internal.newMetrics.submitOperationalEvent` | `WXCC_SDK_` prefix + uppercase (e.g. `WXCC_SDK_STATION_LOGIN_SUCCESS`) | None |
176
+ | `pendingBusinessEvents` | business | `webex.internal.newMetrics.submitBusinessEvent` | `WXCC_SDK_` prefix + uppercase (same as operational) | `metadata: {appType: 'wxcc_sdk'}` |
177
+
178
+ ### Submission Guards
179
+
180
+ - **readyToSubmitEvents**: Set to `true` only after `webex.once('ready')` fires. Events queue until then.
181
+ - **submittingEvents**: Lock flag to prevent concurrent `submitPendingEvents()` calls.
182
+ - **metricsDisabled**: When `true`, all `track*` methods return early and `clearPendingEvents()` empties all queues.
183
+
184
+ ---
185
+
186
+ ## Timing Pattern (`timeEvent` / `trackEvent`)
187
+
188
+ ```mermaid
189
+ flowchart LR
190
+ A[timeEvent keys] --> B[runningEvents stores startTime + key Set]
191
+ B --> C[trackEvent called with one of the keys]
192
+ C --> D[addDurationIfTimed matches key]
193
+ D --> E[Calculates duration_ms = now - startTime]
194
+ E --> F[Removes all keys for that operation]
195
+ F --> G[Attaches duration_ms to payload]
196
+ ```
197
+
198
+ Usage pattern from `cc.ts`:
199
+
200
+ ```typescript
201
+ // Before operation
202
+ this.metricsManager.timeEvent([
203
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
204
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
205
+ ]);
206
+
207
+ // On success
208
+ this.metricsManager.trackEvent(
209
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
210
+ { ...MetricsManager.getCommonTrackingFieldForAQMResponse(resp) },
211
+ ['behavioral', 'operational', 'business']
212
+ );
213
+
214
+ // On failure
215
+ this.metricsManager.trackEvent(
216
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
217
+ { ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure) },
218
+ ['behavioral', 'operational', 'business']
219
+ );
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Payload Preparation
225
+
226
+ `preparePayload()` processes every event payload before submission:
227
+
228
+ 1. **Removes empty/null/undefined fields** — strips keys with `undefined`, `null`, `''`, any arrays, or empty objects
229
+ 2. **Converts spaces to underscores** — `spacesToUnderscore()` applied to all key names
230
+ 3. **Adds common metadata** — appends `tabHidden: document.hidden` in browser environments
231
+
232
+ ---
233
+
234
+ ## Error Handling Strategy
235
+
236
+ MetricsManager does not throw errors to callers. Instead:
237
+
238
+ - Invalid metric types are logged via `LoggerProxy.error`
239
+ - Empty `timeEvent` key arrays are logged and ignored
240
+ - Disabled state (`metricsDisabled`) silently drops events
241
+ - The `submittingEvents` lock prevents race conditions during concurrent submissions
242
+
243
+ ---
244
+
245
+ ## Common Tracking Field Extraction
246
+
247
+ Two static helpers extract standardized fields from AQM responses for metric payloads:
248
+
249
+ ### `getCommonTrackingFieldForAQMResponse(response)`
250
+
251
+ Extracts: `agentId`, `agentSessionId`, `teamId`, `siteId`, `orgId`, `eventType`, `trackingId`, `notifTrackingId`
252
+
253
+ ### `getCommonTrackingFieldForAQMResponseFailed(failureResponse)`
254
+
255
+ Extracts: `agentId`, `trackingId`, `notifTrackingId`, `orgId`, `failureType`, `failureReason`, `reasonCode`
256
+
257
+ ---
258
+
259
+ ## Metric Event Names
260
+
261
+ All event names are defined in `constants.ts` as `METRIC_EVENT_NAMES`. Events follow a `{Domain} {Action} {Success|Failed}` naming convention:
262
+
263
+ | Category | Success Event | Failure Event |
264
+ | ---------------------- | -------------------------------------- | -------------------------------------- |
265
+ | Station Login | `STATION_LOGIN_SUCCESS` | `STATION_LOGIN_FAILED` |
266
+ | Station Logout | `STATION_LOGOUT_SUCCESS` | `STATION_LOGOUT_FAILED` |
267
+ | Station Relogin | `STATION_RELOGIN_SUCCESS` | `STATION_RELOGIN_FAILED` |
268
+ | State Change | `AGENT_STATE_CHANGE_SUCCESS` | `AGENT_STATE_CHANGE_FAILED` |
269
+ | Buddy Agents | `FETCH_BUDDY_AGENTS_SUCCESS` | `FETCH_BUDDY_AGENTS_FAILED` |
270
+ | WebSocket Register | `WEBSOCKET_REGISTER_SUCCESS` | `WEBSOCKET_REGISTER_FAILED` |
271
+ | Task Accept | `TASK_ACCEPT_SUCCESS` | `TASK_ACCEPT_FAILED` |
272
+ | Task Decline | `TASK_DECLINE_SUCCESS` | `TASK_DECLINE_FAILED` |
273
+ | Task End | `TASK_END_SUCCESS` | `TASK_END_FAILED` |
274
+ | Task Wrapup | `TASK_WRAPUP_SUCCESS` | `TASK_WRAPUP_FAILED` |
275
+ | Task Hold | `TASK_HOLD_SUCCESS` | `TASK_HOLD_FAILED` |
276
+ | Task Resume | `TASK_RESUME_SUCCESS` | `TASK_RESUME_FAILED` |
277
+ | Task Consult Start | `TASK_CONSULT_START_SUCCESS` | `TASK_CONSULT_START_FAILED` |
278
+ | Task Consult End | `TASK_CONSULT_END_SUCCESS` | `TASK_CONSULT_END_FAILED` |
279
+ | Task Transfer | `TASK_TRANSFER_SUCCESS` | `TASK_TRANSFER_FAILED` |
280
+ | Task Resume Recording | `TASK_RESUME_RECORDING_SUCCESS` | `TASK_RESUME_RECORDING_FAILED` |
281
+ | Task Pause Recording | `TASK_PAUSE_RECORDING_SUCCESS` | `TASK_PAUSE_RECORDING_FAILED` |
282
+ | Task Accept Consult | `TASK_ACCEPT_CONSULT_SUCCESS` | `TASK_ACCEPT_CONSULT_FAILED` |
283
+ | Task Auto Answer | `TASK_AUTO_ANSWER_SUCCESS` | `TASK_AUTO_ANSWER_FAILED` |
284
+ | Conference Start | `TASK_CONFERENCE_START_SUCCESS` | `TASK_CONFERENCE_START_FAILED` |
285
+ | Conference End | `TASK_CONFERENCE_END_SUCCESS` | `TASK_CONFERENCE_END_FAILED` |
286
+ | Conference Transfer | `TASK_CONFERENCE_TRANSFER_SUCCESS` | `TASK_CONFERENCE_TRANSFER_FAILED` |
287
+ | Conference Exit | `TASK_CONFERENCE_EXIT_SUCCESS` | `TASK_CONFERENCE_EXIT_FAILED` |
288
+ | Switch Call | `TASK_SWITCH_CALL_SUCCESS` | `TASK_SWITCH_CALL_FAILED` |
289
+ | Outdial | `TASK_OUTDIAL_SUCCESS` | `TASK_OUTDIAL_FAILED` |
290
+ | Upload Logs | `UPLOAD_LOGS_SUCCESS` | `UPLOAD_LOGS_FAILED` |
291
+ | WebSocket Deregister | `WEBSOCKET_DEREGISTER_SUCCESS` | `WEBSOCKET_DEREGISTER_FAIL` |
292
+ | Device Type Update | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `AGENT_DEVICE_TYPE_UPDATE_FAILED` |
293
+ | EntryPoint | `ENTRYPOINT_FETCH_SUCCESS` | `ENTRYPOINT_FETCH_FAILED` |
294
+ | AddressBook | `ADDRESSBOOK_FETCH_SUCCESS` | `ADDRESSBOOK_FETCH_FAILED` |
295
+ | Queue | `QUEUE_FETCH_SUCCESS` | `QUEUE_FETCH_FAILED` |
296
+ | Outdial ANI Entries | `OUTDIAL_ANI_EP_FETCH_SUCCESS` | `OUTDIAL_ANI_EP_FETCH_FAILED` |
297
+
298
+ Special events (no success/failure pair):
299
+ - `AGENT_RONA` — has behavioral taxonomy (`service.agent_rona.set`)
300
+ - `AGENT_CONTACT_ASSIGN_FAILED` — has behavioral taxonomy (`service.agent_contact_assign.fail`)
301
+ - `AGENT_INVITE_FAILED` — has behavioral taxonomy (`service.agent_invite.fail`)
302
+ - `WEBSOCKET_EVENT_RECEIVED` — **no** behavioral taxonomy (not in `eventTaxonomyMap`)
303
+
304
+ Events **without** behavioral taxonomy (not in `eventTaxonomyMap`): `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_EVENT_RECEIVED`
305
+
306
+ ---
307
+
308
+ ## Troubleshooting
309
+
310
+ ### Issue: Metrics not being submitted
311
+
312
+ **Cause**: Webex SDK not yet ready when `trackEvent` is called
313
+
314
+ **Solution**: Events are automatically queued in `pending*Events` arrays and flushed once `webex.once('ready')` fires. Verify the SDK is initializing correctly.
315
+
316
+ ### Issue: Duration not attached to metric
317
+
318
+ **Cause**: `timeEvent` was not called before `trackEvent`, or the event name does not match any key in `runningEvents`
319
+
320
+ **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.
321
+
322
+ ### Issue: Metrics silently dropped
323
+
324
+ **Cause**: `metricsDisabled` is set to `true`
325
+
326
+ **Solution**: Check if `setMetricsDisabled(true)` was called. This clears all pending queues and causes all `track*` methods to return early.
327
+
328
+ ---
329
+
330
+ ## Related Files
331
+
332
+ - [MetricsManager.ts](../MetricsManager.ts) — Singleton metrics manager
333
+ - [behavioral-events.ts](../behavioral-events.ts) — Event taxonomy mapping
334
+ - [constants.ts](../constants.ts) — METRIC_EVENT_NAMES definitions
335
+ - [cc.ts](../../cc.ts) — Main plugin class (primary consumer)
336
+ - [constants.ts](../../constants.ts) — PRODUCT_NAME used in event prefixing