Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ff0880b
feat(cc-store): add AI summary lifecycle
rsarika Oct 8, 2026
bf6d1fd
feat(cc-components): add AI summary presentation
rsarika Oct 8, 2026
57dfea3
feat(cc-widgets): wire AI summary workflows
rsarika Oct 8, 2026
c8a3b29
fix(cc-store): simplify AI summary handling
rsarika Oct 9, 2026
9821155
refactor(cc-store): retain SDK AI summary payloads
rsarika Oct 9, 2026
c75883b
refactor(cc-store): update summary content in store
rsarika Oct 9, 2026
5b31f07
refactor(cc-store): retain SDK summary response records
rsarika Oct 9, 2026
c082890
refactor(cc-store): read SDK summary flags directly
rsarika Oct 9, 2026
79b8573
refactor(cc-store): rely on SDK summary enablement
rsarika Oct 9, 2026
199f875
refactor(cc-store): simplify summary event handling
rsarika Oct 9, 2026
8727856
refactor(cc-store): inline summary state operations
rsarika Oct 9, 2026
f3f75bb
refactor(cc-store): inline summary selector and use published SDK
rsarika Oct 9, 2026
762dcba
fix(cc-components): integrate simplified AI summary store
rsarika Oct 9, 2026
fcab674
fix(cc-widgets): align summaries with simplified store
rsarika Oct 9, 2026
1d6eb17
refactor(cc-components): simplify AI summary presentation
rsarika Oct 10, 2026
28135ae
refactor(cc-components): reuse assistant presentation primitives
rsarika Oct 10, 2026
9b24639
refactor(cc-widgets): simplify AI summary integration
rsarika Oct 10, 2026
783afb6
refactor(cc-task): trim AI summary integration scaffolding
rsarika Oct 10, 2026
e8793be
Revert unrelated SDK sample changes
rsarika Oct 14, 2026
c267377
Revert store build script change
rsarika Oct 14, 2026
adf585a
refactor(cc-store): simplify AI summary state and response flow
rsarika Oct 14, 2026
dc5a89f
refactor(cc-store): separate AI summary request flows
rsarika Oct 14, 2026
5d4e1ad
fix(cc-store): restore CI install and test compatibility
rsarika Oct 14, 2026
e36e95b
Merge branch 'codex/ai-summary-store' into codex/ai-summary-components
rsarika Oct 14, 2026
a5f2adf
fix(cc-components): address AI summary review feedback
rsarika Oct 14, 2026
6ff5574
Merge remote-tracking branch 'upstream/next' into codex/ai-summary-co…
rsarika Oct 16, 2026
201d738
Merge remote-tracking branch 'upstream/next' into codex/ai-summary-wi…
rsarika Oct 16, 2026
33e6032
fix(cc-components): address AI summary review findings
rsarika Oct 16, 2026
c9e4457
feat(cc-components): include CallControl AI summary views
rsarika Oct 16, 2026
4a3c5dd
fix(cc-widgets): align AI summary integration with components
rsarika Oct 16, 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 ai-docs/CONTRACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ The React entry of `@webex/cc-widgets` re-exports the widgets, including `AIAssi
| ai-assistant.AIAssistant | `@webex/cc-widgets` and `@webex/cc-ai-assistant` (React); `@webex/cc-widgets/wc` (custom element) | `AIAssistant`; `widget-cc-ai-assistant` | React component and existing custom element; wellness callbacks `onWellnessBreakOffered`, `onWellnessBreakAccepted`, `onWellnessBreakStarted`, `onWellnessBreakEnded`, `onWellnessBreakError`; optional `wellnessAudioUrl` and `wellnessBreakOverlayTarget` | Both React import paths remain available; WC entry retains the tag | `packages/contact-center/ai-assistant/ai-docs/ai-assistant-spec.md` | `packages/contact-center/ai-assistant/src/ai-assistant.types.ts`, `packages/contact-center/cc-widgets/src/index.ts`, `packages/contact-center/cc-widgets/src/wc.ts` |
| ai-assistant.wellness-types | `@webex/cc-ai-assistant` | `WellnessBreakPhase`, `WellnessBreakErrorCode`, `WellnessBreakError`, `WellnessBreakOverlayTarget` | Exported TypeScript lifecycle, error, and layout contracts | additive minor surface; literals are stable | `packages/contact-center/ai-assistant/ai-docs/ai-assistant-spec.md` | `packages/contact-center/ai-assistant/src/ai-assistant.types.ts` |
| cc-components.WellnessBreak | `@webex/cc-components` | `WellnessBreakRequestCard`, `WellnessBreakOfferCard`, `WellnessBreakModal`, `WellnessBreakError` | Props-only Agent Wellness Break presentation; modal accepts an optional animation loader supplied by AI Assistant and owns no wellness media | monorepo component surface; no SDK/store or Lottie dependency | `packages/contact-center/cc-components/ai-docs/cc-components-spec.md` | `packages/contact-center/cc-components/src/components/AIAssistant/WellnessBreak/` |
| cc-components.AIAssistantReceiverSummary | `@webex/cc-components` | `AIAssistantComponentProps.receiverSummary`, `AIAssistantReceiverSummary` | Props-only receiver view with a stable interaction `branchKey`, SDK `content`, feedback selection/pending state, and copy/feedback callbacks bound by the widget to that displayed summary. Callbacks carry no numeric revision. The component renders a display-only card and copies only visible card text; SDK requests and submission remain in the store. | additive presentation surface; consumed by the widget integration | `packages/contact-center/cc-components/ai-docs/cc-components-spec.md` (AI summary presentation) | `packages/contact-center/cc-components/src/components/AIAssistant/ai-assistant.types.ts` |
| cc-components.CallControlAISummary | `@webex/cc-components` | `CallControlComponentProps.aiSummary`, `CallControlAISummaryProps`, `ConsultTransferSummaryView`, `WrapUpSummaryView` | Optional props-only consult/transfer and wrap-up summary views; raw SDK content and local `editedSummary` are separate. Callbacks carry no numeric revision. Requests, edits and SDK submission are supplied by the widget/store. | additive optional props; `wrapupCall` supports void or Promise<boolean>; the internal AISummary component remains private | `packages/contact-center/cc-components/ai-docs/cc-components-spec.md` (AI summaries in CallControl) | `packages/contact-center/cc-components/src/components/task/task.types.ts` |
| cc-widgets.store | `@webex/cc-store` | `store` | MobX singleton (`Store.getInstance()`); `init(options: InitParams, setupEventListeners): Promise<void>`; sole SDK access point via `store.cc.*` | stable semver | `packages/contact-center/store/ai-docs/store-spec.md` | `packages/contact-center/store/src/index.ts:1,4` |
| store.ai-summary | `@webex/cc-store` | `cc.aiSummaries`, summary request/receiver-feedback/send methods, `AISummaryEntry`, `AISummaryRole`, `AISummaryResponseSubmissionResult` | Entries keyed by interaction and role retain the unchanged SDK `AISummary` in `content` and `AISummaryResponse` in `response`. Requests return `AISummary` or `undefined`; senders return `AISummaryResponseSubmissionResult`. Widget helpers own local editing, copy tracking and deferred feedback through response fields. `setReceiverSummaryFeedback(feedback, expectedSummary, task)` uses the displayed SDK payload to guard its asynchronous acknowledgement. Consumers read enablement from `task.aiSummaryCapabilities`; `task:featureEnablement` refreshes the task view. Wrap-up sends `sendPostCallSummaryResponse(wrapUpCode, originalTask)` before `task.wrapup()`. | additive; uses published `@webex/contact-center` `3.12.0-next.148` | `packages/contact-center/store/ai-docs/store-spec.md` (AI summaries) | `packages/contact-center/store/src/store.types.ts`, `packages/contact-center/store/src/storeEventsWrapper.ts` |
| store.host-init.enableWxBetterTogether | `@webex/cc-store` | `webexConfig.cc.enableWxBetterTogether` → `store.enableWxBetterTogether` | Host init boolean (default `false`), set **before** `store.init()`: read from `webexConfig.cc` on `{ webexConfig, access_token }` init or `webex.config.cc` on `{ webex }` init; read-only observable after init (Phase 1: re-init to change). Gates wxApp Mute/Keypad visibility and wxApp mute seed; SDK owns mute/DTMF API routing. | stable semver; renaming or removing the init key or store getter is breaking | `packages/contact-center/store/ai-docs/store-spec.md` (`STORE-R-030`); `packages/contact-center/task/ai-docs/task-spec.md` (WXCC-6026) | `packages/contact-center/store/src/store.ts`, `packages/contact-center/store/src/store.types.ts` (`WebexCcInitConfig`) |
Expand All @@ -38,6 +40,13 @@ The React entry of `@webex/cc-widgets` re-exports the widgets, including `AIAssi
| ui-logging.withMetrics | `@webex/cc-ui-logging` | `withMetrics` | `withMetrics<P extends object>(Component, widgetName: string): React.MemoExoticComponent` HOC that auto-emits mount/unmount/error metrics; every widget export is wrapped with it | stable semver; signature change is breaking | `packages/contact-center/ui-logging/ai-docs/ui-logging-spec.md` | `packages/contact-center/ui-logging/src/index.ts` |
| ui-logging.WidgetMetrics | `@webex/cc-ui-logging` | `WidgetMetrics` (type) | `{ widgetName; event: 'WIDGET_MOUNTED' \| 'ERROR' \| 'WIDGET_UNMOUNTED' \| 'PROPS_UPDATED'; props?; timestamp; additionalContext? }` | stable semver; narrowing the `event` union or removing a field is breaking | `packages/contact-center/ui-logging/ai-docs/ui-logging-spec.md` | `packages/contact-center/ui-logging/src/index.ts` |

