Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
8 changes: 8 additions & 0 deletions ai-docs/CONTRACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The React entry of `@webex/cc-widgets` re-exports the widgets, including `AIAssi
| 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-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` | `aiSummaries`, summary request/edit/copy/feedback methods, `getAISummarySurface`, `onAISummaryStatusChange` | Entries keyed by interaction and role retain SDK `AISummary` in `content` and `AISummaryResponse` in `response`. Store actions update SDK response fields; senders forward that record. Consumers read feedback and counts from `entry.response` and enablement directly from `task.aiSummaryCapabilities`; `task:featureEnablement` refreshes the task view. Components choose the display format. Capture the post-call response before wrap-up, then call `sendPostCallSummaryResponse(response, originalTask)` with the required captured task. Status callbacks contain no summary text. | 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`) |
| store.types | `@webex/cc-store` | Existing queue/entry-point request, response, and entity type re-exports plus `TaskUIControls` | TypeScript exports describing the SDK-backed domain surface; established entity rows and Task destination controls pass through without a widget destination abstraction | stable semver; SDK-shaped types track SDK | `@webex/contact-center` types (`node_modules/@webex/contact-center/dist/types/index.d.ts`) (SDK source); `packages/contact-center/store/ai-docs/store-spec.md` | `packages/contact-center/store/src/store.types.ts` |
| cc-components.consult-transfer-lists | `@webex/cc-components` | `CallControl` consult/transfer fetch props plus `action`, `availableDestinations`, and optional list-item `presence` | `FetchPaginatedList<ContactServiceQueue>` / `FetchPaginatedList<EntryPointRecord>` and the SDK-ordered destination array from `TaskUIControls`; UI preserves list/category order, passes buddy availability to Momentum Avatar as semantic presence, shows `AddressBookEntry.number` and `EntryPointRecord.number` subtitles, and may only apply host hide overrides | stable semver; entity and control types track store/SDK contracts; optional presentation prop is additive | `packages/contact-center/cc-components/ai-docs/cc-components-spec.md`; `ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md` | `packages/contact-center/cc-components/src/components/task/task.types.ts` |
Expand All @@ -37,6 +38,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
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
},
"resolutions": {
"@momentum-design/components": "0.133.23",
"@momentum-design/icons": "0.17.0"
"@momentum-design/icons": "0.17.0",
"@webex/contact-center": "3.12.0-next.148",
"@webex/internal-plugin-device@npm:3.12.0-next.55": "3.12.0-next.54",
"@webex/internal-plugin-metrics@npm:3.12.0-next.56": "3.12.0-next.55",
"@webex/internal-plugin-mercury@npm:3.12.0-next.62": "3.12.0-next.61"
},
"scripts": {
"clean": "yarn workspaces foreach --all --topological --parallel run clean && rm -rf node_modules",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,23 @@ 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.aiSummaries`, maps its surface with `getAISummarySurface`, and passes the
raw SDK content, revision, confirmed feedback from `entry.response.feedback` and
pending state to the presentation component. Its copy and feedback callbacks pass the
captured Task and displayed revision to the store. 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.
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
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
28 changes: 26 additions & 2 deletions packages/contact-center/ai-assistant/src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import store from '@webex/cc-store';
import type {RealTimeAssistPayload} from '@webex/cc-store';
import store, {getAISummarySurface} 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 +299,27 @@ 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.aiSummaries[interactionId]?.receiver : undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Use more specific name which at least helps us identify that this entry object is nothing but actually summary data something like summaryData or summary itself

const surface = getAISummarySurface(entry);
const branchKey = `mid-call:receiver:${interactionId}`;
if (surface === 'content' && entry.content.adaptiveCard) {
return {
surface,
branchKey,
content: entry.content,
contentRevision: entry.revision,
selectedFeedback: entry.response.feedback,
feedbackPending: Boolean(entry.feedbackPending),
recordReceiverSummaryCopied: (expectedRevision) =>
store.recordAISummaryCopied('receiver', expectedRevision, task),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods which we are invoking from store can just be placed in this file itself if they have no dependency on SDK

setReceiverSummaryFeedback: (feedback, expectedRevision) =>
void store.setReceiverSummaryFeedback(feedback, expectedRevision, task),
};
}
return surface === 'unavailable' || surface === 'generic-error' ? {surface, branchKey} : undefined;
};

export {REAL_TIME_ASSIST_FLAG};
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jest.mock('@webex/cc-components', () => {

jest.mock('@webex/cc-store', () => ({
__esModule: true,
getAISummarySurface: jest.requireActual('@webex/cc-store').getAISummarySurface,
default: {
cc: {
apiAIAssistant: {
Expand All @@ -70,6 +71,7 @@ jest.mock('@webex/cc-store', () => ({
agentId: 'agent-1',
featureFlags: {isSuggestedResponsesEnabled: true},
realTimeAssist: {'interaction-1': []},
aiSummaries: {},
clearRealTimeAssist: jest.fn(),
logger: {error: jest.fn(), warn: jest.fn()},
},
Expand Down
Loading
Loading