Skip to content
Open
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
4639263
feat: specs for midcall and postcall summaries
rsarika Aug 7, 2026
90e2152
docs(contact-center): document AI summary requirements
rsarika Aug 7, 2026
8d58105
fix: prog-mini-artifacts ignored
rsarika Aug 7, 2026
b454148
matrixbuilder: publish design
rsarika Aug 7, 2026
a5bc4ba
fix: gate husky hooks entirely on _/husky.sh presence
rsarika Aug 7, 2026
1510aef
matrixbuilder: publish design_fix[0]
rsarika Aug 7, 2026
9c36a3f
matrixbuilder: publish design_fix[1]
rsarika Aug 7, 2026
06f3cfa
matrixbuilder: publish design_fix[2]
rsarika Aug 7, 2026
dc3fb65
matrixbuilder: publish design_fix[3]
rsarika Aug 7, 2026
92e20e9
matrixbuilder: publish design_fix[4]
rsarika Aug 7, 2026
40e8c82
matrixbuilder: publish design_fix[5]
rsarika Aug 7, 2026
424c49d
matrixbuilder: publish design_fix[6]
rsarika Aug 7, 2026
7078829
matrixbuilder: publish design_fix[7]
rsarika Aug 7, 2026
211bf49
matrixbuilder: publish design_fix[8]
rsarika Aug 7, 2026
92dd17d
matrixbuilder: publish design_fix[9]
rsarika Aug 7, 2026
6d5d909
matrixbuilder: publish design_fix[10]
rsarika Aug 7, 2026
e53daab
matrixbuilder: publish design_fix[11]
rsarika Aug 7, 2026
f3a54c4
matrixbuilder: publish design_fix[12]
rsarika Aug 7, 2026
80570f2
matrixbuilder: publish design_fix[13]
rsarika Aug 7, 2026
eea3311
matrixbuilder: publish design_fix[14]
rsarika Aug 7, 2026
ad291aa
matrixbuilder: publish design_fix[15]
rsarika Aug 7, 2026
76f249b
matrixbuilder: publish design_fix[16]
rsarika Aug 7, 2026
1d6694d
matrixbuilder: publish design_fix[17]
rsarika Aug 7, 2026
acffbc6
matrixbuilder: publish design_fix[18]
rsarika Aug 7, 2026
64ff269
matrixbuilder: publish design_fix[19]
rsarika Aug 7, 2026
30a0296
matrixbuilder: publish design_fix[20]
rsarika Aug 7, 2026
9a724af
matrixbuilder: publish design_fix[21]
rsarika Aug 7, 2026
affbe7c
matrixbuilder: publish design_fix[22]
rsarika Aug 7, 2026
5ee13f1
matrixbuilder: publish design_fix[23]
rsarika Aug 7, 2026
08a694a
matrixbuilder: publish design_fix[24]
rsarika Aug 7, 2026
1711ca2
matrixbuilder: publish design_fix[25]
rsarika Aug 7, 2026
e9399d6
matrixbuilder: publish design_fix[26]
rsarika Aug 7, 2026
5c50d28
matrixbuilder: publish design_fix[27]
rsarika Aug 7, 2026
e4ddbe4
matrixbuilder: publish design_fix[28]
rsarika Aug 7, 2026
d679a0a
matrixbuilder: publish design_fix[29]
rsarika Aug 7, 2026
31e4369
matrixbuilder: publish design_fix[30]
rsarika Aug 7, 2026
aa4313b
matrixbuilder: publish design_fix[31]
rsarika Aug 7, 2026
00f384b
matrixbuilder: publish design_fix[32]
rsarika Aug 7, 2026
2d6854b
fix: repair design_spec matrix references and dag task coverage
rsarika Aug 7, 2026
a061fc5
fix: replace nvm use with nvm exec in dag acceptance commands
rsarika Aug 7, 2026
9864717
Remove minified sample bundles from git tracking
rsarika Aug 10, 2026
1e394eb
feat: midcall and postcall summaries
rsarika Aug 14, 2026
8e1158c
feat: midcall and postcall summaries sample app changes
rsarika Aug 17, 2026
7227a63
fix: send IGNORED state when mid/post-call summary not triggered; rem…
rsarika Aug 17, 2026
ae51c95
fix: sample app — read-only/edit mode, delta payload, suggested wrapu…
rsarika Aug 17, 2026
ace189a
feat: emit task:featureEnablement on task object (Issue 17)
rsarika Aug 17, 2026
e4145b4
fix: simplify summary text extraction to sections then summaryText
rsarika Aug 17, 2026
544af1f
fix: add agentName to AISummaryMidCallResponseTransportPayload type
rsarika Aug 17, 2026
bbd3b66
Merge remote-tracking branch 'upstream/next' into cc-summaries
rsarika Aug 17, 2026
2b91b67
docs: fix ai-docs drift from cc-summaries and upstream merge
rsarika Aug 17, 2026
e0c51e5
chore: reset husky hooks to upstream/next version
rsarika Aug 17, 2026
d390b3a
fix: remove cc-level featureEnablement forwarding in favour of task:f…
rsarika Aug 17, 2026
12dd0d8
fix: access mock.calls[0][1] instead of whole calls[0] array for toSt…
rsarika Aug 17, 2026
1bf3652
fix: drive sample summaries off sections and dismiss them after wrapup
rsarika Aug 18, 2026
05120c1
docs: remove artifiser working files and re-home their content into a…
rsarika Aug 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,18 @@ docs/samples/webex.min.js
docs/samples/webex.min.js.map
docs/samples/meetings.min.js
docs/samples/meetings.min.js.map
docs/samples/contact-center.min.js
docs/samples/contact-center.min.js.map
docs/samples/encryption.min.js
docs/samples/encryption.min.js.map
docs/samples/calling.min.js
docs/samples/calling.min.js.map
coverage