## AI summary surfaces

| Contract ID | Package | Surface | Purpose | Compatibility | Detail |
| --- | --- | --- | --- | --- | --- |
| task.ai-summary-status | `@webex/cc-task`, `@webex/cc-widgets` | `CallControl.onAISummaryStatusChange`, `AISummaryStatusDetail` | Optional content-free status callback; the CallControl custom element accepts a function property, with inert callback attributes | additive optional callback and type | `packages/contact-center/task/ai-docs/task-spec.md`, `packages/contact-center/cc-widgets/ai-docs/cc-widgets-spec.md` |
| cc-components.ai-summary-props | `@webex/cc-components` | `CallControlComponentProps.aiSummary`, `AIAssistantComponentProps.receiverSummary` | Optional summary views and callbacks; the shared AISummary component is internal | additive optional props; wrapupCall supports void or Promise<boolean> | `packages/contact-center/cc-components/ai-docs/cc-components-spec.md` |

## Requires — what this repo depends on
| Dependency (service / package / datastore) | What is consumed | Schema / detail link | Availability assumption | Fallback on failure | Version floor |
|---|---|---|---|---|---|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,29 @@ views and the ErrorBoundary path. `tests/ai-assistant/feedback.tsx` covers like/
and the missing-`adaptiveCardId` warning. UI-level rendering (spinner, error text,
snapshots) is covered in `cc-components/tests/components/AIAssistant/`.

