@webex/contact-center 3.11.0 → 3.12.0-llmrefactor.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 (267) hide show
  1. package/.sdd/manifest.json +882 -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 +379 -50
  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 +22 -2
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +27 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +114 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +32 -3
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +318 -0
  52. package/dist/services/ApiAiAssistant.js.map +1 -0
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/agent/types.js.map +1 -1
  56. package/dist/services/config/Util.js +8 -4
  57. package/dist/services/config/Util.js.map +1 -1
  58. package/dist/services/config/constants.js +35 -2
  59. package/dist/services/config/constants.js.map +1 -1
  60. package/dist/services/config/index.js +41 -2
  61. package/dist/services/config/index.js.map +1 -1
  62. package/dist/services/config/types.js +66 -8
  63. package/dist/services/config/types.js.map +1 -1
  64. package/dist/services/constants.js +27 -1
  65. package/dist/services/constants.js.map +1 -1
  66. package/dist/services/core/Err.js.map +1 -1
  67. package/dist/services/core/Utils.js +122 -25
  68. package/dist/services/core/Utils.js.map +1 -1
  69. package/dist/services/core/aqm-reqs.js +92 -17
  70. package/dist/services/core/aqm-reqs.js.map +1 -1
  71. package/dist/services/core/websocket/WebSocketManager.js +22 -6
  72. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  73. package/dist/services/core/websocket/connection-service.js +3 -1
  74. package/dist/services/core/websocket/connection-service.js.map +1 -1
  75. package/dist/services/core/websocket/types.js.map +1 -1
  76. package/dist/services/index.js +6 -0
  77. package/dist/services/index.js.map +1 -1
  78. package/dist/services/task/Task.js +688 -0
  79. package/dist/services/task/Task.js.map +1 -0
  80. package/dist/services/task/TaskFactory.js +45 -0
  81. package/dist/services/task/TaskFactory.js.map +1 -0
  82. package/dist/services/task/TaskManager.js +751 -457
  83. package/dist/services/task/TaskManager.js.map +1 -1
  84. package/dist/services/task/TaskUtils.js +220 -23
  85. package/dist/services/task/TaskUtils.js.map +1 -1
  86. package/dist/services/task/constants.js +23 -2
  87. package/dist/services/task/constants.js.map +1 -1
  88. package/dist/services/task/dialer.js +129 -0
  89. package/dist/services/task/dialer.js.map +1 -1
  90. package/dist/services/task/digital/Digital.js +77 -0
  91. package/dist/services/task/digital/Digital.js.map +1 -0
  92. package/dist/services/task/state-machine/TaskStateMachine.js +873 -0
  93. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  94. package/dist/services/task/state-machine/actions.js +567 -0
  95. package/dist/services/task/state-machine/actions.js.map +1 -0
  96. package/dist/services/task/state-machine/constants.js +161 -0
  97. package/dist/services/task/state-machine/constants.js.map +1 -0
  98. package/dist/services/task/state-machine/guards.js +382 -0
  99. package/dist/services/task/state-machine/guards.js.map +1 -0
  100. package/dist/services/task/state-machine/index.js +53 -0
  101. package/dist/services/task/state-machine/index.js.map +1 -0
  102. package/dist/services/task/state-machine/types.js +54 -0
  103. package/dist/services/task/state-machine/types.js.map +1 -0
  104. package/dist/services/task/state-machine/uiControlsComputer.js +603 -0
  105. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  106. package/dist/services/task/taskDataNormalizer.js +99 -0
  107. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  108. package/dist/services/task/types.js +227 -4
  109. package/dist/services/task/types.js.map +1 -1
  110. package/dist/services/task/voice/Voice.js +1044 -0
  111. package/dist/services/task/voice/Voice.js.map +1 -0
  112. package/dist/services/task/voice/WebRTC.js +149 -0
  113. package/dist/services/task/voice/WebRTC.js.map +1 -0
  114. package/dist/types/cc.d.ts +894 -0
  115. package/dist/types/config.d.ts +72 -0
  116. package/dist/types/constants.d.ts +66 -0
  117. package/dist/types/index.d.ts +199 -0
  118. package/dist/types/logger-proxy.d.ts +71 -0
  119. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  120. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  121. package/dist/types/metrics/constants.d.ts +181 -0
  122. package/dist/types/services/AddressBook.d.ts +74 -0
  123. package/dist/types/services/ApiAiAssistant.d.ts +49 -0
  124. package/dist/types/services/EntryPoint.d.ts +67 -0
  125. package/dist/types/services/Queue.d.ts +76 -0
  126. package/dist/types/services/UserPreference.d.ts +118 -0
  127. package/dist/types/services/WebCallingService.d.ts +1 -0
  128. package/dist/types/services/agent/index.d.ts +46 -0
  129. package/dist/types/services/agent/types.d.ts +413 -0
  130. package/dist/types/services/config/Util.d.ts +20 -0
  131. package/dist/types/services/config/constants.d.ts +270 -0
  132. package/dist/types/services/config/index.d.ts +177 -0
  133. package/dist/types/services/config/types.d.ts +1368 -0
  134. package/dist/types/services/constants.d.ts +110 -0
  135. package/dist/types/services/core/Err.d.ts +125 -0
  136. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  137. package/dist/types/services/core/Utils.d.ts +121 -0
  138. package/dist/types/services/core/WebexRequest.d.ts +22 -0
  139. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  140. package/dist/types/services/core/constants.d.ts +99 -0
  141. package/dist/types/services/core/types.d.ts +47 -0
  142. package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
  143. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  144. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  145. package/dist/types/services/core/websocket/types.d.ts +37 -0
  146. package/dist/types/services/index.d.ts +54 -0
  147. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  148. package/dist/types/services/task/Task.d.ts +157 -0
  149. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  150. package/dist/types/services/task/TaskManager.d.ts +1 -0
  151. package/dist/types/services/task/TaskUtils.d.ts +138 -0
  152. package/dist/types/services/task/constants.d.ts +91 -0
  153. package/dist/types/services/task/contact.d.ts +69 -0
  154. package/dist/types/services/task/dialer.d.ts +73 -0
  155. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  156. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1194 -0
  157. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  158. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  159. package/dist/types/services/task/state-machine/guards.d.ts +102 -0
  160. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  161. package/dist/types/services/task/state-machine/types.d.ts +269 -0
  162. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  163. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  164. package/dist/types/services/task/types.d.ts +1856 -0
  165. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  166. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  167. package/dist/types/types.d.ts +778 -0
  168. package/dist/types/utils/PageCache.d.ts +173 -0
  169. package/dist/types/webex-config.d.ts +53 -0
  170. package/dist/types/webex.d.ts +8 -0
  171. package/dist/types.js +130 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/webex.js +14 -2
  174. package/dist/webex.js.map +1 -1
  175. package/package.json +16 -12
  176. package/src/cc.ts +477 -51
  177. package/src/config.ts +6 -0
  178. package/src/constants.ts +21 -1
  179. package/src/index.ts +24 -5
  180. package/src/metrics/ai-docs/AGENTS.md +350 -0
  181. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  182. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  183. package/src/metrics/behavioral-events.ts +120 -0
  184. package/src/metrics/constants.ts +37 -3
  185. package/src/services/ApiAiAssistant.ts +412 -0
  186. package/src/services/UserPreference.ts +509 -0
  187. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  188. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  189. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  190. package/src/services/agent/types.ts +1 -1
  191. package/src/services/ai-docs/AGENTS.md +386 -0
  192. package/src/services/ai-docs/services-spec.md +492 -0
  193. package/src/services/config/Util.ts +10 -2
  194. package/src/services/config/ai-docs/AGENTS.md +255 -0
  195. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  196. package/src/services/config/ai-docs/config-spec.md +669 -0
  197. package/src/services/config/constants.ts +37 -1
  198. package/src/services/config/index.ts +45 -1
  199. package/src/services/config/types.ts +241 -11
  200. package/src/services/constants.ts +29 -0
  201. package/src/services/core/Err.ts +3 -0
  202. package/src/services/core/Utils.ts +143 -30
  203. package/src/services/core/ai-docs/AGENTS.md +381 -0
  204. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  205. package/src/services/core/ai-docs/core-spec.md +783 -0
  206. package/src/services/core/aqm-reqs.ts +100 -22
  207. package/src/services/core/websocket/WebSocketManager.ts +23 -6
  208. package/src/services/core/websocket/connection-service.ts +5 -1
  209. package/src/services/core/websocket/types.ts +1 -1
  210. package/src/services/index.ts +4 -0
  211. package/src/services/task/Task.ts +837 -0
  212. package/src/services/task/TaskFactory.ts +55 -0
  213. package/src/services/task/TaskManager.ts +793 -521
  214. package/src/services/task/TaskUtils.ts +314 -24
  215. package/src/services/task/ai-docs/AGENTS.md +457 -0
  216. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  217. package/src/services/task/ai-docs/task-spec.md +1319 -0
  218. package/src/services/task/constants.ts +23 -0
  219. package/src/services/task/dialer.ts +136 -1
  220. package/src/services/task/digital/Digital.ts +95 -0
  221. package/src/services/task/state-machine/TaskStateMachine.ts +1166 -0
  222. package/src/services/task/state-machine/actions.ts +738 -0
  223. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  224. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  225. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2177 -0
  226. package/src/services/task/state-machine/constants.ts +172 -0
  227. package/src/services/task/state-machine/guards.ts +445 -0
  228. package/src/services/task/state-machine/index.ts +28 -0
  229. package/src/services/task/state-machine/types.ts +243 -0
  230. package/src/services/task/state-machine/uiControlsComputer.ts +961 -0
  231. package/src/services/task/taskDataNormalizer.ts +137 -0
  232. package/src/services/task/types.ts +734 -71
  233. package/src/services/task/voice/Voice.ts +1270 -0
  234. package/src/services/task/voice/WebRTC.ts +187 -0
  235. package/src/types.ts +205 -2
  236. package/src/utils/AGENTS.md +278 -0
  237. package/src/utils/ai-docs/utils-spec.md +381 -0
  238. package/src/webex.js +2 -0
  239. package/test/unit/spec/cc.ts +503 -43
  240. package/test/unit/spec/logger-proxy.ts +70 -0
  241. package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
  242. package/test/unit/spec/services/UserPreference.ts +401 -0
  243. package/test/unit/spec/services/WebCallingService.ts +7 -1
  244. package/test/unit/spec/services/config/index.ts +85 -29
  245. package/test/unit/spec/services/core/Utils.ts +481 -2
  246. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +137 -41
  247. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  248. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  249. package/test/unit/spec/services/task/Task.ts +477 -0
  250. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  251. package/test/unit/spec/services/task/TaskManager.ts +1001 -1003
  252. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  253. package/test/unit/spec/services/task/dialer.ts +372 -96
  254. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  255. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +2651 -0
  256. package/test/unit/spec/services/task/state-machine/guards.ts +637 -0
  257. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  258. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2663 -0
  259. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  260. package/test/unit/spec/services/task/voice/Voice.ts +649 -0
  261. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  262. package/umd/contact-center.min.js +2 -2
  263. package/umd/contact-center.min.js.map +1 -1
  264. package/dist/services/task/index.js +0 -1525
  265. package/dist/services/task/index.js.map +0 -1
  266. package/src/services/task/index.ts +0 -1801
  267. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,70 @@
