@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,205 @@
1
+ # New Service - Test Generation
2
+
3
+ > **Purpose**: Create unit tests for the new service.
4
+
5
+ ---
6
+
7
+ ## Test File Location
8
+
9
+ Create: `test/unit/spec/services/ServiceName.ts`
10
+
11
+ ---
12
+
13
+ ## Test File Template
14
+
15
+ ```typescript
16
+ import 'jsdom-global/register';
17
+ import MockWebex from '@webex/test-helper-mock-webex';
18
+ import ServiceName, {
19
+ ServiceListResponse,
20
+ ServiceSearchParams,
21
+ } from '../../../../src/services/ServiceName';
22
+
23
+ jest.mock('../../../../src/logger-proxy', () => ({
24
+ __esModule: true,
25
+ default: {
26
+ log: jest.fn(),
27
+ error: jest.fn(),
28
+ info: jest.fn(),
29
+ warn: jest.fn(),
30
+ trace: jest.fn(),
31
+ initialize: jest.fn(),
32
+ },
33
+ }));
34
+
35
+ describe('ServiceName', () => {
36
+ let webex: any;
37
+ let service: ServiceName;
38
+
39
+ // Mock data
40
+ const mockOrgId = 'mock-org-id';
41
+ const mockResponse: ServiceListResponse = {
42
+ data: [
43
+ { id: 'item-1', name: 'Item 1' },
44
+ { id: 'item-2', name: 'Item 2' },
45
+ ],
46
+ meta: {
47
+ page: 0,
48
+ pageSize: 50,
49
+ totalPages: 1,
50
+ totalRecords: 2,
51
+ },
52
+ };
53
+
54
+ beforeEach(() => {
55
+ webex = MockWebex({
56
+ logger: {
57
+ log: jest.fn(),
58
+ error: jest.fn(),
59
+ info: jest.fn(),
60
+ },
61
+ credentials: {
62
+ getOrgId: jest.fn(() => mockOrgId),
63
+ },
64
+ request: jest.fn(),
65
+ });
66
+
67
+ service = new ServiceName(webex);
68
+ });
69
+
70
+ afterEach(() => {
71
+ jest.clearAllMocks();
72
+ });
73
+
74
+ describe('getItems', () => {
75
+ it('should fetch items successfully', async () => {
76
+ // Arrange
77
+ webex.request.mockResolvedValue({ body: mockResponse });
78
+
79
+ // Act
80
+ const result = await service.getItems();
81
+
82
+ // Assert
83
+ expect(result).toEqual(mockResponse);
84
+ expect(webex.request).toHaveBeenCalledWith({
85
+ method: 'GET',
86
+ uri: expect.stringContaining(mockOrgId),
87
+ });
88
+ });
89
+
90
+ it('should apply pagination parameters', async () => {
91
+ // Arrange
92
+ webex.request.mockResolvedValue({ body: mockResponse });
93
+ const params: ServiceSearchParams = { page: 1, pageSize: 25 };
94
+
95
+ // Act
96
+ await service.getItems(params);
97
+
98
+ // Assert
99
+ expect(webex.request).toHaveBeenCalledWith({
100
+ method: 'GET',
101
+ uri: expect.stringContaining('page=1'),
102
+ });
103
+ expect(webex.request).toHaveBeenCalledWith({
104
+ method: 'GET',
105
+ uri: expect.stringContaining('pageSize=25'),
106
+ });
107
+ });
108
+
109
+ it('should apply search parameter', async () => {
110
+ // Arrange
111
+ webex.request.mockResolvedValue({ body: mockResponse });
112
+ const params: ServiceSearchParams = { search: 'test' };
113
+
114
+ // Act
115
+ await service.getItems(params);
116
+
117
+ // Assert
118
+ expect(webex.request).toHaveBeenCalledWith({
119
+ method: 'GET',
120
+ uri: expect.stringContaining('search=test'),
121
+ });
122
+ });
123
+
124
+ it('should throw error on API failure', async () => {
125
+ // Arrange
126
+ const mockError = new Error('API Error');
127
+ webex.request.mockRejectedValue(mockError);
128
+
129
+ // Act & Assert
130
+ await expect(service.getItems()).rejects.toThrow('API Error');
131
+ });
132
+ });
133
+
134
+ describe('getItemById', () => {
135
+ it('should fetch single item successfully', async () => {
136
+ // Arrange
137
+ const mockItem = { id: 'item-1', name: 'Item 1' };
138
+ webex.request.mockResolvedValue({ body: mockItem });
139
+
140
+ // Act
141
+ const result = await service.getItemById('item-1');
142
+
143
+ // Assert
144
+ expect(result).toEqual(mockItem);
145
+ expect(webex.request).toHaveBeenCalledWith({
146
+ method: 'GET',
147
+ uri: expect.stringContaining('item-1'),
148
+ });
149
+ });
150
+
151
+ it('should throw error if item not found', async () => {
152
+ // Arrange
153
+ const mockError = new Error('Not Found');
154
+ webex.request.mockRejectedValue(mockError);
155
+
156
+ // Act & Assert
157
+ await expect(service.getItemById('invalid-id')).rejects.toThrow('Not Found');
158
+ });
159
+ });
160
+ });
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Test Integration with cc.ts
166
+
167
+ Add to `test/unit/spec/cc.ts`:
168
+
169
+ ```typescript
170
+ describe('cc.serviceName', () => {
171
+ it('should initialize service on ready', () => {
172
+ expect(webex.cc.serviceName).toBeDefined();
173
+ });
174
+
175
+ it('should fetch items through service', async () => {
176
+ // Mock the service method
177
+ const mockResponse = { data: [], meta: {} };
178
+ jest.spyOn(webex.cc.serviceName, 'getItems').mockResolvedValue(mockResponse);
179
+
180
+ // Act
181
+ const result = await webex.cc.serviceName.getItems();
182
+
183
+ // Assert
184
+ expect(result).toEqual(mockResponse);
185
+ });
186
+ });
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Running Tests
192
+
193
+ ```bash
194
+ # Run specific test file
195
+ yarn workspace @webex/contact-center test -- --testPathPattern=ServiceName
196
+
197
+ # Run with coverage
198
+ yarn workspace @webex/contact-center test -- --coverage --testPathPattern=ServiceName
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Next Step
204
+
205
+ Proceed to: [`05-validation.md`](05-validation.md)
@@ -0,0 +1,145 @@
1
+ # New Service - Validation Checklist
2
+
3
+ > **Purpose**: Final quality check before completing service creation.
4
+
5
+ ---
6
+
7
+ ## Code Quality Checklist
8
+
9
+ ### Service Class
10
+ - [ ] File created at correct location (based on placement from pre-questions Q3)
11
+ - [ ] Class has proper JSDoc with `@public` tag
12
+ - [ ] Methods have JSDoc with `@param`, `@returns`, `@example`
13
+ - [ ] Uses `LoggerProxy` for all logging (no `console.log`)
14
+ - [ ] Error handling uses `getErrorDetails` pattern (logs and re-throws)
15
+ - [ ] Module name constant defined (`SERVICE_FILE`)
16
+ - [ ] Method name constants defined (`METHODS`)
17
+
18
+ ### Types & Constants
19
+ - [ ] Types placed in the correct location:
20
+ - Folder-based service: service folder's `types.ts`
21
+ - Single-file service: root `src/types.ts`
22
+ - Sub-module: parent service's `types.ts`
23
+ - [ ] Constants: no duplicates — every constant was searched across the hierarchy before adding:
24
+ - `src/constants.ts` (SDK-wide: file names, method names, global settings)
25
+ - `src/services/constants.ts` (shared: API gateways, auth, network)
26
+ - `src/metrics/constants.ts` (all metric event names)
27
+ - `src/services/config/constants.ts` (endpoint maps, pagination, agent states)
28
+ - `src/services/{ServiceName}/constants.ts` (service-specific only)
29
+ - [ ] Constants placed at the correct level (not duplicated at a lower level when shared exists)
30
+ - [ ] New `constants.ts` file created ONLY if service is folder-based AND no existing file AND constants are service-specific
31
+ - [ ] All public types have JSDoc
32
+ - [ ] Response types match actual API response
33
+ - [ ] Parameter types define all optional/required fields
34
+
35
+ ### Metrics
36
+ - [ ] `metricsManager.timeEvent` called at method entry with success + failure event names
37
+ - [ ] `metricsManager.trackEvent` called on success path
38
+ - [ ] `metricsManager.trackEvent` called on failure path (in catch block)
39
+ - [ ] Metric event names added to `src/metrics/constants.ts` (`METRIC_EVENT_NAMES`)
40
+
41
+ ### Integration
42
+ - [ ] Service initialized at the correct integration point:
43
+ - Folder-based / single-file: in `cc.ts` or `Services` singleton (depends on AQM vs non-AQM)
44
+ - Sub-module: instantiated by parent service
45
+ - [ ] Types re-exported from `src/types.ts` (if public)
46
+
47
+ ### Tests
48
+ - [ ] Test file created mirroring source path under `test/unit/spec/`
49
+ - [ ] LoggerProxy mocked
50
+ - [ ] Success cases tested
51
+ - [ ] Error cases tested
52
+ - [ ] Metrics tracking verified (timeEvent and trackEvent calls asserted)
53
+ - [ ] Tests pass: `yarn workspace @webex/contact-center test:unit`
54
+
55
+ ---
56
+
57
+ ## Pattern Compliance
58
+
59
+ ### LoggerProxy Usage
60
+ ```typescript
61
+ // ✅ Correct
62
+ LoggerProxy.info('Starting operation', {
63
+ module: SERVICE_FILE,
64
+ method: METHODS.GET_ITEMS,
65
+ });
66
+
67
+ // ❌ Wrong
68
+ console.log('Starting operation');
69
+ ```
70
+
71
+ ### Error Handling
72
+ ```typescript
73
+ // ✅ Correct
74
+ catch (error) {
75
+ LoggerProxy.error(`Failed: ${error}`, {
76
+ module: SERVICE_FILE,
77
+ method: METHODS.GET_ITEMS,
78
+ error,
79
+ });
80
+ throw error;
81
+ }
82
+
83
+ // ❌ Wrong - swallowing error
84
+ catch (error) {
85
+ console.error(error);
86
+ }
87
+ ```
88
+
89
+ ### Type Exports
90
+ ```typescript
91
+ // ✅ Correct - in src/types.ts
92
+ export type {
93
+ ServiceItem,
94
+ ServiceSearchParams,
95
+ ServiceListResponse,
96
+ } from './services/ServiceName';
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Build & Test Verification
102
+
103
+ ```bash
104
+ # Lint
105
+ yarn workspace @webex/contact-center test:styles
106
+
107
+ # Test unit tests
108
+ yarn workspace @webex/contact-center test:unit
109
+
110
+ # Build
111
+ yarn workspace @webex/contact-center build:src
112
+ ```
113
+
114
+ All should pass without errors.
115
+
116
+ ---
117
+
118
+ ## Documentation
119
+
120
+ ### Update Root AGENTS.md?
121
+ If this is a significant new service, update the root [`AGENTS.md`](../../../AGENTS.md):
122
+ - [ ] Added new service to the [Service Routing Table](../../../AGENTS.md#service-routing-table)
123
+ - [ ] Added to repository structure tree
124
+ - [ ] Added usage example if applicable
125
+
126
+ ### Create Service ai-docs?
127
+ For complex services, create service-level documentation (use [`create-agents-md.md`](../documentation/create-agents-md.md) and [`create-architecture-md.md`](../documentation/create-architecture-md.md) templates):
128
+ - [ ] `src/services/ServiceName/ai-docs/AGENTS.md` — usage guide, API reference
129
+ - [ ] `src/services/ServiceName/ai-docs/ARCHITECTURE.md` — technical deep-dive, data flow
130
+
131
+ ---
132
+
133
+ ## Final Review
134
+
135
+ Ask yourself:
136
+ 1. Can another developer understand this service by reading the JSDoc?
137
+ 2. Are all error paths properly handled and logged?
138
+ 3. Do tests cover the main use cases?
139
+ 4. Is the API surface clean and consistent with other services?
140
+
141
+ ---
142
+
143
+ ## Complete!
144
+
145
+ Service creation is complete when all checkboxes are checked.
package/dist/cc.js CHANGED
@@ -85,7 +85,6 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
85
85
  * - `task:established` - Task/call has been connected
86
86
  * - `task:ended` - Task/call has ended
87
87
  * - `task:error` - An error occurred during task handling
88
- * - `task:campaignPreviewReservation` - Campaign preview contact offered to agent
89
88
  *
90
89
  * @public
91
90
  *
@@ -318,7 +317,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
318
317
  this.metricsManager = _MetricsManager.default.getInstance({
319
318
  webex: this.$webex
320
319
  });
321
- this.taskManager = _TaskManager.default.getTaskManager(this.apiAIAssistant, this.services.contact, this.webCallingService, this.services.webSocketManager);
320
+ this.taskManager = _TaskManager.default.getTaskManager(this.apiAIAssistant, this.services.contact, this.webCallingService, this.services.webSocketManager, this.services.rtdWebSocketManager);
322
321
  this.incomingTaskListener();
323
322
 
324
323
  // Initialize API instances
@@ -360,19 +359,6 @@ class ContactCenter extends _webexCore.WebexPlugin {
360
359
  this.trigger(_types4.TASK_EVENTS.TASK_MERGED, task);
361
360
  };
362
361
 
363
- /**
364
- * Handles campaign preview reservation events when a contact is offered to the agent
365
- * @private
366
- * @param {ITask} task The campaign reservation task
367
- */
368
- handleCampaignPreviewReservation = task => {
369
- // @ts-ignore
370
- this.trigger(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
371
- };
372
- handleRTDWebsocketMessage = payload => {
373
- this.taskManager.handleRealtimeWebsocketEvent(payload);
374
- };
375
-
376
362
  /**
377
363
  * Sets up event listeners for incoming tasks and task hydration
378
364
  * Subscribes to task events from the task manager
@@ -382,7 +368,6 @@ class ContactCenter extends _webexCore.WebexPlugin {
382
368
  this.taskManager.on(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
383
369
  this.taskManager.on(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
384
370
  this.taskManager.on(_types4.TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
385
- this.taskManager.on(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
386
371
  }
387
372
 
388
373
  /**
@@ -491,7 +476,6 @@ class ContactCenter extends _webexCore.WebexPlugin {
491
476
  this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL]);
492
477
  this.taskManager.off(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
493
478
  this.taskManager.off(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
494
- this.taskManager.off(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
495
479
  this.taskManager.unregisterIncomingCallEvent();
496
480
  this.services.webSocketManager.off('message', this.handleWebsocketMessage);
497
481
  this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
@@ -513,7 +497,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
513
497
  this.services.webSocketManager.close(false, 'Unregistering the SDK');
514
498
  }
515
499
  if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
516
- this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
500
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
517
501
  }
518
502
 
519
503
  // Clear any cached agent configuration
@@ -621,62 +605,76 @@ class ContactCenter extends _webexCore.WebexPlugin {
621
605
  method: _constants.METHODS.CONNECT_WEBSOCKET
622
606
  });
623
607
  try {
624
- const data = await this.services.webSocketManager.initWebSocket({
608
+ return this.services.webSocketManager.initWebSocket({
625
609
  body: this.getConnectionConfig(),
626
610
  resource: _constants3.SUBSCRIBE_API
627
- });
628
- const agentId = data.agentId;
629
- const orgId = this.$webex.credentials.getOrgId();
630
- this.agentConfig = await this.services.config.getAgentConfig(orgId, agentId);
631
- _loggerProxy.default.log(`Agent config is fetched successfully`, {
632
- module: _constants.CC_FILE,
633
- method: _constants.METHODS.CONNECT_WEBSOCKET
634
- });
635
-
636
- // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
637
- this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
638
- this.taskManager.setAgentId(this.agentConfig.agentId);
639
- this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
640
- this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
641
- if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable) {
642
- _loggerProxy.default.info('Connecting to RTD websocket', {
611
+ }).then(async data => {
612
+ const agentId = data.agentId;
613
+ const orgId = this.$webex.credentials.getOrgId();
614
+ this.agentConfig = await this.services.config.getAgentConfig(orgId, agentId);
615
+ _loggerProxy.default.log(`Agent config is fetched successfully`, {
643
616
  module: _constants.CC_FILE,
644
617
  method: _constants.METHODS.CONNECT_WEBSOCKET
645
618
  });
646
- await this.services.rtdWebSocketManager.initWebSocket({
647
- body: this.getConnectionConfig(),
648
- resource: _constants3.RTD_SUBSCRIBE_API
649
- }).then(() => {
650
- _loggerProxy.default.log('RTD websocket connected successfully', {
651
- module: _constants.CC_FILE,
652
- method: _constants.METHODS.CONNECT_WEBSOCKET
653
- });
654
- this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
655
- }).catch(error => {
656
- _loggerProxy.default.error(`Error during RTD websocket setup: ${error}`, {
619
+ const configFlags = {
620
+ isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
621
+ isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
622
+ webRtcEnabled: this.agentConfig.webRtcEnabled,
623
+ autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
624
+ aiFeature: this.agentConfig.aiFeature
625
+ };
626
+ this.taskManager.setConfigFlags(configFlags);
627
+ // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
628
+ this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
629
+ this.taskManager.setAgentId(this.agentConfig.agentId);
630
+ this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
631
+ this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
632
+ /**
633
+ * TODO: We need to re-check this condition if this websocket is only for realtime transcripts
634
+ * or other AI Assistant features will also use the same.
635
+ * If the latter is true, we need to update this condition.
636
+ */
637
+ if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable) {
638
+ _loggerProxy.default.info('Connecting to RTD websocket', {
657
639
  module: _constants.CC_FILE,
658
640
  method: _constants.METHODS.CONNECT_WEBSOCKET
659
641
  });
660
- });
661
- }
662
- if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER)) {
663
- try {
664
- await this.$webex.internal.mercury.connect();
665
- _loggerProxy.default.log('Authentication: webex.internal.mercury.connect successful', {
666
- module: _constants.CC_FILE,
667
- method: _constants.METHODS.CONNECT_WEBSOCKET
642
+ this.services.rtdWebSocketManager.initWebSocket({
643
+ body: this.getConnectionConfig(),
644
+ resource: _constants3.RTD_SUBSCRIBE_API
645
+ }).then(() => {
646
+ _loggerProxy.default.log('RTD websocket connected successfully', {
647
+ module: _constants.CC_FILE,
648
+ method: _constants.METHODS.CONNECT_WEBSOCKET
649
+ });
650
+ this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
651
+ }).catch(error => {
652
+ _loggerProxy.default.error(`Error connecting to RTD websocket ${error}`, {
653
+ module: _constants.CC_FILE,
654
+ method: _constants.METHODS.CONNECT_WEBSOCKET
655
+ });
668
656
  });
669
- } catch (error) {
670
- _loggerProxy.default.error(`Error occurred during mercury.connect() ${error}`, {
671
- module: _constants.CC_FILE,
672
- method: _constants.METHODS.CONNECT_WEBSOCKET
657
+ }
658
+ if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER)) {
659
+ this.$webex.internal.mercury.connect().then(() => {
660
+ _loggerProxy.default.log('Authentication: webex.internal.mercury.connect successful', {
661
+ module: _constants.CC_FILE,
662
+ method: _constants.METHODS.CONNECT_WEBSOCKET
663
+ });
664
+ }).catch(error => {
665
+ _loggerProxy.default.error(`Error occurred during mercury.connect() ${error}`, {
666
+ module: _constants.CC_FILE,
667
+ method: _constants.METHODS.CONNECT_WEBSOCKET
668
+ });
673
669
  });
674
670
  }
675
- }
676
- if (this.$config && this.$config.allowAutomatedRelogin) {
677
- await this.silentRelogin();
678
- }
679
- return this.agentConfig;
671
+ if (this.$config && this.$config.allowAutomatedRelogin) {
672
+ await this.silentRelogin();
673
+ }
674
+ return this.agentConfig;
675
+ }).catch(error => {
676
+ throw error;
677
+ });
680
678
  } catch (error) {
681
679
  _loggerProxy.default.error(`Error during register: ${error}`, {
682
680
  module: _constants.CC_FILE,
@@ -721,8 +719,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
721
719
  });
722
720
  try {
723
721
  this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, _constants4.METRIC_EVENT_NAMES.STATION_LOGIN_FAILED]);
