@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
@@ -11,6 +11,7 @@ jest.mock('../../../../../src/logger-proxy', () => ({
11
11
  log: jest.fn(),
12
12
  error: jest.fn(),
13
13
  info: jest.fn(),
14
+ warn: jest.fn(),
14
15
  initialize: jest.fn(),
15
16
  },
16
17
  }));
@@ -244,7 +245,7 @@ describe('Utils', () => {
244
245
  const result = Utils.getStationLoginErrorData(failure, LoginOption.AGENT_DN);
245
246
  expect(result).toEqual({
246
247
  message:
247
- 'Enter a valid US dial number. For help, reach out to your administrator or support team.',
248
+ 'Enter a valid dial number. For help, reach out to your administrator or support team.',
248
249
  fieldName: LoginOption.AGENT_DN,
249
250
  });
250
251
  });
@@ -530,7 +531,7 @@ describe('Utils', () => {
530
531
  };
531
532
 
532
533
  const result = Utils.calculateDestAgentId(interaction, currentAgentId);
533
- expect(result).toBeUndefined();
534
+ expect(result).toBe('');
534
535
  });
535
536
 
536
537
  it('should handle CBT scenario when phone number is not a direct participant key', () => {
@@ -557,4 +558,482 @@ describe('Utils', () => {
557
558
  });
558
559
  });
559
560
 
