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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/.sdd/manifest.json +887 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +62 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +52 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +382 -0
  17. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  18. package/ai-docs/features/contact-owner-change-refactor.md +78 -0
  19. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  20. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  21. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  22. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  23. package/ai-docs/patterns/testing-patterns.md +480 -0
  24. package/ai-docs/patterns/typescript-patterns.md +365 -0
  25. package/ai-docs/templates/README.md +102 -0
  26. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  27. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  28. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  29. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  30. package/ai-docs/templates/new-method/00-master.md +80 -0
  31. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  32. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  33. package/ai-docs/templates/new-method/03-tests.md +201 -0
  34. package/ai-docs/templates/new-method/04-validation.md +141 -0
  35. package/ai-docs/templates/new-service/00-master.md +109 -0
  36. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  37. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  38. package/ai-docs/templates/new-service/03-integration.md +178 -0
  39. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  40. package/ai-docs/templates/new-service/05-validation.md +145 -0
  41. package/dist/cc.js +1010 -47
  42. package/dist/cc.js.map +1 -1
  43. package/dist/config.js +13 -0
  44. package/dist/config.js.map +1 -1
  45. package/dist/constants.js +40 -3
  46. package/dist/constants.js.map +1 -1
  47. package/dist/index.js +54 -17
  48. package/dist/index.js.map +1 -1
  49. package/dist/metrics/MetricsManager.js +16 -0
  50. package/dist/metrics/MetricsManager.js.map +1 -1
  51. package/dist/metrics/behavioral-events.js +211 -0
  52. package/dist/metrics/behavioral-events.js.map +1 -1
  53. package/dist/metrics/constants.js +58 -4
  54. package/dist/metrics/constants.js.map +1 -1
  55. package/dist/services/AddressBook.js +18 -15
  56. package/dist/services/AddressBook.js.map +1 -1
  57. package/dist/services/AnswerCallOnWebexService.js +202 -0
  58. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  59. package/dist/services/ApiAiAssistant.js +351 -14
  60. package/dist/services/ApiAiAssistant.js.map +1 -1
  61. package/dist/services/EntryPoint.js +46 -68
  62. package/dist/services/EntryPoint.js.map +1 -1
  63. package/dist/services/Queue.js +27 -22
  64. package/dist/services/Queue.js.map +1 -1
  65. package/dist/services/UserPreference.js +427 -0
  66. package/dist/services/UserPreference.js.map +1 -0
  67. package/dist/services/WebexCrossClientService.js +225 -0
  68. package/dist/services/WebexCrossClientService.js.map +1 -0
  69. package/dist/services/WxAppTelephonyMercurySync.js +111 -0
  70. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  71. package/dist/services/config/Util.js +48 -48
  72. package/dist/services/config/Util.js.map +1 -1
  73. package/dist/services/config/constants.js +56 -78
  74. package/dist/services/config/constants.js.map +1 -1
  75. package/dist/services/config/index.js +100 -226
  76. package/dist/services/config/index.js.map +1 -1
  77. package/dist/services/config/types.js +97 -24
  78. package/dist/services/config/types.js.map +1 -1
  79. package/dist/services/core/Err.js.map +1 -1
  80. package/dist/services/core/Utils.js +111 -32
  81. package/dist/services/core/Utils.js.map +1 -1
  82. package/dist/services/core/WebexRequest.js +6 -2
  83. package/dist/services/core/WebexRequest.js.map +1 -1
  84. package/dist/services/core/aqm-reqs.js +28 -14
  85. package/dist/services/core/aqm-reqs.js.map +1 -1
  86. package/dist/services/core/types.js.map +1 -1
  87. package/dist/services/core/websocket/WebSocketManager.js +2 -1
  88. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  89. package/dist/services/core/websocket/types.js.map +1 -1
  90. package/dist/services/index.js +1 -1
  91. package/dist/services/index.js.map +1 -1
  92. package/dist/services/task/Task.js +955 -0
  93. package/dist/services/task/Task.js.map +1 -0
  94. package/dist/services/task/TaskFactory.js +50 -0
  95. package/dist/services/task/TaskFactory.js.map +1 -0
  96. package/dist/services/task/TaskManager.js +1272 -523
  97. package/dist/services/task/TaskManager.js.map +1 -1
  98. package/dist/services/task/TaskUtils.js +221 -27
  99. package/dist/services/task/TaskUtils.js.map +1 -1
  100. package/dist/services/task/WebexCallingUtils.js +70 -0
  101. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  102. package/dist/services/task/constants.js +21 -2
  103. package/dist/services/task/constants.js.map +1 -1
  104. package/dist/services/task/contact.js +29 -0
  105. package/dist/services/task/contact.js.map +1 -1
  106. package/dist/services/task/dialer.js +78 -0
  107. package/dist/services/task/dialer.js.map +1 -1
  108. package/dist/services/task/digital/Digital.js +78 -0
  109. package/dist/services/task/digital/Digital.js.map +1 -0
  110. package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
  111. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  112. package/dist/services/task/state-machine/actions.js +572 -0
  113. package/dist/services/task/state-machine/actions.js.map +1 -0
  114. package/dist/services/task/state-machine/constants.js +161 -0
  115. package/dist/services/task/state-machine/constants.js.map +1 -0
  116. package/dist/services/task/state-machine/guards.js +379 -0
  117. package/dist/services/task/state-machine/guards.js.map +1 -0
  118. package/dist/services/task/state-machine/index.js +53 -0
  119. package/dist/services/task/state-machine/index.js.map +1 -0
  120. package/dist/services/task/state-machine/types.js +54 -0
  121. package/dist/services/task/state-machine/types.js.map +1 -0
  122. package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
  123. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  124. package/dist/services/task/taskDataNormalizer.js +99 -0
  125. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  126. package/dist/services/task/types.js +259 -5
  127. package/dist/services/task/types.js.map +1 -1
  128. package/dist/services/task/voice/Voice.js +1504 -0
  129. package/dist/services/task/voice/Voice.js.map +1 -0
  130. package/dist/services/task/voice/WebRTC.js +152 -0
  131. package/dist/services/task/voice/WebRTC.js.map +1 -0
  132. package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
  133. package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
  134. package/dist/services/task/voice/wxAppVoiceMethods.js +395 -0
  135. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  136. package/dist/services/wxAppDiagnosticLogging.js +159 -0
  137. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  138. package/dist/services/wxAppTelephonyUtils.js +19 -0
  139. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  140. package/dist/types/cc.d.ts +196 -9
  141. package/dist/types/config.d.ts +13 -0
  142. package/dist/types/constants.d.ts +38 -1
  143. package/dist/types/index.d.ts +54 -20
  144. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  145. package/dist/types/metrics/constants.d.ts +52 -1
  146. package/dist/types/services/AddressBook.d.ts +2 -1
  147. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  148. package/dist/types/services/ApiAiAssistant.d.ts +58 -5
  149. package/dist/types/services/EntryPoint.d.ts +7 -5
  150. package/dist/types/services/Queue.d.ts +5 -3
  151. package/dist/types/services/UserPreference.d.ts +118 -0
  152. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  153. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  154. package/dist/types/services/config/Util.d.ts +4 -8
  155. package/dist/types/services/config/constants.d.ts +49 -76
  156. package/dist/types/services/config/index.d.ts +16 -64
  157. package/dist/types/services/config/types.d.ts +248 -114
  158. package/dist/types/services/core/Err.d.ts +6 -0
  159. package/dist/types/services/core/Utils.d.ts +35 -13
  160. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  161. package/dist/types/services/core/types.d.ts +2 -0
  162. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  163. package/dist/types/services/core/websocket/types.d.ts +1 -1
  164. package/dist/types/services/index.d.ts +1 -1
  165. package/dist/types/services/task/Task.d.ts +186 -0
  166. package/dist/types/services/task/TaskFactory.d.ts +14 -0
  167. package/dist/types/services/task/TaskUtils.d.ts +54 -2
  168. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  169. package/dist/types/services/task/constants.d.ts +19 -0
  170. package/dist/types/services/task/contact.d.ts +4 -0
  171. package/dist/types/services/task/dialer.d.ts +30 -0
  172. package/dist/types/services/task/digital/Digital.d.ts +23 -0
  173. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
  174. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  175. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  176. package/dist/types/services/task/state-machine/guards.d.ts +103 -0
  177. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  178. package/dist/types/services/task/state-machine/types.d.ts +277 -0
  179. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  180. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  181. package/dist/types/services/task/types.d.ts +790 -66
  182. package/dist/types/services/task/voice/Voice.d.ts +241 -0
  183. package/dist/types/services/task/voice/WebRTC.d.ts +54 -0
  184. package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
  185. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +68 -0
  186. package/dist/types/services/wxAppDiagnosticLogging.d.ts +63 -0
  187. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  188. package/dist/types/types.d.ts +213 -11
  189. package/dist/types/utils/PageCache.d.ts +20 -3
  190. package/dist/types/webex.d.ts +1 -0
  191. package/dist/types.js +168 -6
  192. package/dist/types.js.map +1 -1
  193. package/dist/utils/PageCache.js +19 -5
  194. package/dist/utils/PageCache.js.map +1 -1
  195. package/dist/webex.js +22 -2
  196. package/dist/webex.js.map +1 -1
  197. package/package.json +16 -12
  198. package/src/cc.ts +1338 -121
  199. package/src/config.ts +13 -0
  200. package/src/constants.ts +41 -1
  201. package/src/index.ts +62 -15
  202. package/src/metrics/MetricsManager.ts +16 -0
  203. package/src/metrics/ai-docs/AGENTS.md +350 -0
  204. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  205. package/src/metrics/ai-docs/metrics-spec.md +971 -0
  206. package/src/metrics/behavioral-events.ts +218 -0
  207. package/src/metrics/constants.ts +63 -4
  208. package/src/services/AddressBook.ts +17 -6
  209. package/src/services/AnswerCallOnWebexService.ts +239 -0
  210. package/src/services/ApiAiAssistant.ts +470 -15
  211. package/src/services/EntryPoint.ts +59 -60
  212. package/src/services/Queue.ts +29 -12
  213. package/src/services/UserPreference.ts +509 -0
  214. package/src/services/WebexCrossClientService.ts +298 -0
  215. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  216. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  217. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  218. package/src/services/agent/ai-docs/agent-spec.md +505 -0
  219. package/src/services/ai-docs/AGENTS.md +386 -0
  220. package/src/services/ai-docs/services-spec.md +505 -0
  221. package/src/services/config/Util.ts +54 -84
  222. package/src/services/config/ai-docs/AGENTS.md +250 -0
  223. package/src/services/config/ai-docs/ARCHITECTURE.md +413 -0
  224. package/src/services/config/ai-docs/config-spec.md +669 -0
  225. package/src/services/config/constants.ts +63 -85
  226. package/src/services/config/index.ts +116 -277
  227. package/src/services/config/types.ts +233 -125
  228. package/src/services/core/Err.ts +3 -0
  229. package/src/services/core/Utils.ts +127 -37
  230. package/src/services/core/WebexRequest.ts +3 -1
  231. package/src/services/core/ai-docs/AGENTS.md +381 -0
  232. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  233. package/src/services/core/ai-docs/core-spec.md +787 -0
  234. package/src/services/core/aqm-reqs.ts +30 -15
  235. package/src/services/core/types.ts +33 -1
  236. package/src/services/core/websocket/WebSocketManager.ts +2 -0
  237. package/src/services/core/websocket/types.ts +1 -1
  238. package/src/services/index.ts +1 -1
  239. package/src/services/task/Task.ts +1211 -0
  240. package/src/services/task/TaskFactory.ts +82 -0
  241. package/src/services/task/TaskManager.ts +1584 -643
  242. package/src/services/task/TaskUtils.ts +316 -25
  243. package/src/services/task/WebexCallingUtils.ts +136 -0
  244. package/src/services/task/ai-docs/AGENTS.md +457 -0
  245. package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
  246. package/src/services/task/ai-docs/task-spec.md +1501 -0
  247. package/src/services/task/constants.ts +20 -0
  248. package/src/services/task/contact.ts +30 -0
  249. package/src/services/task/dialer.ts +80 -0
  250. package/src/services/task/digital/Digital.ts +102 -0
  251. package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
  252. package/src/services/task/state-machine/actions.ts +741 -0
  253. package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
  254. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
  255. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
  256. package/src/services/task/state-machine/constants.ts +172 -0
  257. package/src/services/task/state-machine/guards.ts +442 -0
  258. package/src/services/task/state-machine/index.ts +28 -0
  259. package/src/services/task/state-machine/types.ts +258 -0
  260. package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
  261. package/src/services/task/taskDataNormalizer.ts +137 -0
  262. package/src/services/task/types.ts +974 -70
  263. package/src/services/task/voice/Voice.ts +1878 -0
  264. package/src/services/task/voice/WebRTC.ts +194 -0
  265. package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
  266. package/src/services/task/voice/wxAppVoiceMethods.ts +575 -0
  267. package/src/services/wxAppDiagnosticLogging.ts +286 -0
  268. package/src/services/wxAppTelephonyUtils.ts +14 -0
  269. package/src/types.ts +272 -13
  270. package/src/utils/AGENTS.md +289 -0
  271. package/src/utils/PageCache.ts +38 -5
  272. package/src/utils/ai-docs/utils-spec.md +391 -0
  273. package/src/webex.js +10 -0
  274. package/test/unit/fixtures/aiSummaryTestUtils.ts +40 -0
  275. package/test/unit/spec/cc.ts +3705 -150
  276. package/test/unit/spec/index.ts +885 -0
  277. package/test/unit/spec/logger-proxy.ts +70 -0
  278. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  279. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  280. package/test/unit/spec/services/AddressBook.ts +37 -6
  281. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  282. package/test/unit/spec/services/ApiAiAssistant.ts +429 -23
  283. package/test/unit/spec/services/EntryPoint.ts +87 -40
  284. package/test/unit/spec/services/Queue.ts +123 -12
  285. package/test/unit/spec/services/UserPreference.ts +401 -0
  286. package/test/unit/spec/services/WebCallingService.ts +7 -1
  287. package/test/unit/spec/services/WebexCrossClientService.ts +509 -0
  288. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  289. package/test/unit/spec/services/config/Util.ts +501 -0
  290. package/test/unit/spec/services/config/index.ts +317 -547
  291. package/test/unit/spec/services/core/Utils.ts +425 -8
  292. package/test/unit/spec/services/core/WebexRequest.ts +8 -7
  293. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  294. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
  295. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  296. package/test/unit/spec/services/task/Task.ts +2723 -0
  297. package/test/unit/spec/services/task/TaskFactory.ts +116 -0
  298. package/test/unit/spec/services/task/TaskManager.ts +4781 -1585
  299. package/test/unit/spec/services/task/TaskUtils.ts +307 -0
  300. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  301. package/test/unit/spec/services/task/contact.ts +33 -0
  302. package/test/unit/spec/services/task/dialer.ts +190 -0
  303. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  304. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
  305. package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
  306. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  307. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
  308. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  309. package/test/unit/spec/services/task/voice/Voice.ts +2355 -0
  310. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  311. package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
  312. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +719 -0
  313. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +179 -0
  314. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  315. package/typedoc.md +1 -1
  316. package/umd/contact-center.min.js +2 -2
  317. package/umd/contact-center.min.js.map +1 -1
  318. package/dist/services/task/index.js +0 -1530
  319. package/dist/services/task/index.js.map +0 -1
  320. package/dist/types/services/task/index.d.ts +0 -650
  321. package/src/services/task/index.ts +0 -1806
  322. package/test/unit/spec/services/task/index.ts +0 -2205
