@webex/contact-center 3.12.0-next.9 → 3.12.0-next.90

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 (229) hide show
  1. package/.sdd/manifest.json +876 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +46 -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 +48 -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 +341 -0
  17. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  18. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  19. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  20. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  21. package/ai-docs/patterns/testing-patterns.md +480 -0
  22. package/ai-docs/patterns/typescript-patterns.md +365 -0
  23. package/ai-docs/templates/README.md +102 -0
  24. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  25. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  26. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  27. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  28. package/ai-docs/templates/new-method/00-master.md +80 -0
  29. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  30. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  31. package/ai-docs/templates/new-method/03-tests.md +201 -0
  32. package/ai-docs/templates/new-method/04-validation.md +141 -0
  33. package/ai-docs/templates/new-service/00-master.md +109 -0
  34. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  35. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  36. package/ai-docs/templates/new-service/03-integration.md +178 -0
  37. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  38. package/ai-docs/templates/new-service/05-validation.md +145 -0
  39. package/dist/cc.js +265 -29
  40. package/dist/cc.js.map +1 -1
  41. package/dist/config.js +6 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.js +17 -1
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +20 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +101 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +25 -4
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +153 -8
  52. package/dist/services/ApiAiAssistant.js.map +1 -1
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/config/Util.js +3 -3
  56. package/dist/services/config/Util.js.map +1 -1
  57. package/dist/services/config/constants.js +23 -2
  58. package/dist/services/config/constants.js.map +1 -1
  59. package/dist/services/config/types.js +49 -9
  60. package/dist/services/config/types.js.map +1 -1
  61. package/dist/services/core/Err.js.map +1 -1
  62. package/dist/services/core/Utils.js +107 -32
  63. package/dist/services/core/Utils.js.map +1 -1
  64. package/dist/services/core/websocket/WebSocketManager.js +2 -1
  65. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  66. package/dist/services/core/websocket/types.js.map +1 -1
  67. package/dist/services/index.js +1 -1
  68. package/dist/services/index.js.map +1 -1
  69. package/dist/services/task/Task.js +688 -0
  70. package/dist/services/task/Task.js.map +1 -0
  71. package/dist/services/task/TaskFactory.js +45 -0
  72. package/dist/services/task/TaskFactory.js.map +1 -0
  73. package/dist/services/task/TaskManager.js +728 -527
  74. package/dist/services/task/TaskManager.js.map +1 -1
  75. package/dist/services/task/TaskUtils.js +162 -26
  76. package/dist/services/task/TaskUtils.js.map +1 -1
  77. package/dist/services/task/constants.js +9 -2
  78. package/dist/services/task/constants.js.map +1 -1
  79. package/dist/services/task/dialer.js +78 -0
  80. package/dist/services/task/dialer.js.map +1 -1
  81. package/dist/services/task/digital/Digital.js +77 -0
  82. package/dist/services/task/digital/Digital.js.map +1 -0
  83. package/dist/services/task/state-machine/TaskStateMachine.js +837 -0
  84. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  85. package/dist/services/task/state-machine/actions.js +543 -0
  86. package/dist/services/task/state-machine/actions.js.map +1 -0
  87. package/dist/services/task/state-machine/constants.js +161 -0
  88. package/dist/services/task/state-machine/constants.js.map +1 -0
  89. package/dist/services/task/state-machine/guards.js +340 -0
  90. package/dist/services/task/state-machine/guards.js.map +1 -0
  91. package/dist/services/task/state-machine/index.js +53 -0
  92. package/dist/services/task/state-machine/index.js.map +1 -0
  93. package/dist/services/task/state-machine/types.js +54 -0
  94. package/dist/services/task/state-machine/types.js.map +1 -0
  95. package/dist/services/task/state-machine/uiControlsComputer.js +553 -0
  96. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  97. package/dist/services/task/taskDataNormalizer.js +99 -0
  98. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  99. package/dist/services/task/types.js +212 -4
  100. package/dist/services/task/types.js.map +1 -1
  101. package/dist/services/task/voice/Voice.js +1042 -0
  102. package/dist/services/task/voice/Voice.js.map +1 -0
  103. package/dist/services/task/voice/WebRTC.js +149 -0
  104. package/dist/services/task/voice/WebRTC.js.map +1 -0
  105. package/dist/types/cc.d.ts +94 -1
  106. package/dist/types/config.d.ts +6 -0
  107. package/dist/types/constants.d.ts +17 -1
  108. package/dist/types/index.d.ts +21 -6
  109. package/dist/types/metrics/constants.d.ts +21 -1
  110. package/dist/types/services/ApiAiAssistant.d.ts +22 -4
  111. package/dist/types/services/UserPreference.d.ts +118 -0
  112. package/dist/types/services/config/constants.d.ts +21 -0
  113. package/dist/types/services/config/types.d.ts +171 -10
  114. package/dist/types/services/core/Err.d.ts +4 -0
  115. package/dist/types/services/core/Utils.d.ts +33 -13
  116. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  117. package/dist/types/services/core/websocket/types.d.ts +1 -1
  118. package/dist/types/services/index.d.ts +1 -1
  119. package/dist/types/services/task/Task.d.ts +157 -0
  120. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  121. package/dist/types/services/task/TaskUtils.d.ts +46 -2
  122. package/dist/types/services/task/constants.d.ts +7 -0
  123. package/dist/types/services/task/dialer.d.ts +30 -0
  124. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  125. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1144 -0
  126. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  127. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  128. package/dist/types/services/task/state-machine/guards.d.ts +90 -0
  129. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  130. package/dist/types/services/task/state-machine/types.d.ts +267 -0
  131. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  132. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  133. package/dist/types/services/task/types.d.ts +603 -66
  134. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  135. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  136. package/dist/types/types.d.ts +135 -0
  137. package/dist/types/webex.d.ts +1 -0
  138. package/dist/types.js +118 -2
  139. package/dist/types.js.map +1 -1
  140. package/dist/webex.js +14 -2
  141. package/dist/webex.js.map +1 -1
  142. package/package.json +15 -12
  143. package/src/cc.ts +329 -30
  144. package/src/config.ts +6 -0
  145. package/src/constants.ts +17 -1
  146. package/src/index.ts +23 -5
  147. package/src/metrics/ai-docs/AGENTS.md +350 -0
  148. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  149. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  150. package/src/metrics/behavioral-events.ts +106 -0
  151. package/src/metrics/constants.ts +27 -4
  152. package/src/services/ApiAiAssistant.ts +203 -8
  153. package/src/services/UserPreference.ts +509 -0
  154. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  155. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  156. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  157. package/src/services/ai-docs/AGENTS.md +386 -0
  158. package/src/services/ai-docs/services-spec.md +492 -0
  159. package/src/services/config/Util.ts +3 -3
  160. package/src/services/config/ai-docs/AGENTS.md +255 -0
  161. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  162. package/src/services/config/ai-docs/config-spec.md +669 -0
  163. package/src/services/config/constants.ts +25 -1
  164. package/src/services/config/types.ts +174 -11
  165. package/src/services/core/Err.ts +2 -0
  166. package/src/services/core/Utils.ts +123 -37
  167. package/src/services/core/ai-docs/AGENTS.md +381 -0
  168. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  169. package/src/services/core/ai-docs/core-spec.md +783 -0
  170. package/src/services/core/websocket/WebSocketManager.ts +2 -0
  171. package/src/services/core/websocket/types.ts +1 -1
  172. package/src/services/index.ts +1 -1
  173. package/src/services/task/Task.ts +837 -0
  174. package/src/services/task/TaskFactory.ts +55 -0
  175. package/src/services/task/TaskManager.ts +739 -614
  176. package/src/services/task/TaskUtils.ts +205 -25
  177. package/src/services/task/ai-docs/AGENTS.md +457 -0
  178. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  179. package/src/services/task/ai-docs/task-spec.md +1319 -0
  180. package/src/services/task/constants.ts +7 -0
  181. package/src/services/task/dialer.ts +80 -0
  182. package/src/services/task/digital/Digital.ts +95 -0
  183. package/src/services/task/state-machine/TaskStateMachine.ts +1077 -0
  184. package/src/services/task/state-machine/actions.ts +685 -0
  185. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  186. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  187. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2115 -0
  188. package/src/services/task/state-machine/constants.ts +172 -0
  189. package/src/services/task/state-machine/guards.ts +406 -0
  190. package/src/services/task/state-machine/index.ts +28 -0
  191. package/src/services/task/state-machine/types.ts +241 -0
  192. package/src/services/task/state-machine/uiControlsComputer.ts +867 -0
  193. package/src/services/task/taskDataNormalizer.ts +137 -0
  194. package/src/services/task/types.ts +710 -71
  195. package/src/services/task/voice/Voice.ts +1267 -0
  196. package/src/services/task/voice/WebRTC.ts +187 -0
  197. package/src/types.ts +166 -2
  198. package/src/utils/AGENTS.md +278 -0
  199. package/src/utils/ai-docs/utils-spec.md +381 -0
  200. package/src/webex.js +2 -0
  201. package/test/unit/spec/cc.ts +343 -23
  202. package/test/unit/spec/logger-proxy.ts +70 -0
  203. package/test/unit/spec/services/ApiAiAssistant.ts +178 -20
  204. package/test/unit/spec/services/UserPreference.ts +401 -0
  205. package/test/unit/spec/services/WebCallingService.ts +7 -1
  206. package/test/unit/spec/services/config/index.ts +30 -30
  207. package/test/unit/spec/services/core/Utils.ts +425 -8
  208. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
  209. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  210. package/test/unit/spec/services/task/Task.ts +477 -0
  211. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  212. package/test/unit/spec/services/task/TaskManager.ts +834 -1704
  213. package/test/unit/spec/services/task/TaskUtils.ts +206 -0
  214. package/test/unit/spec/services/task/dialer.ts +190 -0
  215. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  216. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +1825 -0
  217. package/test/unit/spec/services/task/state-machine/guards.ts +479 -0
  218. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  219. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2020 -0
  220. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  221. package/test/unit/spec/services/task/voice/Voice.ts +631 -0
  222. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  223. package/umd/contact-center.min.js +2 -2
  224. package/umd/contact-center.min.js.map +1 -1
  225. package/dist/services/task/index.js +0 -1525
  226. package/dist/services/task/index.js.map +0 -1
  227. package/dist/types/services/task/index.d.ts +0 -650
  228. package/src/services/task/index.ts +0 -1801
  229. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,698 @@