# patch-package
patches.hash
bundle.js.map
junit.xml
.matrix/
.prog/
requirementVersion/
183 changes: 183 additions & 0 deletions ai-summary-initiator-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# AI Mid-Call Summary Initiator Flow

This companion view follows the implemented CONSULT and TRANSFER initiator
paths. The authoritative contract is `ai-summary.md`, synchronized to
`design/default/design_spec.md`.

## Component Map

```mermaid
flowchart LR
App[Consumer application]
Task[Task]
Coord[AISummaryCoordinator]
API[ApiAIAssistant]
Backend[api-ai-assistant]
RTD[Realtime websocket]
TM[TaskManager]
Handoff[consult or transfer API]

App -->|requestMidCallSummary| Task
Task -->|register MID_CALL_SUMMARY by conversationId| Coord
Task -->|sendSummaryGetEvent| API
API -->|POST /event| Backend
Backend --> RTD
RTD --> TM
TM -->|resolve pending result| Coord
Coord -->|Promise payload| Task
Task --> App
App -->|sendMidCallSummaryResponse| Task
Task --> API
App -->|after response attempt| Handoff
```

The initiating consumer receives the generated summary through the returned
Promise. There is no public initiator `task:midCallSummary` event in this SDK
slice.

## Happy Path

```mermaid
sequenceDiagram
actor App
participant Task
participant Coord as AISummaryCoordinator
participant API as ApiAIAssistant
participant Backend
participant TM as TaskManager

App->>Task: requestMidCallSummary(CONSULT or TRANSFER)
Task->>Task: validate action and current flags
alt consultTransferSummariesEnabled !== true or midCallEnabled !== true
Task-->>App: reject MID_CALL_SUMMARY_DISABLED
else enabled
Task->>Coord: register MID_CALL_SUMMARY
Coord-->>Task: {requestToken, result}
Task->>API: sendSummaryGetEvent(action-specific GET)
Task->>Task: Promise.all(result, acknowledgement)
API->>Backend: POST /event
Backend-->>API: 2xx acknowledgement
Backend->>TM: RTD MID_CALL_SUMMARY
TM->>Coord: resolve by conversationId + MID_CALL_SUMMARY
Coord-->>Task: summary payload
Task-->>App: resolve summary payload
App->>Task: sendMidCallSummaryResponse(payload, actionType)
Task->>API: sendSummaryResponseEvent(action-specific response)
API->>Backend: POST /event
Backend-->>API: 2xx acknowledgement
Task-->>App: response attempt fulfilled
App->>Task: consult(...) or transfer(...)
end
```