## Receiving-agent AI summaries

`getReceiverSummary` in `src/helper.ts` reads the current interaction's receiver entry
from `store.cc.aiSummaries`, maps its request status and SDK `areTranscriptsAvailable`
flag to a display surface, and passes the raw SDK content, confirmed feedback from
`entry.response.feedback` and pending state to the presentation component. Received
content remains visible during a failed or pending refresh. Copy callbacks increment
`response.numberOfTimesCopied` in `store.cc.aiSummaries` inside `runInAction`.
Copy and feedback callbacks retain the displayed SDK content and Task identities;
replacement content or a different active Task invalidates those callbacks. Feedback
passes that SDK payload and Task to `store.setReceiverSummaryFeedback`, which owns
submission and acknowledgement. The widget makes no summary SDK calls.

The receiver branch is independent of Real-time Assist enablement. A missing receiver
entry supplies no branch. SDK content with `areTranscriptsAvailable: false` supplies
the unavailable surface, and a rejected request supplies the generic error surface.
Raw content without an adaptive card still supplies a branch; card rendering and
unsupported-card fallback belong to the presentation component.
Summary changes are observed by `AIAssistantInternal`.

Evidence: `src/helper.ts`, `src/ai-assistant/index.tsx`;
tests: receiver summary cases in `tests/ai-assistant/index.tsx`.

## Traceability