1
+ # Core Service - Architecture
2
+
3
+ > **Legacy/reference-only.** Canonical SDD: [`core-spec.md`](core-spec.md). Use the package [manifest](../../../../.sdd/manifest.json) and [`SPEC_INDEX.md`](../../../../ai-docs/SPEC_INDEX.md) for routing; code and tests remain the behavioral referee.
4
+
5
+ > **Purpose**: Technical documentation for core infrastructure components.
6
+
7
+ ---
8
+
9
+ ## WebSocketManager
10
+
11
+ ### Connection Sequence
12
+
13
+ ```mermaid
14
+ sequenceDiagram
15
+ participant cc
16
+ participant WSM as WebSocketManager
17
+ participant WS as WebSocket
18
+ participant BE as ccBackend
19
+
20
+ cc->>WSM: initWebSocket(config)
21
+ Note right of cc: config is SubscribeRequest passed as {body: config}
22
+
23
+ WSM->>BE: POST /subscribe (get WS URL)
24
+ BE-->>WSM: {webSocketUrl, subscriptionId}
25
+ WSM->>WS: new WebSocket(url)
26
+ WS->>BE: Connect
27
+ BE-->>WS: Welcome event
28
+ WS-->>WSM: onmessage(Welcome)
29
+ WSM-->>cc: Resolve with WelcomeEvent
30
+
31
+ loop Message handling
32
+ BE-->>WS: Events
33
+ WS-->>WSM: onmessage
34
+ WSM->>WSM: emit('message', event)
35
+ end
36
+ ```
37
+
38
+ Config reference:
39
+
40
+ - `initWebSocket(options: {body: SubscribeRequest})`: [src/services/core/websocket/WebSocketManager.ts](../websocket/WebSocketManager.ts)
41
+ - `SubscribeRequest` type: [src/types.ts](../../../types.ts)
42
+
43
+ ### End-to-End Core Flow (Complete Picture)
44
+
45
+ This diagram shows the complete lifecycle from component instantiation through normal operation, including when and how each layer is created, engaged, and their method invocation sequences.
46
+
47
+ ```mermaid
48
+ sequenceDiagram
49
+ autonumber
50
+ participant CC as CC Plugin
51
+ participant Svc as Services
52
+ participant AQM as AqmReqs
53
+ participant WSM as WebSocketManager
54
+ participant WS as WebSocket
55
+ participant CS as ConnectionService
56
+ participant KW as Keepalive Worker
57
+ participant WR as WebexRequest
58
+ participant BE as CC Backend
59
+
60
+ Note over CC,BE: INSTANTIATION: Services constructor (src/services/index.ts:39-51)
61
+ CC->>Svc: new Services({webex, connectionConfig})
62
+ Svc->>WSM: new WebSocketManager({webex})
63
+ Note right of WSM: Creates keepalive worker via Blob + URL.createObjectURL<br/>(Worker created but NOT started yet)
64
+ Svc->>AQM: new AqmReqs(webSocketManager)
65
+ Svc->>CS: new ConnectionService({webSocketManager, subscribeRequest})
66
+ CS->>CS: setupEventListeners()
67
+ CS->>WSM: webSocketManager.on('message', onPing)
68
+ CS->>WSM: webSocketManager.on('socketClose', onSocketClose)
69
+ CC->>WSM: webSocketManager.on('message', handleWebSocketMessage)
70
+
71
+ Note over CC,BE: REGISTRATION: cc.register() (src/cc.ts:457-486)
72
+ CC->>CC: setupEventListeners()
73
+ CC->>CS: connectionService.on('connectionLost', handleConnectionLost)
74
+ CC->>CC: connectWebsocket()
75
+ CC->>WSM: initWebSocket({body: subscribeRequest})
76
+
77
+ Note over WSM,BE: WebSocketManager.initWebSocket() (WebSocketManager.ts:47-61)
78
+ WSM->>BE: POST /v1/notification/subscribe
79
+ BE-->>WSM: {webSocketUrl, subscriptionId}
80
+ WSM->>WSM: connect()
81
+ WSM->>WS: new WebSocket(url)
82
+
83
+ Note over WSM,KW: websocket.onopen handler (WebSocketManager.ts:107-133)
84
+ WSM->>WS: send({keepalive: 'true'}) - initial ping
85
+ WSM->>WSM: Setup keepaliveWorker.onmessage handler
86
+ WSM->>KW: postMessage({type: 'start', intervalDuration: 4000, closeSocketTimeout: 5000})
87
+ Note right of KW: ⚡ Worker starts periodic interval<br/>⚡ Begins monitoring navigator.onLine
88
+ WSM->>WS: Setup handlers such as websocket.onMessage, websocket.onClose
89
+
90
+ BE-->>WS: WELCOME event
91
+ WS-->>WSM: WELCOME event
92
+ WSM->>CS: emit('message', welcomeEvent)
93
+ CS->>CS: onPing(welcomeEvent)
94
+ Note right of CS: setTimeout(handleConnectionLost, 8000)<br/>setTimeout(handleRestoreFailed, 50000)
95
+ WSM-->>CC: Resolve with WelcomeResponse
96
+
97
+ Note over CC,BE: NORMAL OPERATION: API calls with websocket bind pattern
98
+ CC->>AQM: req({url, method, body, bind})
99
+ AQM->>WR: request({service, resource, method, body})
100
+ WR->>BE: HTTP request
101
+ BE-->>WR: HTTP response (ack/tracking)
102
+ WR-->>AQM: IHttpResponse
103
+ AQM->>AQM: wait for matching websocket bind event
104
+ BE-->>WSM: async notification event
105
+ WSM->>CS: emit('message', event)
106
+ CS->>CS: onPing() - clearTimeout & reset timers
107
+ WSM->>AQM: message event matches bind
108
+ AQM-->>CC: resolve promise with result
109
+
110
+ Note over KW,BE: KEEPALIVE: Periodic pings every 4s (keepalive.worker.js)
111
+ loop Every 4 seconds
112
+ KW->>KW: checkNetworkStatus()
113
+ KW-->>WSM: postMessage({type: 'keepalive', onlineStatus})
114
+ WSM->>BE: send({keepalive: 'true'})
115
+ BE-->>WSM: {keepalive: 'true'}
116
+ WSM->>CS: emit('message', {keepalive: 'true'})
117
+ CS->>CS: onPing() - reset timers
118
+ end
119
+
120
+ Note over KW,CS: OFFLINE DETECTION: Network goes offline
121
+ alt Browser goes offline (navigator.onLine = false)
122
+ KW->>KW: Start closeSocketTimeout timer (5s)
123
+ alt Socket doesn't close within 5s
124
+ KW-->>WSM: postMessage({type: 'closeSocket'})
125
+ WSM->>WSM: websocket.close()
126
+ WSM->>KW: postMessage({type: 'terminate'})
127
+ WSM->>CS: emit('socketClose')
128
+ end
129
+
130
+ Note over CS: onSocketClose() (connection-service.ts:135-141)
131
+ CS->>CS: clearTimerOnRestoreFailed()
132
+ CS->>CS: setInterval(handleSocketClose, 5000)
133
+
134
+ loop Every 5s (CONNECTIVITY_CHECK_INTERVAL)
135
+ CS->>CS: handleSocketClose() - check navigator.onLine
136
+ alt Browser back online
137
+ CS->>WSM: initWebSocket({body: subscribeRequest})
138
+ WSM->>BE: POST /subscribe + WebSocket reconnect
139
+ WSM->>KW: postMessage({type: 'start', ...})
140
+ BE-->>WSM: WELCOME event
141
+ WSM->>CS: emit('message', welcomeEvent)
142
+ CS->>CS: onPing() detects isSocketReconnected=true
143
+ CS->>CS: dispatchConnectionEvent(socketReconnected=true)
144
+ CS->>CC: emit('connectionLost', {isSocketReconnected: true})
145
+ CS->>CS: clearInterval(reconnectInterval)
146
+ else Still offline
147
+ CS->>CS: Wait for next interval
148
+ end
149
+ end
150
+ end
151
+
152
+ Note over CS,CC: CONNECTION LOST: No messages received within 8s
153
+ alt reconnectingTimer fires (WS_DISCONNECT_ALLOWED = 8s)
154
+ CS->>CS: handleConnectionLost()
155
+ CS->>CS: isConnectionLost = true
156
+ CS->>CC: emit('connectionLost', {isConnectionLost: true})
157
+
158
+ alt restoreTimer fires (LOST_CONNECTION_RECOVERY_TIMEOUT = 50s)
159
+ CS->>CS: handleRestoreFailed()
160
+ CS->>CS: isRestoreFailed = true
161
+ CS->>WSM: shouldReconnect = false
162
+ CS->>CS: clearInterval(reconnectInterval)
163
+ CS->>CC: emit('connectionLost', {isRestoreFailed: true})
164
+ end
165
+ end
166
+ ```
167
+
168
+ #### Component Instantiation Order
169
+
170
+ 1. **WebSocketManager** ([src/services/core/websocket/WebSocketManager.ts:32-45](../websocket/WebSocketManager.ts#L32-L45)) - Creates keepalive worker (not started)
171
+ 2. **AqmReqs** ([src/services/core/aqm-reqs.ts](../aqm-reqs.ts)) - Initialized with WebSocketManager reference
172
+ 3. **Service layers** (config, agent, contact, dialer) - Created with AqmReqs reference
173
+ 4. **ConnectionService** ([src/services/core/websocket/connection-service.ts:30-41](../websocket/connection-service.ts#L30-L41)) - Wires event listeners immediately
174
+
175
+ #### Key Method Invocations
176
+
177
+ **WebSocketManager.initWebSocket** (WebSocketManager.ts:47-61):
178
+
179
+ - `register()` → POST /subscribe → get WebSocket URL
180
+ - `connect()` → Create WebSocket → Setup handlers (onopen, onmessage, onclose, onerror)
181
+
182
+ **websocket.onopen** (WebSocketManager.ts:107-133):
183
+
184
+ - Send initial keepalive ping
185
+ - Wire `keepaliveWorker.onmessage` handler
186
+ - **Start keepalive worker** via `postMessage({type: 'start', intervalDuration: 4000, closeSocketTimeout: 5000})`
187
+
188
+ **ConnectionService.onPing** (connection-service.ts:92-118) - Called on every message:
189
+
190
+ - Clear existing timers (reconnectingTimer, restoreTimer)
191
+ - Handle connection recovery state transitions
192
+ - Schedule new timers: `setTimeout(handleConnectionLost, 8000)`, `setTimeout(handleRestoreFailed, 50000)`
193
+
194
+ **ConnectionService.onSocketClose** (connection-service.ts:135-141):
195
+
196
+ - Clear reconnect interval
197
+ - Start periodic reconnection attempts: `setInterval(handleSocketClose, 5000)`
198
+
199
+ **ConnectionService.handleSocketClose** (connection-service.ts:120-133):
200
+
201
+ - Check `navigator.onLine`
202
+ - If online: reinitialize WebSocket and set `isSocketReconnected = true`
203
+
204
+ ---
205
+
206
+ ## ConnectionService
207
+
208
+ Monitors WebSocket health via keepalive messages, detects disconnections, triggers reconnection attempts, and emits connection state events to the application layer. Extends `EventEmitter`.
209
+
210
+ ### Constructor
211
+
212
+ ```typescript
213
+ constructor(options: ConnectionServiceOptions)
214
+
215
+ type ConnectionServiceOptions = {
216
+ webSocketManager: WebSocketManager;
217
+ subscribeRequest: SubscribeRequest;
218
+ };
219
+ ```
220
+
221
+ ### Type References
222
+
223
+ - `SubscribeRequest`: [src/types.ts](../../../types.ts)
224
+ - `ConnectionProp`: [src/services/core/websocket/types.ts](../websocket/types.ts)
225
+ - `ConnectionServiceOptions`: [src/services/core/websocket/types.ts](../websocket/types.ts)
226
+ - `ConnectionLostDetails`: [src/services/core/websocket/types.ts](../websocket/types.ts)
227
+
228
+ The constructor wires up two listeners on `WebSocketManager`:
229
+
230
+ - `'message'` → `onPing` (resets disconnect/restore timers on every incoming message)
231
+ - `'socketClose'` → `onSocketClose` (starts the reconnection interval)
232
+
233
+ Code reference (`src/services/core/websocket/connection-service.ts`):
234
+
235
+ ```typescript
236
+ private setupEventListeners() {
237
+ this.webSocketManager.on('message', this.onPing.bind(this));
238
+ this.webSocketManager.on('socketClose', this.onSocketClose.bind(this));
239
+ }
240
+ ```
241
+
242
+ ### Key Constants
243
+
244
+ | Constant | Value | Purpose |
245
+ | ---------------------------------- | --------- | -------------------------------------------- |
246
+ | `LOST_CONNECTION_RECOVERY_TIMEOUT` | 50 000 ms | Max wait before declaring restore failed |
247
+ | `WS_DISCONNECT_ALLOWED` | 8 000 ms | Grace period before flagging connection lost |
248
+ | `CONNECTIVITY_CHECK_INTERVAL` | 5 000 ms | Interval between reconnection attempts |
249
+
250
+ ### Properties
251
+
252
+ | Property | Type | Description |
253
+ | --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
254
+ | `connectionProp` | `ConnectionProp` | Runtime configuration object (for example `lostConnectionRecoveryTimeout`) used by reconnect/restore timers. |
255
+ | `wsDisconnectAllowed` | `number` | Timeout window before `handleConnectionLost` is triggered when no ping/message is received. |
256
+ | `reconnectingTimer` | `ReturnType<typeof setTimeout>` | Per-message timeout that schedules lost-connection detection. |
257
+ | `restoreTimer` | `ReturnType<typeof setTimeout>` | Timeout that marks restore failure if recovery does not complete in time. |
258
+ | `reconnectInterval` | `ReturnType<typeof setInterval>` | Periodic retry loop started after socket close to attempt reconnection. |
259
+ | `isConnectionLost` | `boolean` | Indicates that the connection has been marked as lost. |
260
+ | `isRestoreFailed` | `boolean` | Indicates that recovery has exceeded the configured restore timeout. |
261
+ | `isSocketReconnected` | `boolean` | Indicates that a reconnect attempt succeeded and socket is back. |
262
+ | `isKeepAlive` | `boolean` | Tracks whether the latest incoming message is a keepalive signal. |
263
+ | `webSocketManager` | `WebSocketManager` | Core WebSocket dependency used for event subscription and re-initialization. |
264
+ | `subscribeRequest` | `SubscribeRequest` | Cached subscribe payload reused during reconnect (`initWebSocket`). |
265
+
266
+ ### Methods
267
+
268
+ The only public method is `setConnectionProp`. All other methods are private implementation details and are documented for architectural understanding.
269
+
270
+ 1. `setConnectionProp(prop: ConnectionProp): void` (public)
271
+
272
+ - **Purpose**: Updates connection-level runtime settings used by timers (mainly recovery timeout behavior).
273
+ - **Params**: `prop` - new connection config object.
274
+ - **Returns**: `void`.
275
+ - **Usage**: Called by higher layers when timeout behavior must be tuned after initialization.
276
+
277
+ 2. `setupEventListeners(): void` (private)
278
+
279
+ - **Purpose**: Wires `WebSocketManager` events to internal handlers (`'message' -> onPing`, `'socketClose' -> onSocketClose`).
280
+ - **Params**: none.
281
+ - **Returns**: `void`.
282
+ - **Usage**: Invoked from the constructor once, during `ConnectionService` setup.
283
+
284
+ 3. `onPing(event: any): void` (private)
285
+
286
+ - **Purpose**: Handles every incoming socket message, resets timers, updates keepalive/recovery flags, and emits recovery events when state changes.
287
+ - **Params**: `event` - raw message payload (JSON string) received from the socket event stream.
288
+ - **Returns**: `void`.
289
+ - **Usage**: Triggered automatically by the `'message'` listener.
290
+
291
+ 4. `onSocketClose(): void` (private)
292
+
293
+ - **Purpose**: Starts the reconnect interval when socket close is detected.
294
+ - **Params**: none.
295
+ - **Returns**: `void`.
296
+ - **Usage**: Triggered automatically by the `'socketClose'` listener.
297
+
298
+ 5. `handleSocketClose(): Promise<void>` (private)
299
+
300
+ - **Purpose**: Performs one reconnect attempt; if browser is online, reinitializes WebSocket and marks socket as reconnected.
301
+ - **Params**: none.
302
+ - **Returns**: `Promise<void>` (rejects when browser is offline).
303
+ - **Usage**: Called repeatedly from `onSocketClose` interval loop.
304
+
305
+ 6. `handleConnectionLost(): void` (private)
306
+
307
+ - **Purpose**: Marks the connection as lost and dispatches a connection status event.
308
+ - **Params**: none.
309
+ - **Returns**: `void`.
310
+ - **Usage**: Scheduled by `onPing` via `reconnectingTimer` after inactivity.
311
+
312
+ 7. `handleRestoreFailed(): Promise<void>` (private)
313
+
314
+ - **Purpose**: Marks restore as failed, disables reconnect, emits failure state, and clears reconnect interval.
315
+ - **Params**: none.
316
+ - **Returns**: `Promise<void>`.
317
+ - **Usage**: Scheduled by `onPing` via `restoreTimer`.
318
+
319
+ 8. `clearTimerOnRestoreFailed(): Promise<void>` (private)
320
+
321
+ - **Purpose**: Stops active reconnect interval to avoid duplicate retries.
322
+ - **Params**: none.
323
+ - **Returns**: `Promise<void>`.
324
+ - **Usage**: Called from reconnect/failure paths whenever interval cleanup is needed.
325
+
326
+ 9. `updateConnectionData(): void` (private)
327
+
328
+ - **Purpose**: Resets transient connection flags (`isConnectionLost`, `isRestoreFailed`, `isSocketReconnected`) after recovery.
329
+ - **Params**: none.
330
+ - **Returns**: `void`.
331
+ - **Usage**: Called inside `onPing` before dispatching recovered state.
332
+
333
+ 10. `dispatchConnectionEvent(socketReconnected = false): void` (private)
334
+ - **Purpose**: Builds `ConnectionLostDetails`, forwards it to `WebSocketManager.handleConnectionLost`, and emits `'connectionLost'`.
335
+ - **Params**: `socketReconnected` - optional override used when reconnect is explicitly detected.
336
+ - **Returns**: `void`.
337
+ - **Usage**: Used by lost/recovered/restore-failed paths to publish uniform connection state.
338
+
339
+ ### Reconnection Flow
340
+
341
+ ```mermaid
342
+ sequenceDiagram
343
+ participant App as cc
344
+ participant CS as ConnectionService
345
+ participant WSM as WebSocketManager
346
+
347
+ Note over WSM: WebSocket closes
348
+ WSM->>CS: emit('socketClose')
349
+ CS->>CS: onSocketClose()
350
+ CS->>CS: clearTimerOnRestoreFailed()
351
+ CS->>CS: Start reconnectInterval every 5 s
352
+
353
+ loop Every CONNECTIVITY_CHECK_INTERVAL
354
+ CS->>CS: handleSocketClose()
355
+ alt Browser online
356
+ CS->>WSM: initWebSocket({body: subscribeRequest})
357
+ CS->>CS: clearTimerOnRestoreFailed()
358
+ CS->>CS: isSocketReconnected = true
359
+ else Browser offline
360
+ CS->>CS: Throw error, retry next interval
361
+ end
362
+ end
363
+
364
+ Note over CS: Next keepalive ping arrives
365
+ CS->>CS: dispatchConnectionEvent(socketReconnected=true)
366
+ CS->>App: emit('connectionLost', details)
367
+ ```
368
+
369
+ ### Events
370
+
371
+ ```typescript
372
+ type ConnectionLostDetails = {
373
+ isConnectionLost: boolean;
374
+ isRestoreFailed: boolean;
375
+ isSocketReconnected: boolean;
376
+ isKeepAlive: boolean;
377
+ };
378
+
379
+ connectionService.on('connectionLost', (details: ConnectionLostDetails) => {
380
+ if (details.isConnectionLost) {
381
+ // Connection lost — waiting for recovery
382
+ } else if (details.isRestoreFailed) {
383
+ // Recovery timeout (50 s) exceeded
384
+ } else if (details.isSocketReconnected) {
385
+ // Socket successfully reconnected
386
+ }
387
+ });
388
+ ```
389
+
390
+ ---
391
+
392
+ ## AqmReqs Pattern
393
+
394
+ `AqmReqs` coordinates the Contact Center request lifecycle by sending an HTTP request and then waiting for the matching WebSocket response bind. This gives service methods a single promise-based API that resolves only when the backend confirms completion.
395
+
396
+ ```typescript
397
+ import AqmReqs from '../aqm-reqs';
398
+
399
+ const aqmReqs = new AqmReqs();
400
+
401
+ const response = await aqmReqs.req({
402
+ url: '/v1/agent/state',
403
+ method: 'POST',
404
+ body: {agentId: 'agent-123', state: 'AVAILABLE'},
405
+ bind: {
406
+ eventType: 'agent-state-change',
407
+ matcher: (event) => event.agentId === 'agent-123',
408
+ },
409
+ });
410
+
411
+ // `response` resolves after matching bind event arrives
412
+ ```
413
+
414
+ ### Request/Response Flow
415
+
416
+ ```mermaid
417
+ flowchart TD
418
+ A[Service method called] --> B[AqmReqs.req]
419
+ B --> C[Build request config]
420
+ C --> D[Send HTTP request via WebexRequest.request]
421
+ D --> E[Wait for matching WebSocket notification]
422
+ E --> F{Success bind matched?}
423
+ F -->|Yes| G[Resolve promise]
424
+ F -->|No| H[Reject with error]
425
+ ```
426
+
427
+ ## WebexRequest
428
+
429
+ ### Singleton Pattern
430
+
431
+ ```typescript
432
+ class WebexRequest {
433
+ private static instance: WebexRequest;
434
+ private webex: WebexSDK;
435
+
436
+ private constructor(options: {webex: WebexSDK}) {}
437
+
438
+ public static getInstance(options?: {webex: WebexSDK}): WebexRequest {
439
+ if (!WebexRequest.instance && options && options.webex) {
440
+ WebexRequest.instance = new WebexRequest(options);
441
+ }
442
+ return WebexRequest.instance;
443
+ }
444
+
445
+ public async request(options: {
446
+ service: string; // Service key used by `webex.request` to resolve the target host
447
+ resource: string; // API path within the service (for example: v1/notification/subscribe)
448
+ method: HTTP_METHODS;
449
+ body?: RequestBody;
450
+ }): Promise<IHttpResponse>;
451
+
452
+ public async uploadLogs(metaData: LogsMetaData = {}): Promise<UploadLogsResponse>;
453
+ }
454
+
455
+ export default WebexRequest;
456
+ ```
457
+
458
+ Type references:
459
+
460
+ - `WebexSDK`: [src/types.ts](../../../types.ts)
461
+ - `HTTP_METHODS`: [src/types.ts](../../../types.ts)
462
+ - `RequestBody`: [src/types.ts](../../../types.ts)
463
+ - `IHttpResponse`: [src/types.ts](../../../types.ts)
464
+ - `LogsMetaData`: [src/types.ts](../../../types.ts)
465
+ - `UploadLogsResponse`: [src/types.ts](../../../types.ts)
466
+
467
+ ### Request Flow
468
+
469
+ ```mermaid
470
+ sequenceDiagram
471
+ participant Svc as Service
472
+ participant WR as WebexRequest
473
+ participant WX as webex.request
474
+ participant API as Backend
475
+
476
+ Svc->>WR: request(config)
477
+ WR->>WR: Build request options
478
+ WR->>WX: webex.request(options)
479
+ WX->>API: HTTP request
480
+ API-->>WX: Response
481
+ WX-->>WR: {statusCode, body, headers}
482
+ WR-->>Svc: Response
483
+ ```
484
+
485
+ ---
486
+
487
+ ## Keepalive Worker
488
+
489
+ ### Purpose
490
+
491
+ Maintains WebSocket connection with periodic pings and monitors network status. Has a dual role:
492
+
493
+ 1. **Keepalive**: Sends periodic messages to detect connection issues
494
+ 2. **Network Monitoring**: Tracks online/offline transitions and forces socket closure if offline too long
495
+
496
+ ### Implementation
497
+
498
+ > **Source**: [`keepalive.worker.js`](../websocket/keepalive.worker.js) — a Web Worker script embedded as a string and loaded via `Blob` + `URL.createObjectURL` in `WebSocketManager`.
499
+
500
+ #### Worker Message Contract
501
+
502
+ **Inbound (main thread → worker):**
503
+
504
+ | Message Type | Fields | Effect |
505
+ | ------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
506
+ | `start` | `intervalDuration` (default 4000ms), `isSocketClosed`, `closeSocketTimeout` (default 5000ms) | Starts periodic keepalive interval and resets offline handler |
507
+ | `terminate` | — | Clears the keepalive interval and resets offline handler |
508
+
509
+ **Outbound (worker → main thread):**
510
+
511
+ | Message Type | Fields | Trigger |
512
+ | ------------- | ----------------------- | ------------------------------------------------------------------------------------ |
513
+ | `keepalive` | `onlineStatus: boolean` | Every `intervalDuration` ms, and on browser online/offline events |
514
+ | `closeSocket` | — | When offline for longer than `closeSocketTimeout` and socket hasn't closed naturally |
515
+
516
+ #### Key Behavior
517
+
518
+ 1. **Periodic ping**: Every `intervalDuration` ms, calls `checkNetworkStatus()` which posts a `keepalive` message with the current `navigator.onLine` status
519
+ 2. **Offline detection**: When network goes offline, starts a `closeSocketTimeout` timer. If the socket hasn't closed naturally by then, posts `closeSocket` to force closure
520
+ 3. **Online/offline listeners**: The worker also listens to browser `online`/`offline` events for immediate network change detection
521
+
522
+ ---
523
+
524
+ ## Error Handling
525
+
526
+ This section documents shared error helpers in `Utils.ts` that normalize errors, enrich them with context, and ensure consistent logging/upload behavior across services.
527
+
528
+ ### Error Types
529
+
530
+ ```typescript
531
+ // Msg - Generic message interface (GlobalTypes.ts:7-16)
532
+ export type Msg<T = any> = {
533
+ type: string;
534
+ orgId: string;
535
+ trackingId: string;
536
+ data: T;
537
+ };
538
+
539
+ // Failure - Backend error structure (GlobalTypes.ts:23-34)
540
+ // Built on Msg<T> with specific error data fields
541
+ export type Failure = Msg<{
542
+ agentId: string;
543
+ trackingId: string;
544
+ reasonCode: number;
545
+ orgId: string;
546
+ reason: string;
547
+ }>;
548
+
549
+ // AugmentedError - Extended Error with flexible data field (GlobalTypes.ts:59-61)
550
+ export interface AugmentedError extends Error {
551
+ data?: Record<string, any>;
552
+ }
553
+ ```
554
+
555
+ ### getErrorDetails Flow
556
+
557
+ ```mermaid
558
+ flowchart TD
559
+ A[Error caught] --> B[Cast error.details to Failure]
560
+ B --> C[Extract reason from failure.data.reason]
561
+ C --> D{Is silentRelogin + AGENT_NOT_FOUND?}
562
+ D -->|Yes| E[Skip logging/upload]
563
+ D -->|No| F[Log error with LoggerProxy]
564
+ F --> G[Upload logs via WebexRequest]
565
+ G --> H[Check if stationLogin]
566
+ H -->|Yes| I[Get field-specific error data]
567
+ H -->|No| J[Use generic error]
568
+ I --> K[Create Error with data property]
569
+ J --> K
570
+ K --> L["Return {error, reason}"]
571
+ ```
572
+
573
+ ### getErrorDetails
574
+
575
+ Use this helper for agent/config-style flows where backend failure payloads are transformed into a user-facing `Error` plus `reason`, with optional station-login field metadata.
576
+
577
+ ```typescript
578
+ export const getErrorDetails = (error: any, methodName: string, moduleName: string) => {
579
+ let errData = {message: '', fieldName: ''};
580
+
581
+ const failure = error.details as Failure;
582
+ const reason = failure?.data?.reason ?? `Error while performing ${methodName}`;
583
+
584
+ // Log error (unless AGENT_NOT_FOUND in silentRelogin)
585
+ if (!(reason === 'AGENT_NOT_FOUND' && methodName === 'silentRelogin')) {
586
+ LoggerProxy.error(`${methodName} failed with reason: ${reason}`, {
587
+ module: moduleName,
588
+ method: methodName,
589
+ trackingId: failure?.trackingId,
590
+ });
591
+
592
+ // Upload logs
593
+ WebexRequest.getInstance().uploadLogs({
594
+ correlationId: failure?.trackingId,
595
+ });
596
+ }
597
+
598
+ // For stationLogin, extract field-specific error data (message + fieldName)
599
+ if (methodName === 'stationLogin') {
600
+ errData = getStationLoginErrorData(failure, error.loginOption);
601
+ }
602
+
603
+ const err = new Error(reason);
604
+ // @ts-ignore - custom property for backward compatibility
605
+ err.data = errData;
606
+
607
+ return {error: err, reason};
608
+ };
609
+ ```
610
+
611
+ ### generateTaskErrorObject
612
+
613
+ Use this helper for task/interaction flows where richer task error metadata (`errorType`, `errorData`, `reasonCode`, `trackingId`) is required on the returned `AugmentedError`.
614
+
615
+ ```typescript
616
+ export const generateTaskErrorObject = (
617
+ error: any,
618
+ methodName: string,
619
+ moduleName: string
620
+ ): AugmentedError => {
621
+ const trackingId = error?.details?.trackingId || error?.trackingId || '';
622
+ const errorMsg = error?.details?.msg;
623
+
624
+ const errorMessage = errorMsg?.errorMessage || error.message || 'Error';
625
+ const errorType = errorMsg?.errorType || error.name || 'Unknown Error';
626
+ const errorData = errorMsg?.errorData || '';
627
+ const reasonCode = errorMsg?.reasonCode || 0;
628
+
629
+ LoggerProxy.error(`${methodName} failed: ${errorMessage} (${errorType})`, {
630
+ module: moduleName,
631
+ method: methodName,
632
+ trackingId,
633
+ });
634
+ WebexRequest.getInstance().uploadLogs({correlationId: trackingId});
635
+
636
+ const reason = `${errorType}: ${errorMessage}${errorData ? ` (${errorData})` : ''}`;
637
+ const err: AugmentedError = new Error(reason);
638
+ err.data = {
639
+ message: errorMessage,
640
+ errorType,
641
+ errorData,
642
+ reasonCode,
643
+ trackingId,
644
+ };
645
+
646
+ return err;
647
+ };
648
+ ```
649
+
650
+ ### Usage Guidance
651
+
652
+ ```typescript
653
+ // Use getErrorDetails for:
654
+ // - Agent service operations
655
+ // - Station login/logout flows
656
+ //
657
+ // Use generateTaskErrorObject for:
658
+ // - Task service operations
659
+ // - Interaction-related errors
660
+ ```
661
+
662
+ ---
663
+
664
+ ## Troubleshooting
665
+
666
+ ### Issue: WebSocket not connecting
667
+
668
+ **Cause**: Subscribe API failed or invalid URL
669
+
670
+ **Solution**: Check subscribe response and WebSocket URL
671
+
672
+ ### Issue: Messages not received
673
+
674
+ **Cause**: Event listener not registered
675
+
676
+ **Solution**: Ensure `on('message', handler)` called before connect
677
+
678
+ ### Issue: Connection drops frequently
679
+
680
+ **Cause**: Keepalive not enabled or network issues
681
+
682
+ **Solution**: Verify worker-driven keepalive is running after socket `onopen`, and check network/offline transitions
683
+
684
+ ---
685
+
686
+ ## Related Files
687
+
688
+ - [Root Orchestrator AGENTS.md](../../../../AGENTS.md) — Task routing, critical rules, cross-service patterns
689
+ - [Core AGENTS.md](./AGENTS.md) — Core service usage guide and modification patterns
690
+ - [WebSocketManager.ts](../websocket/WebSocketManager.ts) — WebSocket lifecycle, keepalive worker integration
691
+ - [ConnectionService.ts](../websocket/connection-service.ts) — Reconnection logic, connection state events
692
+ - [keepalive.worker.js](../websocket/keepalive.worker.js) — Web Worker for periodic keepalive and offline detection
693
+ - [WebexRequest.ts](../WebexRequest.ts) — Singleton HTTP request handler
694
+ - [Utils.ts](../Utils.ts) — `getErrorDetails` (line 88), `generateTaskErrorObject` (line 143), consult utilities
695
+ - [Err.ts](../Err.ts) — `Err.Message` and `Err.Details` error classes
696
+ - [aqm-reqs.ts](../aqm-reqs.ts) — AQM request/response pattern, WebSocket notification binding
697
+ - [GlobalTypes.ts](../GlobalTypes.ts) — `Msg`, `Failure`, `AugmentedError` type definitions
698
+ - [types.ts](../types.ts) — `Pending`, `Req`, `Conf`, `Res` types for AqmReqs