Consumer sequencing for the handoff is advisory and documentation-only from the
SDK perspective: the application attempts and awaits the summary response before
independently invoking consult or transfer, catches and records response failure,
and still continues the handoff. Unit tests prove event-name selection and
bounded response settlement, not cross-call ordering between public APIs.

## IGNORED Branch

When the feature is enabled (`midCallEnabled === true`) but no summary was ever
requested — for example, the feature flag arrived after the consult/transfer
decision was already made, or the request was never triggered — the application
must send `sendMidCallSummaryResponse` with `state: 'IGNORED'`, `summaryReceived:
false`, `summary: ''`, and all counters at zero before invoking the handoff. The
SDK accepts `IGNORED` in the unavailable branch (`summaryReceived: false`)
alongside `NOT_RECEIVED` and `MID_CALL_CANCELLED`.

## Consumer Recovery Example

This is the authoritative consumer control-flow example for FR-6 recovery
sequencing. It records only bounded response failure metadata, keeps the
handoff call independent of the advisory response attempt, and treats
`MID_CALL_CANCELLED` as the no-handoff branch.

```typescript
import type {
AISummaryActionType,
ConsultPayload,
ITask,
MidCallSummaryResponsePayload,
TransferPayLoad
} from '@webex/contact-center';

type BoundedMidCallResponseFailure = {
actionType: AISummaryActionType;
errorCode: string;
};

type MidCallHandoffOptions =
| {
actionType: 'CONSULT';
consultPayload: ConsultPayload;
responsePayload: MidCallSummaryResponsePayload;
recordSummaryResponseFailure: (
failure: BoundedMidCallResponseFailure
) => void;
}
| {
actionType: 'TRANSFER';
responsePayload: MidCallSummaryResponsePayload;
transferPayload: TransferPayLoad;
recordSummaryResponseFailure: (
failure: BoundedMidCallResponseFailure
) => void;
};

function getBoundedAISummaryErrorCode(error: unknown): string {
const data =
error instanceof Error
? (error as Error & {data?: {errorCode?: unknown}}).data
: undefined;

return typeof data?.errorCode === 'string'
? data.errorCode
: 'MID_CALL_SUMMARY_RESPONSE_FAILED';
}

export async function completeMidCallHandoff(
task: ITask,
options: MidCallHandoffOptions
): Promise<void> {
const {actionType, responsePayload} = options;

if (responsePayload.state === 'MID_CALL_CANCELLED') {
await task.sendMidCallSummaryResponse(responsePayload, actionType);
return;
}

try {
await task.sendMidCallSummaryResponse(responsePayload, actionType);
} catch (error) {
options.recordSummaryResponseFailure({
actionType,
errorCode: getBoundedAISummaryErrorCode(error)
});
}

if (actionType === 'CONSULT') {
await task.consult(options.consultPayload);
return;
}

await task.transfer(options.transferPayload);
}
```

## Contract References

This page owns the initiator and consumer handoff sequence, including the
recovery example above. The canonical contract owns the repeated details:

- [Public Task APIs](./ai-summary.md#public-task-apis) — CONSULT/TRANSFER event
selection and the no-handoff cancellation branch.
- [Feature Enablement](./ai-summary.md#feature-enablement) — organization and
interaction gating.
- [Request Coordination](./ai-summary.md#request-coordination) — the shared
conversation-scoped pending slot, overlap, cleanup, and timeout.
- [Response Payload Rules](./ai-summary.md#response-payload-rules) and
[Transport](./ai-summary.md#transport) — discriminators, counters,
timestamps, action-specific response events, and bounded settlement.
- [Metrics And Privacy](./ai-summary.md#metrics-and-privacy) — final outcomes,
recovery, and sensitive-data exclusions.
77 changes: 77 additions & 0 deletions ai-summary-open-issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# AI Summary — Open Issues (Requirement Gaps)

These issues were identified during review of the staged changes in
`docs/samples/contact-center/` against the AI summary spec (`ai-summary.md` §17)
and by comparing the sample app against the agent desktop (`wxcc-desktop`).

---

## Issue 2 — `wrapUpCode` field: display label vs. code UUID

**File:** `docs/samples/contact-center/app.js` · **Line:** ~3612

**What happens:**
The `wrapUpCode` field in `sendPostCallSummaryResponse` is set to `wrapupReason`
(the dropdown's `.text` — e.g. `"After Call Work"`), not `auxCodeId`
(the dropdown's `.value` — the UUID).

**Why this is ambiguous:**
The spec's `wrapupCall` pseudocode (§17.2) explicitly uses `wrapUpCode: wrapupReason`
where `wrapupReason = options[...].text`. The wire example in §6.2 also shows
`"wrapUpCode": "Sale"` — a human-readable label, not a UUID.
The implementation follows the spec exactly.

**Decision needed:**
Does the `api-ai-assistant` backend expect the wrapup code **name/label** or the
**UUID**? Needs confirmation from the backend contract or agent-desktop source.
If UUID is required, both the spec pseudocode and the implementation need updating.

---

## Issue 16 — SDK requires callers to pass fields it already owns (SDK miss — fixed)

**Files:** `packages/@webex/contact-center/src/services/task/Task.ts` · `types.ts` · `TaskManager.ts` · `TaskFactory.ts` · `cc.ts`

**What happened:**
`sendMidCallSummaryResponse` required callers to pass `conversationId`, `interactionId`, and `agentName` in the payload — all of which the SDK already has internally:

- `conversationId` and `interactionId` — derived from `getAISummaryCorrelation(this.data)` inside the SDK. The caller-provided values were silently ignored by the transport layer. They were never in the TypeScript type definitions either; the sample app was passing dead fields.
- `agentName` — available in `agentConfig.agentName` at registration time. The SDK had `agentId` threaded through to the Task but not `agentName`, forcing every consumer to supply it.

`sendPostCallSummaryResponse` had the same issue for `conversationId` and `interactionId`.

**Classification:** SDK miss — the API required callers to echo back data the SDK already owned or could derive.

**Fix applied:**
- `agentName` made optional in `MidCallReceivedResponse` and `MidCallUnavailableResponse` types
- `agentName` threaded from `agentConfig` → `TaskManager.setAgentName` → `TaskFactory.createTask` → `Task` constructor
- `buildMidCallSummaryResponseTransportPayload` falls back to `this.agentName` when the caller omits it
- `validateMidCallSummaryResponsePayload` no longer requires `agentName` from the caller
- Sample app (`app.js`) — all `sendMidCallSummaryResponse`/`sendPostCallSummaryResponse` calls stripped of `conversationId`, `interactionId`, and `agentName`

---

## Issue 17 — Feature enablement delivered at cc level; consumers forced to maintain per-task map (wrong implementation — fixed)

**Files:** `packages/@webex/contact-center/src/services/task/TaskManager.ts` · `types.ts` · `docs/samples/contact-center/app.js`

**What happened:**
The `FEATURE_ENABLEMENT` RTD frame was only re-emitted as `cc:featureEnablement` on the cc object. Consumers had to listen globally, extract the `interactionId` from the payload, and maintain their own `interactionId → flags` Map to gate summary UI per task.

**Classification:** Wrong implementation — best practice is to deliver task-scoped events on the task object so consumers do not need to build secondary cross-task state.

**Fix applied:**
- Added `TASK_FEATURE_ENABLEMENT = 'task:featureEnablement'` to `TASK_EVENTS` in `types.ts`
- `TaskManager.handleFeatureEnablementEvent`: switched from `.some()` to `.find()` to keep the task reference, then calls `task.emit(TASK_EVENTS.TASK_FEATURE_ENABLEMENT, featurePayload)` when the task is already registered
- `TaskManager.retainFeatureEnablementForTask`: after clearing the orphan timeout, reads the stored payload via `coordinator.getFeatureEnablement(interactionId)` and emits `TASK_FEATURE_ENABLEMENT` on the task — covers the case where the frame arrived before `task:assigned`
- `app.js`: removed the `cc:featureEnablement` global listener; added `task:featureEnablement` listener inside `wireSummaryListeners`; also persists flags to sessionStorage immediately on arrival

---

## Summary

| # | Area | Gap | Priority |
|---|---|---|---|
| 2 | Post-call — payload | `wrapUpCode` label vs. UUID — needs backend confirmation | Medium |
| 16 | SDK API | Required callers to pass SDK-owned fields — **fixed** | ~~High~~ |
| 17 | SDK event delivery | `cc:featureEnablement` forced consumers to maintain per-task map — **fixed** | ~~High~~ |
100 changes: 100 additions & 0 deletions ai-summary-postcall-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# AI Post-Call Summary Flow

This companion view follows the implemented post-call path. The authoritative
contract is `ai-summary.md`, which is synchronized to
`design/default/design_spec.md`.

## Component Map

```mermaid
flowchart LR
App[Consumer application]
Task[Task]
Coord[AISummaryCoordinator]
API[ApiAIAssistant]
Backend[api-ai-assistant]
RTD[Realtime websocket]
TM[TaskManager]

App -->|requestPostCallSummary| Task
Task -->|register POST_CALL_SUMMARY by conversationId| Coord
Task -->|sendSummaryGetEvent| API
API -->|POST /event| Backend
Backend --> RTD
RTD --> TM
TM -->|resolve pending result| Coord
Coord -->|Promise payload| Task
Task --> App
App -->|wrapup first| Task
App -->|sendPostCallSummaryResponse| Task
Task --> API
```

There is no public `task:postCallSummary` completion event in this SDK slice.
The initiating consumer receives the summary through the returned Promise.

## Happy Path

```mermaid
sequenceDiagram
actor App
participant Task
participant Coord as AISummaryCoordinator
participant API as ApiAIAssistant
participant Backend
participant TM as TaskManager

App->>Task: requestPostCallSummary()
Task->>Task: read current org flags and feature snapshot
alt wrapUpSummariesEnabled !== true or postCallEnabled !== true
Task-->>App: reject POST_CALL_SUMMARY_DISABLED
else enabled
Task->>Task: capture {conversationId, interactionId}
Task->>Coord: register POST_CALL_SUMMARY
Coord-->>Task: {requestToken, result}
Task->>API: sendSummaryGetEvent(GET_POST_CALL_SUMMARY)
Task->>Task: Promise.all(result, acknowledgement)
API->>Backend: POST /event
Backend-->>API: 2xx acknowledgement
Backend->>TM: RTD POST_CALL_SUMMARY
TM->>Coord: resolve by conversationId + POST_CALL_SUMMARY
Coord-->>Task: summary payload
Task-->>App: resolve summary payload
App->>Task: wrapup(...)
Task-->>App: wrap-up completed
App->>Task: sendPostCallSummaryResponse(payload)
Task->>API: sendSummaryResponseEvent(POST_CALL_SUMMARY_RESPONSE)
API->>Backend: POST /event
Backend-->>API: 2xx acknowledgement
Task-->>App: resolve void
end
```

The existing wrap-up API runs before the advisory summary response. A summary
request rejection must not block wrap-up.

## IGNORED Branch

When the feature is enabled (`postCallEnabled === true`) but no summary was ever
requested — for example, the feature flag arrived after wrapup was already
initiated — the application must send `sendPostCallSummaryResponse` with `state:
'IGNORED'`, `summary: ''`, all counters at zero, and the actual `wrapUpCode`
before completing wrapup.

## Contract References

This page owns the post-call sequence only. The canonical contract defines the
rules used at each step:

- [Feature Enablement](./ai-summary.md#feature-enablement) — organization and
interaction gating.
- [Correlation](./ai-summary.md#correlation) and
[Lifecycle](./ai-summary.md#lifecycle) — request-time identifiers, retained
response context, and cleanup.
- [Request Coordination](./ai-summary.md#request-coordination) — overlap,
cancellation tokens, timeout, and ownership.
- [Response Payload Rules](./ai-summary.md#response-payload-rules) and
[Transport](./ai-summary.md#transport) — response branches, counters,
timestamps, and wire-key omission.
- [Metrics And Privacy](./ai-summary.md#metrics-and-privacy) — final outcomes,
recovery, and sensitive-data exclusions.
Loading