Skip to content
Open
Show file tree
Hide file tree
Changes from 44 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/
5 changes: 3 additions & 2 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

_husky_sh="$(dirname "$0")/_/husky.sh"
[ -f "$_husky_sh" ] || exit 0
. "$_husky_sh"
yarn commitlint --edit $1
5 changes: 3 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Skip if in CI
[ -n "$CI" ] && exit 0

. "$(dirname "$0")/_/husky.sh"

_husky_sh="$(dirname "$0")/_/husky.sh"
[ -f "$_husky_sh" ] || exit 0
. "$_husky_sh"
yarn run --silent srcdist && yarn run --silent lint:staged
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.
133 changes: 133 additions & 0 deletions ai-summary-open-issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# 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 12 — Always in edit mode; no read-only → edit transition

**File:** `docs/samples/contact-center/app.js` · `renderSummarySection`

**Desktop behaviour:**
The summary is initially rendered in read-only mode using the `adaptiveCard` schema (TextBlock
labels). The agent clicks an explicit Edit button to switch into edit mode, at which point the
`editAdaptiveCard` schema (Input.Text fields) is rendered.

**Sample app:**
`renderSummarySection` always calls `extractEditFields` and renders textareas immediately. There is
no read-only phase, no edit button, and the read-only `adaptiveCard` is never used. The agent sees
editable fields from the moment the summary arrives.

**Impact:** Minor UX gap; no impact on payload correctness since edits are already tracked via
`isSummaryEdited`. Low priority for a sample app.

---

## Issue 13 — Full section values sent on submit vs. changed-only delta

**File:** `docs/samples/contact-center/app.js` · `buildSummaryPayload`

**Desktop behaviour:**
On submit, `getModifiedSections(originalSections, currentSections)` computes only the sections that
differ from the original and sends them as the payload. Unchanged sections are omitted.

**Sample app:**
`buildSummaryPayload` collects all current textarea values and returns them in full. When the agent
edits, all sections (including unmodified ones) are sent.

**Impact:** The backend may treat any non-empty section object as an edit. Low priority unless
the backend uses the delta to determine `numberOfTimesEdited` independently.

---

## Issue 14 — Suggested wrapup codes not implemented

**File:** `docs/samples/contact-center/` (no implementation)

**Desktop behaviour:**
The post-call summary event may include a `suggestedWrapUpCodes` array. The desktop maps these
against the agent's wrapup reason list and pre-selects or highlights the matching code in the
wrapup dropdown. Controlled by the org-level feature flag `isSuggestedWrapupReasonsEnabled`.

**Sample app:**
`suggestedWrapUpCodes` is never read. The wrapup dropdown is always manually selected by the agent.

**Decision needed:** Whether this feature is in scope for the SDK sample app.

---

## Issue 15 — No session-storage persistence; summary lost on page refresh

**File:** `docs/samples/contact-center/app.js` · module-level state

**Desktop behaviour:**
`AISummaryStore` serialises `midCallSummaryMap`, `postCallSummaryMap`, and feature flags to
`sessionStorage` on every mutation and restores them on initialisation. If the agent refreshes
during wrapup, the post-call summary payload is recovered and the response can still be sent.

**Sample app:**
All summary state lives in module-level variables (`midCallSummary`, `postCallSummary`,
`postCallSummaryPending`, `summaryFeatureMap`). A page refresh during an active call or wrapup
resets everything. The post-call response would never be sent after a refresh.

**Decision needed:** Whether session-storage resilience is in scope for a sample app.

---

## 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`

---

## Summary

| # | Area | Gap | Priority |
|---|---|---|---|
| 2 | Post-call — payload | `wrapUpCode` label vs. UUID — needs backend confirmation | Medium |
| 12 | Mid-call — UX | Always edit mode; no read-only → edit transition | Low |
| 13 | Mid-call — payload | Full sections sent vs. changed-only delta | Low |
| 14 | Post-call — feature | Suggested wrapup codes not implemented | Medium |
| 15 | General | No session-storage persistence; state lost on refresh | Medium |
| 16 | SDK API | Required callers to pass SDK-owned fields — **fixed** | ~~High~~ |
Loading