724
- const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
725
- if (data.loginOption === _types.LoginOption.AGENT_DN && !(0, _Utils.isValidDialNumber)(data.dialNumber, dialPlanEntries)) {
722
+ if (data.loginOption === _types.LoginOption.AGENT_DN && !(0, _Utils.isValidDialNumber)(data.dialNumber)) {
726
723
  const error = new Error('INVALID_DIAL_NUMBER');
727
724
  // @ts-ignore - adding custom key to the error object
728
725
  error.details = {
@@ -1070,6 +1067,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
1070
1067
  break;
1071
1068
  }
1072
1069
  };
1070
+ handleRTDWebsocketMessage = event => {
1071
+ this.taskManager.handleRealtimeWebsocketEvent(event);
1072
+ };
1073
1073
 
1074
1074
  /**
1075
1075
  * Initializes event listeners for the Contact Center service
@@ -1386,64 +1386,6 @@ class ContactCenter extends _webexCore.WebexPlugin {
1386
1386
  }
1387
1387
  }
1388
1388
 
1389
- /**
1390
- * Accepts a campaign preview contact, initiating the outbound call.
1391
- *
1392
- * When a campaign manager reserves a contact for an agent, the agent receives an
1393
- * `AgentOfferCampaignReservation` event. The agent can then accept the preview contact
1394
- * to initiate the outbound call.
1395
- *
1396
- * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
1397
- * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
1398
- * @throws {Error} If the operation fails (network error, customer unavailable, etc.)
1399
- *
1400
- * @example
1401
- * ```typescript
1402
- * webex.cc.on('task:campaignPreviewReservation', async (task) => {
1403
- * const { interactionId } = task.data;
1404
- * // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID
1405
- * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
1406
- *
1407
- * const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId });
1408
- * });
1409
- * ```
1410
- */
1411
- async acceptPreviewContact(payload) {
1412
- _loggerProxy.default.info('Accepting campaign preview contact', {
1413
- module: _constants.CC_FILE,
1414
- method: _constants.METHODS.ACCEPT_PREVIEW_CONTACT
1415
- });
1416
- try {
1417
- this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED]);
1418
- const result = await this.services.dialer.acceptPreviewContact({
1419
- data: payload
1420
- });
1421
- this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS, {
1422
- ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
1423
- interactionId: payload.interactionId,
1424
- campaignId: payload.campaignId
1425
- }, ['behavioral', 'business', 'operational']);
1426
- _loggerProxy.default.log('Campaign preview contact accepted successfully', {
1427
- module: _constants.CC_FILE,
1428
- method: _constants.METHODS.ACCEPT_PREVIEW_CONTACT,
1429
- trackingId: result.trackingId,
1430
- interactionId: payload.interactionId
1431
- });
1432
- return result;
1433
- } catch (error) {
1434
- const failure = error.details;
1435
- this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED, {
1436
- ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
1437
- interactionId: payload.interactionId,
1438
- campaignId: payload.campaignId
1439
- }, ['behavioral', 'business', 'operational']);
1440
- const {
1441
- error: detailedError
1442
- } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.ACCEPT_PREVIEW_CONTACT, _constants.CC_FILE);
1443
- throw detailedError;
1444
- }
1445
- }
1446
-
1447
1389
  /**
1448
1390
  * Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
1449
1391
  *