@webex/contact-center 3.12.0-next.147 → 3.12.0-next.149

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 (82) hide show
  1. package/.sdd/manifest.json +4 -2
  2. package/ai-docs/CONTRACTS.md +2 -0
  3. package/dist/cc.js +3 -1
  4. package/dist/cc.js.map +1 -1
  5. package/dist/constants.js +10 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/index.js +9 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/metrics/constants.js +9 -0
  10. package/dist/metrics/constants.js.map +1 -1
  11. package/dist/services/ApiAiAssistant.js +87 -10
  12. package/dist/services/ApiAiAssistant.js.map +1 -1
  13. package/dist/services/config/types.js +10 -4
  14. package/dist/services/config/types.js.map +1 -1
  15. package/dist/services/core/types.js.map +1 -1
  16. package/dist/services/task/Task.js +202 -1
  17. package/dist/services/task/Task.js.map +1 -1
  18. package/dist/services/task/TaskFactory.js +4 -4
  19. package/dist/services/task/TaskFactory.js.map +1 -1
  20. package/dist/services/task/TaskManager.js +113 -26
  21. package/dist/services/task/TaskManager.js.map +1 -1
  22. package/dist/services/task/TaskUtils.js +11 -1
  23. package/dist/services/task/TaskUtils.js.map +1 -1
  24. package/dist/services/task/constants.js +11 -2
  25. package/dist/services/task/constants.js.map +1 -1
  26. package/dist/services/task/digital/Digital.js +2 -2
  27. package/dist/services/task/digital/Digital.js.map +1 -1
  28. package/dist/services/task/types.js +31 -0
  29. package/dist/services/task/types.js.map +1 -1
  30. package/dist/services/task/voice/Voice.js +2 -2
  31. package/dist/services/task/voice/Voice.js.map +1 -1
  32. package/dist/services/task/voice/WebRTC.js +2 -2
  33. package/dist/services/task/voice/WebRTC.js.map +1 -1
  34. package/dist/types/constants.d.ts +9 -0
  35. package/dist/types/index.d.ts +4 -5
  36. package/dist/types/metrics/constants.d.ts +8 -0
  37. package/dist/types/services/ApiAiAssistant.d.ts +4 -2
  38. package/dist/types/services/config/types.d.ts +17 -7
  39. package/dist/types/services/task/Task.d.ts +15 -4
  40. package/dist/types/services/task/TaskFactory.d.ts +3 -2
  41. package/dist/types/services/task/TaskUtils.d.ts +6 -0
  42. package/dist/types/services/task/constants.d.ts +9 -0
  43. package/dist/types/services/task/digital/Digital.d.ts +3 -2
  44. package/dist/types/services/task/types.d.ts +109 -1
  45. package/dist/types/services/task/voice/Voice.d.ts +3 -2
  46. package/dist/types/services/task/voice/WebRTC.d.ts +3 -2
  47. package/dist/types/types.d.ts +10 -4
  48. package/dist/types.js +8 -4
  49. package/dist/types.js.map +1 -1
  50. package/dist/webex.js +1 -1
  51. package/package.json +3 -3
  52. package/src/cc.ts +4 -0
  53. package/src/constants.ts +12 -0
  54. package/src/index.ts +10 -2
  55. package/src/metrics/ai-docs/metrics-spec.md +12 -4
  56. package/src/metrics/constants.ts +10 -0
  57. package/src/services/ApiAiAssistant.ts +120 -12
  58. package/src/services/config/types.ts +10 -4
  59. package/src/services/core/types.ts +31 -1
  60. package/src/services/task/Task.ts +308 -5
  61. package/src/services/task/TaskFactory.ts +26 -5
  62. package/src/services/task/TaskManager.ts +148 -29
  63. package/src/services/task/TaskUtils.ts +13 -0
  64. package/src/services/task/ai-docs/task-spec.md +30 -5
  65. package/src/services/task/constants.ts +10 -0
  66. package/src/services/task/digital/Digital.ts +8 -3
  67. package/src/services/task/types.ts +139 -0
  68. package/src/services/task/voice/Voice.ts +8 -3
  69. package/src/services/task/voice/WebRTC.ts +6 -3
  70. package/src/types.ts +10 -4
  71. package/test/unit/fixtures/aiSummaryTestUtils.ts +40 -0
  72. package/test/unit/spec/cc.ts +1314 -32
  73. package/test/unit/spec/index.ts +881 -0
  74. package/test/unit/spec/services/ApiAiAssistant.ts +122 -6
  75. package/test/unit/spec/services/core/WebexRequest.ts +5 -6
  76. package/test/unit/spec/services/task/Task.ts +1988 -10
  77. package/test/unit/spec/services/task/TaskFactory.ts +17 -0
  78. package/test/unit/spec/services/task/TaskManager.ts +2327 -180
  79. package/test/unit/spec/services/task/TaskUtils.ts +72 -0
  80. package/test/unit/spec/services/task/voice/Voice.ts +353 -18
  81. package/umd/contact-center.min.js +2 -2
  82. package/umd/contact-center.min.js.map +1 -1
@@ -13,10 +13,12 @@ import {
13
13
  WebSocketMessage,
14
14
  TaskEventActions,
15
15
  EventContext,
16
+ AISummaryFeatureEnablement,
17
+ AISummary,
16
18
  } from './types';
17
19
  import {TASK_MANAGER_FILE} from '../../constants';
