@webex/contact-center 3.12.0-next.9 → 3.12.0-task-refactor.2

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