561
+ describe('calculateDestType', () => {
562
+ const currentAgentId = 'current-agent-123';
563
+
564
+ it('should return DIALNUMBER when pType is DN', () => {
565
+ const interaction: any = {
566
+ media: {
567
+ consult: {
568
+ mType: 'consult',
569
+ participants: [currentAgentId, 'dest-agent-456'],
570
+ },
571
+ },
572
+ participants: {
573
+ [currentAgentId]: {type: 'Agent', pType: 'Agent'},
574
+ 'dest-agent-456': {type: 'Agent', pType: 'DN', id: 'dest-agent-456'},
575
+ },
576
+ };
577
+
578
+ const result = Utils.calculateDestType(interaction, currentAgentId);
579
+ expect(result).toBe('dialNumber');
580
+ });
581
+
582
+ it('should return ENTRYPOINT when pType is EP-DN', () => {
583
+ const interaction: any = {
584
+ media: {
585
+ consult: {
586
+ mType: 'consult',
587
+ participants: [currentAgentId, 'dest-agent-456'],
588
+ },
589
+ },
590
+ participants: {
591
+ [currentAgentId]: {type: 'Agent', pType: 'Agent'},
592
+ 'dest-agent-456': {type: 'Agent', pType: 'EP-DN', id: 'dest-agent-456'},
593
+ },
594
+ };
595
+
596
+ const result = Utils.calculateDestType(interaction, currentAgentId);
597
+ expect(result).toBe('entryPoint');
598
+ });
599
+
600
+ it('should return lowercase pType for other types', () => {
601
+ const interaction: any = {
602
+ media: {
603
+ consult: {
604
+ mType: 'consult',
605
+ participants: [currentAgentId, 'dest-agent-456'],
606
+ },
607
+ },
608
+ participants: {
609
+ [currentAgentId]: {type: 'Agent', pType: 'Agent'},
610
+ 'dest-agent-456': {type: 'Agent', pType: 'Agent', id: 'dest-agent-456'},
611
+ },
612
+ };
613
+
614
+ const result = Utils.calculateDestType(interaction, currentAgentId);
615
+ expect(result).toBe('agent');
616
+ });
617
+
618
+ it('should return agent when no destination agent found', () => {
619
+ const interaction: any = {
620
+ media: {},
621
+ participants: {
622
+ [currentAgentId]: {type: 'Agent', pType: 'Agent'},
623
+ },
624
+ };
625
+
626
+ const result = Utils.calculateDestType(interaction, currentAgentId);
627
+ expect(result).toBe('agent');
628
+ });
629
+ });
630
+
631
+ describe('buildConsultConferenceParamData', () => {
632
+ it('maps entryPoint destinationType correctly', () => {
633
+ const result = Utils.buildConsultConferenceParamData(
634
+ {
635
+ agentId: 'agent1',
636
+ destinationType: 'entryPoint',
637
+ destAgentId: 'ep123',
638
+ },
639
+ 'interaction123'
640
+ );
641
+
642
+ expect(result).toEqual({
643
+ interactionId: 'interaction123',
644
+ data: {
645
+ agentId: 'agent1',
646
+ to: 'ep123',
647
+ destinationType: 'entryPoint',
648
+ },
649
+ });
650
+ });
651
+
652
+ it('maps EP-DN/EP_DN destinationType variants to entryPoint', () => {
653
+ const hyphenResult = Utils.buildConsultConferenceParamData(
654
+ {
655
+ agentId: 'agent1',
656
+ destinationType: 'EP-DN',
657
+ destAgentId: 'ep123',
658
+ },
659
+ 'interaction123'
660
+ );
661
+ const underscoreResult = Utils.buildConsultConferenceParamData(
662
+ {
663
+ agentId: 'agent1',
664
+ destinationType: 'EP_DN',
665
+ destAgentId: 'ep123',
666
+ },
667
+ 'interaction123'
668
+ );
669
+
670
+ expect(hyphenResult.data.destinationType).toBe('entryPoint');
671
+ expect(underscoreResult.data.destinationType).toBe('entryPoint');
672
+ });
673
+ });
674
+
675
+ describe('getDestAgentIdForCBT', () => {
676
+ it('should return destination agent ID for CBT scenario', () => {
677
+ const interaction: any = {
678
+ participants: {
679
+ 'agent-uuid-123': {
680
+ type: 'Agent',
681
+ pType: 'dn',
682
+ dn: '5551234567',
683
+ id: 'agent-uuid-123',
684
+ },
685
+ 'customer-1': {
686
+ type: 'Customer',
687
+ pType: 'Customer',
688
+ id: 'customer-1',
689
+ },
690
+ },
691
+ };
692
+ const consultingAgent = '5551234567'; // Phone number, not in participants as key
693
+
694
+ const result = Utils.getDestAgentIdForCBT(interaction, consultingAgent);
695
+ expect(result).toBe('agent-uuid-123');
696
+ });
697
+
698
+ it('should return empty string when consultingAgent is in participants (non-CBT)', () => {
699
+ const interaction: any = {
700
+ participants: {
701
+ 'agent-123': {
702
+ type: 'Agent',
703
+ pType: 'Agent',
704
+ id: 'agent-123',
705
+ },
706
+ },
707
+ };
708
+ const consultingAgent = 'agent-123'; // Exists as key in participants
709
+
710
+ const result = Utils.getDestAgentIdForCBT(interaction, consultingAgent);
711
+ expect(result).toBe('');
712
+ });
713
+
714
+ it('should return empty string when no matching dial number found', () => {
715
+ const interaction: any = {
716
+ participants: {
717
+ 'agent-uuid-123': {
718
+ type: 'Agent',
719
+ pType: 'dn',
720
+ dn: '5559999999',
721
+ id: 'agent-uuid-123',
722
+ },
723
+ },
724
+ };
725
+ const consultingAgent = '5551234567'; // Different number
726
+
727
+ const result = Utils.getDestAgentIdForCBT(interaction, consultingAgent);
728
+ expect(result).toBe('');
729
+ });
730
+
731
+ it('should return empty string when consultingAgent is empty', () => {
732
+ const interaction: any = {
733
+ participants: {
734
+ 'agent-uuid-123': {
735
+ type: 'Agent',
736
+ pType: 'dn',
737
+ dn: '5551234567',
738
+ },
739
+ },
740
+ };
741
+
742
+ const result = Utils.getDestAgentIdForCBT(interaction, '');
743
+ expect(result).toBe('');
744
+ });
745
+
746
+ it('should match only when participant type is dial number and type is Agent', () => {
747
+ const interaction: any = {
748
+ participants: {
749
+ 'participant-1': {
750
+ type: 'Customer',
751
+ pType: 'dn',
752
+ dn: '5551234567',
753
+ },
754
+ 'participant-2': {
755
+ type: 'Agent',
756
+ pType: 'Agent',
757
+ dn: '5551234567',
758
+ },
759
+ 'participant-3': {
760
+ type: 'Agent',
761
+ pType: 'dn',
762
+ dn: '5551234567',
763
+ id: 'correct-agent',
764
+ },
765
+ },
766
+ };
767
+
768
+ const result = Utils.getDestAgentIdForCBT(interaction, '5551234567');
769
+ expect(result).toBe('participant-3');
770
+ });
771
+
772
+ it('should handle case-insensitive participant type comparison', () => {
773
+ const interaction: any = {
774
+ participants: {
775
+ 'agent-uuid': {
776
+ type: 'Agent',
777
+ pType: 'DN', // Uppercase (dial number)
778
+ dn: '5551234567',
779
+ },
780
+ },
781
+ };
782
+
783
+ const result = Utils.getDestAgentIdForCBT(interaction, '5551234567');
784
+ expect(result).toBe('agent-uuid');
785
+ });
786
+ });
787
+
788
+ describe('calculateDestAgentId', () => {
789
+ const currentAgentId = 'agent-123';
790
+
791
+ it('should return destAgentIdCBT when found', () => {
792
+ const interaction: any = {
793
+ media: {
794
+ consult: {
795
+ mType: 'consult',
796
+ participants: [currentAgentId, '5551234567'],
797
+ },
798
+ },
799
+ participants: {
800
+ 'agent-uuid-456': {
801
+ type: 'Agent',
802
+ pType: 'dn',
803
+ dn: '5551234567',
804
+ id: 'agent-uuid-456',
805
+ },
806
+ },
807
+ };
808
+
809
+ const result = Utils.calculateDestAgentId(interaction, currentAgentId);
810
+ expect(result).toBe('agent-uuid-456');
811
+ });
812
+
813
+ it('should return participant id for regular agent when not CBT', () => {
814
+ const consultedAgentId = 'agent-456';
815
+ const interaction: any = {
816
+ media: {
817
+ consult: {
818
+ mType: 'consult',
819
+ participants: [currentAgentId, consultedAgentId],
820
+ },
821
+ },
822
+ participants: {
823
+ [consultedAgentId]: {
824
+ type: 'Agent',
825
+ id: consultedAgentId,
826
+ },
827
+ },
828
+ };
829
+
830
+ const result = Utils.calculateDestAgentId(interaction, currentAgentId);
831
+ expect(result).toBe(consultedAgentId);
832
+ });
833
+
834
+ it('should return epId for EpDn type participants', () => {
835
+ const consultedAgentId = 'epdn-456';
836
+ const interaction: any = {
837
+ media: {
838
+ consult: {
839
+ mType: 'consult',
840
+ participants: [currentAgentId, consultedAgentId],
841
+ },
842
+ },
843
+ participants: {
844
+ [consultedAgentId]: {
845
+ type: 'EpDn',
846
+ id: consultedAgentId,
847
+ epId: 'entry-point-id-789',
848
+ },
849
+ },
850
+ };
851
+
852
+ const result = Utils.calculateDestAgentId(interaction, currentAgentId);
853
+ expect(result).toBe('entry-point-id-789');
854
+ });
855
+
856
+ it('should return undefined when no consulting agent found', () => {
857
+ const interaction: any = {
858
+ media: {
859
+ mainCall: {
860
+ mType: 'mainCall',
861
+ participants: [currentAgentId],
862
+ },
863
+ },
864
+ participants: {},
865
+ };
866
+
867
+ const result = Utils.calculateDestAgentId(interaction, currentAgentId);
868
+ expect(result).toBe('');
869
+ });
870
+
871
+ it('should handle CBT scenario when phone number is not a direct participant key', () => {
872
+ const interaction: any = {
873
+ media: {
874
+ consult: {
875
+ mType: 'consult',
876
+ participants: [currentAgentId, '5551234567'], // Phone number in media
877
+ },
878
+ },
879
+ participants: {
880
+ // Note: '5551234567' is NOT a key - this is CBT
881
+ 'agent-uuid-cbt': {
882
+ type: 'Agent',
883
+ pType: 'dn',
884
+ dn: '5551234567', // Found by matching DN
885
+ id: 'agent-uuid-cbt',
886
+ },
887
+ },
888
+ };
889
+
890
+ const result = Utils.calculateDestAgentId(interaction, currentAgentId);
891
+ expect(result).toBe('agent-uuid-cbt'); // Returns the CBT agent
892
+ });
893
+ });
894
+
895
+ describe('isValidDialNumber', () => {
896
+ const anyFormatEntry = {
897
+ name: 'Any Format',
898
+ prefix: '',
899
+ regex: '([0-9a-zA-Z]+[-._])*[0-9a-zA-Z]+',
900
+ strippedChars: '( )-',
901
+ };
902
+
903
+ const usOnlyEntry = {
904
+ name: 'US',
905
+ prefix: '1',
906
+ regex: '1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}',
907
+ strippedChars: '( )-',
908
+ };
909
+
910
+ describe('with multiple dial plan entries (Any Format + US)', () => {
911
+ const dialPlanEntries = [anyFormatEntry, usOnlyEntry];
912
+
913
+ it('should return true for a valid US phone number', () => {
914
+ const result = Utils.isValidDialNumber('12223334567', dialPlanEntries);
915
+ expect(result).toBe(true);
916
+ });
917
+
918
+ it('should return true for a UK phone number', () => {
919
+ const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries);
920
+ expect(result).toBe(true);
921
+ });
922
+
923
+ it('should return true for a European number', () => {
924
+ const result = Utils.isValidDialNumber('6955577166', dialPlanEntries);
925
+ expect(result).toBe(true);
926
+ });
927
+ });
928
+
929
+ describe('with US-only dial plan entry', () => {
930
+ const dialPlanEntries = [usOnlyEntry];
931
+
932
+ it('should return true for a valid US phone number', () => {
933
+ const result = Utils.isValidDialNumber('12223334567', dialPlanEntries);
934
+ expect(result).toBe(true);
935
+ });
936
+
937
+ it('should return false for a UK phone number', () => {
938
+ const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries);
939
+ expect(result).toBe(false);
940
+ });
941
+
942
+ it('should return false for an invalid US number format', () => {
943
+ const result = Utils.isValidDialNumber('1234567890', dialPlanEntries);
944
+ expect(result).toBe(false);
945
+ });
946
+ });
947
+
948
+ describe('with empty dial plan entries (defers to server)', () => {
949
+ it('should return true for any dial number', () => {
950
+ expect(Utils.isValidDialNumber('12223334567', [])).toBe(true);
951
+ });
952
+
953
+ it('should return true for a UK phone number', () => {
954
+ expect(Utils.isValidDialNumber('+442030484377', [])).toBe(true);
955
+ });
956
+
957
+ it('should return true for a European number', () => {
958
+ expect(Utils.isValidDialNumber('6955577166', [])).toBe(true);
959
+ });
960
+ });
961
+
962
+ describe('strippedChars handling', () => {
963
+ it('should strip characters before regex matching', () => {
964
+ const strictEntry = {
965
+ name: 'Digits Only',
966
+ prefix: '',
967
+ regex: '^[0-9]{10,15}$',
968
+ strippedChars: '( )-+',
969
+ };
970
+ const result = Utils.isValidDialNumber('+44 (203) 048-4377', [strictEntry]);
971
+ expect(result).toBe(true);
972
+ });
973
+
974
+ it('should handle entries with no strippedChars', () => {
975
+ const noStripEntry = {
976
+ name: 'No Strip',
977
+ prefix: '',
978
+ regex: '^[0-9]+$',
979
+ strippedChars: '',
980
+ };
981
+ expect(Utils.isValidDialNumber('12345', [noStripEntry])).toBe(true);
982
+ expect(Utils.isValidDialNumber('+12345', [noStripEntry])).toBe(false);
983
+ });
984
+ });
985
+
986
+ describe('empty or undefined dial number', () => {
987
+ it('should return false and log warning for undefined dial number', () => {
988
+ const result = Utils.isValidDialNumber(undefined as any, [anyFormatEntry]);
989
+ expect(result).toBe(false);
990
+ expect(LoggerProxy.warn).toHaveBeenCalledWith(
991
+ 'Dial number is empty or undefined.',
992
+ expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
993
+ );
994
+ });
995
+
996
+ it('should return false and log warning for empty string dial number', () => {
997
+ const result = Utils.isValidDialNumber('', [anyFormatEntry]);
998
+ expect(result).toBe(false);
999
+ expect(LoggerProxy.warn).toHaveBeenCalledWith(
1000
+ 'Dial number is empty or undefined.',
1001
+ expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
1002
+ );
1003
+ });
1004
+ });
1005
+
1006
+ describe('invalid regex handling', () => {
1007
+ it('should return false and log warning for invalid regex pattern', () => {
1008
+ const badEntry = {
1009
+ name: 'Bad Regex',
1010
+ prefix: '',
1011
+ regex: '[invalid(',
1012
+ strippedChars: '',
1013
+ };
1014
+ const result = Utils.isValidDialNumber('12345', [badEntry]);
1015
+ expect(result).toBe(false);
1016
+ expect(LoggerProxy.warn).toHaveBeenCalledWith(
1017
+ expect.stringContaining('Failed to validate dial number against entry "Bad Regex"'),
1018
+ expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
1019
+ );
1020
+ });
1021
+ });
1022
+ });
1023
+
1024
+ describe('stripDialPlanChars', () => {
1025
+ it('should remove specified characters from input', () => {
1026
+ expect(Utils.stripDialPlanChars('+44 (203) 048-4377', '( )-+')).toBe('442030484377');
1027
+ });
1028
+
1029
+ it('should return input unchanged when strippedChars is empty', () => {
1030
+ expect(Utils.stripDialPlanChars('+442030484377', '')).toBe('+442030484377');
1031
+ });
1032
+
1033
+ it('should return input unchanged when strippedChars is null/undefined', () => {
1034
+ expect(Utils.stripDialPlanChars('12345', null as any)).toBe('12345');
1035
+ expect(Utils.stripDialPlanChars('12345', undefined as any)).toBe('12345');
1036
+ });
1037
+ });
1038
+
560
1039
  });