18
20
  import {METHODS, TRANSCRIPT_EVENT_MAP} from './constants';
19
- import {CC_EVENTS, WrapupData} from '../config/types';
21
+ import {CC_EVENTS, CC_TASK_EVENTS, WrapupData} from '../config/types';
20
22
  import {ConfigFlags, LoginOption, AIAssistantEventType, AIAssistantEventName} from '../../types';
21
23
  import LoggerProxy from '../../logger-proxy';
22
24
  import {
@@ -77,9 +79,15 @@ export default class TaskManager extends EventEmitter {
77
79
  private configFlags?: ConfigFlags;
78
80
  private wrapupData: WrapupData;
79
81
  private agentId: string;
82
+ private agentName: string;
80
83
  private webRtcEnabled: boolean;
81
84
  private answerCallOnWebexService?: AnswerCallOnWebexService;
82
85
  private apiAIAssistant?: ApiAIAssistant;
86
+ private receivingSummaryBuffer = new Map<string, AISummary>();
87
+
88
+ // RTD feature frames can arrive before AQM creates the task.
89
+ private pendingFeatureEnablement = new Map<string, AISummaryFeatureEnablement>();
90
+
83
91
  /**
84
92
  * @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
85
93
  * @param webCallingService - Webrtc Service Layer
@@ -100,7 +108,6 @@ export default class TaskManager extends EventEmitter {
100
108
  this.rtdWebSocketManager = rtdWebSocketManager;
101
109
  this.taskCollection = {};
102
110
  this.webRtcEnabled = false;
103
-
104
111
  this.registerTaskListeners();
105
112
  this.registerIncomingCallEvent();
106
113
  }
@@ -109,6 +116,56 @@ export default class TaskManager extends EventEmitter {
109
116
  try {
110
117
  const payload = JSON.parse(event);
111
118
 
119
+ switch (payload?.type) {
120
+ case CC_TASK_EVENTS.FEATURE_ENABLEMENT: {
121
+ const enablement: AISummaryFeatureEnablement = payload?.data?.data;
122
+ if (!enablement?.interactionId) return;
123
+
124
+ const task = this.taskCollection[enablement.interactionId];
125
+ if (task) {
126
+ this.pendingFeatureEnablement.delete(enablement.interactionId);
127
+ task.setFeatureEnablement?.(enablement);
128
+ task.emit(TASK_EVENTS.TASK_FEATURE_ENABLEMENT, enablement);
129
+ } else {
130
+ this.pendingFeatureEnablement.set(enablement.interactionId, enablement);
131
+ }
132
+
133
+ return;
134
+ }
135
+ case CC_TASK_EVENTS.POST_CALL_SUMMARY:
136
+ case CC_TASK_EVENTS.MID_CALL_SUMMARY: {
137
+ if (!payload?.data?.data?.conversationId) return;
138
+
139
+ this.apiAIAssistant?.resolveFromRtdEvent(
140
+ payload.type,
141
+ payload.data.data.conversationId,
142
+ payload.data.data
143
+ );
144
+
145
+ return;
146
+ }
147
+ case CC_TASK_EVENTS.MID_CALL_SUMMARY_RESPONSE_SUBSEQUENT_AGENT: {
148
+ if (!payload?.data?.data?.conversationId) return;
149
+
150
+ const summary: AISummary = payload.data.data;
151
+ const {conversationId} = summary;
152
+ const task = this.taskCollection[conversationId];
153
+ const taskState = task?.state?.value;
154
+
155
+ // Emit only once the task is answered; until then it waits for the answer event.
156
+ if (task && taskState !== TaskState.IDLE && taskState !== TaskState.OFFERED) {
157
+ this.receivingSummaryBuffer.delete(conversationId);
158
+ task.emit(TASK_EVENTS.TASK_MID_CALL_SUMMARY_RECEIVED, summary);
159
+ } else {
160
+ this.receivingSummaryBuffer.set(conversationId, summary);
161
+ }
162
+
163
+ return;
164
+ }
165
+ default:
166
+ break;
167
+ }
168
+
112
169
  const interactionId = payload?.data?.data?.conversationId;
113
170
  if (!interactionId) return;
114
171
 
@@ -128,6 +185,8 @@ export default class TaskManager extends EventEmitter {
128
185
  case CC_EVENTS.SUGGESTED_RESPONSE:
129
186
  task.emit(payload.type, payload.data);
130
187
  break;
188
+ default:
189
+ break;
131
190
  }
132
191
  } catch (error) {
133
192
  LoggerProxy.error('Failed to parse RTD WebSocket message', {
@@ -145,6 +204,13 @@ export default class TaskManager extends EventEmitter {
145
204
  this.configFlags = configFlags;
146
205
  }
147
206
 
207
+ public clearAISummaryState(): void {
208
+ this.apiAIAssistant?.clearAllRtdRequests();
209
+ Object.values(this.taskCollection).forEach((task) => task.clearFeatureEnablement?.());
210
+ this.receivingSummaryBuffer.clear();
211
+ this.pendingFeatureEnablement.clear();
212
+ }
213
+
148
214
  /**
149
215
  * Set wrapup configuration data
150
216
  */
@@ -159,6 +225,10 @@ export default class TaskManager extends EventEmitter {
159
225
  this.agentId = agentId;
160
226
  }
161
227
 
228
+ public setAgentName(agentName: string) {
229
+ this.agentName = agentName;
230
+ }
231
+
162
232
  /**
163
233
  * Gets the current agent ID
164
234
  * @returns {string} The agent ID set for this task manager instance
@@ -723,6 +793,15 @@ export default class TaskManager extends EventEmitter {
723
793
  task.sendStateMachineEvent(stateMachineEvent);
724
794
  }
725
795
 
796
+ // A receiving summary held while the task rang is emitted right after the answer
797
+ // event above (AgentContactAssigned / AgentConsulting) has been emitted.
798
+ const bufferedSummary = this.receivingSummaryBuffer.get(task.data.interactionId);
799
+ const taskState = task.state?.value;
800
+ if (bufferedSummary && taskState !== TaskState.IDLE && taskState !== TaskState.OFFERED) {
801
+ this.receivingSummaryBuffer.delete(task.data.interactionId);
802
+ task.emit(TASK_EVENTS.TASK_MID_CALL_SUMMARY_RECEIVED, bufferedSummary);
803
+ }
804
+
726
805
  // Emit TASK_POST_CALL_ACTIVITY for ParticipantPostCallActivity events so
727
806
  // consumers (Widgets) can detect the interaction state change to post_call.
728
807
  if (eventContext.eventType === CC_EVENTS.PARTICIPANT_POST_CALL_ACTIVITY) {
@@ -789,6 +868,7 @@ export default class TaskManager extends EventEmitter {
789
868
  // Re-key the task under the new interaction ID and remove the old entry
790
869
  delete this.taskCollection[reservationInteractionId];
791
870
  this.taskCollection[interactionId] = task;
871
+ this.pendingFeatureEnablement.delete(reservationInteractionId);
792
872
  }
793
873
  }
794
874
 
@@ -1161,7 +1241,9 @@ export default class TaskManager extends EventEmitter {
1161
1241
  this.configFlags,
1162
1242
  this.wrapupData,
1163
1243
  this.agentId,
1164
- this.answerCallOnWebexService
1244
+ this.agentName,
1245
+ this.answerCallOnWebexService,
1246
+ this.apiAIAssistant
1165
1247
  );
1166
1248
 
1167
1249
  this.taskCollection[stableInteractionId] = task;
@@ -1272,7 +1354,9 @@ export default class TaskManager extends EventEmitter {
1272
1354
  this.configFlags,
1273
1355
  this.wrapupData,
1274
1356
  this.agentId,
1275
- this.answerCallOnWebexService
1357
+ this.agentName,
1358
+ this.answerCallOnWebexService,
1359
+ this.apiAIAssistant
1276
1360
  );
1277
1361
  this.setupTaskListeners(task);
1278
1362
  this.taskCollection[payload.interactionId] = task;
@@ -1311,7 +1395,9 @@ export default class TaskManager extends EventEmitter {
1311
1395
  this.configFlags,
1312
1396
  this.wrapupData,
1313
1397
  this.agentId,
1314
- this.answerCallOnWebexService
1398
+ this.agentName,
1399
+ this.answerCallOnWebexService,
1400
+ this.apiAIAssistant
1315
1401
  );
1316
1402
 
1317
1403
  this.setupTaskListeners(task);
@@ -1352,7 +1438,9 @@ export default class TaskManager extends EventEmitter {
1352
1438
  this.configFlags,
1353
1439
  this.wrapupData,
1354
1440
  this.agentId,
1355
- this.answerCallOnWebexService
1441
+ this.agentName,
1442
+ this.answerCallOnWebexService,
1443
+ this.apiAIAssistant
1356
1444
  );
1357
1445
  this.setupTaskListeners(task);
1358
1446
  this.taskCollection[payload.interactionId] = task;
@@ -1389,6 +1477,10 @@ export default class TaskManager extends EventEmitter {
1389
1477
  }
1390
1478
  });
1391
1479
  this.taskCollection[taskData.interactionId] = task;
1480
+ const enablement = this.pendingFeatureEnablement.get(task.data.interactionId);
1481
+ if (enablement) {
1482
+ task.setFeatureEnablement?.(enablement);
1483
+ }
1392
1484
 
1393
1485
  return task;
1394
1486
  }
@@ -1398,31 +1490,46 @@ export default class TaskManager extends EventEmitter {
1398
1490
  * This replaces the previous callback injection pattern
1399
1491
  */
1400
1492
  private setupTaskListeners(task: ITask): void {
1401
- // Listen for TASK_INCOMING and re-emit so webex.cc can notify consumers
1402
- task.on(TASK_EVENTS.TASK_INCOMING, (t: ITask) => {
1403
- LoggerProxy.log(`Task incoming event received`, {
1404
- module: TASK_MANAGER_FILE,
1405
- method: METHODS.REGISTER_TASK_LISTENERS,
1406
- interactionId: t.data?.interactionId,
1407
- });
1408
-
1409
- this.emit(TASK_EVENTS.TASK_INCOMING, t);
1410
- });
1493
+ // Apply flags before publication; retain the frame until consumers can attach listeners.
1494
+ const enablement = this.pendingFeatureEnablement.get(task.data.interactionId);
1495
+ if (enablement) {
1496
+ task.setFeatureEnablement?.(enablement);
1497
+ }
1411
1498
 
1412
- task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, (t: ITask) => {
1413
- LoggerProxy.log(`Campaign preview reservation event received`, {
1414
- module: TASK_MANAGER_FILE,
1415
- method: METHODS.REGISTER_TASK_LISTENERS,
1416
- interactionId: t.data?.interactionId,
1417
- });
1499
+ [
1500
+ TASK_EVENTS.TASK_INCOMING,
1501
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
1502
+ TASK_EVENTS.TASK_HYDRATE,
1503
+ TASK_EVENTS.TASK_ASSIGNED,
1504
+ ].forEach((eventName) => {
1505
+ task.on(eventName, (t: ITask) => {
1506
+ if (
1507
+ eventName === TASK_EVENTS.TASK_INCOMING ||
1508
+ eventName === TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION
1509
+ ) {
1510
+ LoggerProxy.log(
1511
+ eventName === TASK_EVENTS.TASK_INCOMING
1512
+ ? 'Task incoming event received'
1513
+ : 'Campaign preview reservation event received',
1514
+ {
1515
+ module: TASK_MANAGER_FILE,
1516
+ method: METHODS.REGISTER_TASK_LISTENERS,
1517
+ interactionId: t.data?.interactionId,
1518
+ }
1519
+ );
1520
+ }
1418
1521
 
1419
- this.emit(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, t);
1420
- });
1522
+ // Assignment is already emitted on the task; the other events publish it to consumers.
1523
+ if (eventName !== TASK_EVENTS.TASK_ASSIGNED) {
1524
+ this.emit(eventName, t);
1525
+ }
1421
1526
 
1422
- // Listen for TASK_HYDRATE on the task and re-emit on TaskManager
1423
- task.on(TASK_EVENTS.TASK_HYDRATE, (t: ITask) => {
1424
- // Task data is already updated by the task itself before emitting
1425
- this.emit(TASK_EVENTS.TASK_HYDRATE, t);
1527
+ const pendingEnablement = this.pendingFeatureEnablement.get(task.data.interactionId);
1528
+ if (pendingEnablement) {
1529
+ this.pendingFeatureEnablement.delete(task.data.interactionId);
1530
+ task.emit(TASK_EVENTS.TASK_FEATURE_ENABLEMENT, pendingEnablement);
1531
+ }
1532
+ });
1426
1533
  });
1427
1534
 
1428
1535
  task.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, (t: ITask) => {
@@ -1444,11 +1551,16 @@ export default class TaskManager extends EventEmitter {
1444
1551
  }
1445
1552
 
1446
1553
  private removeTaskFromCollection(task: ITask) {
1554
+ task.clearFeatureEnablement?.();
1447
1555
  task.disposeWxAppOfferObservability?.();
1448
1556
  if (typeof task.cancelAutoWrapupTimer === 'function') {
1449
1557
  task.cancelAutoWrapupTimer();
1450
1558
  }
1451
1559
  if (task?.data?.interactionId) {
1560
+ this.pendingFeatureEnablement.delete(task.data.interactionId);
1561
+ // An offer that ends unanswered (RONA, declined, consult cancelled) is removed here,
1562
+ // so its held receiving summary is dropped instead of being emitted.
1563
+ this.receivingSummaryBuffer.delete(task.data.interactionId);
1452
1564
  Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
1453
1565
  if (candidate === task) {
1454
1566
  delete this.taskCollection[taskId];
@@ -1507,7 +1619,9 @@ export default class TaskManager extends EventEmitter {
1507
1619
  this.configFlags,
1508
1620
  this.wrapupData,
1509
1621
  this.agentId,
1510
- this.answerCallOnWebexService
1622
+ this.agentName,
1623
+ this.answerCallOnWebexService,
1624
+ this.apiAIAssistant
1511
1625
  );
1512
1626
  this.taskCollection[payload.interactionId] = task;
1513
1627
 
@@ -1527,6 +1641,11 @@ export default class TaskManager extends EventEmitter {
1527
1641
 
1528
1642
  if (task) {
1529
1643
  this.emit(TASK_EVENTS.TASK_MERGED, task);
1644
+ const enablement = this.pendingFeatureEnablement.get(task.data.interactionId);
1645
+ if (enablement) {
1646
+ this.pendingFeatureEnablement.delete(task.data.interactionId);
1647
+ task.emit(TASK_EVENTS.TASK_FEATURE_ENABLEMENT, enablement);
1648
+ }
1530
1649
  }
1531
1650
 
1532
1651
  return {task};
@@ -12,6 +12,19 @@ import {TaskContext} from './state-machine/types';
12
12
  import {CC_EVENTS} from '../config/types';
13
13
  import {OUTBOUND_TYPE, OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE} from '../../constants';
14
14
 
15
+ type AISummaryError = Error & {data?: Record<string, unknown>};
16
+
17
+ export const isNonEmptyString = (value: unknown): value is string =>
18
+ typeof value === 'string' && value.length > 0;
19
+
20
+ export const createSummaryError = (errorCode: string): AISummaryError => {
21
+ const error = new Error(errorCode) as AISummaryError;
22
+
23
+ error.data = {errorCode};
24
+
25
+ return error;
26
+ };
27
+
15
28
  const CAMPAIGN_PREVIEW_OUTBOUND_TYPES = ['STANDARD_PREVIEW_CAMPAIGN', 'DIRECT_PREVIEW_CAMPAIGN'];
16
29
  const CAMPAIGN_PREVIEW_CAMPAIGN_TYPES = ['preview_standard', 'preview_direct'];
17
30
 
@@ -11,8 +11,8 @@
11
11
  | Doc kind | Module spec |
12
12
  | Coverage score | Partial (manifest-authoritative); 15/15 required document fields present |
13
13
  | Generated from | `module-spec` @ SDLC template library `0.2.1` |
14
- | generated_by / approved_by / updated_at | Codex generator / developer-approved follow-up review remediation / 2026-07-21 |
15
- | Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); 1 existing test-coverage gap; coverage remains Partial |
14
+ | generated_by / approved_by / updated_at | Codex generator / developer-approved follow-up review remediation / 2026-10-06 |
15
+ | Validation status | AI summary methods, delivery, and cleanup updates pending independent review (2026-10-06); prior follow-up validation passed (independent Claude fallback, 2026-07-21); 1 existing test-coverage gap; coverage remains Partial |
16
16
 
17
17
  ## Evidence Rules
18
18
  Every requirement cites stable source and test file paths. Code/tests are the behavioral referee; routed source text supplies explicit intent and rationale. Missing or contradictory evidence blocks promotion.
@@ -143,6 +143,7 @@ services/task/
143
143
  | `task.preview-campaign` | SDK/AQM API | `acceptPreviewContact`, `skipPreviewContact`, `removePreviewContact`, and `PreviewContactPayload`. | Accept, skip, or remove a reserved campaign preview contact; each method returns `Promise<TaskResponse>`. | Additive semver-public methods; removals or signature changes are breaking. | `src/cc.ts`, `src/services/task/dialer.ts`, `src/services/task/types.ts` | `../../../../ai-docs/CONTRACTS.md` |
144
144
  | `task.consult-transfer-controls` | SDK task controls | `TaskUIControls.consultTransferDestinations` with ordered `consult` and `transfer` arrays. | Surface default destination availability on every Task without an extra policy method. | Additive semver-public field/types; array order is meaningful and the first item is the default. | `src/services/task/types.ts`, `src/services/task/state-machine/uiControlsComputer.ts` | `../../../../ai-docs/CONTRACTS.md` |
145
145
  | `task.conference-participant-drop` | SDK/AQM API | `task.dropConferenceParticipant(payload: DropConferenceParticipantPayload): Promise<TaskResponse>`. | Remove a supported target from a voice conference after correlated routing completion. | Additive semver-public method and payload; removals or signature changes are breaking. | `src/services/task/voice/Voice.ts`, `src/services/task/contact.ts`, `src/services/task/types.ts` | `../../../../ai-docs/CONTRACTS.md` |
146
+ | `task.ai-summary` | SDK/RTD API | Post-call and mid-call summary request/response methods, feature enablement, and receiving-agent summary events. | Provide task-scoped generated summaries without exposing RTD correlation to SDK consumers. | Additive semver-public methods, types, and events; removals or signature changes are breaking. | `src/services/task/Task.ts`, `src/services/task/TaskManager.ts`, `src/services/task/types.ts`, `src/services/ApiAiAssistant.ts` | `../../../../ai-docs/CONTRACTS.md` |
146
147
 
147
148
  Compatibility notes:
148
149
  - Do not remove or reinterpret exported symbols/events without a documented consumer migration.
@@ -151,6 +152,8 @@ Compatibility notes:
151
152
 
152
153
  - `TaskData`, `TaskId`, `TaskResponse`, `TaskUIControls` (`types.ts`)
153
154
 
155
+ - `AISummary`, `AISummaryResponse`, `AISummaryAction`, `AISummaryFeatureEnablement`, and `AISummaryCapabilities` (`types.ts`)
156
+
154
157
  - `PreviewContactPayload` (`types.ts`) with `interactionId` and campaign-name `campaignId`
155
158
 
156
159
  - `DropConferenceParticipantPayload` (`types.ts`) with a runtime-validated non-empty `participantId`
@@ -191,6 +194,8 @@ Compatibility notes:
191
194
  | `task:switchCall` | Switched between consult and main call |
192
195
  | `task:outdialFailed` | Outdial operation failed |
193
196
  | `task:ui-controls-updated` | UI controls changed due to state transition |
197
+ | `task:midCallSummaryReceived` | Mid-call summary delivered to a receiving agent |
198
+ | `task:featureEnablement` | Per-task AI summary enablement flags received |
194
199
  | `task:cleanup` | Internal cleanup signal emitted by state machine |
195
200
 
196
201
  > Full list is defined in `TASK_EVENTS` (`types.ts`).
@@ -200,6 +205,23 @@ Compatibility notes:
200
205
  | `REAL_TIME_TRANSCRIPTION` | A realtime transcript payload is received for the task interaction |
201
206
  | `SUGGESTED_RESPONSE` | A final AI Assistant suggestion payload is received for the task interaction |
202
207
 
208
+ ### AI summary APIs
209
+
210
+ - `requestPostCallSummary(): Promise<AISummary>` requests a post-call summary.
211
+ - `sendPostCallSummaryResponse(response: AISummaryResponse): Promise<void>` reports the agent's post-call summary outcome.
212
+ - `requestMidCallSummary(action: AISummaryAction): Promise<AISummary>` requests a consult or transfer summary.
213
+ - `sendMidCallSummaryResponse(response: AISummaryResponse, action: AISummaryAction): Promise<void>` reports the corresponding mid-call outcome.
214
+
215
+ Summary requests require both the organization-level generated-summary flag and the task-level feature enablement flag. The dedicated request methods read the current organization flags directly from the existing `ApiAIAssistant.aiFeature` field, populated by ContactCenter from the agent configuration. They check `generatedSummaries.wrapUpSummariesEnabled` or `generatedSummaries.consultTransferSummariesEnabled`; missing flags disable the corresponding request. No separate feature-flags argument or task-local organization configuration is needed. A request resolves only after the event POST succeeds and the matching RTD payload arrives; otherwise it rejects with the applicable disabled, transport, or timeout error. Receiving-agent summaries are delivered separately through `task:midCallSummaryReceived`. Summary content is application data and must not be written to logs or metrics.
216
+
217
+ Task-level feature flags survive `updateTaskData()`, including interaction ID changes during consults or task re-keying. Flags remain available throughout wrapup for post-call summary requests, and TaskManager clears them when the task is removed after wrapup completion or another terminal lifecycle event. Session cleanup also clears them.
218
+
219
+ Each dedicated Task summary method owns its event selection, payload construction, metrics, and error handling. The request methods call `ApiAIAssistant.requestAndWaitForRtd()`. Both response methods read `interactionId` from the current task data and use `interaction.mainInteractionId` as `conversationId`, falling back to `interactionId`. They call `ApiAIAssistant.sendEvent()` directly without caching request IDs on the task.
220
+
221
+ TaskManager reads the receiving task directly from `taskCollection[summary.conversationId]`; the RTD conversation ID is the task's collection key (`task.data.interactionId`). It emits `task:midCallSummaryReceived` immediately when that task exists. Otherwise it stores the latest summary for that ID in an in-memory map without an expiry timer, so an RTD frame can wait for task creation. The common AQM event-processing path replays the stored payload after task creation, updates that register the task under that ID, or merge publication. Replay after creation or merge follows the public task event so consumers can attach listeners. Delivery removes the stored summary, and session cleanup clears all pending summaries.
222
+
223
+ `clearAISummaryState()` cancels pending summary requests, clears task feature enablement, and empties both pending maps. ContactCenter's `deregister()` removes the RTD message listener synchronously before asynchronous teardown. RTD listener registration and removal control inbound delivery; `setConfigFlags()` only supplies task configuration.
224
+
203
225
  Initiate outbound call.
204
226
 
205
227
  **Parameters**:
@@ -366,7 +388,7 @@ await task.dropConferenceParticipant({participantId});
366
388
 
367
389
  ### Complete TASK_EVENTS inventory
368
390
 
369
- The public `TASK_EVENTS` enum contains 49 members; every member is listed below from `src/services/task/types.ts`.
391
+ The public `TASK_EVENTS` enum contains 52 members; every member is listed below from `src/services/task/types.ts`.
370
392
 
371
393
  | Constant | Event string |
372
394
  |---|---|
@@ -380,6 +402,7 @@ The public `TASK_EVENTS` enum contains 49 members; every member is listed below
380
402
  | `TASK_CONSULT_QUEUE_CANCELLED` | `task:consultQueueCancelled` |
381
403
  | `TASK_CONSULT_QUEUE_FAILED` | `task:consultQueueFailed` |
382
404
  | `TASK_UI_CONTROLS_UPDATED` | `task:ui-controls-updated` |
405
+ | `TASK_WXAPP_MUTE_STATE_UPDATED` | `task:wxapp-mute-state-updated` |
383
406
  | `TASK_CONSULT_ACCEPTED` | `task:consultAccepted` |
384
407
  | `TASK_CONSULTING` | `task:consulting` |
385
408
  | `TASK_CONSULT_CREATED` | `task:consultCreated` |
@@ -419,6 +442,8 @@ The public `TASK_EVENTS` enum contains 49 members; every member is listed below
419
442
  | `TASK_CAMPAIGN_PREVIEW_SKIP_FAILED` | `task:campaignPreviewSkipFailed` |
420
443
  | `TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED` | `task:campaignPreviewRemoveFailed` |
421
444
  | `TASK_CAMPAIGN_CONTACT_UPDATED` | `task:campaignContactUpdated` |
445
+ | `TASK_MID_CALL_SUMMARY_RECEIVED` | `task:midCallSummaryReceived` |
446
+ | `TASK_FEATURE_ENABLEMENT` | `task:featureEnablement` |
422
447
 
423
448
  ## Requires (dependencies)
424
449
  - Services contact/dialer AQM factories
@@ -1153,7 +1178,7 @@ sequenceDiagram
1153
1178
 
1154
1179
  **API**
1155
1180
 
1156
- - `createTask(contact, webCallingService, data, configFlags, wrapupData?, agentId?): Task`
1181
+ - `createTask(contact, webCallingService, data, configFlags, wrapupData?, agentId?, agentName?): Task`
1157
1182
 
1158
1183
  **Behavior**
1159
1184
 
@@ -1229,7 +1254,7 @@ classDiagram
1229
1254
  }
1230
1255
 
1231
1256
  class TaskFactory {
1232
- + createTask(contact, webCallingService, data, configFlags, wrapupData, agentId) Task
1257
+ + createTask(contact, webCallingService, data, configFlags, wrapupData, agentId, agentName) Task
1233
1258
  }
1234
1259
 
1235
1260
  Task <|-- Voice
@@ -30,6 +30,10 @@ export const CAMPAIGN_PREVIEW_SKIP = '/skip';
30
30
  export const CAMPAIGN_PREVIEW_REMOVE = '/remove';
31
31
  /** 80-second timeout for accepting preview contact (outbound call setup takes longer than default 20s) */
32
32
  export const TIMEOUT_PREVIEW_ACCEPT = 80000;
33
+ /** RTD result deadline; the initiating HTTP acknowledgement uses the 20-second service timeout. */
34
+ export const AI_SUMMARY_DURATION_MS = 15000;
35
+ export const AI_SUMMARY_REQUEST_CANCELLED = 'AI_SUMMARY_REQUEST_CANCELLED' as const;
36
+
33
37
  export const TASK_MANAGER_FILE = 'taskManager';
34
38
  export const TASK_FILE = 'task';
35
39
 
@@ -93,6 +97,12 @@ export const METHODS = {
93
97
  SETUP_AUTO_WRAPUP_TIMER: 'setupAutoWrapupTimer',
94
98
  CANCEL_AUTO_WRAPUP_TIMER: 'cancelAutoWrapupTimer',
95
99
  REQUEST_REAL_TIME_TRANSCRIPTS: 'requestRealTimeTranscripts',
100
+ REQUEST_POST_CALL_SUMMARY: 'requestPostCallSummary',
101
+ SEND_POST_CALL_SUMMARY_RESPONSE: 'sendPostCallSummaryResponse',
102
+ REQUEST_MID_CALL_SUMMARY: 'requestMidCallSummary',
103
+ SEND_MID_CALL_SUMMARY_RESPONSE: 'sendMidCallSummaryResponse',
104
+ HANDLE_AI_SUMMARY_EVENT: 'handleAISummaryEvent',
105
+ CLEAR_AI_SUMMARY_STATE: 'clearAISummaryState',
96
106
  };
97
107
 
98
108
  export const TRANSCRIPT_EVENT_MAP = {
@@ -1,7 +1,8 @@
1
1
  import {CC_FILE, METHODS} from '../../../constants';
2
2
  import {getErrorDetails} from '../../core/Utils';
3
3
  import {ConsultTransferDestinationConfig, IDigital, TaskResponse, TaskData} from '../types';
4
- import {WrapupData} from '../../config/types';
4
+ import type {WrapupData} from '../../config/types';
5
+ import type ApiAIAssistant from '../../ApiAiAssistant';
5
6
  import Task from '../Task';
6
7
  import routingContact from '../contact';
7
8
  import LoggerProxy from '../../../logger-proxy';
@@ -14,7 +15,9 @@ export default class Digital extends Task implements IDigital {
14
15
  data: TaskData,
15
16
  wrapupData?: WrapupData,
16
17
  agentId?: string,
17
- consultTransferConfig?: ConsultTransferDestinationConfig
18
+ consultTransferConfig?: ConsultTransferDestinationConfig,
19
+ agentName?: string,
20
+ apiAIAssistant?: ApiAIAssistant
18
21
  ) {
19
22
  super(
20
23
  contact,
@@ -26,7 +29,9 @@ export default class Digital extends Task implements IDigital {
26
29
  consultTransferConfig,
27
30
  },
28
31
  wrapupData,
29
- agentId
32
+ agentId,
33
+ agentName,
34
+ apiAIAssistant
30
35
  );
31
36
  }
32
37
 
@@ -716,6 +716,27 @@ export enum TASK_EVENTS {
716
716
  * ```
717
717
  */
718
718
  TASK_CAMPAIGN_CONTACT_UPDATED = 'task:campaignContactUpdated',
719
+
720
+ /**
721
+ * Triggered when a mid-call summary is received for the task.
722
+ */
723
+ TASK_MID_CALL_SUMMARY_RECEIVED = 'task:midCallSummaryReceived',
724
+
725
+ /**
726
+ * Triggered on the task object when AI feature enablement flags arrive for that task's interaction.
727
+ * Fires immediately if the frame arrived after the task was registered, or at registration time
728
+ * if the frame arrived before the task (orphan retention). Consumers should use this event
729
+ * instead of the cc-level `FEATURE_ENABLEMENT` event so they do not need to maintain an
730
+ * interactionId → flags map manually.
731
+ * @example
732
+ * ```typescript
733
+ * task.on(TASK_EVENTS.TASK_FEATURE_ENABLEMENT, (enablement: AISummaryFeatureEnablement) => {
734
+ * const { midCallEnabled, postCallEnabled } = enablement;
735
+ * // gate summary UI on these flags
736
+ * });
737
+ * ```
738
+ */
739
+ TASK_FEATURE_ENABLEMENT = 'task:featureEnablement',
719
740
  }
720
741
 
721
742
  /**
@@ -1669,6 +1690,78 @@ export type ParticipantBooleanKey =
1669
1690
  */
1670
1691
  export type TaskResponse = AgentContact | Error | void;
1671
1692
 
1693
+ /** AI-assisted mid-call summary action. @public */
1694
+ export type AISummaryAction = 'CONSULT' | 'TRANSFER';
1695
+
1696
+ /** Feedback value sent with an AI summary response. @public */
1697
+ export type AISummaryFeedback = 'none' | 'thumbs_up' | 'thumbs_down';
1698
+
1699
+ /** AI summary response state for post-call and mid-call flows. @public */
1700
+ export type AISummaryState =
1701
+ | 'DEFAULT'
1702
+ | 'EXCLUDED'
1703
+ | 'IGNORED'
1704
+ | 'MID_CALL_CANCELLED'
1705
+ | 'NOT_RECEIVED';
1706
+
1707
+ /** Structured fields shared by post-call and mid-call summaries. @public */
1708
+ export type AISummarySections = {
1709
+ initialContactReason?: string;
1710
+ additionalContactReasons?: string;
1711
+ additionalContext?: string;
1712
+ keyActionsTaken?: string;
1713
+ nextSteps?: string;
1714
+ reasonForTransferOrConsult?: string;
1715
+ };
1716
+
1717
+ /** RTD payload returned or delivered for an AI summary. @public */
1718
+ export type AISummary = {
1719
+ conversationId: string;
1720
+ adaptiveCard?: Record<string, unknown>;
1721
+ adaptiveCardId?: string;
1722
+ editAdaptiveCard?: Record<string, unknown>;
1723
+ editAdaptiveCardId?: string;
1724
+ areTranscriptsAvailable?: boolean;
1725
+ languageCode?: string;
1726
+ resolution?: string;
1727
+ sections?: AISummarySections;
1728
+ suggestedWrapUpCodes?: Array<{name: string; [key: string]: unknown}>;
1729
+ suggestedWrapUpCodesMessage?: string;
1730
+ summaryText?: string;
1731
+ timestamp?: number;
1732
+ [key: string]: unknown;
1733
+ };
1734
+
1735
+ /** RTD payload describing AI-summary feature enablement for an interaction. @public */
1736
+ export type AISummaryFeatureEnablement = {
1737
+ interactionId: string;
1738
+ midCallEnabled?: boolean;
1739
+ postCallEnabled?: boolean;
1740
+ actionTimestamp?: number;
1741
+ [key: string]: unknown;
1742
+ };
1743
+
1744
+ /** Current AI-summary capabilities for a task. @public */
1745
+ export type AISummaryCapabilities = Required<
1746
+ Pick<AISummaryFeatureEnablement, 'midCallEnabled' | 'postCallEnabled'>
1747
+ >;
1748
+
1749
+ /**
1750
+ * Payload sent when reporting a post-call or mid-call summary result.
1751
+ * Transport timestamps are generated by the SDK when the response is sent.
1752
+ * @public
1753
+ */
1754
+ export type AISummaryResponse = {
1755
+ summary: AISummarySections | string;
1756
+ feedback: AISummaryFeedback;
1757
+ state: AISummaryState;
1758
+ numberOfTimesViewed: number;
1759
+ numberOfTimesEdited: number;
1760
+ numberOfTimesCopied: number;
1761
+ summaryReceived?: boolean;
1762
+ wrapUpCode?: string;
1763
+ };
1764
+
1672
1765
  /**
1673
1766
  * Request payload for removing a supported participant from an active conference.
1674
1767
  * @public
@@ -1717,6 +1810,12 @@ export interface ITask extends IEventEmitter {
1717
1810
  */
1718
1811
  data: TaskData;
1719
1812
 
1813
+ /**
1814
+ * Current AI-summary capabilities for this task. Both flags are false until
1815
+ * the matching feature-enablement event is received.
1816
+ */
1817
+ readonly aiSummaryCapabilities: Readonly<AISummaryCapabilities>;
1818
+
1720
1819
  /**
1721
1820
  * Map associating tasks with their corresponding call identifiers.
1722
1821
  */
@@ -1804,6 +1903,46 @@ export interface ITask extends IEventEmitter {
1804
1903
  */
1805
1904
  cancelAutoWrapupTimer(): void;
1806
1905
 
1906
+ /**
1907
+ * Applies the AI-summary feature flags received for this task's interaction.
1908
+ * @internal
1909
+ */
1910
+ setFeatureEnablement?(enablement: AISummaryFeatureEnablement): void;
1911
+
1912
+ /**
1913
+ * Clears the task-local AI-summary feature flags on task removal or session cleanup.
1914
+ * @internal
1915
+ */
1916
+ clearFeatureEnablement?(): void;
1917
+
1918
+ /**
1919
+ * Requests an AI-generated post-call summary for this task.
1920
+ * @returns Promise resolving with the matching post-call summary payload.
1921
+ */
1922
+ requestPostCallSummary(): Promise<AISummary>;
1923
+
1924
+ /**
1925
+ * Sends the consumer's post-call summary response.
1926
+ * @param response - Validated post-call response observations and summary state.
1927
+ * @returns Promise resolving when AI Assistant acknowledges the response.
1928
+ */
1929
+ sendPostCallSummaryResponse(response: AISummaryResponse): Promise<void>;
1930
+
1931
+ /**
1932
+ * Requests an AI-generated mid-call summary for a consult or transfer flow.
1933
+ * @param action - The consult or transfer flow requesting the summary.
1934
+ * @returns Promise resolving with the matching mid-call summary payload.
1935
+ */
1936
+ requestMidCallSummary(action: AISummaryAction): Promise<AISummary>;
1937
+
1938
+ /**
1939
+ * Sends the consumer's mid-call summary response for a consult or transfer flow.
1940
+ * @param response - Validated mid-call response observations and summary state.
1941
+ * @param action - The consult or transfer flow responding to the summary.
1942
+ * @returns Promise resolving when AI Assistant acknowledges the response.
1943
+ */
1944
+ sendMidCallSummaryResponse(response: AISummaryResponse, action: AISummaryAction): Promise<void>;
1945
+
1807
1946
  /**
1808
1947
  * Deregisters all web call event listeners.
1809
1948
  * Used when cleaning up task resources.