1
+ import LoggerProxy from '../../../src/logger-proxy';
2
+
3
+ describe('LoggerProxy', () => {
4
+ beforeEach(() => {
5
+ // Reset between tests
6
+ (LoggerProxy as any).logger = undefined;
7
+ });
8
+
9
+ it('no-ops when logger is not initialized', () => {
10
+ expect(() => {
11
+ LoggerProxy.log('msg');
12
+ LoggerProxy.info('msg');
13
+ LoggerProxy.warn('msg');
14
+ LoggerProxy.trace('msg');
15
+ LoggerProxy.error('msg');
16
+ }).not.toThrow();
17
+ });
18
+
19
+ it('routes calls to the injected logger with formatted context', () => {
20
+ const logger = {
21
+ log: jest.fn(),
22
+ info: jest.fn(),
23
+ warn: jest.fn(),
24
+ trace: jest.fn(),
25
+ error: jest.fn(),
26
+ };
27
+
28
+ LoggerProxy.initialize(logger as any);
29
+
30
+ LoggerProxy.log('hello', {module: 'm', method: 'fn', interactionId: 'i', trackingId: 't'});
31
+ expect(logger.log).toHaveBeenCalled();
32
+ expect(String(logger.log.mock.calls[0][0])).toContain('module:m - method:fn');
33
+ expect(String(logger.log.mock.calls[0][0])).toContain('interactionId:i');
34
+ expect(String(logger.log.mock.calls[0][0])).toContain('trackingId:t');
35
+
36
+ LoggerProxy.info('hello', {module: 'm', method: 'fn'});
37
+ LoggerProxy.warn('hello', {module: 'm', method: 'fn'});
38
+ LoggerProxy.trace('hello', {module: 'm', method: 'fn'});
39
+ LoggerProxy.error('hello', {module: 'm', method: 'fn'});
40
+ expect(logger.info).toHaveBeenCalled();
41
+ expect(logger.warn).toHaveBeenCalled();
42
+ expect(logger.trace).toHaveBeenCalled();
43
+ expect(logger.error).toHaveBeenCalled();
44
+ });
45
+
46
+ it('handles unserializable data and different error shapes', () => {
47
+ const logger = {
48
+ log: jest.fn(),
49
+ info: jest.fn(),
50
+ warn: jest.fn(),
51
+ trace: jest.fn(),
52
+ error: jest.fn(),
53
+ };
54
+ LoggerProxy.initialize(logger as any);
55
+
56
+ const circular: any = {};
57
+ circular.self = circular;
58
+
59
+ LoggerProxy.info('msg', {module: 'm', method: 'fn', data: circular, error: 'oops'});
60
+ expect(String(logger.info.mock.calls[0][0])).toContain('data:[object]');
61
+ expect(String(logger.info.mock.calls[0][0])).toContain('error:oops');
62
+
63
+ const err = new Error('boom');
64
+ LoggerProxy.error('msg', {module: 'm', method: 'fn', error: err});
65
+ const errorLine = String(logger.error.mock.calls[0][0]);
66
+ expect(errorLine).toContain('error:Error:boom');
67
+ expect(errorLine).toContain('stack:');
68
+ });
69
+ });
70
+
@@ -0,0 +1,273 @@
1
+ import ApiAIAssistant from '../../../../src/services/ApiAiAssistant';
2
+ import MetricsManager from '../../../../src/metrics/MetricsManager';
3
+ import LoggerProxy from '../../../../src/logger-proxy';
4
+ import WebexRequest from '../../../../src/services/core/WebexRequest';
5
+ import {HTTP_METHODS, RealTimeAssistanceUserActionId, WebexSDK} from '../../../../src/types';
6
+
7
+ jest.mock('../../../../src/metrics/MetricsManager');
8
+ jest.mock('../../../../src/logger-proxy');
9
+
10
+ describe('ApiAIAssistant', () => {
11
+ let apiAIAssistant: ApiAIAssistant;
12
+ let mockWebex: WebexSDK;
13
+ let mockMetricsManager: jest.Mocked<MetricsManager>;
14
+
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ jest.spyOn(WebexRequest, 'getInstance').mockReturnValue({
18
+ uploadLogs: jest.fn(),
19
+ } as any);
20
+
21
+ mockWebex = {
22
+ credentials: {
23
+ getOrgId: jest.fn().mockReturnValue('test-org-id'),
24
+ },
25
+ request: jest.fn(),
26
+ internal: {
27
+ services: {
28
+ get: jest.fn().mockReturnValue('https://api.wxcc-us1.cisco.com'),
29
+ },
30
+ newMetrics: {
31
+ submitBehavioralEvent: jest.fn(),
32
+ submitOperationalEvent: jest.fn(),
33
+ submitBusinessEvent: jest.fn(),
34
+ },
35
+ },
36
+ ready: true,
37
+ once: jest.fn(),
38
+ } as unknown as WebexSDK;
39
+
40
+ mockMetricsManager = {
41
+ trackEvent: jest.fn(),
42
+ timeEvent: jest.fn(),
43
+ } as unknown as jest.Mocked<MetricsManager>;
44
+ (MetricsManager.getInstance as jest.Mock).mockReturnValue(mockMetricsManager);
45
+
46
+ apiAIAssistant = new ApiAIAssistant(mockWebex);
47
+ });
48
+
49
+ it('should send transcript start event successfully', async () => {
50
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {ok: true}});
51
+
52
+ const result = await apiAIAssistant.sendEvent(
53
+ 'test-agent-id',
54
+ 'interaction-1',
55
+ 'CUSTOM_EVENT',
56
+ 'GET_TRANSCRIPTS',
57
+ {action: 'START'}
58
+ );
59
+
60
+ expect(mockWebex.request).toHaveBeenCalledTimes(1);
61
+ const requestArgs = (mockWebex.request as jest.Mock).mock.calls[0][0];
62
+
63
+ expect(requestArgs.uri).toBe('https://api-ai-assistant.produs1.ciscoccservice.com/event');
64
+ expect(requestArgs.method).toBe(HTTP_METHODS.POST);
65
+ expect(requestArgs.addAuthHeader).toBe(true);
66
+ expect(requestArgs.body.agentId).toBe('test-agent-id');
67
+ expect(requestArgs.body.orgId).toBe('test-org-id');
68
+ expect(requestArgs.body.eventType).toBe('CUSTOM_EVENT');
69
+ expect(requestArgs.body.eventName).toBe('GET_TRANSCRIPTS');
70
+ expect(requestArgs.body.eventDetails.data.interactionId).toBe('interaction-1');
71
+ expect(requestArgs.body.eventDetails.data.action).toBe('START');
72
+ expect(result).toEqual({ok: true});
73
+ });
74
+
75
+ it('should fetch historic transcripts with mapped base URL', async () => {
76
+ const responseBody = {interactionId: 'interaction-1', data: []};
77
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: responseBody});
78
+ apiAIAssistant.setAIFeatureFlags({realtimeTranscripts: {enable: true}} as any);
79
+
80
+ const result = await apiAIAssistant.fetchHistoricTranscripts('test-agent-id', 'interaction-1');
81
+
82
+ expect(mockWebex.request).toHaveBeenCalledWith({
83
+ uri: 'https://api-ai-assistant.produs1.ciscoccservice.com/transcripts/list',
84
+ method: HTTP_METHODS.POST,
85
+ addAuthHeader: true,
86
+ body: {
87
+ agentId: 'test-agent-id',
88
+ orgId: 'test-org-id',
89
+ interactionId: 'interaction-1',
90
+ },
91
+ });
92
+ expect(result).toEqual(responseBody as any);
93
+ });
94
+
95
+ it('should request real-time assistance without extra context using sendEvent', async () => {
96
+ const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
97
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
98
+
99
+ const result = await apiAIAssistant.getRealTimeAssistance({
100
+ agentId: 'test-agent-id',
101
+ interactionId: 'interaction-1',
102
+ });
103
+
104
+ expect(sendEventSpy).toHaveBeenCalledTimes(1);
105
+ const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
106
+ sendEventSpy.mock.calls[0];
107
+
108
+ expect(agentId).toBe('test-agent-id');
109
+ expect(interactionId).toBe('interaction-1');
110
+ expect(eventType).toBe('CUSTOM_EVENT');
111
+ expect(eventName).toBe('GET_SUGGESTIONS');
112
+ expect(metadata).toBeUndefined();
113
+ expect(languageCode).toBe('en');
114
+ expect(typeof trackingId).toBe('string');
115
+ expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
116
+ expect(result).toEqual({ok: true});
117
+ });
118
+
119
+ it('should request real-time assistance with extra context using sendEvent', async () => {
120
+ const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
121
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
122
+
123
+ const result = await apiAIAssistant.getRealTimeAssistance({
124
+ agentId: 'test-agent-id',
125
+ interactionId: 'interaction-1',
126
+ context: 'Need assistance with credit card payment due date',
127
+ });
128
+
129
+ expect(sendEventSpy).toHaveBeenCalledTimes(1);
130
+ const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
131
+ sendEventSpy.mock.calls[0];
132
+
133
+ expect(agentId).toBe('test-agent-id');
134
+ expect(interactionId).toBe('interaction-1');
135
+ expect(eventType).toBe('CUSTOM_EVENT');
136
+ expect(eventName).toBe('ADD_SUGGESTIONS_EXTRA_CONTEXT');
137
+ expect(metadata).toEqual({context: 'Need assistance with credit card payment due date'});
138
+ expect(languageCode).toBe('en');
139
+ expect(typeof trackingId).toBe('string');
140
+ expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
141
+ expect(result).toEqual({ok: true});
142
+ });
143
+
144
+ it('should treat whitespace-only context as GET_SUGGESTIONS', async () => {
145
+ const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
146
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
147
+
148
+ const result = await apiAIAssistant.getRealTimeAssistance({
149
+ agentId: 'test-agent-id',
150
+ interactionId: 'interaction-1',
151
+ context: ' ',
152
+ });
153
+
154
+ expect(sendEventSpy).toHaveBeenCalledTimes(1);
155
+ const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
156
+ sendEventSpy.mock.calls[0];
157
+
158
+ expect(agentId).toBe('test-agent-id');
159
+ expect(interactionId).toBe('interaction-1');
160
+ expect(eventType).toBe('CUSTOM_EVENT');
161
+ expect(eventName).toBe('GET_SUGGESTIONS');
162
+ expect(metadata).toEqual({context: ''});
163
+ expect(languageCode).toBe('en');
164
+ expect(typeof trackingId).toBe('string');
165
+ expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
166
+ expect(result).toEqual({ok: true});
167
+ });
168
+
169
+ [
170
+ RealTimeAssistanceUserActionId.LIKE,
171
+ RealTimeAssistanceUserActionId.DISLIKE,
172
+ RealTimeAssistanceUserActionId.COPY,
173
+ ].forEach((actionId) => {
174
+ it(`should send real-time assistance user action for ${actionId}`, async () => {
175
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {ok: true}});
176
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
177
+
178
+ const result = await apiAIAssistant.sendRealTimeAssistanceUserAction({
179
+ agentId: 'test-agent-id',
180
+ interactionId: 'interaction-1',
181
+ adaptiveCardId: 'adaptive-card-1',
182
+ actionId,
183
+ });
184
+
185
+ expect(mockWebex.request).toHaveBeenCalledTimes(1);
186
+ const requestArgs = (mockWebex.request as jest.Mock).mock.calls[0][0];
187
+ const eventData = requestArgs.body.eventDetails.data;
188
+
189
+ expect(requestArgs.uri).toBe('https://api-ai-assistant.produs1.ciscoccservice.com/event');
190
+ expect(requestArgs.method).toBe(HTTP_METHODS.POST);
191
+ expect(requestArgs.addAuthHeader).toBe(true);
192
+ expect(requestArgs.body.agentId).toBe('test-agent-id');
193
+ expect(requestArgs.body.orgId).toBe('test-org-id');
194
+ expect(requestArgs.body.eventType).toBe('CUSTOM_EVENT');
195
+ expect(requestArgs.body.eventName).toBe('SUGGESTED_RESPONSES_USER_ACTION');
196
+ expect(eventData.interactionId).toBe('interaction-1');
197
+ expect(eventData.adaptiveCardId).toBe('adaptive-card-1');
198
+ expect(eventData.userAction.actionType).toBe('Action.Submit');
199
+ expect(eventData.userAction.actionId).toBe(actionId);
200
+ expect(eventData.languageCode).toBe('en');
201
+ expect(typeof eventData.actionTimeStamp).toBe('string');
202
+ expect(eventData.trackingId.startsWith('WX_CC_SDK_')).toBe(true);
203
+ expect(result).toEqual({ok: true});
204
+ });
205
+ });
206
+
207
+ it('should fail when base URL mapping is not available', async () => {
208
+ (mockWebex.internal.services.get as jest.Mock).mockReturnValue('https://unknown-host.invalid');
209
+
210
+ let failed = false;
211
+ try {
212
+ await apiAIAssistant.sendEvent(
213
+ 'test-agent-id',
214
+ 'interaction-1',
215
+ 'CUSTOM_EVENT',
216
+ 'GET_TRANSCRIPTS',
217
+ {action: 'STOP'}
218
+ );
219
+ } catch (_error) {
220
+ failed = true;
221
+ }
222
+
223
+ expect(failed).toBe(true);
224
+ expect(LoggerProxy.error).toHaveBeenCalled();
225
+ });
226
+
227
+ it('should fail when realtime transcripts feature is disabled', async () => {
228
+ apiAIAssistant.setAIFeatureFlags({realtimeTranscripts: {enable: false}} as any);
229
+ let errorMessage = '';
230
+
231
+ try {
232
+ await apiAIAssistant.fetchHistoricTranscripts('test-agent-id', 'interaction-1');
233
+ } catch (error) {
234
+ errorMessage = (error as Error)?.message || '';
235
+ }
236
+
237
+ expect(errorMessage).toBe('Error while performing fetchHistoricTranscripts');
238
+ });
239
+
240
+ it('should fail when real-time assistance feature is disabled', async () => {
241
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: false}} as any);
242
+ let errorMessage = '';
243
+
244
+ try {
245
+ await apiAIAssistant.getRealTimeAssistance({
246
+ agentId: 'test-agent-id',
247
+ interactionId: 'interaction-1',
248
+ });
249
+ } catch (error) {
250
+ errorMessage = (error as Error)?.message || '';
251
+ }
252
+
253
+ expect(errorMessage).toBe('Error while performing getRealTimeAssistance');
254
+ });
255
+
256
+ it('should fail to send real-time assistance user action when feature is disabled', async () => {
257
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: false}} as any);
258
+ let errorMessage = '';
259
+
260
+ try {
261
+ await apiAIAssistant.sendRealTimeAssistanceUserAction({
262
+ agentId: 'test-agent-id',
263
+ interactionId: 'interaction-1',
264
+ adaptiveCardId: 'adaptive-card-1',
265
+ actionId: RealTimeAssistanceUserActionId.LIKE,
266
+ });
267
+ } catch (error) {
268
+ errorMessage = (error as Error)?.message || '';
269
+ }
270
+
271
+ expect(errorMessage).toBe('Error while performing sendRealTimeAssistanceUserAction');
272
+ });
273
+ });