@@ -1,18 +1,68 @@
1
1
  import ApiAIAssistant from '../../../../src/services/ApiAiAssistant';
2
2
  import MetricsManager from '../../../../src/metrics/MetricsManager';
3
3
  import LoggerProxy from '../../../../src/logger-proxy';
4
- import {HTTP_METHODS, WebexSDK} from '../../../../src/types';
4
+ import WebexRequest from '../../../../src/services/core/WebexRequest';
5
+ import {
6
+ HTTP_METHODS,
7
+ RealTimeAssistanceUserActionId,
8
+ WELLNESS_BREAK_USER_ACTIONS,
9
+ WebexSDK,
10
+ } from '../../../../src/types';
5
11
 
6
- jest.mock('../../../../src/metrics/MetricsManager');
7
- jest.mock('../../../../src/logger-proxy');
12
+ jest.mock('../../../../src/metrics/MetricsManager', () => ({
13
+ __esModule: true,
14
+ default: {
15
+ getInstance: jest.fn(),
16
+ },
17
+ }));
18
+ jest.mock('../../../../src/logger-proxy', () => ({
19
+ __esModule: true,
20
+ default: {
21
+ log: jest.fn(),
22
+ error: jest.fn(),
23
+ info: jest.fn(),
24
+ warn: jest.fn(),
25
+ trace: jest.fn(),
26
+ debug: jest.fn(),
27
+ },
28
+ }));
29
+ jest.mock('../../../../src/services/core/WebexRequest', () => ({
30
+ __esModule: true,
31
+ default: {
32
+ getInstance: jest.fn(),
33
+ },
34
+ }));
35
+ jest.mock('../../../../src/services/core/Utils', () => ({
36
+ __esModule: true,
37
+ getErrorDetails: jest.fn((error: any, methodName: string, moduleName: string) => {
38
+ const LoggerProxyMock = require('../../../../src/logger-proxy').default;
39
+ const reason = error?.details?.data?.reason ?? `Error while performing ${methodName}`;
40
+ const detailedError = new Error(reason) as Error & {data?: Record<string, unknown>};
41
+
42
+ detailedError.data = {};
43
+ LoggerProxyMock.error(`${methodName} failed with reason: ${reason}`, {
44
+ module: moduleName,
45
+ method: methodName,
46
+ });
47
+
48
+ return {error: detailedError, reason};
49
+ }),
50
+ }));
8
51
 
