Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions packages/contact-center/ai-assistant/bable.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const baseConfig = require('../../../babel.config.js');

module.exports = baseConfig;
20 changes: 20 additions & 0 deletions packages/contact-center/ai-assistant/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from 'typescript-eslint';
import pluginReact from 'eslint-plugin-react';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintConfigPrettier from 'eslint-config-prettier';

export default [
{files: ['**/src/**/*.{js,mjs,cjs,ts,jsx,tsx}']},
{ignores: ['.babelrc.js', '*config.{js,ts}', 'dist', 'node_modules', 'coverage']},
{languageOptions: {globals: globals.browser}},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
settings: {react: {version: 'detect'}},
},
eslintPluginPrettierRecommended,
eslintConfigPrettier,
];
6 changes: 6 additions & 0 deletions packages/contact-center/ai-assistant/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const jestConfig = require('../../../jest.config.js');

jestConfig.rootDir = '../../../';
jestConfig.testMatch = ['**/ai-assistant/tests/**/*.ts', '**/ai-assistant/tests/**/*.tsx'];

module.exports = jestConfig;
70 changes: 70 additions & 0 deletions packages/contact-center/ai-assistant/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "@webex/cc-ai-assistant",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the AI assistant spec alongside the module

This introduces a new workspace package/widget, but the change contains no ai-assistant module spec or standing-doc/router update, so the new public surface cannot be routed or validated by the repo's SDD docs. Please add the module spec/standing doc updates in the same change as the code.

AGENTS.md reference: AGENTS.md:L68-L68

Useful? React with 👍 / 👎.

"description": "Webex Contact Center Widgets: AI Assistant",
"license": "Cisco's General Terms (https://www.cisco.com/site/us/en/about/legal/contract-experience/index.html)",
"version": "1.28.0-ccwidgets.126",
"main": "dist/index.js",
"types": "dist/types/index.d.ts",
"publishConfig": {
"access": "public"
},
"files": [
"dist/",
"package.json"
],
"scripts": {
"clean": "rm -rf dist && rm -rf node_modules",
"clean:dist": "rm -rf dist",
"build": "yarn run -T tsc",
"build:src": "yarn run clean:dist && webpack",
"build:watch": "webpack --watch",
"test:unit": "tsc --project tsconfig.test.json && jest --coverage",
"test:styles": "eslint",
"deploy:npm": "yarn npm publish"
},
"dependencies": {
"@webex/cc-components": "workspace:*",
"@webex/cc-store": "workspace:*",
"@webex/cc-ui-logging": "workspace:*",
"mobx-react-lite": "^4.1.0",
"react-error-boundary": "^6.0.0",
"typescript": "5.6.3"
},
"devDependencies": {
"@babel/core": "7.25.2",
"@babel/preset-env": "7.25.4",
"@babel/preset-react": "7.24.7",
"@babel/preset-typescript": "7.25.9",
"@eslint/js": "^9.20.0",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.6.2",
"@testing-library/react": "16.0.1",
"@types/jest": "29.5.14",
"@types/react-test-renderer": "18",
"@webex/test-fixtures": "workspace:*",
"babel-jest": "29.7.0",
"babel-loader": "9.2.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.0.1",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.37.4",
"file-loader": "6.2.0",
"globals": "^16.0.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"prettier": "^3.5.1",
"ts-loader": "9.5.1",
"typescript-eslint": "^8.24.1",
"webpack": "5.94.0",
"webpack-cli": "5.1.4",
"webpack-merge": "6.0.1"
},
"peerDependencies": {
"react": ">=18.3.1",
"react-dom": ">=18.3.1"
}
}
26 changes: 26 additions & 0 deletions packages/contact-center/ai-assistant/src/ai-assistant.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type {RealTimeAssistPayload} from '@webex/cc-store';

/**
* Public props for the `AIAssistant` widget. All callbacks are optional —
* the host can opt into any subset.
*/
export interface IAIAssistantProps {
/** Fired when the launcher is clicked and the panel opens. */
onOpen?: () => void;
/** Fired when the agent minimizes the panel to its collapsed bar. */
onMinimize?: () => void;
/** Fired when the minimized bar is restored to the full panel. */
onRestore?: () => void;
/** Fired when the panel is closed back to the launcher. */
onClose?: () => void;
/** Fired when the chat is reset (suggestions, drafts, session flags wiped). */
onClearChat?: () => void;
/** Fired when the fullscreen affordance is toggled. Host owns layout. */
onFullScreenToggle?: (isFullScreen: boolean) => void;
/** Fired each time a fresh real-time assist response arrives for the active task. */
onRealTimeAssistReceived?: (payload: RealTimeAssistPayload) => void;
/** @deprecated Use `onRealTimeAssistReceived` instead. */
onSuggestionReceived?: (payload: RealTimeAssistPayload) => void;
/** Optional extra class applied to the widget root. */
className?: string;
}
71 changes: 71 additions & 0 deletions packages/contact-center/ai-assistant/src/ai-assistant/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, {useCallback} from 'react';
import store from '@webex/cc-store';
import {observer} from 'mobx-react-lite';
import {ErrorBoundary} from 'react-error-boundary';

import {AIAssistantComponent} from '@webex/cc-components';
import type {AIAssistantFeedbackEvent} from '@webex/cc-components';
import type {RealTimeAssistPayload} from '@webex/cc-store';
import {useAiAssistant, REAL_TIME_ASSIST_FLAG} from '../helper';
import {IAIAssistantProps} from '../ai-assistant.types';

const AIAssistantInternal: React.FunctionComponent<IAIAssistantProps> = observer((props) => {
const {currentTask, agentId, featureFlags, realTimeAssist} = store;
const interactionId = currentTask?.data?.interactionId;
const isFeatureEnabled = Boolean(featureFlags?.[REAL_TIME_ASSIST_FLAG]);
const activeRealTimeAssist = interactionId ? realTimeAssist?.[interactionId] || [] : [];

const hookProps = useAiAssistant({
...props,
interactionId,
agentId,
isFeatureEnabled,
realTimeAssist: activeRealTimeAssist,
});

const handleRealTimeAssistAction = useCallback(
(event: AIAssistantFeedbackEvent, assist: RealTimeAssistPayload) => {
const api = store.cc?.apiAIAssistant;
const adaptiveCardId = assist?.data?.adaptiveCardId;
if (!interactionId || !agentId || !adaptiveCardId || !api?.sendRealTimeAssistanceUserAction) return;

void api
.sendRealTimeAssistanceUserAction({
agentId,
interactionId,
adaptiveCardId,
actionId: event.actionId,
languageCode: typeof assist?.data?.languageCode === 'string' ? assist.data.languageCode : undefined,
})
.catch((error) => {
store.logger?.error(`CC-Widgets: sendRealTimeAssistanceUserAction failed - ${error}`, {
module: 'ai-assistant/index.tsx',
method: 'handleRealTimeAssistAction',
});
});
},
[agentId, interactionId]
);

return (
<AIAssistantComponent
{...hookProps}
isFeatureEnabled={isFeatureEnabled}
className={props.className}
onRealTimeAssistAction={handleRealTimeAssistAction}
/>
);
});

const AIAssistant: React.FunctionComponent<IAIAssistantProps> = (props) => (
<ErrorBoundary
fallbackRender={() => <></>}
onError={(error: Error) => {
if (store.onErrorCallback) store.onErrorCallback('AIAssistant', error);
}}
>
<AIAssistantInternal {...props} />
</ErrorBoundary>
);

export {AIAssistant};
Loading
Loading