@webex/contact-center 3.12.0-next.15 → 3.12.0-next.151

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 (322) hide show
  1. package/.sdd/manifest.json +887 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +62 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +52 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +382 -0
  17. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  18. package/ai-docs/features/contact-owner-change-refactor.md +78 -0
  19. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  20. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  21. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  22. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  23. package/ai-docs/patterns/testing-patterns.md +480 -0
  24. package/ai-docs/patterns/typescript-patterns.md +365 -0
  25. package/ai-docs/templates/README.md +102 -0
  26. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  27. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  28. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  29. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  30. package/ai-docs/templates/new-method/00-master.md +80 -0
  31. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  32. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  33. package/ai-docs/templates/new-method/03-tests.md +201 -0
  34. package/ai-docs/templates/new-method/04-validation.md +141 -0
  35. package/ai-docs/templates/new-service/00-master.md +109 -0
  36. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  37. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  38. package/ai-docs/templates/new-service/03-integration.md +178 -0
  39. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  40. package/ai-docs/templates/new-service/05-validation.md +145 -0
  41. package/dist/cc.js +1010 -47
  42. package/dist/cc.js.map +1 -1
  43. package/dist/config.js +13 -0
  44. package/dist/config.js.map +1 -1
  45. package/dist/constants.js +40 -3
  46. package/dist/constants.js.map +1 -1
  47. package/dist/index.js +54 -17
  48. package/dist/index.js.map +1 -1
  49. package/dist/metrics/MetricsManager.js +16 -0
  50. package/dist/metrics/MetricsManager.js.map +1 -1
  51. package/dist/metrics/behavioral-events.js +211 -0
  52. package/dist/metrics/behavioral-events.js.map +1 -1
  53. package/dist/metrics/constants.js +58 -4
  54. package/dist/metrics/constants.js.map +1 -1
  55. package/dist/services/AddressBook.js +18 -15
  56. package/dist/services/AddressBook.js.map +1 -1
  57. package/dist/services/AnswerCallOnWebexService.js +202 -0
  58. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  59. package/dist/services/ApiAiAssistant.js +351 -14
  60. package/dist/services/ApiAiAssistant.js.map +1 -1
  61. package/dist/services/EntryPoint.js +46 -68
  62. package/dist/services/EntryPoint.js.map +1 -1
  63. package/dist/services/Queue.js +27 -22
  64. package/dist/services/Queue.js.map +1 -1
  65. package/dist/services/UserPreference.js +427 -0
  66. package/dist/services/UserPreference.js.map +1 -0
  67. package/dist/services/WebexCrossClientService.js +225 -0
  68. package/dist/services/WebexCrossClientService.js.map +1 -0
  69. package/dist/services/WxAppTelephonyMercurySync.js +111 -0
  70. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  71. package/dist/services/config/Util.js +48 -48
  72. package/dist/services/config/Util.js.map +1 -1
  73. package/dist/services/config/constants.js +56 -78
  74. package/dist/services/config/constants.js.map +1 -1
  75. package/dist/services/config/index.js +100 -226
  76. package/dist/services/config/index.js.map +1 -1
  77. package/dist/services/config/types.js +97 -24
  78. package/dist/services/config/types.js.map +1 -1
  79. package/dist/services/core/Err.js.map +1 -1
  80. package/dist/services/core/Utils.js +111 -32
  81. package/dist/services/core/Utils.js.map +1 -1
  82. package/dist/services/core/WebexRequest.js +6 -2
  83. package/dist/services/core/WebexRequest.js.map +1 -1
  84. package/dist/services/core/aqm-reqs.js +28 -14
  85. package/dist/services/core/aqm-reqs.js.map +1 -1
  86. package/dist/services/core/types.js.map +1 -1
  87. package/dist/services/core/websocket/WebSocketManager.js +2 -1
  88. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  89. package/dist/services/core/websocket/types.js.map +1 -1
  90. package/dist/services/index.js +1 -1
  91. package/dist/services/index.js.map +1 -1
  92. package/dist/services/task/Task.js +955 -0
  93. package/dist/services/task/Task.js.map +1 -0
  94. package/dist/services/task/TaskFactory.js +50 -0
  95. package/dist/services/task/TaskFactory.js.map +1 -0
  96. package/dist/services/task/TaskManager.js +1272 -523
  97. package/dist/services/task/TaskManager.js.map +1 -1
  98. package/dist/services/task/TaskUtils.js +221 -27
  99. package/dist/services/task/TaskUtils.js.map +1 -1
  100. package/dist/services/task/WebexCallingUtils.js +70 -0
  101. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  102. package/dist/services/task/constants.js +21 -2
  103. package/dist/services/task/constants.js.map +1 -1
  104. package/dist/services/task/contact.js +29 -0
  105. package/dist/services/task/contact.js.map +1 -1
  106. package/dist/services/task/dialer.js +78 -0
  107. package/dist/services/task/dialer.js.map +1 -1
  108. package/dist/services/task/digital/Digital.js +78 -0
  109. package/dist/services/task/digital/Digital.js.map +1 -0
  110. package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
  111. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  112. package/dist/services/task/state-machine/actions.js +572 -0
  113. package/dist/services/task/state-machine/actions.js.map +1 -0
  114. package/dist/services/task/state-machine/constants.js +161 -0
  115. package/dist/services/task/state-machine/constants.js.map +1 -0
  116. package/dist/services/task/state-machine/guards.js +379 -0
  117. package/dist/services/task/state-machine/guards.js.map +1 -0
  118. package/dist/services/task/state-machine/index.js +53 -0
  119. package/dist/services/task/state-machine/index.js.map +1 -0
  120. package/dist/services/task/state-machine/types.js +54 -0
  121. package/dist/services/task/state-machine/types.js.map +1 -0
  122. package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
  123. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  124. package/dist/services/task/taskDataNormalizer.js +99 -0
  125. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  126. package/dist/services/task/types.js +259 -5
  127. package/dist/services/task/types.js.map +1 -1
  128. package/dist/services/task/voice/Voice.js +1504 -0
  129. package/dist/services/task/voice/Voice.js.map +1 -0
  130. package/dist/services/task/voice/WebRTC.js +152 -0
  131. package/dist/services/task/voice/WebRTC.js.map +1 -0
  132. package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
  133. package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
  134. package/dist/services/task/voice/wxAppVoiceMethods.js +395 -0
  135. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  136. package/dist/services/wxAppDiagnosticLogging.js +159 -0
  137. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  138. package/dist/services/wxAppTelephonyUtils.js +19 -0
  139. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  140. package/dist/types/cc.d.ts +196 -9
  141. package/dist/types/config.d.ts +13 -0
  142. package/dist/types/constants.d.ts +38 -1
  143. package/dist/types/index.d.ts +54 -20
  144. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  145. package/dist/types/metrics/constants.d.ts +52 -1
  146. package/dist/types/services/AddressBook.d.ts +2 -1
  147. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  148. package/dist/types/services/ApiAiAssistant.d.ts +58 -5
  149. package/dist/types/services/EntryPoint.d.ts +7 -5
  150. package/dist/types/services/Queue.d.ts +5 -3
  151. package/dist/types/services/UserPreference.d.ts +118 -0
  152. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  153. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  154. package/dist/types/services/config/Util.d.ts +4 -8
  155. package/dist/types/services/config/constants.d.ts +49 -76
  156. package/dist/types/services/config/index.d.ts +16 -64
  157. package/dist/types/services/config/types.d.ts +248 -114
  158. package/dist/types/services/core/Err.d.ts +6 -0
  159. package/dist/types/services/core/Utils.d.ts +35 -13
  160. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  161. package/dist/types/services/core/types.d.ts +2 -0
  162. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  163. package/dist/types/services/core/websocket/types.d.ts +1 -1
  164. package/dist/types/services/index.d.ts +1 -1
  165. package/dist/types/services/task/Task.d.ts +186 -0
  166. package/dist/types/services/task/TaskFactory.d.ts +14 -0
  167. package/dist/types/services/task/TaskUtils.d.ts +54 -2
  168. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  169. package/dist/types/services/task/constants.d.ts +19 -0
  170. package/dist/types/services/task/contact.d.ts +4 -0
  171. package/dist/types/services/task/dialer.d.ts +30 -0
  172. package/dist/types/services/task/digital/Digital.d.ts +23 -0
  173. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
  174. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  175. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  176. package/dist/types/services/task/state-machine/guards.d.ts +103 -0
  177. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  178. package/dist/types/services/task/state-machine/types.d.ts +277 -0
  179. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  180. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  181. package/dist/types/services/task/types.d.ts +790 -66
  182. package/dist/types/services/task/voice/Voice.d.ts +241 -0
  183. package/dist/types/services/task/voice/WebRTC.d.ts +54 -0
  184. package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
  185. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +68 -0
  186. package/dist/types/services/wxAppDiagnosticLogging.d.ts +63 -0
  187. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  188. package/dist/types/types.d.ts +213 -11
  189. package/dist/types/utils/PageCache.d.ts +20 -3
  190. package/dist/types/webex.d.ts +1 -0
  191. package/dist/types.js +168 -6
  192. package/dist/types.js.map +1 -1
  193. package/dist/utils/PageCache.js +19 -5
  194. package/dist/utils/PageCache.js.map +1 -1
  195. package/dist/webex.js +22 -2
  196. package/dist/webex.js.map +1 -1
  197. package/package.json +16 -12
  198. package/src/cc.ts +1338 -121
  199. package/src/config.ts +13 -0
  200. package/src/constants.ts +41 -1
  201. package/src/index.ts +62 -15
  202. package/src/metrics/MetricsManager.ts +16 -0
  203. package/src/metrics/ai-docs/AGENTS.md +350 -0
  204. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  205. package/src/metrics/ai-docs/metrics-spec.md +971 -0
  206. package/src/metrics/behavioral-events.ts +218 -0
  207. package/src/metrics/constants.ts +63 -4
  208. package/src/services/AddressBook.ts +17 -6
  209. package/src/services/AnswerCallOnWebexService.ts +239 -0
  210. package/src/services/ApiAiAssistant.ts +470 -15
  211. package/src/services/EntryPoint.ts +59 -60
  212. package/src/services/Queue.ts +29 -12
  213. package/src/services/UserPreference.ts +509 -0
  214. package/src/services/WebexCrossClientService.ts +298 -0
  215. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  216. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  217. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  218. package/src/services/agent/ai-docs/agent-spec.md +505 -0
  219. package/src/services/ai-docs/AGENTS.md +386 -0
  220. package/src/services/ai-docs/services-spec.md +505 -0
  221. package/src/services/config/Util.ts +54 -84
  222. package/src/services/config/ai-docs/AGENTS.md +250 -0
  223. package/src/services/config/ai-docs/ARCHITECTURE.md +413 -0
  224. package/src/services/config/ai-docs/config-spec.md +669 -0
  225. package/src/services/config/constants.ts +63 -85
  226. package/src/services/config/index.ts +116 -277
  227. package/src/services/config/types.ts +233 -125
  228. package/src/services/core/Err.ts +3 -0
  229. package/src/services/core/Utils.ts +127 -37
  230. package/src/services/core/WebexRequest.ts +3 -1
  231. package/src/services/core/ai-docs/AGENTS.md +381 -0
  232. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  233. package/src/services/core/ai-docs/core-spec.md +787 -0
  234. package/src/services/core/aqm-reqs.ts +30 -15
  235. package/src/services/core/types.ts +33 -1
  236. package/src/services/core/websocket/WebSocketManager.ts +2 -0
  237. package/src/services/core/websocket/types.ts +1 -1
  238. package/src/services/index.ts +1 -1
  239. package/src/services/task/Task.ts +1211 -0
  240. package/src/services/task/TaskFactory.ts +82 -0
  241. package/src/services/task/TaskManager.ts +1584 -643
  242. package/src/services/task/TaskUtils.ts +316 -25
  243. package/src/services/task/WebexCallingUtils.ts +136 -0
  244. package/src/services/task/ai-docs/AGENTS.md +457 -0
  245. package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
  246. package/src/services/task/ai-docs/task-spec.md +1501 -0
  247. package/src/services/task/constants.ts +20 -0
  248. package/src/services/task/contact.ts +30 -0
  249. package/src/services/task/dialer.ts +80 -0
  250. package/src/services/task/digital/Digital.ts +102 -0
  251. package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
  252. package/src/services/task/state-machine/actions.ts +741 -0
  253. package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
  254. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
  255. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
  256. package/src/services/task/state-machine/constants.ts +172 -0
  257. package/src/services/task/state-machine/guards.ts +442 -0
  258. package/src/services/task/state-machine/index.ts +28 -0
  259. package/src/services/task/state-machine/types.ts +258 -0
  260. package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
  261. package/src/services/task/taskDataNormalizer.ts +137 -0
  262. package/src/services/task/types.ts +974 -70
  263. package/src/services/task/voice/Voice.ts +1878 -0
  264. package/src/services/task/voice/WebRTC.ts +194 -0
  265. package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
  266. package/src/services/task/voice/wxAppVoiceMethods.ts +575 -0
  267. package/src/services/wxAppDiagnosticLogging.ts +286 -0
  268. package/src/services/wxAppTelephonyUtils.ts +14 -0
  269. package/src/types.ts +272 -13
  270. package/src/utils/AGENTS.md +289 -0
  271. package/src/utils/PageCache.ts +38 -5
  272. package/src/utils/ai-docs/utils-spec.md +391 -0
  273. package/src/webex.js +10 -0
  274. package/test/unit/fixtures/aiSummaryTestUtils.ts +40 -0
  275. package/test/unit/spec/cc.ts +3705 -150
  276. package/test/unit/spec/index.ts +885 -0
  277. package/test/unit/spec/logger-proxy.ts +70 -0
  278. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  279. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  280. package/test/unit/spec/services/AddressBook.ts +37 -6
  281. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  282. package/test/unit/spec/services/ApiAiAssistant.ts +429 -23
  283. package/test/unit/spec/services/EntryPoint.ts +87 -40
  284. package/test/unit/spec/services/Queue.ts +123 -12
  285. package/test/unit/spec/services/UserPreference.ts +401 -0
  286. package/test/unit/spec/services/WebCallingService.ts +7 -1
  287. package/test/unit/spec/services/WebexCrossClientService.ts +509 -0
  288. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  289. package/test/unit/spec/services/config/Util.ts +501 -0
  290. package/test/unit/spec/services/config/index.ts +317 -547
  291. package/test/unit/spec/services/core/Utils.ts +425 -8
  292. package/test/unit/spec/services/core/WebexRequest.ts +8 -7
  293. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  294. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
  295. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  296. package/test/unit/spec/services/task/Task.ts +2723 -0
  297. package/test/unit/spec/services/task/TaskFactory.ts +116 -0
  298. package/test/unit/spec/services/task/TaskManager.ts +4781 -1585
  299. package/test/unit/spec/services/task/TaskUtils.ts +307 -0
  300. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  301. package/test/unit/spec/services/task/contact.ts +33 -0
  302. package/test/unit/spec/services/task/dialer.ts +190 -0
  303. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  304. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
  305. package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
  306. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  307. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
  308. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  309. package/test/unit/spec/services/task/voice/Voice.ts +2355 -0
  310. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  311. package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
  312. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +719 -0
  313. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +179 -0
  314. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  315. package/typedoc.md +1 -1
  316. package/umd/contact-center.min.js +2 -2
  317. package/umd/contact-center.min.js.map +1 -1
  318. package/dist/services/task/index.js +0 -1530
  319. package/dist/services/task/index.js.map +0 -1
  320. package/dist/types/services/task/index.d.ts +0 -650
  321. package/src/services/task/index.ts +0 -1806
  322. package/test/unit/spec/services/task/index.ts +0 -2205