9
52
  describe('ApiAIAssistant', () => {
10
53
  let apiAIAssistant: ApiAIAssistant;
11
54
  let mockWebex: WebexSDK;
12
55
  let mockMetricsManager: jest.Mocked<MetricsManager>;
56
+ let mockUploadLogs: jest.Mock;
57
+ let wellnessContext: {
58
+ isWellnessBreakEnabled: boolean;
59
+ agentId?: string;
60
+ agentSessionId?: string;
61
+ };
13
62
 
14
63
  beforeEach(() => {
15
64
  jest.clearAllMocks();
65
+ mockUploadLogs = jest.fn();
16
66
 
17
67
  mockWebex = {
18
68
  credentials: {
@@ -33,15 +83,94 @@ describe('ApiAIAssistant', () => {
33
83
  once: jest.fn(),
34
84
  } as unknown as WebexSDK;
35
85
 
86
+ jest.spyOn(WebexRequest, 'getInstance').mockReturnValue({
87
+ request: mockWebex.request,
88
+ uploadLogs: mockUploadLogs,
89
+ } as any);
90
+
36
91
  mockMetricsManager = {
37
92
  trackEvent: jest.fn(),
38
93
  timeEvent: jest.fn(),
39
94
  } as unknown as jest.Mocked<MetricsManager>;
40
95
  (MetricsManager.getInstance as jest.Mock).mockReturnValue(mockMetricsManager);
41
96
 
42
- apiAIAssistant = new ApiAIAssistant(mockWebex);
97
+ wellnessContext = {isWellnessBreakEnabled: false};
98
+ apiAIAssistant = new ApiAIAssistant(mockWebex, () => wellnessContext);
99
+ });
100
+
101
+ it('sends a summary request and resolves it from the matching RTD event', async () => {
102
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {accepted: true}});
103
+
104
+ const request = apiAIAssistant.requestAndWaitForRtd({
105
+ correlationId: 'conversation-1',
106
+ rtdEventType: 'POST_CALL_SUMMARY',
107
+ timeoutMs: 15000,
108
+ createTimeoutError: () => new Error('TIMEOUT'),
109
+ agentId: 'agent-1',
110
+ interactionId: 'interaction-1',
111
+ eventType: 'CTI_EVENT' as any,
112
+ eventName: 'GET_POST_CALL_SUMMARY' as any,
113
+ eventMetaData: {conversationId: 'conversation-1'},
114
+ timeout: 15000,
115
+ });
116
+
117
+ await Promise.resolve();
118
+ expect(apiAIAssistant.resolveFromRtdEvent('POST_CALL_SUMMARY', 'conversation-1', {summary: 'ready'})).toBe(
119
+ 'resolved'
120
+ );
121
+ await expect(request).resolves.toEqual({summary: 'ready'});
122
+ expect(mockWebex.request).toHaveBeenCalledTimes(1);
123
+ const requestBody = (mockWebex.request as jest.Mock).mock.calls[0][0].body;
124
+
125
+ expect(requestBody.publishTimestamp).toEqual(expect.any(Number));
126
+ expect(requestBody.eventDetails.data).toMatchObject({
127
+ conversationId: 'conversation-1',
128
+ interactionId: 'interaction-1',
129
+ actionTimeStamp: expect.any(String),
130
+ });
131
+ });
132
+
133
+ it('rejects an RTD request when its timeout expires', async () => {
134
+ jest.useFakeTimers();
135
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {accepted: true}});
136
+ const request = apiAIAssistant.requestAndWaitForRtd({
137
+ correlationId: 'conversation-1',
138
+ rtdEventType: 'POST_CALL_SUMMARY',
139
+ timeoutMs: 15000,
140
+ createTimeoutError: () => new Error('TIMEOUT'),
141
+ agentId: 'agent-1',
142
+ interactionId: 'interaction-1',
143
+ eventType: 'CTI_EVENT' as any,
144
+ eventName: 'GET_POST_CALL_SUMMARY' as any,
145
+ });
146
+
147
+ jest.advanceTimersByTime(15000);
148
+ await expect(request).rejects.toThrow('TIMEOUT');
149
+ jest.useRealTimers();
43
150
  });
44
151
 
152
+ it('removes the pending RTD request when the HTTP request fails', async () => {
153
+ const error = new Error('HTTP_FAILED');
154
+ (mockWebex.request as jest.Mock).mockRejectedValue(error);
155
+
156
+ const request = apiAIAssistant.requestAndWaitForRtd({
157
+ correlationId: 'conversation-1',
158
+ rtdEventType: 'POST_CALL_SUMMARY',
159
+ timeoutMs: 15000,
160
+ createTimeoutError: () => new Error('TIMEOUT'),
161
+ agentId: 'agent-1',
162
+ interactionId: 'interaction-1',
163
+ eventType: 'CTI_EVENT' as any,
164
+ eventName: 'GET_POST_CALL_SUMMARY' as any,
165
+ });
166
+
167
+ await expect(request).rejects.toBeInstanceOf(Error);
168
+ expect(apiAIAssistant.resolveFromRtdEvent('POST_CALL_SUMMARY', 'conversation-1', {})).toBe(
169
+ 'not-found'
170
+ );
171
+ });
172
+
173
+
45
174
  it('should send transcript start event successfully', async () => {
46
175
  (mockWebex.request as jest.Mock).mockResolvedValue({body: {ok: true}});
47
176
 
@@ -50,26 +179,23 @@ describe('ApiAIAssistant', () => {
50
179
  'interaction-1',
51
180
  'CUSTOM_EVENT',
52
181
  'GET_TRANSCRIPTS',
53
- 'START'
182
+ {action: 'START', actionTimeStamp: 'caller-supplied'}
54
183
  );
55
184
 
56
- expect(mockWebex.request).toHaveBeenCalledWith({
57
- uri: 'https://api-ai-assistant.produs1.ciscoccservice.com/event',
58
- method: HTTP_METHODS.POST,
59
- addAuthHeader: true,
60
- body: {
61
- agentId: 'test-agent-id',
62
- orgId: 'test-org-id',
63
- eventType: 'CUSTOM_EVENT',
64
- eventName: 'GET_TRANSCRIPTS',
65
- eventDetails: {
66
- data: expect.objectContaining({
67
- interactionId: 'interaction-1',
68
- action: 'START',
69
- }),
70
- },
71
- },
72
- });
185
+ expect(mockWebex.request).toHaveBeenCalledTimes(1);
186
+ const requestArgs = (mockWebex.request as jest.Mock).mock.calls[0][0];
187
+
188
+ expect(requestArgs.uri).toBe('https://api-ai-assistant.produs1.ciscoccservice.com/event');
189
+ expect(requestArgs.method).toBe(HTTP_METHODS.POST);
190
+ expect(requestArgs.addAuthHeader).toBe(true);
191
+ expect(requestArgs.body.agentId).toBe('test-agent-id');
192
+ expect(requestArgs.body.orgId).toBe('test-org-id');
193
+ expect(requestArgs.body.eventType).toBe('CUSTOM_EVENT');
194
+ expect(requestArgs.body.eventName).toBe('GET_TRANSCRIPTS');
195
+ expect(requestArgs.body.eventDetails.data.interactionId).toBe('interaction-1');
196
+ expect(requestArgs.body.eventDetails.data.action).toBe('START');
197
+ expect(requestArgs.body.eventDetails.data.actionTimeStamp).toEqual(expect.any(String));
198
+ expect(requestArgs.body.eventDetails.data.actionTimeStamp).not.toBe('caller-supplied');
73
199
  expect(result).toEqual({ok: true});
74
200
  });
75
201
 
@@ -93,6 +219,118 @@ describe('ApiAIAssistant', () => {
93
219
  expect(result).toEqual(responseBody as any);
94
220
  });
95
221
 
222
+ it('should request real-time assistance without extra context using sendEvent', async () => {
223
+ const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
224
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
225
+
226
+ const result = await apiAIAssistant.getRealTimeAssistance({
227
+ agentId: 'test-agent-id',
228
+ interactionId: 'interaction-1',
229
+ });
230
+
231
+ expect(sendEventSpy).toHaveBeenCalledTimes(1);
232
+ const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
233
+ sendEventSpy.mock.calls[0];
234
+
235
+ expect(agentId).toBe('test-agent-id');
236
+ expect(interactionId).toBe('interaction-1');
237
+ expect(eventType).toBe('CUSTOM_EVENT');
238
+ expect(eventName).toBe('GET_SUGGESTIONS');
239
+ expect(metadata).toBeUndefined();
240
+ expect(languageCode).toBe('en');
241
+ expect(typeof trackingId).toBe('string');
242
+ expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
243
+ expect(result).toEqual({ok: true});
244
+ });
245
+
246
+ it('should request real-time assistance with extra context using sendEvent', async () => {
247
+ const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
248
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
249
+
250
+ const result = await apiAIAssistant.getRealTimeAssistance({
251
+ agentId: 'test-agent-id',
252
+ interactionId: 'interaction-1',
253
+ context: 'Need assistance with credit card payment due date',
254
+ });
255
+
256
+ expect(sendEventSpy).toHaveBeenCalledTimes(1);
257
+ const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
258
+ sendEventSpy.mock.calls[0];
259
+
260
+ expect(agentId).toBe('test-agent-id');
261
+ expect(interactionId).toBe('interaction-1');
262
+ expect(eventType).toBe('CUSTOM_EVENT');
263
+ expect(eventName).toBe('ADD_SUGGESTIONS_EXTRA_CONTEXT');
264
+ expect(metadata).toEqual({context: 'Need assistance with credit card payment due date'});
265
+ expect(languageCode).toBe('en');
266
+ expect(typeof trackingId).toBe('string');
267
+ expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
268
+ expect(result).toEqual({ok: true});
269
+ });
270
+
271
+ it('should treat whitespace-only context as GET_SUGGESTIONS', async () => {
272
+ const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
273
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
274
+
275
+ const result = await apiAIAssistant.getRealTimeAssistance({
276
+ agentId: 'test-agent-id',
277
+ interactionId: 'interaction-1',
278
+ context: ' ',
279
+ });
280
+
281
+ expect(sendEventSpy).toHaveBeenCalledTimes(1);
282
+ const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
283
+ sendEventSpy.mock.calls[0];
284
+
285
+ expect(agentId).toBe('test-agent-id');
286
+ expect(interactionId).toBe('interaction-1');
287
+ expect(eventType).toBe('CUSTOM_EVENT');
288
+ expect(eventName).toBe('GET_SUGGESTIONS');
289
+ expect(metadata).toEqual({context: ''});
290
+ expect(languageCode).toBe('en');
291
+ expect(typeof trackingId).toBe('string');
292
+ expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
293
+ expect(result).toEqual({ok: true});
294
+ });
295
+
296
+ [
297
+ RealTimeAssistanceUserActionId.LIKE,
298
+ RealTimeAssistanceUserActionId.DISLIKE,
299
+ RealTimeAssistanceUserActionId.COPY,
300
+ ].forEach((actionId) => {
301
+ it(`should send real-time assistance user action for ${actionId}`, async () => {
302
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {ok: true}});
303
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
304
+
305
+ const result = await apiAIAssistant.sendRealTimeAssistanceUserAction({
306
+ agentId: 'test-agent-id',
307
+ interactionId: 'interaction-1',
308
+ adaptiveCardId: 'adaptive-card-1',
309
+ actionId,
310
+ });
311
+
312
+ expect(mockWebex.request).toHaveBeenCalledTimes(1);
313
+ const requestArgs = (mockWebex.request as jest.Mock).mock.calls[0][0];
314
+ const eventData = requestArgs.body.eventDetails.data;
315
+
316
+ expect(requestArgs.uri).toBe('https://api-ai-assistant.produs1.ciscoccservice.com/event');
317
+ expect(requestArgs.method).toBe(HTTP_METHODS.POST);
318
+ expect(requestArgs.addAuthHeader).toBe(true);
319
+ expect(requestArgs.body.agentId).toBe('test-agent-id');
320
+ expect(requestArgs.body.orgId).toBe('test-org-id');
321
+ expect(requestArgs.body.eventType).toBe('CUSTOM_EVENT');
322
+ expect(requestArgs.body.eventName).toBe('SUGGESTED_RESPONSES_USER_ACTION');
323
+ expect(eventData.interactionId).toBe('interaction-1');
324
+ expect(eventData.adaptiveCardId).toBe('adaptive-card-1');
325
+ expect(eventData.userAction.actionType).toBe('Action.Submit');
326
+ expect(eventData.userAction.actionId).toBe(actionId);
327
+ expect(eventData.languageCode).toBe('en');
328
+ expect(typeof eventData.actionTimeStamp).toBe('string');
329
+ expect(eventData.trackingId.startsWith('WX_CC_SDK_')).toBe(true);
330
+ expect(result).toEqual({ok: true});
331
+ });
332
+ });
333
+
96
334
  it('should fail when base URL mapping is not available', async () => {
97
335
  (mockWebex.internal.services.get as jest.Mock).mockReturnValue('https://unknown-host.invalid');
98
336
 
@@ -103,7 +341,7 @@ describe('ApiAIAssistant', () => {
103
341
  'interaction-1',
104
342
  'CUSTOM_EVENT',
105
343
  'GET_TRANSCRIPTS',
106
- 'STOP'
344
+ {action: 'STOP'}
107
345
  );
108
346
  } catch (_error) {
109
347
  failed = true;
@@ -112,4 +350,172 @@ describe('ApiAIAssistant', () => {
112
350
  expect(failed).toBe(true);
113
351
  expect(LoggerProxy.error).toHaveBeenCalled();
114
352
  });
353
+
354
+ it('should fail when realtime transcripts feature is disabled', async () => {
355
+ apiAIAssistant.setAIFeatureFlags({realtimeTranscripts: {enable: false}} as any);
356
+ let errorMessage = '';
357
+
358
+ try {
359
+ await apiAIAssistant.fetchHistoricTranscripts('test-agent-id', 'interaction-1');
360
+ } catch (error) {
361
+ errorMessage = (error as Error)?.message || '';
362
+ }
363
+
364
+ expect(errorMessage).toBe('Error while performing fetchHistoricTranscripts');
365
+ });
366
+
367
+ it('should fail when real-time assistance feature is disabled', async () => {
368
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: false}} as any);
369
+ let errorMessage = '';
370
+
371
+ try {
372
+ await apiAIAssistant.getRealTimeAssistance({
373
+ agentId: 'test-agent-id',
374
+ interactionId: 'interaction-1',
375
+ });
376
+ } catch (error) {
377
+ errorMessage = (error as Error)?.message || '';
378
+ }
379
+
380
+ expect(errorMessage).toBe('Error while performing getRealTimeAssistance');
381
+ });
382
+
383
+ it('should fail to send real-time assistance user action when feature is disabled', async () => {
384
+ apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: false}} as any);
385
+ let errorMessage = '';
386
+
387
+ try {
388
+ await apiAIAssistant.sendRealTimeAssistanceUserAction({
389
+ agentId: 'test-agent-id',
390
+ interactionId: 'interaction-1',
391
+ adaptiveCardId: 'adaptive-card-1',
392
+ actionId: RealTimeAssistanceUserActionId.LIKE,
393
+ });
394
+ } catch (error) {
395
+ errorMessage = (error as Error)?.message || '';
396
+ }
397
+
398
+ expect(errorMessage).toBe('Error while performing sendRealTimeAssistanceUserAction');
399
+ });
400
+
401
+ describe('Agent Wellness Break actions', () => {
402
+ beforeEach(() => {
403
+ wellnessContext = {
404
+ isWellnessBreakEnabled: true,
405
+ agentId: 'test-agent-id',
406
+ agentSessionId: 'session-1',
407
+ };
408
+ });
409
+
410
+ it('does not expose wellness context mutation on the public assistant instance', () => {
411
+ expect(apiAIAssistant).not.toHaveProperty('setWellnessContext');
412
+ });
413
+
414
+ it('sends the exact REQUESTED body and resolves only for HTTP 202', async () => {
415
+ jest.spyOn(Date, 'now').mockReturnValue(1787640000000);
416
+ (mockWebex.request as jest.Mock).mockResolvedValue({statusCode: 202});
417
+
418
+ await expect(apiAIAssistant.requestWellnessBreak()).resolves.toBeUndefined();
419
+
420
+ expect(mockWebex.request).toHaveBeenCalledWith({
421
+ uri: 'https://api-ai-assistant.produs1.ciscoccservice.com/event',
422
+ method: HTTP_METHODS.POST,
423
+ addAuthHeader: true,
424
+ body: {
425
+ agentId: 'test-agent-id',
426
+ orgId: 'test-org-id',
427
+ eventType: 'CUSTOM_EVENT',
428
+ eventName: 'WellnessBreakAction',
429
+ eventDetails: {
430
+ data: {
431
+ action: 'REQUESTED',
432
+ agentSessionId: 'session-1',
433
+ actionTimeStamp: 1787640000000,
434
+ },
435
+ },
436
+ },
437
+ });
438
+ });
439
+
440
+ it.each([
441
+ WELLNESS_BREAK_USER_ACTIONS.ACCEPTED,
442
+ WELLNESS_BREAK_USER_ACTIONS.REJECTED,
443
+ WELLNESS_BREAK_USER_ACTIONS.NO_RESPONSE,
444
+ ] as const)(
445
+ 'sends the %s offer action',
446
+ async (action) => {
447
+ (mockWebex.request as jest.Mock).mockResolvedValue({statusCode: 202});
448
+
449
+ await apiAIAssistant.respondToWellnessBreak({action});
450
+
451
+ expect(
452
+ (mockWebex.request as jest.Mock).mock.calls[0][0].body.eventDetails.data.action
453
+ ).toBe(action);
454
+ }
455
+ );
456
+
457
+ it('rejects disabled or incomplete internal session context before HTTP', async () => {
458
+ wellnessContext = {
459
+ isWellnessBreakEnabled: false,
460
+ agentId: 'test-agent-id',
461
+ agentSessionId: 'session-1',
462
+ };
463
+ await expect(apiAIAssistant.requestWellnessBreak()).rejects.toThrow(
464
+ 'WELLNESS_BREAK_NOT_ENABLED'
465
+ );
466
+
467
+ wellnessContext = {
468
+ isWellnessBreakEnabled: true,
469
+ };
470
+ await expect(apiAIAssistant.requestWellnessBreak()).rejects.toThrow(
471
+ 'WELLNESS_BREAK_AGENT_ID_REQUIRED'
472
+ );
473
+
474
+ wellnessContext = {
475
+ isWellnessBreakEnabled: true,
476
+ agentId: 'test-agent-id',
477
+ };
478
+ await expect(apiAIAssistant.requestWellnessBreak()).rejects.toThrow(
479
+ 'WELLNESS_BREAK_AGENT_SESSION_REQUIRED'
480
+ );
481
+ expect(mockWebex.request).not.toHaveBeenCalled();
482
+ });
483
+
484
+ it('rejects a missing organization before HTTP', async () => {
485
+ (mockWebex.credentials.getOrgId as jest.Mock).mockReturnValue(' ');
486
+
487
+ await expect(apiAIAssistant.requestWellnessBreak()).rejects.toThrow(
488
+ 'WELLNESS_BREAK_ORG_ID_REQUIRED'
489
+ );
490
+ expect(mockWebex.request).not.toHaveBeenCalled();
491
+ });
492
+
493
+ it.each([200, 204, 400, 401, 403, 404, 409, 429, 500])(
494
+ 'rejects the non-202 HTTP status %i',
495
+ async (statusCode) => {
496
+ (mockWebex.request as jest.Mock).mockResolvedValue({statusCode});
497
+
498
+ await expect(apiAIAssistant.requestWellnessBreak()).rejects.toThrow(
499
+ `WELLNESS_BREAK_ACTION_UNEXPECTED_STATUS_${statusCode}`
500
+ );
501
+ }
502
+ );
503
+
504
+ it('rejects network failures through the structured error path', async () => {
505
+ (mockWebex.request as jest.Mock).mockRejectedValue(new Error('Network unavailable'));
506
+
507
+ await expect(apiAIAssistant.requestWellnessBreak()).rejects.toThrow(
508
+ 'Error while performing requestWellnessBreak'
509
+ );
510
+ });
511
+
512
+ it('rejects unsupported offer actions before HTTP', async () => {
513
+ await expect(
514
+ apiAIAssistant.respondToWellnessBreak({
515
+ action: 'REQUESTED',
516
+ } as unknown as Parameters<ApiAIAssistant['respondToWellnessBreak']>[0])
517
+ ).rejects.toThrow('WELLNESS_BREAK_ACTION_INVALID');
518
+ expect(mockWebex.request).not.toHaveBeenCalled();
519
+ });
520
+ });
115
521
  });