- Repo architecture: `../../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../../ai-docs/SPEC_INDEX.md`
Expand Down
1 change: 1 addition & 0 deletions packages/contact-center/ai-assistant/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@webex/cc-store": "workspace:*",
"@webex/cc-ui-logging": "workspace:*",
"lottie-web": "5.13.0",
"mobx": "6.13.5",
"mobx-react-lite": "^4.1.0",
"react-error-boundary": "^6.0.0",
"typescript": "5.6.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {observer} from 'mobx-react-lite';
import {ErrorBoundary} from 'react-error-boundary';

import {AIAssistantComponent} from '@webex/cc-components';
import {useAiAssistant, REAL_TIME_ASSIST_FLAG} from '../helper';
import {getReceiverSummary, useAiAssistant, REAL_TIME_ASSIST_FLAG} from '../helper';
import {useWellnessBreak} from '../wellness/useWellnessBreak';
import {loadWellnessAnimation} from '../wellness/animation';
import {IAIAssistantProps} from '../ai-assistant.types';
Expand Down Expand Up @@ -71,6 +71,7 @@ const AIAssistantInternal: React.FunctionComponent<IAIAssistantProps> = observer
return (
<AIAssistantComponent
{...hookProps}
receiverSummary={getReceiverSummary(currentTask)}
isFeatureEnabled={isFeatureEnabled}
hasActiveInteraction={Boolean(interactionId)}
agentName={agentProfile?.agentName}
Expand Down
56 changes: 55 additions & 1 deletion packages/contact-center/ai-assistant/src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {runInAction} from 'mobx';
import store from '@webex/cc-store';
import type {RealTimeAssistPayload} from '@webex/cc-store';
import type {ITask, RealTimeAssistPayload} from '@webex/cc-store';
import type {
AIAssistantActionEvent,
AIAssistantChatEntry,
AIAssistantChromeState,
AIAssistantReceiverSummary,
AIAssistantRequestStatus,
} from '@webex/cc-components';
import {
Expand Down Expand Up @@ -298,4 +300,56 @@ export const useAiAssistant = (input: UseAiAssistantInput) => {
return useMemo(() => ({...chrome, ...realTimeAssist}), [chrome, realTimeAssist]);
};

/** The receiving agent's consult/transfer summary, pushed by the backend for the task's interaction. */
export const getReceiverSummary = (task?: ITask): AIAssistantReceiverSummary | undefined => {
const interactionId = task?.data.interactionId;
const entry = interactionId ? store.cc.aiSummaries[interactionId]?.receiver : undefined;
if (!task || !interactionId || !entry) {
return undefined;
}
const branchKey = `mid-call:receiver:${interactionId}`;
const content = entry.content;
if (content?.areTranscriptsAvailable === false) {
return {surface: 'unavailable', branchKey};
}
if (content) {
const getDisplayedEntry = () => {
const current = store.cc.aiSummaries[interactionId]?.receiver;
return store.currentTask === task && current?.content === content ? current : undefined;
};
return {
surface: 'content',
branchKey,
content,
selectedFeedback: entry.response.feedback,
feedbackPending: Boolean(entry.feedbackPending),
recordReceiverSummaryCopied: () => {
const current = getDisplayedEntry();
if (!current || current.feedbackPending) {
return false;
}
runInAction(() => {
store.cc.aiSummaries = {
...store.cc.aiSummaries,
[interactionId]: {
...store.cc.aiSummaries[interactionId],
receiver: {
...current,
response: {...current.response, numberOfTimesCopied: current.response.numberOfTimesCopied + 1},
},
},
};
});
return true;
},
setReceiverSummaryFeedback: (feedback) => {
if (getDisplayedEntry()) {
void store.setReceiverSummaryFeedback(feedback, content, task);
}
},
};
}
return entry.status === 'error' ? {surface: 'generic-error', branchKey} : undefined;
};

export {REAL_TIME_ASSIST_FLAG};
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jest.mock('@webex/cc-store', () => ({
__esModule: true,
default: {
cc: {
aiSummaries: {},
apiAIAssistant: {
getRealTimeAssistance: jest.fn(),
sendRealTimeAssistanceUserAction: jest.fn(),
Expand Down
Loading
Loading