@@ -0,0 +1,971 @@
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 Agent Wellness Break delta / 2026-09-02 |
15
+ | Validation status | Agent Wellness Break v0.4 delta independently validated by claude-code on 2026-09-07; coverage remains Partial until the remaining baseline promotion criteria are satisfied |
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
+ - **Webex Together behavioral naming**: Webex Together (wxApp) events use `*_webex_together` compound targets (e.g. `wxcc_sdk.user.task_accept_webex_together.complete`). Operational/business wire names remain `WXCC_SDK_WXAPP_*` unchanged.
39
+
40
+ - **Payload Preparation**: Automatic cleanup of empty fields, space-to-underscore conversion, and `tabHidden` metadata
41
+
42
+ - **AQM Response Helpers**: Static methods to extract common tracking fields from AQM responses
43
+
44
+ ## Purpose / Responsibility
45
+ Own timing, taxonomy, queuing, payload preparation, and submission for Contact Center behavioral, operational, and business telemetry.
46
+
47
+ ## Stack
48
+ TypeScript 5.4 singleton service, Webex internal metrics APIs, LoggerProxy, Jest 27.
49
+
50
+ ## Folder / Package Structure
51
+ ```text
52
+ src/metrics/
53
+ ├── MetricsManager.ts
54
+ ├── behavioral-events.ts
55
+ ├── constants.ts
56
+ ```
57
+
58
+ ```text
59
+ src/metrics/
60
+ ├── MetricsManager.ts # Singleton metrics manager
61
+ ├── behavioral-events.ts # Behavioral event taxonomy mapping
62
+ ├── constants.ts # METRIC_EVENT_NAMES constants
63
+ └── ai-docs/
64
+ ├── AGENTS.md # Usage documentation (see PR #4762)
65
+ └── ARCHITECTURE.md # Preserved legacy, noncanonical architecture guide
66
+ ```
67
+
68
+ ## Key Files (source of truth)
69
+ | File | Holds |
70
+ |---|---|
71
+ | `src/metrics/MetricsManager.ts` | Authoritative Metrics implementation or contract source. |
72
+ | `src/metrics/behavioral-events.ts` | Authoritative Metrics implementation or contract source. |
73
+ | `src/metrics/constants.ts` | Authoritative Metrics implementation or contract source. |
74
+
75
+ ## Public Surface
76
+ | Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index |
77
+ |---|---|---|---|---|---|---|
78
+ | `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` |
79
+
80
+ Compatibility notes:
81
+ - Do not remove or reinterpret exported symbols/events without a documented consumer migration.
82
+
83
+ Returns the singleton instance. On first call with `{webex}`, binds to the Webex SDK and begins listening for the `ready` event.
84
+
85
+ **Parameters**:
86
+
87
+ - `options` (object, optional): `{webex: WebexSDK}` - The Webex SDK instance
88
+
89
+ **Returns**: `MetricsManager`
90
+
91
+ **Example**:
92
+
93
+ ```typescript
94
+ // During initialization (called internally by cc.register())
95
+ const metrics = MetricsManager.getInstance({webex});
96
+
97
+ // Subsequent calls (no webex needed)
98
+ const metrics = MetricsManager.getInstance();
99
+ ```
100
+
101
+ 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.
102
+
103
+ **Parameters**:
104
+
105
+ - `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.
106
+
107
+ **Returns**: `void`
108
+
109
+ **Example**:
110
+
111
+ ```typescript
112
+ // Single key
113
+ metrics.timeEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS);
114
+
115
+ // Multiple keys (success/failure share one timer)
116
+ metrics.timeEvent([
117
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
118
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
119
+ ]);
120
+ ```
121
+
122
+ Tracks an event across one or more metric services.
123
+
124
+ **Parameters**:
125
+
126
+ - `name` (METRIC_EVENT_NAMES): The event name constant
127
+
128
+ - `payload` (EventPayload, optional): Key-value pairs of event data
129
+
130
+ - `metricServices` (MetricsType[], optional): Array of `'behavioral'` | `'operational'` | `'business'` (default: `['behavioral']`)
131
+
132
+ **Returns**: `void`
133
+
134
+ **Example**:
135
+
136
+ ```typescript
137
+ // Behavioral only (default)
138
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {agentId: '123'});
139
+
140
+ // Multiple services
141
+ metrics.trackEvent(
142
+ METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
143
+ {interactionId: 'abc'},
144
+ ['behavioral', 'operational']
145
+ );
146
+ ```
147
+
148
+ Tracks a single behavioral event. Looks up the event taxonomy from `behavioral-events.ts` and submits via `webex.internal.newMetrics.submitBehavioralEvent`.
149
+
150
+ **Parameters**:
151
+
152
+ - `name` (METRIC_EVENT_NAMES): The event name
153
+
154
+ - `options` (EventPayload, optional): Additional payload data
155
+
156
+ **Returns**: `void`
157
+
158
+ Tracks a single operational event. Prefixes the event name with `WXCC_SDK_` and submits via `webex.internal.newMetrics.submitOperationalEvent`.
159
+
160
+ **Parameters**:
161
+
162
+ - `name` (METRIC_EVENT_NAMES): The event name
163
+
164
+ - `options` (EventPayload, optional): Additional payload data
165
+
166
+ **Returns**: `void`
167
+
168
+ Tracks a single business event. Prefixes the event name with `WXCC_SDK_` and submits via `webex.internal.newMetrics.submitBusinessEvent` with `appType: 'wxcc_sdk'`.
169
+
170
+ **Parameters**:
171
+
172
+ - `name` (METRIC_EVENT_NAMES): The event name
173
+
174
+ - `options` (EventPayload, optional): Additional payload data
175
+
176
+ **Returns**: `void`
177
+
178
+ Enables or disables metrics collection. When disabled, all pending events are cleared and new events are dropped.
179
+
180
+ **Parameters**:
181
+
182
+ - `disabled` (boolean): `true` to disable, `false` to enable
183
+
184
+ **Returns**: `void`
185
+
186
+ Static helper that extracts common tracking fields from an AQM success response.
187
+
188
+ **Parameters**:
189
+
190
+ - `response` (any): The AQM response object
191
+
192
+ **Returns**: `Record<string, any>` with fields: `agentId`, `agentSessionId`, `teamId`, `siteId`, `orgId`, `eventType`, `trackingId`, `notifTrackingId`
193
+
194
+ **Example**:
195
+
196
+ ```typescript
197
+ const fields = MetricsManager.getCommonTrackingFieldForAQMResponse(aqmResponse);
198
+ metrics.trackEvent(METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS, {
199
+ ...fields,
200
+ interactionId: task.interactionId,
201
+ });
202
+ ```
203
+
204
+ Resets the singleton instance. Used for testing only.
205
+
206
+ **Returns**: `void`
207
+
208
+ All event names are defined in `METRIC_EVENT_NAMES` (`constants.ts`). Events follow a `<Category> <Action> <Result>` pattern.
209
+
210
+ | Constant | Value | Description |
211
+ |---|---|---|
212
+ | `STATION_LOGIN_SUCCESS` | `'Station Login Success'` | Agent station login succeeded |
213
+ | `STATION_LOGIN_FAILED` | `'Station Login Failed'` | Agent station login failed |
214
+ | `STATION_LOGOUT_SUCCESS` | `'Station Logout Success'` | Agent station logout succeeded |
215
+ | `STATION_LOGOUT_FAILED` | `'Station Logout Failed'` | Agent station logout failed |
216
+ | `STATION_RELOGIN_SUCCESS` | `'Station Relogin Success'` | Silent relogin succeeded |
217
+ | `STATION_RELOGIN_FAILED` | `'Station Relogin Failed'` | Silent relogin failed |
218
+ | `AGENT_STATE_CHANGE_SUCCESS` | `'Agent State Change Success'` | State change succeeded |
219
+ | `AGENT_STATE_CHANGE_FAILED` | `'Agent State Change Failed'` | State change failed |
220
+ | `FETCH_BUDDY_AGENTS_SUCCESS` | `'Fetch Buddy Agents Success'` | Buddy agents fetch succeeded |
221
+ | `FETCH_BUDDY_AGENTS_FAILED` | `'Fetch Buddy Agents Failed'` | Buddy agents fetch failed |
222
+ | `AGENT_RONA` | `'Agent RONA'` | Agent Ring-On-No-Answer triggered |
223
+ | `AGENT_CONTACT_ASSIGN_FAILED` | `'Agent Contact Assign Failed'` | Contact assignment failed |
224
+ | `AGENT_INVITE_FAILED` | `'Agent Invite Failed'` | Agent invite failed |
225
+ | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `'Agent Device Type Update Success'` | Device type update succeeded |
226
+ | `AGENT_DEVICE_TYPE_UPDATE_FAILED` | `'Agent Device Type Update Failed'` | Device type update failed |
227
+ | `TASK_ACCEPT_SUCCESS` / `FAILED` | `'Task Accept ...'` | Task accept result |
228
+ | `TASK_DECLINE_SUCCESS` / `FAILED` | `'Task Decline ...'` | Task decline result |
229
+ | `TASK_END_SUCCESS` / `FAILED` | `'Task End ...'` | Task end result |
230
+ | `TASK_WRAPUP_SUCCESS` / `FAILED` | `'Task Wrapup ...'` | Task wrapup result |
231
+ | `TASK_HOLD_SUCCESS` / `FAILED` | `'Task Hold ...'` | Task hold result |
232
+ | `TASK_RESUME_SUCCESS` / `FAILED` | `'Task Resume ...'` | Task resume result |
233
+ | `TASK_CONSULT_START_SUCCESS` / `FAILED` | `'Task Consult Start ...'` | Consult start result |
234
+ | `TASK_CONSULT_END_SUCCESS` / `FAILED` | `'Task Consult End ...'` | Consult end result |
235
+ | `TASK_TRANSFER_SUCCESS` / `FAILED` | `'Task Transfer ...'` | Transfer result |
236
+ | `TASK_PAUSE_RECORDING_SUCCESS` / `FAILED` | `'Task Pause Recording ...'` | Pause recording result |
237
+ | `TASK_RESUME_RECORDING_SUCCESS` / `FAILED` | `'Task Resume Recording ...'` | Resume recording result |
238
+ | `TASK_ACCEPT_CONSULT_SUCCESS` / `FAILED` | `'Task Accept Consult ...'` | Accept consult result |
239
+ | `TASK_AUTO_ANSWER_SUCCESS` / `FAILED` | `'Task Auto Answer ...'` | Auto-answer result |
240
+ | `TASK_OUTDIAL_SUCCESS` / `FAILED` | `'Task Outdial ...'` | Outdial result |
241
+ | `WXAPP_TASK_MUTE_SUCCESS` / `FAILED` | `'WxApp Task Mute ...'` | Webex Together task mute/unmute toggle (`wxcc_sdk.user.task_mute_webex_together.complete\|fail`). Mute telephony is queued per task via tail promise chain (`Voice.wxAppMuteToggleTail`) so concurrent toggles do not overwrite the shared `timeEvent` timer or reorder requests. |
242
+ | `WXAPP_TASK_DTMF_SUCCESS` / `FAILED` | `'WxApp Task Dtmf ...'` | Webex Together DTMF/keypad (`wxcc_sdk.user.task_dtmf_webex_together.complete\|fail`). DTMF telephony is queued per task via tail promise chain (`Voice.wxAppDtmfTail`) so concurrent keypad input does not overwrite the shared `timeEvent` timer or reorder digits. |
243
+ | `WXAPP_TASK_ACCEPT_SUCCESS` / `FAILED` | `'WxApp Task Accept ...'` | Webex Together accept/answer (`wxcc_sdk.user.task_accept_webex_together.complete\|fail`) |
244
+ | `WXAPP_TASK_DECLINE_SUCCESS` / `FAILED` | `'WxApp Task Decline ...'` | Webex Together reject/decline (`wxcc_sdk.user.task_reject_webex_together.complete\|fail`). Inbound offers: telephony `rejectCall` via `runWxAppReject`. wxApp outdial cancellations: CC `cancelTask` via `runWxAppOutdialDecline` (additive with `TASK_DECLINE_*`). Outdial failure payloads merge AQM `error.details` via `getCommonTrackingFieldForAQMResponseFailed` so wxApp metrics and diagnostic logs preserve `trackingId` / structured failure reason. |
245
+ | `WXAPP_SESSION_INIT_SUCCESS` / `FAILED` | `'WxApp Session Init ...'` | Webex Together session orchestration (`wxcc_sdk.user.webex_together_session_init.complete\|fail`) |
246
+ | `WXAPP_SESSION_SKIPPED` | `'WxApp Session Skipped'` | Webex Together session skipped (`wxcc_sdk.user.webex_together_session_init.ignore`) |
247
+ | `WXAPP_USERSUB_PUBLISH_SUCCESS` / `FAILED` | `'WxApp Usersub Publish ...'` | Cross-client usersub publish (`wxcc_sdk.user.webex_together_usersub_publish.complete\|fail`). Preflight failures (missing `userId` or device URL before HTTP publish) emit `WXAPP_USERSUB_PUBLISH_FAILED` with `skipReason` (`user_id_unavailable` / `device_url_unavailable`) and no `timeEvent` timer. Stale-generation discards (teardown or concurrent disable while HTTP publish is in flight) call `cancelTimedEvent` without emitting success/fail only when the completing publish still owns the active timer (`usersubPublishMetricsId` guard — a newer overlapping publish must not have its timer cleared). |
248
+ | `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` / `FAILED` | `'WxApp Mercury Subscribe ...'` | Telephony Mercury mute-sync subscribe (`wxcc_sdk.user.webex_together_mercury_subscribe.complete\|fail`) |
249
+ | `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` | `'WxApp Offer Participant Fields Missing'` | Read-only observability when usersub is active but inbound OFFERED participant **still** lacks valid wxApp fields after a **500 ms grace period** (orchestrated in `voice/wxAppOfferObservability.ts`; avoids false alarms from transient WS ordering). After usersub publish, `TaskManager.refreshWxAppOfferObservabilityForAllTasks()` re-runs the observer so a pre-publish OFFERED task can still schedule grace. Task removal calls `disposeWxAppOfferObservability()` so a pending timer cannot emit after `CONTACT_MERGED` / collection drop. Emitted with matching `[CC wxApp] participant fields mismatch` WARN (`wxcc_sdk.user.webex_together_offer_participant_fields.fail`) |
250
+ | `TASK_CONFERENCE_START_SUCCESS` / `FAILED` | `'Task Conference Start ...'` | Conference start result |
251
+ | `TASK_CONFERENCE_END_SUCCESS` / `FAILED` | `'Task Conference End ...'` | Conference end result |
252
+ | `TASK_CONFERENCE_TRANSFER_SUCCESS` / `FAILED` | `'Task Conference Transfer ...'` | Conference transfer result |
253
+ | `TASK_CONFERENCE_EXIT_SUCCESS` / `FAILED` | `'Task Conference Exit ...'` | Conference exit result |
254
+ | `TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS` / `FAILED` | `'Task Conference Participant Drop ...'` | Participant Drop result |
255
+ | `TASK_SWITCH_CALL_SUCCESS` / `FAILED` | `'Task Switch Call ...'` | Switch call result |
256
+ | `WEBSOCKET_REGISTER_SUCCESS` / `FAILED` | `'Websocket Register ...'` | WebSocket registration result |
257
+ | `WEBSOCKET_DEREGISTER_SUCCESS` / `FAIL` | `'Websocket Deregister ...'` | WebSocket deregistration result |
258
+ | `WEBSOCKET_EVENT_RECEIVED` | `'Websocket Event Received'` | WebSocket event received |
259
+ | `UPLOAD_LOGS_SUCCESS` / `FAILED` | `'Upload Logs ...'` | Log upload result |
260
+ | `ENTRYPOINT_FETCH_SUCCESS` / `FAILED` | `'Entrypoint Fetch ...'` | Entry point fetch result |
261
+ | `ADDRESSBOOK_FETCH_SUCCESS` / `FAILED` | `'AddressBook Fetch ...'` | Address book fetch result |
262
+ | `QUEUE_FETCH_SUCCESS` / `FAILED` | `'Queue Fetch ...'` | Queue fetch result |
263
+ | `OUTDIAL_ANI_EP_FETCH_SUCCESS` / `FAILED` | `'Outdial ANI Entries Fetch ...'` | Outdial ANI entries fetch result |
264
+ | `AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED` / `FAILED` | `'AI Assistant Wellness Action ...'` | Direct wellness custom-event result |
265
+ | `AI_ASSISTANT_WELLNESS_EVENT_INVALID` | `'AI Assistant Wellness Event Invalid'` | Malformed or mismatched wellness notification ignored |
266
+ | `WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS` / `FAILED` | `'Wellbeing Break Idle Code Fetch ...'` | System wellness idle-code lookup result |
267
+
268
+ All event names are defined in `constants.ts` as `METRIC_EVENT_NAMES`. Events follow a `{Domain} {Action} {Success|Failed}` naming convention:
269
+
270
+ | Category | Success Event | Failure Event |
271
+ |---|---|---|
272
+ | Station Login | `STATION_LOGIN_SUCCESS` | `STATION_LOGIN_FAILED` |
273
+ | Station Logout | `STATION_LOGOUT_SUCCESS` | `STATION_LOGOUT_FAILED` |
274
+ | Station Relogin | `STATION_RELOGIN_SUCCESS` | `STATION_RELOGIN_FAILED` |
275
+ | State Change | `AGENT_STATE_CHANGE_SUCCESS` | `AGENT_STATE_CHANGE_FAILED` |
276
+ | Buddy Agents | `FETCH_BUDDY_AGENTS_SUCCESS` | `FETCH_BUDDY_AGENTS_FAILED` |
277
+ | WebSocket Register | `WEBSOCKET_REGISTER_SUCCESS` | `WEBSOCKET_REGISTER_FAILED` |
278
+ | Task Accept | `TASK_ACCEPT_SUCCESS` | `TASK_ACCEPT_FAILED` |
279
+ | Task Decline | `TASK_DECLINE_SUCCESS` | `TASK_DECLINE_FAILED` |
280
+ | Task End | `TASK_END_SUCCESS` | `TASK_END_FAILED` |
281
+ | Task Wrapup | `TASK_WRAPUP_SUCCESS` | `TASK_WRAPUP_FAILED` |
282
+ | Task Hold | `TASK_HOLD_SUCCESS` | `TASK_HOLD_FAILED` |
283
+ | Task Resume | `TASK_RESUME_SUCCESS` | `TASK_RESUME_FAILED` |
284
+ | Task Consult Start | `TASK_CONSULT_START_SUCCESS` | `TASK_CONSULT_START_FAILED` |
285
+ | Task Consult End | `TASK_CONSULT_END_SUCCESS` | `TASK_CONSULT_END_FAILED` |
286
+ | Task Transfer | `TASK_TRANSFER_SUCCESS` | `TASK_TRANSFER_FAILED` |
287
+ | Task Resume Recording | `TASK_RESUME_RECORDING_SUCCESS` | `TASK_RESUME_RECORDING_FAILED` |
288
+ | Task Pause Recording | `TASK_PAUSE_RECORDING_SUCCESS` | `TASK_PAUSE_RECORDING_FAILED` |
289
+ | Task Accept Consult | `TASK_ACCEPT_CONSULT_SUCCESS` | `TASK_ACCEPT_CONSULT_FAILED` |
290
+ | Task Auto Answer | `TASK_AUTO_ANSWER_SUCCESS` | `TASK_AUTO_ANSWER_FAILED` |
291
+ | Conference Start | `TASK_CONFERENCE_START_SUCCESS` | `TASK_CONFERENCE_START_FAILED` |
292
+ | Conference End | `TASK_CONFERENCE_END_SUCCESS` | `TASK_CONFERENCE_END_FAILED` |
293
+ | Conference Transfer | `TASK_CONFERENCE_TRANSFER_SUCCESS` | `TASK_CONFERENCE_TRANSFER_FAILED` |
294
+ | Conference Exit | `TASK_CONFERENCE_EXIT_SUCCESS` | `TASK_CONFERENCE_EXIT_FAILED` |
295
+ | Conference Participant Drop | `TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS` | `TASK_CONFERENCE_PARTICIPANT_DROP_FAILED` |
296
+ | Switch Call | `TASK_SWITCH_CALL_SUCCESS` | `TASK_SWITCH_CALL_FAILED` |
297
+ | Outdial | `TASK_OUTDIAL_SUCCESS` | `TASK_OUTDIAL_FAILED` |
298
+ | WxApp Task Mute | `WXAPP_TASK_MUTE_SUCCESS` | `WXAPP_TASK_MUTE_FAILED` |
299
+ | WxApp Task DTMF | `WXAPP_TASK_DTMF_SUCCESS` | `WXAPP_TASK_DTMF_FAILED` |
300
+ | WxApp Task Accept | `WXAPP_TASK_ACCEPT_SUCCESS` | `WXAPP_TASK_ACCEPT_FAILED` |
301
+ | WxApp Task Decline | `WXAPP_TASK_DECLINE_SUCCESS` | `WXAPP_TASK_DECLINE_FAILED` |
302
+ | WxApp Session Init | `WXAPP_SESSION_INIT_SUCCESS` | `WXAPP_SESSION_INIT_FAILED` |
303
+ | WxApp Usersub Publish | `WXAPP_USERSUB_PUBLISH_SUCCESS` | `WXAPP_USERSUB_PUBLISH_FAILED` |
304
+ | WxApp Mercury Subscribe | `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` | `WXAPP_MERCURY_SUBSCRIBE_FAILED` |
305
+ | WxApp Offer Participant Fields | — | `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` |
306
+ | Upload Logs | `UPLOAD_LOGS_SUCCESS` | `UPLOAD_LOGS_FAILED` |
307
+ | WebSocket Deregister | `WEBSOCKET_DEREGISTER_SUCCESS` | `WEBSOCKET_DEREGISTER_FAIL` |
308
+ | Device Type Update | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `AGENT_DEVICE_TYPE_UPDATE_FAILED` |
309
+ | EntryPoint | `ENTRYPOINT_FETCH_SUCCESS` | `ENTRYPOINT_FETCH_FAILED` |
310
+ | AddressBook | `ADDRESSBOOK_FETCH_SUCCESS` | `ADDRESSBOOK_FETCH_FAILED` |
311
+ | Queue | `QUEUE_FETCH_SUCCESS` | `QUEUE_FETCH_FAILED` |
312
+ | Outdial ANI Entries | `OUTDIAL_ANI_EP_FETCH_SUCCESS` | `OUTDIAL_ANI_EP_FETCH_FAILED` |
313
+
314
+ ### Webex Together behavioral taxonomy
315
+
316
+ Webex Together events (`WXAPP_*` constants) use `*_webex_together` compound targets. Assembled Amplitude names follow `wxcc_sdk.user.{target}.{verb}`:
317
+
318
+ | Feature | Behavioral target | Success name | Failure name | Skip name |
319
+ |---|---|---|---|---|
320
+ | Task accept / answer | `task_accept_webex_together` | `wxcc_sdk.user.task_accept_webex_together.complete` | `wxcc_sdk.user.task_accept_webex_together.fail` | — |
321
+ | Task reject / decline | `task_reject_webex_together` | `wxcc_sdk.user.task_reject_webex_together.complete` | `wxcc_sdk.user.task_reject_webex_together.fail` | — |
322
+ | Task mute / unmute toggle | `task_mute_webex_together` | `wxcc_sdk.user.task_mute_webex_together.complete` | `wxcc_sdk.user.task_mute_webex_together.fail` | — |
323
+ | Task DTMF / keypad | `task_dtmf_webex_together` | `wxcc_sdk.user.task_dtmf_webex_together.complete` | `wxcc_sdk.user.task_dtmf_webex_together.fail` | — |
324
+ | Usersub publish | `webex_together_usersub_publish` | `wxcc_sdk.user.webex_together_usersub_publish.complete` | `wxcc_sdk.user.webex_together_usersub_publish.fail` | — |
325
+ | Mercury subscribe | `webex_together_mercury_subscribe` | `wxcc_sdk.user.webex_together_mercury_subscribe.complete` | `wxcc_sdk.user.webex_together_mercury_subscribe.fail` | — |
326
+ | Session init | `webex_together_session_init` | `wxcc_sdk.user.webex_together_session_init.complete` | `wxcc_sdk.user.webex_together_session_init.fail` | `wxcc_sdk.user.webex_together_session_init.ignore` |
327
+ | Offer participant fields | `webex_together_offer_participant_fields` | — | `wxcc_sdk.user.webex_together_offer_participant_fields.fail` | — |
328
+
329
+ Operational and business wire names are unchanged (`WXCC_SDK_WXAPP_*`).
330
+
331
+ ### WxApp metric payload fields (operational / behavioral tags)
332
+
333
+ | Event | Payload fields (booleans/enums; no raw IDs) |
334
+ |---|---|
335
+ | `WXAPP_SESSION_INIT_SUCCESS` | `loginOption`, `enableWxBetterTogether`, `usersubPublished`, `mercurySubscribed`, `telephonyTaskType` |
336
+ | `WXAPP_SESSION_INIT_FAILED` | above + `skipReason` (`usersub_not_published`, `mercury_not_subscribed`, `mercury_subscribe_failed`, `publish_failed`) + optional `error`. `usersubPublished` reflects **retained session state** (`isAnswerCallsStateActive()` / `answerCallsState`), including on catch-path failures — not gated on the current init attempt's `publishedEnable`. |
337
+ | `WXAPP_USERSUB_PUBLISH_SUCCESS` | `enableWxBetterTogether`, `usersubPublished` |
338
+ | `WXAPP_USERSUB_PUBLISH_FAILED` | `enableWxBetterTogether`, `usersubPublished`, optional `skipReason`, `error`. `usersubPublished` reflects **retained session state** (`answerCallsState`), not the requested enable value — a failed disable/refresh while usersub remains active reports `true`. |
339
+ | `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` | `mercurySubscribed: true` |
340
+ | `WXAPP_MERCURY_SUBSCRIBE_FAILED` | `mercurySubscribed: false`, `error` |
341
+ | `WXAPP_TASK_ACCEPT_*` / `DECLINE_*` | `taskId`, `hasDeviceCallId`, `hasDeviceId`, optional `acceptReason`; failures add `trackingId` when available. `acceptReason` is captured **before** the telephony REST call (not hard-coded `wxApp_offer_ready`) so success and failure both retain `wxApp_answer_pending` / `wxApp_accept_in_flight` when those flags were already set. Outdial-cancel AQM failures (`error.details`) include the same participant context fields via `getWxAppTelephonyMetricContext` merged with AQM correlation fields. Outdial cancel during post-accept pending phase may report `acceptReason: wxApp_answer_pending`. |
342
+ | `WXAPP_TASK_MUTE_*` / `DTMF_*` | `taskId`, `hasDeviceCallId`, `hasDeviceId`; failures add `trackingId` when available |
343
+ | `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` | `taskId`, `usersubPublished`, `hasDeviceCallId`, `hasDeviceId`, `acceptReason` |
344
+
345
+ ### Customer console log triage (MMT / support)
346
+
347
+ Customers often embed with `logger.level: 'log'`. wxApp diagnostics use **`LoggerProxy.log`** / **`warn`** / **`error`** with prefix **`[CC wxApp]`** and grep-friendly `key=value` suffixes on the message string (fields also appear in structured `data`).
348
+
349
+ | Search in browser console | Meaning |
350
+ |---|---|
351
+ | `[CC wxApp]` | All wxApp diagnostic lines (primary search) |
352
+ | `WXCC_SDK_WXAPP` | Operational metric wire names (name only in console; payloads go to backend) |
353
+ | `session readiness` | usersub + Mercury state after station login |
354
+ | `offer decision` | Why Accept is visible/enabled; includes `acceptReason`, `hasDeviceCallId`, `usersubPublished` |
355
+ | `participant fields mismatch` | usersub active but WS participant **still** missing wxApp triple after ~500 ms (transient gaps on first OFFERED tick are normal — look for `wxApp_offer_ready` within 1 s) |
356
+ | `telephony accept failed` | REST answer failure; includes `trackingId` when available |
357
+
358
+ Do **not** search `WXAPP` alone — it matches metric wire names but misses `[CC wxApp]` diagnostic lines.
359
+
360
+ Example lines at `logger.level: 'log'`:
361
+
362
+ ```text
363
+ PLUGIN_CC - [LOG]: ... [CC wxApp] session readiness usersubPublished=true mercurySubscribed=true loginOption=EXTENSION ...
364
+ PLUGIN_CC - [LOG]: ... [CC wxApp] offer decision acceptReason=wxApp_offer_ready hasDeviceCallId=true usersubPublished=true interactionId=...
365
+ PLUGIN_CC - [WARN]: ... [CC wxApp] participant fields mismatch usersubPublished=true hasDeviceCallId=false acceptReason=extension_non_wxApp_offer interactionId=...
366
+ PLUGIN_CC - [LOG]: ... operational-events -> @submitEvent. Submit event: WXCC_SDK_WXAPP_TASK_ACCEPT_SUCCESS
367
+ ```
368
+
369
+ Special events (no success/failure pair):
370
+
371
+ - `AGENT_RONA` — has behavioral taxonomy (`service.agent_rona.set`)
372
+
373
+ - `AGENT_CONTACT_ASSIGN_FAILED` — has behavioral taxonomy (`service.agent_contact_assign.fail`)
374
+
375
+ - `AGENT_INVITE_FAILED` — has behavioral taxonomy (`service.agent_invite.fail`)
376
+
377
+ - `WEBSOCKET_EVENT_RECEIVED` — **no** behavioral taxonomy (not in `eventTaxonomyMap`)
378
+
379
+ - `WXAPP_SESSION_SKIPPED` — has behavioral taxonomy (`wxcc_sdk.user.webex_together_session_init.ignore`)
380
+
381
+ Of the 121 defined metric names, 97 have behavioral taxonomy and 24 do not. The unmapped names are the eight original `AI_ASSISTANT_*` names, three Agent Wellness Break `AI_ASSISTANT_WELLNESS_*` names, two `WELLBEING_BREAK_IDLE_CODE_FETCH_*` names, eight `AI_SUMMARY_*` names, and three WebSocket names (`WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, and `WEBSOCKET_EVENT_RECEIVED`). Agent Wellness Break and AI summary metrics are intentionally operational-only.
382
+
383
+ ### Complete METRIC_EVENT_NAMES catalog
384
+
385
+ This table contains all 121 names from `src/metrics/constants.ts`; taxonomy presence is checked against `src/metrics/behavioral-events.ts`: 97 mapped and 24 unmapped.
386
+
387
+ | Constant | Emitted name | Behavioral taxonomy? |
388
+ |---|---|---|
389
+ | `STATION_LOGIN_SUCCESS` | `Station Login Success` | yes |
390
+ | `STATION_LOGIN_FAILED` | `Station Login Failed` | yes |
391
+ | `STATION_LOGOUT_SUCCESS` | `Station Logout Success` | yes |
392
+ | `STATION_LOGOUT_FAILED` | `Station Logout Failed` | yes |
393
+ | `STATION_RELOGIN_SUCCESS` | `Station Relogin Success` | yes |
394
+ | `STATION_RELOGIN_FAILED` | `Station Relogin Failed` | yes |
395
+ | `AGENT_STATE_CHANGE_SUCCESS` | `Agent State Change Success` | yes |
396
+ | `AGENT_STATE_CHANGE_FAILED` | `Agent State Change Failed` | yes |
397
+ | `FETCH_BUDDY_AGENTS_SUCCESS` | `Fetch Buddy Agents Success` | yes |
398
+ | `FETCH_BUDDY_AGENTS_FAILED` | `Fetch Buddy Agents Failed` | yes |
399
+ | `WEBSOCKET_REGISTER_SUCCESS` | `Websocket Register Success` | yes |
400
+ | `WEBSOCKET_REGISTER_FAILED` | `Websocket Register Failed` | yes |
401
+ | `AGENT_RONA` | `Agent RONA` | yes |
402
+ | `AGENT_CONTACT_ASSIGN_FAILED` | `Agent Contact Assign Failed` | yes |
403
+ | `AGENT_INVITE_FAILED` | `Agent Invite Failed` | yes |
404
+ | `TASK_ACCEPT_SUCCESS` | `Task Accept Success` | yes |
405
+ | `TASK_ACCEPT_FAILED` | `Task Accept Failed` | yes |
406
+ | `TASK_DECLINE_SUCCESS` | `Task Decline Success` | yes |
407
+ | `TASK_DECLINE_FAILED` | `Task Decline Failed` | yes |
408
+ | `TASK_END_SUCCESS` | `Task End Success` | yes |
409
+ | `TASK_END_FAILED` | `Task End Failed` | yes |
410
+ | `TASK_WRAPUP_SUCCESS` | `Task Wrapup Success` | yes |
411
+ | `TASK_WRAPUP_FAILED` | `Task Wrapup Failed` | yes |
412
+ | `TASK_HOLD_SUCCESS` | `Task Hold Success` | yes |
413
+ | `TASK_HOLD_FAILED` | `Task Hold Failed` | yes |
414
+ | `TASK_RESUME_SUCCESS` | `Task Resume Success` | yes |
415
+ | `TASK_RESUME_FAILED` | `Task Resume Failed` | yes |
416
+ | `TASK_CONSULT_START_SUCCESS` | `Task Consult Start Success` | yes |
417
+ | `TASK_CONSULT_START_FAILED` | `Task Consult Start Failed` | yes |
418
+ | `TASK_CONSULT_END_SUCCESS` | `Task Consult End Success` | yes |
419
+ | `TASK_CONSULT_END_FAILED` | `Task Consult End Failed` | yes |
420
+ | `TASK_TRANSFER_SUCCESS` | `Task Transfer Success` | yes |
421
+ | `TASK_TRANSFER_FAILED` | `Task Transfer Failed` | yes |
422
+ | `TASK_RESUME_RECORDING_SUCCESS` | `Task Resume Recording Success` | yes |
423
+ | `TASK_RESUME_RECORDING_FAILED` | `Task Resume Recording Failed` | yes |
424
+ | `TASK_PAUSE_RECORDING_SUCCESS` | `Task Pause Recording Success` | yes |
425
+ | `TASK_PAUSE_RECORDING_FAILED` | `Task Pause Recording Failed` | yes |
426
+ | `TASK_ACCEPT_CONSULT_SUCCESS` | `Task Accept Consult Success` | yes |
427
+ | `TASK_ACCEPT_CONSULT_FAILED` | `Task Accept Consult Failed` | yes |
428
+ | `TASK_AUTO_ANSWER_SUCCESS` | `Task Auto Answer Success` | yes |
429
+ | `TASK_AUTO_ANSWER_FAILED` | `Task Auto Answer Failed` | yes |
430
+ | `TASK_CONFERENCE_START_SUCCESS` | `Task Conference Start Success` | yes |
431
+ | `TASK_CONFERENCE_START_FAILED` | `Task Conference Start Failed` | yes |
432
+ | `TASK_CONFERENCE_END_SUCCESS` | `Task Conference End Success` | yes |
433
+ | `TASK_CONFERENCE_END_FAILED` | `Task Conference End Failed` | yes |
434
+ | `TASK_CONFERENCE_TRANSFER_SUCCESS` | `Task Conference Transfer Success` | yes |
435
+ | `TASK_CONFERENCE_TRANSFER_FAILED` | `Task Conference Transfer Failed` | yes |
436
+ | `TASK_CONFERENCE_EXIT_SUCCESS` | `Task Conference Exit Success` | yes |
437
+ | `TASK_CONFERENCE_EXIT_FAILED` | `Task Conference Exit Failed` | yes |
438
+ | `TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS` | `Task Conference Participant Drop Success` | yes |
439
+ | `TASK_CONFERENCE_PARTICIPANT_DROP_FAILED` | `Task Conference Participant Drop Failed` | yes |
440
+ | `TASK_SWITCH_CALL_SUCCESS` | `Task Switch Call Success` | yes |
441
+ | `TASK_SWITCH_CALL_FAILED` | `Task Switch Call Failed` | yes |
442
+ | `TASK_OUTDIAL_SUCCESS` | `Task Outdial Success` | yes |
443
+ | `TASK_OUTDIAL_FAILED` | `Task Outdial Failed` | yes |
444
+ | `WXAPP_TASK_MUTE_SUCCESS` | `WxApp Task Mute Success` | yes |
445
+ | `WXAPP_TASK_MUTE_FAILED` | `WxApp Task Mute Failed` | yes |
446
+ | `WXAPP_TASK_DTMF_SUCCESS` | `WxApp Task Dtmf Success` | yes |
447
+ | `WXAPP_TASK_DTMF_FAILED` | `WxApp Task Dtmf Failed` | yes |
448
+ | `WXAPP_TASK_ACCEPT_SUCCESS` | `WxApp Task Accept Success` | yes |
449
+ | `WXAPP_TASK_ACCEPT_FAILED` | `WxApp Task Accept Failed` | yes |
450
+ | `WXAPP_TASK_DECLINE_SUCCESS` | `WxApp Task Decline Success` | yes |
451
+ | `WXAPP_TASK_DECLINE_FAILED` | `WxApp Task Decline Failed` | yes |
452
+ | `WXAPP_SESSION_INIT_SUCCESS` | `WxApp Session Init Success` | yes |
453
+ | `WXAPP_SESSION_INIT_FAILED` | `WxApp Session Init Failed` | yes |
454
+ | `WXAPP_SESSION_SKIPPED` | `WxApp Session Skipped` | yes |
455
+ | `WXAPP_USERSUB_PUBLISH_SUCCESS` | `WxApp Usersub Publish Success` | yes |
456
+ | `WXAPP_USERSUB_PUBLISH_FAILED` | `WxApp Usersub Publish Failed` | yes |
457
+ | `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` | `WxApp Mercury Subscribe Success` | yes |
458
+ | `WXAPP_MERCURY_SUBSCRIBE_FAILED` | `WxApp Mercury Subscribe Failed` | yes |
459
+ | `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` | `WxApp Offer Participant Fields Missing` | yes |
460
+ | `UPLOAD_LOGS_SUCCESS` | `Upload Logs Success` | yes |
461
+ | `UPLOAD_LOGS_FAILED` | `Upload Logs Failed` | yes |
462
+ | `WEBSOCKET_DEREGISTER_SUCCESS` | `Websocket Deregister Success` | no |
463
+ | `WEBSOCKET_DEREGISTER_FAIL` | `Websocket Deregister Failed` | no |
464
+ | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `Agent Device Type Update Success` | yes |
465
+ | `AGENT_DEVICE_TYPE_UPDATE_FAILED` | `Agent Device Type Update Failed` | yes |
466
+ | `WEBSOCKET_EVENT_RECEIVED` | `Websocket Event Received` | no |
467
+ | `ENTRYPOINT_FETCH_SUCCESS` | `Entrypoint Fetch Success` | yes |
468
+ | `ENTRYPOINT_FETCH_FAILED` | `Entrypoint Fetch Failed` | yes |
469
+ | `ADDRESSBOOK_FETCH_SUCCESS` | `AddressBook Fetch Success` | yes |
470
+ | `ADDRESSBOOK_FETCH_FAILED` | `AddressBook Fetch Failed` | yes |
471
+ | `QUEUE_FETCH_SUCCESS` | `Queue Fetch Success` | yes |
472
+ | `QUEUE_FETCH_FAILED` | `Queue Fetch Failed` | yes |
473
+ | `OUTDIAL_ANI_EP_FETCH_SUCCESS` | `Outdial ANI Entries Fetch Success` | yes |
474
+ | `OUTDIAL_ANI_EP_FETCH_FAILED` | `Outdial ANI Entries Fetch Failed` | yes |
475
+ | `CAMPAIGN_PREVIEW_ACCEPT_SUCCESS` | `Campaign Preview Accept Success` | yes |
476
+ | `CAMPAIGN_PREVIEW_ACCEPT_FAILED` | `Campaign Preview Accept Failed` | yes |
477
+ | `CAMPAIGN_PREVIEW_SKIP_SUCCESS` | `Campaign Preview Skip Success` | yes |
478
+ | `CAMPAIGN_PREVIEW_SKIP_FAILED` | `Campaign Preview Skip Failed` | yes |
479
+ | `CAMPAIGN_PREVIEW_REMOVE_SUCCESS` | `Campaign Preview Remove Success` | yes |
480
+ | `CAMPAIGN_PREVIEW_REMOVE_FAILED` | `Campaign Preview Remove Failed` | yes |
481
+ | `AI_ASSISTANT_SEND_EVENT_SUCCESS` | `AI Assistant Send Event Success` | no |
482
+ | `AI_ASSISTANT_SEND_EVENT_FAILED` | `AI Assistant Send Event Failed` | no |
483
+ | `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_SUCCESS` | `AI Assistant Get Real Time Assistance Success` | no |
484
+ | `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_FAILED` | `AI Assistant Get Real Time Assistance Failed` | no |
485
+ | `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_SUCCESS` | `AI Assistant Send Real Time Assistance User Action Success` | no |
486
+ | `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED` | `AI Assistant Send Real Time Assistance User Action Failed` | no |
487
+ | `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS` | `AI Assistant Fetch Historic Transcripts Success` | no |
488
+ | `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED` | `AI Assistant Fetch Historic Transcripts Failed` | no |
489
+ | `AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED` | `AI Assistant Wellness Action Accepted` | no |
490
+ | `AI_ASSISTANT_WELLNESS_ACTION_FAILED` | `AI Assistant Wellness Action Failed` | no |
491
+ | `AI_ASSISTANT_WELLNESS_EVENT_INVALID` | `AI Assistant Wellness Event Invalid` | no |
492
+ | `WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS` | `Wellbeing Break Idle Code Fetch Success` | no |
493
+ | `WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED` | `Wellbeing Break Idle Code Fetch Failed` | no |
494
+ | `AI_SUMMARY_GET_POST_CALL_SUCCESS` | `Post Call Summary Get Success` | no |
495
+ | `AI_SUMMARY_GET_POST_CALL_FAILED` | `Post Call Summary Get Failed` | no |
496
+ | `AI_SUMMARY_GET_MID_CALL_SUCCESS` | `Mid Call Summary Get Success` | no |
497
+ | `AI_SUMMARY_GET_MID_CALL_FAILED` | `Mid Call Summary Get Failed` | no |
498
+ | `AI_SUMMARY_POST_CALL_RESPONSE_SUCCESS` | `Post Call Summary Response Success` | no |
499
+ | `AI_SUMMARY_POST_CALL_RESPONSE_FAILED` | `Post Call Summary Response Failed` | no |
500
+ | `AI_SUMMARY_MID_CALL_RESPONSE_SUCCESS` | `Mid Call Summary Response Success` | no |
501
+ | `AI_SUMMARY_MID_CALL_RESPONSE_FAILED` | `Mid Call Summary Response Failed` | no |
502
+ | `USER_PREFERENCE_GET_SUCCESS` | `User Preference Get Success` | yes |
503
+ | `USER_PREFERENCE_GET_FAILED` | `User Preference Get Failed` | yes |
504
+ | `USER_PREFERENCE_CREATE_SUCCESS` | `User Preference Create Success` | yes |
505
+ | `USER_PREFERENCE_CREATE_FAILED` | `User Preference Create Failed` | yes |
506
+ | `USER_PREFERENCE_UPDATE_SUCCESS` | `User Preference Update Success` | yes |
507
+ | `USER_PREFERENCE_UPDATE_FAILED` | `User Preference Update Failed` | yes |
508
+ | `USER_PREFERENCE_DELETE_SUCCESS` | `User Preference Delete Success` | yes |
509
+ | `USER_PREFERENCE_DELETE_FAILED` | `User Preference Delete Failed` | yes |
510
+
511
+ Defined names without an `eventTaxonomyMap` entry: `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_EVENT_RECEIVED`, all eight AI Assistant request names, and the seven Agent Wellness Break notification/action/system-code/ASC names listed above.
512
+
513
+ ## Requires (dependencies)
514
+ - `webex.internal.newMetrics` submission APIs
515
+ - Webex ready lifecycle
516
+ - LoggerProxy and browser visibility metadata
517
+
518
+ - **`@webex/internal-plugin-metrics`**: Provides `webex.internal.newMetrics` for actual metric submission (`submitBehavioralEvent`, `submitOperationalEvent`, `submitBusinessEvent`)
519
+
520
+ - **`LoggerProxy`**: Used for error logging within the metrics module
521
+
522
+ - **`Failure` type** (from `services/core/GlobalTypes`): Used in `getCommonTrackingFieldForAQMResponseFailed`
523
+
524
+ - **`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
525
+
526
+ ## Requirements
527
+ | ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence |
528
+ |---|---|---|---|---|---|---|
529
+ | 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 |
530
+ | 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 |
531
+ | 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 |
532
+ | 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 |
533
+ | 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 |
534
+ | METRICS-R-006 | Track wellness action, invalid notification, system-code lookup, and ASC state-change outcomes as operational metrics using fixed names and reason-only/context-minimized payloads. | The feature needs diagnosable lifecycle signals without logging wellness payloads or agent PII. | `src/metrics/constants.ts`, `src/cc.ts`, `src/services/ApiAiAssistant.ts` | `test/unit/spec/cc.ts`, `test/unit/spec/services/ApiAiAssistant.ts` | These names intentionally have no behavioral taxonomy. | PRESENT |
535
+
536
+ ## Design Overview
537
+ 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.
538
+
539
+ > **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.
540
+
541
+ Each behavioral event maps to a structured taxonomy in `behavioral-events.ts`:
542
+
543
+ ```text
544
+ {product}.{agent}.{target}.{verb}
545
+ ```
546
+
547
+ - **product**: Always `'wxcc_sdk'` (from `PRODUCT_NAME`)
548
+
549
+ - **agent**: `'user'` for user-initiated actions, `'service'` for system-generated events
550
+
551
+ - **target**: Snake_case description of the action (e.g., `'station_login'`, `'task_accept'`)
552
+
553
+ - **verb**: `'complete'` for success, `'fail'` for failure, `'set'` for RONA events
554
+
555
+ **Example**: `STATION_LOGIN_SUCCESS` maps to `wxcc_sdk.user.station_login.complete`
556
+
557
+ > **Note**: The 20 names catalogued above as lacking `eventTaxonomyMap` entries, including all nine new wellness-related names, are operational-only.
558
+ >
559
+ > Calling `trackBehavioralEvent` with these event names will push an event with an `undefined` taxonomy.
560
+
561
+ > **Purpose**: Technical documentation for the metrics collection, batching, and submission system within the Contact Center SDK.
562
+
563
+ Each metric event name maps to a `BehavioralEventTaxonomy` with four fields:
564
+
565
+ ```typescript
566
+ type BehavioralEventTaxonomy = {
567
+ product: MetricEventProduct; // Always PRODUCT_NAME ('wxcc_sdk')
568
+ agent: MetricEventAgent; // 'user' or 'service'
569
+ target: string; // e.g., 'station_login', 'task_accept'
570
+ verb: MetricEventVerb; // 'complete' for success, 'fail' for failure
571
+ };
572
+ ```
573
+
574
+ The final behavioral event name is constructed as: `{product}.{agent}.{target}.{verb}`
575
+
576
+ Example: `wxcc_sdk.user.station_login.complete`
577
+
578
+ The mapping is defined in `behavioral-events.ts` via `eventTaxonomyMap` and accessed through `getEventTaxonomy(name)`.
579
+
580
+ Participant Drop uses target `task_conference_participant_drop` with `complete` for success and `fail` for failure. Its metric payload is limited to task/request correlation and standard AQM tracking fields; participant IDs, numbers, names, URLs, headers, and raw routing payloads are excluded.
581
+
582
+ ```mermaid
583
+ flowchart LR
584
+ A[timeEvent keys] --> B[runningEvents stores startTime + key Set]
585
+ B --> C[trackEvent called with one of the keys]
586
+ C --> D[addDurationIfTimed matches key]
587
+ D --> E[Calculates duration_ms = now - startTime]
588
+ E --> F[Removes all keys for that operation]
589
+ F --> G[Attaches duration_ms to payload]
590
+ ```
591
+
592
+ Usage pattern from `cc.ts`:
593
+
594
+ ```typescript
595
+ // Before operation
596
+ this.metricsManager.timeEvent([
597
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
598
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
599
+ ]);
600
+
601
+ // On success
602
+ this.metricsManager.trackEvent(
603
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
604
+ { ...MetricsManager.getCommonTrackingFieldForAQMResponse(resp) },
605
+ ['behavioral', 'operational', 'business']
606
+ );
607
+
608
+ // On failure
609
+ this.metricsManager.trackEvent(
610
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
611
+ { ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure) },
612
+ ['behavioral', 'operational', 'business']
613
+ );
614
+ ```
615
+
616
+ Two static helpers extract standardized fields from AQM responses for metric payloads:
617
+
618
+ Extracts: `agentId`, `agentSessionId`, `teamId`, `siteId`, `orgId`, `eventType`, `trackingId`, `notifTrackingId`
619
+
620
+ ## Data Flow
621
+ ```mermaid
622
+ flowchart TD
623
+ A[cc.ts calls metricsManager.timeEvent] --> B[Store startTime + keys in runningEvents]
624
+ B --> C[Operation executes]
625
+ C --> D{Success or Failure?}
626
+ D -->|Success| E[cc.ts calls metricsManager.trackEvent with success name]
627
+ D -->|Failure| F[cc.ts calls metricsManager.trackEvent with failure name]
628
+ E --> G[addDurationIfTimed attaches duration_ms]
629
+ F --> G
630
+ G --> H[preparePayload cleans and enriches]
631
+ H --> I{Metric type?}
632
+ I -->|behavioral| J[Push to pendingBehavioralEvents]
633
+ I -->|operational| K[Push to pendingOperationalEvents]
634
+ I -->|business| L[Push to pendingBusinessEvents]
635
+ J --> M[submitPendingBehavioralEvents]
636
+ K --> N[submitPendingOperationalEvents]
637
+ L --> O[submitPendingBusinessEvents]
638
+ M --> P[webex.internal.newMetrics.submitBehavioralEvent]
639
+ N --> Q[webex.internal.newMetrics.submitOperationalEvent]
640
+ O --> R[webex.internal.newMetrics.submitBusinessEvent]
641
+ ```
642
+
643
+ ## Sequence Diagram(s)
644
+ Sequence coverage:
645
+
646
+ | Operation group | Diagram | Failure / recovery coverage |
647
+ |---|---|---|
648
+ | Time and track an event | Timing and tracking | Success/failure keys share one timer; missing timing data leaves the payload untimed. |
649
+ | Queue until SDK readiness | SDK readiness | A not-ready SDK retains events; READY drains them through the configured metrics service. |
650
+ | Submit behavioral/operational/business events | Category submission | The module does not retry or requeue after handing an event to `newMetrics`. |
651
+ | Disable metrics | Disablement | Pending queues are cleared and future timing/tracking calls return immediately. |
652
+
653
+ ### Timing and tracking
654
+
655
+ ```mermaid
656
+ sequenceDiagram
657
+ participant CC as ContactCenter (cc.ts)
658
+ participant MM as MetricsManager
659
+ participant NM as webex.internal.newMetrics
660
+
661
+ CC->>MM: timeEvent([SUCCESS_KEY, FAILURE_KEY])
662
+ Note over MM: Store startTime + key set in runningEvents
663
+ CC->>CC: Execute operation (e.g., stationLogin)
664
+ Note over MM: trackEvent defaults to ['behavioral'] only if no metricServices specified
665
+ alt Success
666
+ CC->>MM: trackEvent(SUCCESS_KEY, payload, ['behavioral', 'operational', 'business'])
667
+ else Failure
668
+ CC->>MM: trackEvent(FAILURE_KEY, payload, ['behavioral', 'operational', 'business'])
669
+ end
670
+ MM->>MM: addDurationIfTimed → attach duration_ms
671
+ MM->>MM: preparePayload → clean empty fields, add tabHidden
672
+ loop For each metric type
673
+ MM->>MM: Push to pending queue
674
+ alt readyToSubmitEvents
675
+ MM->>NM: submit[Behavioral|Operational|Business]Event
676
+ else not ready
677
+ Note over MM: Events stay queued until SDK ready
678
+ end
679
+ end
680
+ ```
681
+
682
+ ### SDK readiness
683
+
684
+ ```mermaid
685
+ sequenceDiagram
686
+ participant CC as ContactCenter
687
+ participant MM as MetricsManager
688
+ participant Webex as WebexSDK
689
+
690
+ CC->>MM: getInstance({webex})
691
+ MM->>MM: Create singleton (if needed)
692
+ MM->>MM: setWebex(webex)
693
+ opt webex.ready === true
694
+ MM->>MM: setReadyToSubmitEvents()
695
+ MM->>MM: submitPendingEvents()
696
+ end
697
+ MM->>Webex: webex.once('ready', callback)
698
+ Note over MM: 'ready' listener is always registered
699
+ Webex-->>MM: 'ready' event fires
700
+ MM->>MM: setReadyToSubmitEvents()
701
+ MM->>MM: submitPendingEvents()
702
+ ```
703
+
704
+ ### Category submission
705
+
706
+ ```mermaid
707
+ sequenceDiagram
708
+ participant Caller
709
+ participant MM as MetricsManager
710
+ participant NM as webex.internal.newMetrics
711
+ Caller->>MM: trackEvent(name, payload, metricServices)
712
+ loop each requested metric service
713
+ alt behavioral
714
+ MM->>NM: submitBehavioralEvent(taxonomy + payload)
715
+ else operational
716
+ MM->>NM: submitOperationalEvent(WXCC_SDK_* + payload)
717
+ else business
718
+ MM->>NM: submitBusinessEvent(WXCC_SDK_* + appType)
719
+ else invalid metric service
720
+ MM->>MM: LoggerProxy.error(invalid type)
721
+ end
722
+ end
723
+ Note over MM,NM: No module-level retry or requeue after submission handoff
724
+ ```
725
+
726
+ ### Disablement
727
+
728
+ ```mermaid
729
+ sequenceDiagram
730
+ participant Caller
731
+ participant MM as MetricsManager
732
+ Caller->>MM: setMetricsDisabled(true)
733
+ MM->>MM: clear pending behavioral/operational/business queues
734
+ Caller->>MM: timeEvent(...) / trackEvent(...)
735
+ alt metricsDisabled
736
+ MM-->>Caller: return without timing, queueing, or submission
737
+ else re-enabled
738
+ MM->>MM: use normal timing/queue flow
739
+ end
740
+ ```
741
+
742
+ ## Class / Component Relationships
743
+ ```mermaid
744
+ classDiagram
745
+ class ContactCenter
746
+ class MetricsManager
747
+ class METRIC_EVENT_NAMES
748
+ class eventTaxonomyMap
749
+ class NewMetrics
750
+ ContactCenter --> MetricsManager : timeEvent / trackEvent
751
+ MetricsManager --> METRIC_EVENT_NAMES : canonical names
752
+ MetricsManager --> eventTaxonomyMap : behavioral taxonomy lookup
753
+ MetricsManager --> NewMetrics : submit queued event types
754
+ ```
755
+
756
+ | Component | File | Responsibility |
757
+ |---|---|---|
758
+ | `MetricsManager` | `MetricsManager.ts` | Singleton that manages event queuing, timing, payload preparation, and submission |
759
+ | `BehavioralEventTaxonomy`| `behavioral-events.ts` | Maps metric event names to structured taxonomy for behavioral analytics |
760
+ | `METRIC_EVENT_NAMES` | `constants.ts` | Canonical constant object of all tracked metric event names |
761
+
762
+ `MetricsManager` uses a private constructor with a static `getInstance` factory:
763
+
764
+ ```typescript
765
+ // MetricsManager.ts
766
+ export default class MetricsManager {
767
+ private static instance: MetricsManager;
768
+ private constructor() {}
769
+
770
+ public static getInstance(options?: {webex: WebexSDK}): MetricsManager {
771
+ if (!MetricsManager.instance) {
772
+ MetricsManager.instance = new MetricsManager();
773
+ }
774
+ if (!MetricsManager.instance.webex && options?.webex) {
775
+ MetricsManager.instance.setWebex(options.webex);
776
+ }
777
+ return MetricsManager.instance;
778
+ }
779
+
780
+ public static resetInstance() {
781
+ MetricsManager.instance = undefined;
782
+ }
783
+ }
784
+ ```
785
+
786
+ - The Webex SDK instance is set once via `setWebex()`, which listens for the `ready` event before flushing pending queues.
787
+
788
+ - `resetInstance()` sets the singleton to `undefined`, allowing a fresh instance to be created. Primarily used in tests.
789
+
790
+ ## Use Cases
791
+ - **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`.
792
+ - **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`.
793
+ - **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`.
794
+
795
+ ```typescript
796
+ import MetricsManager from '../metrics/MetricsManager';
797
+ import {METRIC_EVENT_NAMES} from '../metrics/constants';
798
+
799
+ // Get the singleton instance (webex is set during the ContactCenter READY callback)
800
+ const metrics = MetricsManager.getInstance();
801
+
802
+ // Time an operation, then track its result
803
+ metrics.timeEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS);
804
+ // ... perform the operation ...
805
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {agentId: '123'});
806
+ ```
807
+
808
+ The standard pattern used throughout the Contact Center SDK:
809
+
810
+ ```typescript
811
+ const metrics = MetricsManager.getInstance();
812
+
813
+ // 1. Start timing before the operation
814
+ metrics.timeEvent([
815
+ METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
816
+ METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
817
+ ]);
818
+
819
+ try {
820
+ const response = await performLogin(params);
821
+
822
+ // 2a. Track success (duration_ms auto-added)
823
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {
824
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(response),
825
+ });
826
+ } catch (error) {
827
+ // 2b. Track failure (duration_ms auto-added)
828
+ metrics.trackEvent(METRIC_EVENT_NAMES.STATION_LOGIN_FAILED, {
829
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error),
830
+ });
831
+ }
832
+ ```
833
+
834
+ ## State Model
835
+ 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.
836
+
837
+ `preparePayload()` processes every event payload before submission:
838
+
839
+ 1. **Removes empty/null/undefined fields** — strips keys with `undefined`, `null`, `''`, any arrays, or empty objects
840
+
841
+ 2. **Converts spaces to underscores** — `spacesToUnderscore()` applied to all key names
842
+
843
+ 3. **Adds common metadata** — appends `tabHidden: document.hidden` in browser environments
844
+
845
+ ## Business Rules & Invariants
846
+ - `timeEvent` and every tracking method return immediately while metrics are disabled.
847
+ - Only names present in `eventTaxonomyMap` receive behavioral taxonomy; 81 of 101 names are mapped and all 20 exceptions are explicitly catalogued as unmapped.
848
+ - `submittingEvents` prevents overlapping queue-drain executions.
849
+ - Authentication is inherited from the host Webex SDK's metrics client; MetricsManager owns no credentials, tokens, or authorization policy.
850
+
851
+ ## Concurrency & Reactive Flow
852
+ - 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.
853
+
854
+ MetricsManager maintains three independent pending event queues:
855
+
856
+ | Queue | Type | Submitted Via | Name Transform | Extra Metadata |
857
+ |---|---|---|---|---|
858
+ | `pendingBehavioralEvents` | behavioral | `webex.internal.newMetrics.submitBehavioralEvent` | Taxonomy-based (`{product}.{agent}.{target}.{verb}`) | None |
859
+ | `pendingOperationalEvents` | operational | `webex.internal.newMetrics.submitOperationalEvent` | `WXCC_SDK_` prefix + uppercase (e.g. `WXCC_SDK_STATION_LOGIN_SUCCESS`) | None |
860
+ | `pendingBusinessEvents` | business | `webex.internal.newMetrics.submitBusinessEvent` | `WXCC_SDK_` prefix + uppercase (same as operational) | `metadata: {appType: 'wxcc_sdk'}` |
861
+
862
+ ## State Machine
863
+ ```mermaid
864
+ stateDiagram-v2
865
+ [*] --> Buffering
866
+ Buffering --> ReadyToSubmit: host already ready / ready event
867
+ ReadyToSubmit --> Submitting: pending events and lock acquired
868
+ Submitting --> ReadyToSubmit: queues drained and lock released
869
+ Buffering --> Disabled: setMetricsDisabled(true)
870
+ ReadyToSubmit --> Disabled: setMetricsDisabled(true)
871
+ Disabled --> Buffering: enabled before host ready
872
+ Disabled --> ReadyToSubmit: enabled after host ready
873
+ ```
874
+
875
+ - **readyToSubmitEvents**: Set to `true` only after `webex.once('ready')` fires. Events queue until then.
876
+
877
+ - **submittingEvents**: Lock flag to prevent concurrent `submitPendingEvents()` calls.
878
+
879
+ - **metricsDisabled**: When `true`, `timeEvent` and all `track*` methods return early, and `clearPendingEvents()` empties all queues.
880
+
881
+ ## Pitfalls
882
+ - `METRIC_EVENT_NAMES` and `eventTaxonomyMap` are different inventories: 24 defined names intentionally have no behavioral taxonomy.
883
+ - `setMetricsDisabled(true)` clears pending queues but does not create a delivery receipt; callers must not infer that previously submitted events were accepted.
884
+ - Submission helpers hand events to `webex.internal.newMetrics` without a module-level retry/requeue policy, so telemetry must remain non-blocking and non-authoritative.
885
+
886
+ Static helper that extracts common tracking fields from an AQM failure response.
887
+
888
+ **Parameters**:
889
+
890
+ - `failureResponse` (Failure): The AQM failure response object
891
+
892
+ **Returns**: `Record<string, any>` with fields: `agentId`, `trackingId`, `notifTrackingId`, `orgId`, `failureType`, `failureReason`, `reasonCode`
893
+
894
+ MetricsManager is designed to be non-blocking. Metric failures do not propagate to callers:
895
+
896
+ - If `webex` is not yet ready, events are queued in `pendingBehavioralEvents`, `pendingOperationalEvents`, or `pendingBusinessEvents`
897
+
898
+ - Once `webex.ready` fires, all pending events are flushed
899
+
900
+ - If metrics are disabled via `setMetricsDisabled(true)`, all track methods silently return
901
+
902
+ - Invalid metric types log an error via `LoggerProxy` but do not throw
903
+
904
+ MetricsManager does not throw errors to callers. Instead:
905
+
906
+ - Invalid metric types are logged via `LoggerProxy.error`
907
+
908
+ - Empty `timeEvent` key arrays are logged and ignored
909
+
910
+ - Disabled state (`metricsDisabled`) prevents both timing and tracking and silently drops new events
911
+
912
+ - The `submittingEvents` lock prevents race conditions during concurrent submissions
913
+
914
+ Extracts: `agentId`, `trackingId`, `notifTrackingId`, `orgId`, `failureType`, `failureReason`, `reasonCode`
915
+
916
+ **Cause**: Webex SDK not yet ready when `trackEvent` is called
917
+
918
+ **Solution**: Events are automatically queued in `pending*Events` arrays and flushed once `webex.once('ready')` fires. Verify the SDK is initializing correctly.
919
+
920
+ **Cause**: `timeEvent` was not called before `trackEvent`, or the event name does not match any key in `runningEvents`
921
+
922
+ **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.
923
+
924
+ **Cause**: `metricsDisabled` is set to `true`
925
+
926
+ **Solution**: Check if `setMetricsDisabled(true)` was called. This clears all pending queues and causes all `track*` methods to return early.
927
+
928
+ ## Module Do's / Don'ts
929
+ - DO use `METRIC_EVENT_NAMES` and the matching category submitter instead of constructing event names manually.
930
+ - DO keep timing success/failure keys together so either outcome clears the same running timer.
931
+ - DON'T add a behavioral taxonomy entry unless the backend taxonomy contract exists.
932
+ - DON'T make product behavior depend on metrics delivery.
933
+
934
+ ## Key Design Trade-off
935
+ - Telemetry is deliberately non-blocking and queue-backed so product behavior never waits for metrics; failures are logged rather than propagated.
936
+
937
+ ## Test-Case Strategy (module)
938
+ 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.
939
+
940
+ | Behavior / Requirement | Existing test evidence | Gap |
941
+ |---|---|---|
942
+ | `METRICS-R-001` | `test/unit/spec/metrics/MetricsManager.ts` | Add a catalog parity assertion if constants change. |
943
+ | `METRICS-R-002` | `test/unit/spec/metrics/behavioral-events.ts` | Keep explicit coverage for all 24 unmapped names. |
944
+ | `METRICS-R-003` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
945
+ | `METRICS-R-004` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
946
+ | `METRICS-R-005` | `test/unit/spec/metrics/MetricsManager.ts` | Authentication ownership is verified indirectly through the host metrics client. |
947
+ | `METRICS-R-006` | `test/unit/spec/cc.ts`, `test/unit/spec/services/ApiAiAssistant.ts` | Add direct catalog-parity coverage if metrics constants receive a dedicated test. |
948
+
949
+ ## Traceability
950
+ - Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../ai-docs/SPEC_INDEX.md`
951
+ - Coverage state and contracts baseline: `../../../.sdd/manifest.json`
952
+
953
+ - [`MetricsManager.ts`](../MetricsManager.ts) - Singleton metrics manager implementation
954
+
955
+ - [`behavioral-events.ts`](../behavioral-events.ts) - Event taxonomy mapping
956
+
957
+ - [`constants.ts`](../constants.ts) - `METRIC_EVENT_NAMES` definitions
958
+
959
+ - [`../../constants.ts`](../../constants.ts) - `PRODUCT_NAME` constant
960
+
961
+ - [`services/core/GlobalTypes.ts`](../../services/core/GlobalTypes.ts) - `Failure` type definition
962
+
963
+ - [MetricsManager.ts](../MetricsManager.ts) — Singleton metrics manager
964
+
965
+ - [behavioral-events.ts](../behavioral-events.ts) — Event taxonomy mapping
966
+
967
+ - [constants.ts](../constants.ts) — METRIC_EVENT_NAMES definitions
968
+
969
+ - [cc.ts](../../cc.ts) — Main plugin class (primary consumer)
970
+
971
+ - [constants.ts](../../constants.ts) — PRODUCT_NAME used in event prefixing