@exotel-npm-dev/exotel-ai-assist 1.3.6 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/key-components/TransferSummaryCard.d.ts +5 -0
- package/dist/controller/index.js +18 -0
- package/dist/hooks/useExotelAIAssist.d.ts +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +153 -2
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +154 -2
- package/dist/types/index.d.ts +17 -1
- package/package.json +1 -1
- package/src/components/ExotelAIAssistApp.tsx +12 -1
- package/src/components/key-components/TransferSummaryCard.tsx +105 -0
- package/src/controller/index.ts +25 -0
- package/src/hooks/useExotelAIAssist.ts +29 -2
- package/src/index.ts +10 -1
- package/src/react/index.ts +12 -1
- package/src/styles/index.css +41 -0
- package/src/types/index.ts +20 -1
package/dist/controller/index.js
CHANGED
|
@@ -1849,6 +1849,24 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1849
1849
|
};
|
|
1850
1850
|
this.emit("sentiment", sentiment);
|
|
1851
1851
|
}
|
|
1852
|
+
if (event.event_type === "transfer_summary" && event.value) {
|
|
1853
|
+
const summary = typeof event.value.summary === "string" ? event.value.summary.trim() : "";
|
|
1854
|
+
if (!summary) continue;
|
|
1855
|
+
const rawSentiment = event.value.sentiment;
|
|
1856
|
+
const sentimentLabel = rawSentiment === undefined || rawSentiment === null ? null : String(rawSentiment);
|
|
1857
|
+
const transferSummary = {
|
|
1858
|
+
summary,
|
|
1859
|
+
sentiment: sentimentLabel
|
|
1860
|
+
};
|
|
1861
|
+
this.emit("transferSummary", transferSummary);
|
|
1862
|
+
if (sentimentLabel) {
|
|
1863
|
+
const sentiment = {
|
|
1864
|
+
label: sentimentLabel.toLowerCase(),
|
|
1865
|
+
timestamp: now
|
|
1866
|
+
};
|
|
1867
|
+
this.emit("sentiment", sentiment);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1852
1870
|
}
|
|
1853
1871
|
}
|
|
1854
1872
|
_handleStreamStatus(state) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, BotConfig, StreamState } from "../types";
|
|
1
|
+
import { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, TransferSummary, BotConfig, StreamState } from "../types";
|
|
2
2
|
export interface UseExotelAIAssistReturn {
|
|
3
3
|
/**
|
|
4
4
|
* `true` once the WebSocket connection is established and the server has
|
|
@@ -21,6 +21,8 @@ export interface UseExotelAIAssistReturn {
|
|
|
21
21
|
transcripts: TranscriptLine[];
|
|
22
22
|
/** Latest sentiment reading, or null before the first event. */
|
|
23
23
|
sentiment: Sentiment | null;
|
|
24
|
+
/** Warm-transfer summary from handover-context cache, if present. */
|
|
25
|
+
transferSummary: TransferSummary | null;
|
|
24
26
|
lastError: Error | null;
|
|
25
27
|
connect: () => void;
|
|
26
28
|
disconnect: () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import { ExotelAIAssistParams } from "./types";
|
|
|
2
2
|
export declare function mountExotelAIAssist(container: HTMLElement, params: ExotelAIAssistParams): void;
|
|
3
3
|
export declare function unmountExotelAIAssist(container: HTMLElement): void;
|
|
4
4
|
export { ExotelAIAssistController } from "./controller";
|
|
5
|
-
export type { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, ConnectionStatus, StreamState } from "./types";
|
|
5
|
+
export type { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, TransferSummary, TransferSummaryValue, ConnectionStatus, StreamState, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -20175,6 +20175,24 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
20175
20175
|
};
|
|
20176
20176
|
var createLucideIcon$1 = createLucideIcon;
|
|
20177
20177
|
|
|
20178
|
+
/**
|
|
20179
|
+
* lucide-react v0.0.1 - ISC
|
|
20180
|
+
*/
|
|
20181
|
+
|
|
20182
|
+
const ChevronDown = createLucideIcon$1("ChevronDown", [["path", {
|
|
20183
|
+
d: "m6 9 6 6 6-6",
|
|
20184
|
+
key: "qrunsl"
|
|
20185
|
+
}]]);
|
|
20186
|
+
|
|
20187
|
+
/**
|
|
20188
|
+
* lucide-react v0.0.1 - ISC
|
|
20189
|
+
*/
|
|
20190
|
+
|
|
20191
|
+
const ChevronUp = createLucideIcon$1("ChevronUp", [["path", {
|
|
20192
|
+
d: "m18 15-6-6-6 6",
|
|
20193
|
+
key: "153udz"
|
|
20194
|
+
}]]);
|
|
20195
|
+
|
|
20178
20196
|
/**
|
|
20179
20197
|
* lucide-react v0.0.1 - ISC
|
|
20180
20198
|
*/
|
|
@@ -22332,6 +22350,24 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
22332
22350
|
};
|
|
22333
22351
|
this.emit("sentiment", sentiment);
|
|
22334
22352
|
}
|
|
22353
|
+
if (event.event_type === "transfer_summary" && event.value) {
|
|
22354
|
+
const summary = typeof event.value.summary === "string" ? event.value.summary.trim() : "";
|
|
22355
|
+
if (!summary) continue;
|
|
22356
|
+
const rawSentiment = event.value.sentiment;
|
|
22357
|
+
const sentimentLabel = rawSentiment === undefined || rawSentiment === null ? null : String(rawSentiment);
|
|
22358
|
+
const transferSummary = {
|
|
22359
|
+
summary,
|
|
22360
|
+
sentiment: sentimentLabel
|
|
22361
|
+
};
|
|
22362
|
+
this.emit("transferSummary", transferSummary);
|
|
22363
|
+
if (sentimentLabel) {
|
|
22364
|
+
const sentiment = {
|
|
22365
|
+
label: sentimentLabel.toLowerCase(),
|
|
22366
|
+
timestamp: now
|
|
22367
|
+
};
|
|
22368
|
+
this.emit("sentiment", sentiment);
|
|
22369
|
+
}
|
|
22370
|
+
}
|
|
22335
22371
|
}
|
|
22336
22372
|
}
|
|
22337
22373
|
_handleStreamStatus(state) {
|
|
@@ -22360,6 +22396,7 @@ function useExotelAIAssist(params) {
|
|
|
22360
22396
|
const [suggestions, setSuggestions] = reactExports.useState([]);
|
|
22361
22397
|
const [transcripts, setTranscripts] = reactExports.useState([]);
|
|
22362
22398
|
const [sentiment, setSentiment] = reactExports.useState(null);
|
|
22399
|
+
const [transferSummary, setTransferSummary] = reactExports.useState(null);
|
|
22363
22400
|
const [botConfig, setBotConfig] = reactExports.useState(null);
|
|
22364
22401
|
const [lastError, setLastError] = reactExports.useState(null);
|
|
22365
22402
|
const paramHash = Utils.hash(params);
|
|
@@ -22369,6 +22406,7 @@ function useExotelAIAssist(params) {
|
|
|
22369
22406
|
setSuggestions([]);
|
|
22370
22407
|
setTranscripts([]);
|
|
22371
22408
|
setSentiment(null);
|
|
22409
|
+
setTransferSummary(null);
|
|
22372
22410
|
setBotConfig(null);
|
|
22373
22411
|
setLastError(null);
|
|
22374
22412
|
const ctrl = new ExotelAIAssistController(params);
|
|
@@ -22379,6 +22417,7 @@ function useExotelAIAssist(params) {
|
|
|
22379
22417
|
setSuggestions([]);
|
|
22380
22418
|
setTranscripts([]);
|
|
22381
22419
|
setSentiment(null);
|
|
22420
|
+
setTransferSummary(null);
|
|
22382
22421
|
}
|
|
22383
22422
|
});
|
|
22384
22423
|
ctrl.on("streamState", setStreamState);
|
|
@@ -22388,6 +22427,7 @@ function useExotelAIAssist(params) {
|
|
|
22388
22427
|
return [...prev, s].slice(-50);
|
|
22389
22428
|
}));
|
|
22390
22429
|
ctrl.on("sentiment", setSentiment);
|
|
22430
|
+
ctrl.on("transferSummary", setTransferSummary);
|
|
22391
22431
|
ctrl.on("transcript", lines => setTranscripts(prev => {
|
|
22392
22432
|
const map = new Map(prev.map(l => [l.id, l]));
|
|
22393
22433
|
for (const l of lines) map.set(l.id, l);
|
|
@@ -22440,6 +22480,7 @@ function useExotelAIAssist(params) {
|
|
|
22440
22480
|
suggestions,
|
|
22441
22481
|
transcripts,
|
|
22442
22482
|
sentiment,
|
|
22483
|
+
transferSummary,
|
|
22443
22484
|
botConfig,
|
|
22444
22485
|
lastError,
|
|
22445
22486
|
connect,
|
|
@@ -22474,7 +22515,7 @@ function styleInject(css, ref) {
|
|
|
22474
22515
|
}
|
|
22475
22516
|
}
|
|
22476
22517
|
|
|
22477
|
-
var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon,.oa-feedback-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover,.oa-feedback-icon:hover{background:#f9fafb;color:#374151}.oa-feedback-icon--positive{background:#dcfce7!important;border-color:#86efac!important;color:#16a34a!important}.oa-feedback-icon--negative{background:#fee2e2!important;border-color:#fca5a5!important;color:#dc2626!important}.oa-feedback-icon:disabled{cursor:default;opacity:.5}.oa-feedback-icon--negative:disabled,.oa-feedback-icon--positive:disabled{opacity:1}.oa-feedback-reason-btn{align-items:center;background:#fef2f2;border:1px solid #fecaca;border-radius:16px;color:#991b1b;cursor:pointer;display:inline-flex;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;padding:4px 12px;transition:background .15s,border-color .15s}.oa-feedback-reason-btn:hover:not(:disabled){background:#fee2e2;border-color:#f87171}.oa-feedback-reason-btn--selected{background:#dc2626!important;border-color:#dc2626!important;color:#fff!important}.oa-feedback-reason-btn:disabled{cursor:default;opacity:.45}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-muted{color:#6b7280}";
|
|
22518
|
+
var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon,.oa-feedback-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover,.oa-feedback-icon:hover{background:#f9fafb;color:#374151}.oa-feedback-icon--positive{background:#dcfce7!important;border-color:#86efac!important;color:#16a34a!important}.oa-feedback-icon--negative{background:#fee2e2!important;border-color:#fca5a5!important;color:#dc2626!important}.oa-feedback-icon:disabled{cursor:default;opacity:.5}.oa-feedback-icon--negative:disabled,.oa-feedback-icon--positive:disabled{opacity:1}.oa-feedback-reason-btn{align-items:center;background:#fef2f2;border:1px solid #fecaca;border-radius:16px;color:#991b1b;cursor:pointer;display:inline-flex;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;padding:4px 12px;transition:background .15s,border-color .15s}.oa-feedback-reason-btn:hover:not(:disabled){background:#fee2e2;border-color:#f87171}.oa-feedback-reason-btn--selected{background:#dc2626!important;border-color:#dc2626!important;color:#fff!important}.oa-feedback-reason-btn:disabled{cursor:default;opacity:.45}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-transfer-summary-card{background:#f9f7ff;border:1px solid #ece7f8;border-radius:12px;padding:12px 14px}.oa-transfer-summary-header{user-select:none}.oa-transfer-summary-body,.oa-transfer-summary-text{text-align:left}.oa-transfer-summary-badge{align-items:center;border-radius:999px;display:inline-flex;font-size:13px;font-weight:600;padding:2px 10px}.oa-transfer-summary-badge--positive{background:#dcfce7;color:#166534}.oa-transfer-summary-badge--neutral{background:#f3f4f6;color:#374151}.oa-transfer-summary-badge--negative{background:#ffe0db;color:#9f1239}.oa-muted{color:#6b7280}";
|
|
22478
22519
|
styleInject(css_248z);
|
|
22479
22520
|
|
|
22480
22521
|
function Sparkle() {
|
|
@@ -23088,6 +23129,113 @@ function TranscriptTab({
|
|
|
23088
23129
|
});
|
|
23089
23130
|
}
|
|
23090
23131
|
|
|
23132
|
+
/** Renders `**bold**` markdown spans; leaves the rest as plain text. */
|
|
23133
|
+
function renderSummaryWithBold(text) {
|
|
23134
|
+
const parts = text.split(/(\*\*[^*]+\*\*)/g);
|
|
23135
|
+
return parts.map((part, i) => {
|
|
23136
|
+
if (part.startsWith("**") && part.endsWith("**") && part.length > 4) {
|
|
23137
|
+
return jsxRuntimeExports.jsx("strong", {
|
|
23138
|
+
children: part.slice(2, -2)
|
|
23139
|
+
}, i);
|
|
23140
|
+
}
|
|
23141
|
+
return jsxRuntimeExports.jsx(React.Fragment, {
|
|
23142
|
+
children: part
|
|
23143
|
+
}, i);
|
|
23144
|
+
});
|
|
23145
|
+
}
|
|
23146
|
+
/** Normalizes inline bullet markers into separate left-aligned lines with bold support. */
|
|
23147
|
+
function formatSummaryLines(summary) {
|
|
23148
|
+
const normalizedSummary = summary.replace(/\s+([*-•])\s+/g, "\n$1 ");
|
|
23149
|
+
const lines = normalizedSummary.split("\n").map(line => line.trim()).filter(Boolean);
|
|
23150
|
+
return lines.map((line, index) => {
|
|
23151
|
+
const isBulletLine = /^[-*•]\s+/.test(line);
|
|
23152
|
+
const lineContent = isBulletLine ? line.replace(/^[-*•]\s+/, "") : line;
|
|
23153
|
+
return jsxRuntimeExports.jsxs("span", {
|
|
23154
|
+
style: {
|
|
23155
|
+
display: "block"
|
|
23156
|
+
},
|
|
23157
|
+
children: [isBulletLine ? "• " : "", renderSummaryWithBold(lineContent)]
|
|
23158
|
+
}, `line-${index}`);
|
|
23159
|
+
});
|
|
23160
|
+
}
|
|
23161
|
+
function TransferSummaryCard({
|
|
23162
|
+
data
|
|
23163
|
+
}) {
|
|
23164
|
+
var _a, _b;
|
|
23165
|
+
const [expanded, setExpanded] = reactExports.useState(true);
|
|
23166
|
+
const sentiment = (_a = data.sentiment) !== null && _a !== void 0 ? _a : "";
|
|
23167
|
+
const lower = sentiment.toLowerCase();
|
|
23168
|
+
const icon = lower.includes("positive") ? jsxRuntimeExports.jsx(Smile, {
|
|
23169
|
+
size: 16
|
|
23170
|
+
}) : lower.includes("neutral") ? jsxRuntimeExports.jsx(Meh, {
|
|
23171
|
+
size: 16
|
|
23172
|
+
}) : lower.includes("negative") ? jsxRuntimeExports.jsx(Frown, {
|
|
23173
|
+
size: 16
|
|
23174
|
+
}) : null;
|
|
23175
|
+
const badgeClass = lower.includes("positive") ? "oa-transfer-summary-badge oa-transfer-summary-badge--positive" : lower.includes("negative") ? "oa-transfer-summary-badge oa-transfer-summary-badge--negative" : "oa-transfer-summary-badge oa-transfer-summary-badge--neutral";
|
|
23176
|
+
return jsxRuntimeExports.jsxs("div", {
|
|
23177
|
+
className: "oa-transfer-summary-card",
|
|
23178
|
+
style: {
|
|
23179
|
+
marginBottom: 12
|
|
23180
|
+
},
|
|
23181
|
+
children: [jsxRuntimeExports.jsxs("div", {
|
|
23182
|
+
className: "oa-transfer-summary-header",
|
|
23183
|
+
onClick: () => setExpanded(v => !v),
|
|
23184
|
+
style: {
|
|
23185
|
+
display: "flex",
|
|
23186
|
+
alignItems: "center",
|
|
23187
|
+
justifyContent: "space-between",
|
|
23188
|
+
cursor: "pointer"
|
|
23189
|
+
},
|
|
23190
|
+
children: [jsxRuntimeExports.jsxs("div", {
|
|
23191
|
+
style: {
|
|
23192
|
+
display: "flex",
|
|
23193
|
+
alignItems: "center",
|
|
23194
|
+
gap: 8
|
|
23195
|
+
},
|
|
23196
|
+
children: [jsxRuntimeExports.jsx("span", {
|
|
23197
|
+
style: {
|
|
23198
|
+
fontWeight: 700,
|
|
23199
|
+
fontSize: 16,
|
|
23200
|
+
color: "#111827"
|
|
23201
|
+
},
|
|
23202
|
+
children: "Transfer Summary"
|
|
23203
|
+
}), sentiment ? jsxRuntimeExports.jsx("span", {
|
|
23204
|
+
className: badgeClass,
|
|
23205
|
+
children: jsxRuntimeExports.jsxs("span", {
|
|
23206
|
+
style: {
|
|
23207
|
+
display: "inline-flex",
|
|
23208
|
+
alignItems: "center",
|
|
23209
|
+
gap: 4
|
|
23210
|
+
},
|
|
23211
|
+
children: [icon, sentiment]
|
|
23212
|
+
})
|
|
23213
|
+
}) : null]
|
|
23214
|
+
}), expanded ? jsxRuntimeExports.jsx(ChevronUp, {
|
|
23215
|
+
size: 16
|
|
23216
|
+
}) : jsxRuntimeExports.jsx(ChevronDown, {
|
|
23217
|
+
size: 16
|
|
23218
|
+
})]
|
|
23219
|
+
}), expanded ? jsxRuntimeExports.jsx("div", {
|
|
23220
|
+
className: "oa-transfer-summary-body",
|
|
23221
|
+
style: {
|
|
23222
|
+
marginTop: 8
|
|
23223
|
+
},
|
|
23224
|
+
children: jsxRuntimeExports.jsx("div", {
|
|
23225
|
+
className: "oa-transfer-summary-text",
|
|
23226
|
+
style: {
|
|
23227
|
+
color: "#374151",
|
|
23228
|
+
margin: 0,
|
|
23229
|
+
lineHeight: 1.5,
|
|
23230
|
+
fontSize: 14,
|
|
23231
|
+
textAlign: "left"
|
|
23232
|
+
},
|
|
23233
|
+
children: formatSummaryLines((_b = data.summary) !== null && _b !== void 0 ? _b : "")
|
|
23234
|
+
})
|
|
23235
|
+
}) : null]
|
|
23236
|
+
});
|
|
23237
|
+
}
|
|
23238
|
+
|
|
23091
23239
|
function ExotelAIAssist(_a) {
|
|
23092
23240
|
var {
|
|
23093
23241
|
className,
|
|
@@ -23100,6 +23248,7 @@ function ExotelAIAssist(_a) {
|
|
|
23100
23248
|
suggestions,
|
|
23101
23249
|
transcripts,
|
|
23102
23250
|
sentiment,
|
|
23251
|
+
transferSummary,
|
|
23103
23252
|
botConfig,
|
|
23104
23253
|
sendSuggestionFeedback
|
|
23105
23254
|
} = useExotelAIAssist(params);
|
|
@@ -23145,7 +23294,9 @@ function ExotelAIAssist(_a) {
|
|
|
23145
23294
|
children: [jsxRuntimeExports.jsx(Header, {
|
|
23146
23295
|
sentiment: sentiment,
|
|
23147
23296
|
botConfig: botConfig
|
|
23148
|
-
}),
|
|
23297
|
+
}), transferSummary ? jsxRuntimeExports.jsx(TransferSummaryCard, {
|
|
23298
|
+
data: transferSummary
|
|
23299
|
+
}) : null, streamState !== "throttled" ? jsxRuntimeExports.jsxs(Root2$1, {
|
|
23149
23300
|
defaultValue: "suggestions",
|
|
23150
23301
|
className: "oa-tabs",
|
|
23151
23302
|
style: {
|
package/dist/react/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export { ExotelAIAssist, ExotelAIAssistApp } from "../components/ExotelAIAssistA
|
|
|
2
2
|
export { ExotelAIAssistProvider, useExotelAIAssistContext } from "../components/ExotelAIAssistProvider";
|
|
3
3
|
export { useExotelAIAssist } from "../hooks/useExotelAIAssist";
|
|
4
4
|
export { ExotelAIAssistController } from "../controller";
|
|
5
|
-
export type { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, ConnectionStatus, StreamState } from "../types";
|
|
5
|
+
export type { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, TransferSummary, TransferSummaryValue, ConnectionStatus, StreamState, } from "../types";
|
|
6
6
|
export type { UseExotelAIAssistReturn } from "../hooks/useExotelAIAssist";
|
|
7
7
|
export type { ExotelAIAssistProps } from "../components/ExotelAIAssistApp";
|
|
8
8
|
export type { ExotelAIAssistProviderProps } from "../components/ExotelAIAssistProvider";
|
|
9
|
+
export { TransferSummaryCard } from "../components/key-components/TransferSummaryCard";
|
|
10
|
+
export type { TransferSummaryCardProps } from "../components/key-components/TransferSummaryCard";
|
package/dist/react/index.js
CHANGED
|
@@ -5457,6 +5457,24 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
5457
5457
|
};
|
|
5458
5458
|
var createLucideIcon$1 = createLucideIcon;
|
|
5459
5459
|
|
|
5460
|
+
/**
|
|
5461
|
+
* lucide-react v0.0.1 - ISC
|
|
5462
|
+
*/
|
|
5463
|
+
|
|
5464
|
+
const ChevronDown = createLucideIcon$1("ChevronDown", [["path", {
|
|
5465
|
+
d: "m6 9 6 6 6-6",
|
|
5466
|
+
key: "qrunsl"
|
|
5467
|
+
}]]);
|
|
5468
|
+
|
|
5469
|
+
/**
|
|
5470
|
+
* lucide-react v0.0.1 - ISC
|
|
5471
|
+
*/
|
|
5472
|
+
|
|
5473
|
+
const ChevronUp = createLucideIcon$1("ChevronUp", [["path", {
|
|
5474
|
+
d: "m18 15-6-6-6 6",
|
|
5475
|
+
key: "153udz"
|
|
5476
|
+
}]]);
|
|
5477
|
+
|
|
5460
5478
|
/**
|
|
5461
5479
|
* lucide-react v0.0.1 - ISC
|
|
5462
5480
|
*/
|
|
@@ -7618,6 +7636,24 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
7618
7636
|
};
|
|
7619
7637
|
this.emit("sentiment", sentiment);
|
|
7620
7638
|
}
|
|
7639
|
+
if (event.event_type === "transfer_summary" && event.value) {
|
|
7640
|
+
const summary = typeof event.value.summary === "string" ? event.value.summary.trim() : "";
|
|
7641
|
+
if (!summary) continue;
|
|
7642
|
+
const rawSentiment = event.value.sentiment;
|
|
7643
|
+
const sentimentLabel = rawSentiment === undefined || rawSentiment === null ? null : String(rawSentiment);
|
|
7644
|
+
const transferSummary = {
|
|
7645
|
+
summary,
|
|
7646
|
+
sentiment: sentimentLabel
|
|
7647
|
+
};
|
|
7648
|
+
this.emit("transferSummary", transferSummary);
|
|
7649
|
+
if (sentimentLabel) {
|
|
7650
|
+
const sentiment = {
|
|
7651
|
+
label: sentimentLabel.toLowerCase(),
|
|
7652
|
+
timestamp: now
|
|
7653
|
+
};
|
|
7654
|
+
this.emit("sentiment", sentiment);
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7621
7657
|
}
|
|
7622
7658
|
}
|
|
7623
7659
|
_handleStreamStatus(state) {
|
|
@@ -7646,6 +7682,7 @@ function useExotelAIAssist(params) {
|
|
|
7646
7682
|
const [suggestions, setSuggestions] = React.useState([]);
|
|
7647
7683
|
const [transcripts, setTranscripts] = React.useState([]);
|
|
7648
7684
|
const [sentiment, setSentiment] = React.useState(null);
|
|
7685
|
+
const [transferSummary, setTransferSummary] = React.useState(null);
|
|
7649
7686
|
const [botConfig, setBotConfig] = React.useState(null);
|
|
7650
7687
|
const [lastError, setLastError] = React.useState(null);
|
|
7651
7688
|
const paramHash = Utils.hash(params);
|
|
@@ -7655,6 +7692,7 @@ function useExotelAIAssist(params) {
|
|
|
7655
7692
|
setSuggestions([]);
|
|
7656
7693
|
setTranscripts([]);
|
|
7657
7694
|
setSentiment(null);
|
|
7695
|
+
setTransferSummary(null);
|
|
7658
7696
|
setBotConfig(null);
|
|
7659
7697
|
setLastError(null);
|
|
7660
7698
|
const ctrl = new ExotelAIAssistController(params);
|
|
@@ -7665,6 +7703,7 @@ function useExotelAIAssist(params) {
|
|
|
7665
7703
|
setSuggestions([]);
|
|
7666
7704
|
setTranscripts([]);
|
|
7667
7705
|
setSentiment(null);
|
|
7706
|
+
setTransferSummary(null);
|
|
7668
7707
|
}
|
|
7669
7708
|
});
|
|
7670
7709
|
ctrl.on("streamState", setStreamState);
|
|
@@ -7674,6 +7713,7 @@ function useExotelAIAssist(params) {
|
|
|
7674
7713
|
return [...prev, s].slice(-50);
|
|
7675
7714
|
}));
|
|
7676
7715
|
ctrl.on("sentiment", setSentiment);
|
|
7716
|
+
ctrl.on("transferSummary", setTransferSummary);
|
|
7677
7717
|
ctrl.on("transcript", lines => setTranscripts(prev => {
|
|
7678
7718
|
const map = new Map(prev.map(l => [l.id, l]));
|
|
7679
7719
|
for (const l of lines) map.set(l.id, l);
|
|
@@ -7726,6 +7766,7 @@ function useExotelAIAssist(params) {
|
|
|
7726
7766
|
suggestions,
|
|
7727
7767
|
transcripts,
|
|
7728
7768
|
sentiment,
|
|
7769
|
+
transferSummary,
|
|
7729
7770
|
botConfig,
|
|
7730
7771
|
lastError,
|
|
7731
7772
|
connect,
|
|
@@ -7760,7 +7801,7 @@ function styleInject(css, ref) {
|
|
|
7760
7801
|
}
|
|
7761
7802
|
}
|
|
7762
7803
|
|
|
7763
|
-
var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon,.oa-feedback-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover,.oa-feedback-icon:hover{background:#f9fafb;color:#374151}.oa-feedback-icon--positive{background:#dcfce7!important;border-color:#86efac!important;color:#16a34a!important}.oa-feedback-icon--negative{background:#fee2e2!important;border-color:#fca5a5!important;color:#dc2626!important}.oa-feedback-icon:disabled{cursor:default;opacity:.5}.oa-feedback-icon--negative:disabled,.oa-feedback-icon--positive:disabled{opacity:1}.oa-feedback-reason-btn{align-items:center;background:#fef2f2;border:1px solid #fecaca;border-radius:16px;color:#991b1b;cursor:pointer;display:inline-flex;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;padding:4px 12px;transition:background .15s,border-color .15s}.oa-feedback-reason-btn:hover:not(:disabled){background:#fee2e2;border-color:#f87171}.oa-feedback-reason-btn--selected{background:#dc2626!important;border-color:#dc2626!important;color:#fff!important}.oa-feedback-reason-btn:disabled{cursor:default;opacity:.45}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-muted{color:#6b7280}";
|
|
7804
|
+
var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon,.oa-feedback-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover,.oa-feedback-icon:hover{background:#f9fafb;color:#374151}.oa-feedback-icon--positive{background:#dcfce7!important;border-color:#86efac!important;color:#16a34a!important}.oa-feedback-icon--negative{background:#fee2e2!important;border-color:#fca5a5!important;color:#dc2626!important}.oa-feedback-icon:disabled{cursor:default;opacity:.5}.oa-feedback-icon--negative:disabled,.oa-feedback-icon--positive:disabled{opacity:1}.oa-feedback-reason-btn{align-items:center;background:#fef2f2;border:1px solid #fecaca;border-radius:16px;color:#991b1b;cursor:pointer;display:inline-flex;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;padding:4px 12px;transition:background .15s,border-color .15s}.oa-feedback-reason-btn:hover:not(:disabled){background:#fee2e2;border-color:#f87171}.oa-feedback-reason-btn--selected{background:#dc2626!important;border-color:#dc2626!important;color:#fff!important}.oa-feedback-reason-btn:disabled{cursor:default;opacity:.45}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-transfer-summary-card{background:#f9f7ff;border:1px solid #ece7f8;border-radius:12px;padding:12px 14px}.oa-transfer-summary-header{user-select:none}.oa-transfer-summary-body,.oa-transfer-summary-text{text-align:left}.oa-transfer-summary-badge{align-items:center;border-radius:999px;display:inline-flex;font-size:13px;font-weight:600;padding:2px 10px}.oa-transfer-summary-badge--positive{background:#dcfce7;color:#166534}.oa-transfer-summary-badge--neutral{background:#f3f4f6;color:#374151}.oa-transfer-summary-badge--negative{background:#ffe0db;color:#9f1239}.oa-muted{color:#6b7280}";
|
|
7764
7805
|
styleInject(css_248z);
|
|
7765
7806
|
|
|
7766
7807
|
function Sparkle() {
|
|
@@ -8374,6 +8415,113 @@ function TranscriptTab({
|
|
|
8374
8415
|
});
|
|
8375
8416
|
}
|
|
8376
8417
|
|
|
8418
|
+
/** Renders `**bold**` markdown spans; leaves the rest as plain text. */
|
|
8419
|
+
function renderSummaryWithBold(text) {
|
|
8420
|
+
const parts = text.split(/(\*\*[^*]+\*\*)/g);
|
|
8421
|
+
return parts.map((part, i) => {
|
|
8422
|
+
if (part.startsWith("**") && part.endsWith("**") && part.length > 4) {
|
|
8423
|
+
return jsxRuntime.jsx("strong", {
|
|
8424
|
+
children: part.slice(2, -2)
|
|
8425
|
+
}, i);
|
|
8426
|
+
}
|
|
8427
|
+
return jsxRuntime.jsx(React__namespace.default.Fragment, {
|
|
8428
|
+
children: part
|
|
8429
|
+
}, i);
|
|
8430
|
+
});
|
|
8431
|
+
}
|
|
8432
|
+
/** Normalizes inline bullet markers into separate left-aligned lines with bold support. */
|
|
8433
|
+
function formatSummaryLines(summary) {
|
|
8434
|
+
const normalizedSummary = summary.replace(/\s+([*-•])\s+/g, "\n$1 ");
|
|
8435
|
+
const lines = normalizedSummary.split("\n").map(line => line.trim()).filter(Boolean);
|
|
8436
|
+
return lines.map((line, index) => {
|
|
8437
|
+
const isBulletLine = /^[-*•]\s+/.test(line);
|
|
8438
|
+
const lineContent = isBulletLine ? line.replace(/^[-*•]\s+/, "") : line;
|
|
8439
|
+
return jsxRuntime.jsxs("span", {
|
|
8440
|
+
style: {
|
|
8441
|
+
display: "block"
|
|
8442
|
+
},
|
|
8443
|
+
children: [isBulletLine ? "• " : "", renderSummaryWithBold(lineContent)]
|
|
8444
|
+
}, `line-${index}`);
|
|
8445
|
+
});
|
|
8446
|
+
}
|
|
8447
|
+
function TransferSummaryCard({
|
|
8448
|
+
data
|
|
8449
|
+
}) {
|
|
8450
|
+
var _a, _b;
|
|
8451
|
+
const [expanded, setExpanded] = React.useState(true);
|
|
8452
|
+
const sentiment = (_a = data.sentiment) !== null && _a !== void 0 ? _a : "";
|
|
8453
|
+
const lower = sentiment.toLowerCase();
|
|
8454
|
+
const icon = lower.includes("positive") ? jsxRuntime.jsx(Smile, {
|
|
8455
|
+
size: 16
|
|
8456
|
+
}) : lower.includes("neutral") ? jsxRuntime.jsx(Meh, {
|
|
8457
|
+
size: 16
|
|
8458
|
+
}) : lower.includes("negative") ? jsxRuntime.jsx(Frown, {
|
|
8459
|
+
size: 16
|
|
8460
|
+
}) : null;
|
|
8461
|
+
const badgeClass = lower.includes("positive") ? "oa-transfer-summary-badge oa-transfer-summary-badge--positive" : lower.includes("negative") ? "oa-transfer-summary-badge oa-transfer-summary-badge--negative" : "oa-transfer-summary-badge oa-transfer-summary-badge--neutral";
|
|
8462
|
+
return jsxRuntime.jsxs("div", {
|
|
8463
|
+
className: "oa-transfer-summary-card",
|
|
8464
|
+
style: {
|
|
8465
|
+
marginBottom: 12
|
|
8466
|
+
},
|
|
8467
|
+
children: [jsxRuntime.jsxs("div", {
|
|
8468
|
+
className: "oa-transfer-summary-header",
|
|
8469
|
+
onClick: () => setExpanded(v => !v),
|
|
8470
|
+
style: {
|
|
8471
|
+
display: "flex",
|
|
8472
|
+
alignItems: "center",
|
|
8473
|
+
justifyContent: "space-between",
|
|
8474
|
+
cursor: "pointer"
|
|
8475
|
+
},
|
|
8476
|
+
children: [jsxRuntime.jsxs("div", {
|
|
8477
|
+
style: {
|
|
8478
|
+
display: "flex",
|
|
8479
|
+
alignItems: "center",
|
|
8480
|
+
gap: 8
|
|
8481
|
+
},
|
|
8482
|
+
children: [jsxRuntime.jsx("span", {
|
|
8483
|
+
style: {
|
|
8484
|
+
fontWeight: 700,
|
|
8485
|
+
fontSize: 16,
|
|
8486
|
+
color: "#111827"
|
|
8487
|
+
},
|
|
8488
|
+
children: "Transfer Summary"
|
|
8489
|
+
}), sentiment ? jsxRuntime.jsx("span", {
|
|
8490
|
+
className: badgeClass,
|
|
8491
|
+
children: jsxRuntime.jsxs("span", {
|
|
8492
|
+
style: {
|
|
8493
|
+
display: "inline-flex",
|
|
8494
|
+
alignItems: "center",
|
|
8495
|
+
gap: 4
|
|
8496
|
+
},
|
|
8497
|
+
children: [icon, sentiment]
|
|
8498
|
+
})
|
|
8499
|
+
}) : null]
|
|
8500
|
+
}), expanded ? jsxRuntime.jsx(ChevronUp, {
|
|
8501
|
+
size: 16
|
|
8502
|
+
}) : jsxRuntime.jsx(ChevronDown, {
|
|
8503
|
+
size: 16
|
|
8504
|
+
})]
|
|
8505
|
+
}), expanded ? jsxRuntime.jsx("div", {
|
|
8506
|
+
className: "oa-transfer-summary-body",
|
|
8507
|
+
style: {
|
|
8508
|
+
marginTop: 8
|
|
8509
|
+
},
|
|
8510
|
+
children: jsxRuntime.jsx("div", {
|
|
8511
|
+
className: "oa-transfer-summary-text",
|
|
8512
|
+
style: {
|
|
8513
|
+
color: "#374151",
|
|
8514
|
+
margin: 0,
|
|
8515
|
+
lineHeight: 1.5,
|
|
8516
|
+
fontSize: 14,
|
|
8517
|
+
textAlign: "left"
|
|
8518
|
+
},
|
|
8519
|
+
children: formatSummaryLines((_b = data.summary) !== null && _b !== void 0 ? _b : "")
|
|
8520
|
+
})
|
|
8521
|
+
}) : null]
|
|
8522
|
+
});
|
|
8523
|
+
}
|
|
8524
|
+
|
|
8377
8525
|
function ExotelAIAssist(_a) {
|
|
8378
8526
|
var {
|
|
8379
8527
|
className,
|
|
@@ -8386,6 +8534,7 @@ function ExotelAIAssist(_a) {
|
|
|
8386
8534
|
suggestions,
|
|
8387
8535
|
transcripts,
|
|
8388
8536
|
sentiment,
|
|
8537
|
+
transferSummary,
|
|
8389
8538
|
botConfig,
|
|
8390
8539
|
sendSuggestionFeedback
|
|
8391
8540
|
} = useExotelAIAssist(params);
|
|
@@ -8431,7 +8580,9 @@ function ExotelAIAssist(_a) {
|
|
|
8431
8580
|
children: [jsxRuntime.jsx(Header, {
|
|
8432
8581
|
sentiment: sentiment,
|
|
8433
8582
|
botConfig: botConfig
|
|
8434
|
-
}),
|
|
8583
|
+
}), transferSummary ? jsxRuntime.jsx(TransferSummaryCard, {
|
|
8584
|
+
data: transferSummary
|
|
8585
|
+
}) : null, streamState !== "throttled" ? jsxRuntime.jsxs(Root2$1, {
|
|
8435
8586
|
defaultValue: "suggestions",
|
|
8436
8587
|
className: "oa-tabs",
|
|
8437
8588
|
style: {
|
|
@@ -8516,5 +8667,6 @@ exports.ExotelAIAssist = ExotelAIAssist;
|
|
|
8516
8667
|
exports.ExotelAIAssistApp = ExotelAIAssist;
|
|
8517
8668
|
exports.ExotelAIAssistController = ExotelAIAssistController;
|
|
8518
8669
|
exports.ExotelAIAssistProvider = ExotelAIAssistProvider;
|
|
8670
|
+
exports.TransferSummaryCard = TransferSummaryCard;
|
|
8519
8671
|
exports.useExotelAIAssist = useExotelAIAssist;
|
|
8520
8672
|
exports.useExotelAIAssistContext = useExotelAIAssistContext;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -88,7 +88,21 @@ interface WssSuggestionEvent {
|
|
|
88
88
|
event_type: "suggestion";
|
|
89
89
|
value: SuggestionValue;
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
/** Value payload for handover / warm-transfer summary events. */
|
|
92
|
+
export interface TransferSummaryValue {
|
|
93
|
+
summary?: string;
|
|
94
|
+
sentiment?: string | number | null;
|
|
95
|
+
}
|
|
96
|
+
/** Parsed transfer summary shown above the panel tabs. */
|
|
97
|
+
export interface TransferSummary {
|
|
98
|
+
summary: string;
|
|
99
|
+
sentiment: string | null;
|
|
100
|
+
}
|
|
101
|
+
interface WssTransferSummaryEvent {
|
|
102
|
+
event_type: "transfer_summary";
|
|
103
|
+
value: TransferSummaryValue;
|
|
104
|
+
}
|
|
105
|
+
export type WssEvent = WssTranscriptEvent | WssSentimentEvent | WssSuggestionEvent | WssTransferSummaryEvent;
|
|
92
106
|
export interface InitialHandshakeResponse {
|
|
93
107
|
type: string;
|
|
94
108
|
config: BotConfig;
|
|
@@ -104,6 +118,8 @@ export interface ControllerEvents {
|
|
|
104
118
|
suggestion: (data: Suggestion) => void;
|
|
105
119
|
transcript: (lines: TranscriptLine[]) => void;
|
|
106
120
|
sentiment: (data: Sentiment) => void;
|
|
121
|
+
/** Warm-transfer / handover summary pushed at stream-setup. */
|
|
122
|
+
transferSummary: (data: TransferSummary) => void;
|
|
107
123
|
/** Internal: bot feature-flag config. Consumed by the UI component only. */
|
|
108
124
|
botConfig: (config: BotConfig) => void;
|
|
109
125
|
/**
|
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ import "../styles/index.css";
|
|
|
10
10
|
import { Header } from "./key-components/Header";
|
|
11
11
|
import { SuggestionsTab } from "./key-components/SuggestionTab";
|
|
12
12
|
import { TranscriptTab } from "./key-components/TranscriptTab";
|
|
13
|
+
import { TransferSummaryCard } from "./key-components/TransferSummaryCard";
|
|
13
14
|
import { EmptyState } from "./EmptyState";
|
|
14
15
|
|
|
15
16
|
export interface ExotelAIAssistProps extends ExotelAIAssistParams {
|
|
@@ -19,7 +20,16 @@ export interface ExotelAIAssistProps extends ExotelAIAssistParams {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function ExotelAIAssist({ className, onReady, ...params }: ExotelAIAssistProps): JSX.Element {
|
|
22
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
streamState,
|
|
25
|
+
isReady,
|
|
26
|
+
suggestions,
|
|
27
|
+
transcripts,
|
|
28
|
+
sentiment,
|
|
29
|
+
transferSummary,
|
|
30
|
+
botConfig,
|
|
31
|
+
sendSuggestionFeedback,
|
|
32
|
+
} = useExotelAIAssist(params as ExotelAIAssistParams);
|
|
23
33
|
|
|
24
34
|
const onReadyRef = useRef(onReady);
|
|
25
35
|
onReadyRef.current = onReady;
|
|
@@ -54,6 +64,7 @@ export function ExotelAIAssist({ className, onReady, ...params }: ExotelAIAssist
|
|
|
54
64
|
<div className={`oa-panel${className ? ` ${className}` : ""}`}>
|
|
55
65
|
<div style={{ flex: 1, width: "100%", minHeight: 0, padding: "0 16px", display: "flex", flexDirection: "column" }}>
|
|
56
66
|
<Header sentiment={sentiment} botConfig={botConfig} />
|
|
67
|
+
{transferSummary ? <TransferSummaryCard data={transferSummary} /> : null}
|
|
57
68
|
|
|
58
69
|
{streamState !== "throttled" ? (
|
|
59
70
|
<Tabs.Root defaultValue="suggestions" className="oa-tabs" style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { ChevronUp, ChevronDown, Smile, Meh, Frown } from "lucide-react";
|
|
3
|
+
import { TransferSummary } from "../../types";
|
|
4
|
+
|
|
5
|
+
export interface TransferSummaryCardProps {
|
|
6
|
+
data: TransferSummary;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Renders `**bold**` markdown spans; leaves the rest as plain text. */
|
|
10
|
+
function renderSummaryWithBold(text: string): React.ReactNode[] {
|
|
11
|
+
const parts = text.split(/(\*\*[^*]+\*\*)/g);
|
|
12
|
+
return parts.map((part, i) => {
|
|
13
|
+
if (part.startsWith("**") && part.endsWith("**") && part.length > 4) {
|
|
14
|
+
return <strong key={i}>{part.slice(2, -2)}</strong>;
|
|
15
|
+
}
|
|
16
|
+
return <React.Fragment key={i}>{part}</React.Fragment>;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Normalizes inline bullet markers into separate left-aligned lines with bold support. */
|
|
21
|
+
function formatSummaryLines(summary: string): React.ReactNode[] {
|
|
22
|
+
const normalizedSummary = summary.replace(/\s+([*-•])\s+/g, "\n$1 ");
|
|
23
|
+
const lines = normalizedSummary
|
|
24
|
+
.split("\n")
|
|
25
|
+
.map((line) => line.trim())
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
|
|
28
|
+
return lines.map((line, index) => {
|
|
29
|
+
const isBulletLine = /^[-*•]\s+/.test(line);
|
|
30
|
+
const lineContent = isBulletLine ? line.replace(/^[-*•]\s+/, "") : line;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<span key={`line-${index}`} style={{ display: "block" }}>
|
|
34
|
+
{isBulletLine ? "• " : ""}
|
|
35
|
+
{renderSummaryWithBold(lineContent)}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function TransferSummaryCard({ data }: TransferSummaryCardProps): JSX.Element {
|
|
42
|
+
const [expanded, setExpanded] = useState(true);
|
|
43
|
+
const sentiment = data.sentiment ?? "";
|
|
44
|
+
const lower = sentiment.toLowerCase();
|
|
45
|
+
|
|
46
|
+
const icon = lower.includes("positive") ? (
|
|
47
|
+
<Smile size={16} />
|
|
48
|
+
) : lower.includes("neutral") ? (
|
|
49
|
+
<Meh size={16} />
|
|
50
|
+
) : lower.includes("negative") ? (
|
|
51
|
+
<Frown size={16} />
|
|
52
|
+
) : null;
|
|
53
|
+
|
|
54
|
+
const badgeClass = lower.includes("positive")
|
|
55
|
+
? "oa-transfer-summary-badge oa-transfer-summary-badge--positive"
|
|
56
|
+
: lower.includes("negative")
|
|
57
|
+
? "oa-transfer-summary-badge oa-transfer-summary-badge--negative"
|
|
58
|
+
: "oa-transfer-summary-badge oa-transfer-summary-badge--neutral";
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="oa-transfer-summary-card" style={{ marginBottom: 12 }}>
|
|
62
|
+
<div
|
|
63
|
+
className="oa-transfer-summary-header"
|
|
64
|
+
onClick={() => setExpanded((v) => !v)}
|
|
65
|
+
style={{
|
|
66
|
+
display: "flex",
|
|
67
|
+
alignItems: "center",
|
|
68
|
+
justifyContent: "space-between",
|
|
69
|
+
cursor: "pointer",
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
73
|
+
<span style={{ fontWeight: 700, fontSize: 16, color: "#111827" }}>
|
|
74
|
+
Transfer Summary
|
|
75
|
+
</span>
|
|
76
|
+
{sentiment ? (
|
|
77
|
+
<span className={badgeClass}>
|
|
78
|
+
<span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
|
|
79
|
+
{icon}
|
|
80
|
+
{sentiment}
|
|
81
|
+
</span>
|
|
82
|
+
</span>
|
|
83
|
+
) : null}
|
|
84
|
+
</div>
|
|
85
|
+
{expanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
|
86
|
+
</div>
|
|
87
|
+
{expanded ? (
|
|
88
|
+
<div className="oa-transfer-summary-body" style={{ marginTop: 8 }}>
|
|
89
|
+
<div
|
|
90
|
+
className="oa-transfer-summary-text"
|
|
91
|
+
style={{
|
|
92
|
+
color: "#374151",
|
|
93
|
+
margin: 0,
|
|
94
|
+
lineHeight: 1.5,
|
|
95
|
+
fontSize: 14,
|
|
96
|
+
textAlign: "left",
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
{formatSummaryLines(data.summary ?? "")}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
) : null}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
package/src/controller/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Suggestion,
|
|
7
7
|
TranscriptLine,
|
|
8
8
|
Sentiment,
|
|
9
|
+
TransferSummary,
|
|
9
10
|
WssEvent,
|
|
10
11
|
InitialHandshakeResponse,
|
|
11
12
|
WssResponse,
|
|
@@ -292,6 +293,30 @@ export class ExotelAIAssistController extends EventEmitter<ControllerEvents> {
|
|
|
292
293
|
};
|
|
293
294
|
this.emit("sentiment", sentiment);
|
|
294
295
|
}
|
|
296
|
+
|
|
297
|
+
if (event.event_type === "transfer_summary" && event.value) {
|
|
298
|
+
const summary =
|
|
299
|
+
typeof event.value.summary === "string" ? event.value.summary.trim() : "";
|
|
300
|
+
if (!summary) continue;
|
|
301
|
+
|
|
302
|
+
const rawSentiment = event.value.sentiment;
|
|
303
|
+
const sentimentLabel =
|
|
304
|
+
rawSentiment === undefined || rawSentiment === null ? null : String(rawSentiment);
|
|
305
|
+
|
|
306
|
+
const transferSummary: TransferSummary = {
|
|
307
|
+
summary,
|
|
308
|
+
sentiment: sentimentLabel,
|
|
309
|
+
};
|
|
310
|
+
this.emit("transferSummary", transferSummary);
|
|
311
|
+
|
|
312
|
+
if (sentimentLabel) {
|
|
313
|
+
const sentiment: Sentiment = {
|
|
314
|
+
label: sentimentLabel.toLowerCase() as Sentiment["label"],
|
|
315
|
+
timestamp: now,
|
|
316
|
+
};
|
|
317
|
+
this.emit("sentiment", sentiment);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
295
320
|
}
|
|
296
321
|
}
|
|
297
322
|
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2
2
|
import { ExotelAIAssistController } from "../controller";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ExotelAIAssistParams,
|
|
5
|
+
Suggestion,
|
|
6
|
+
TranscriptLine,
|
|
7
|
+
Sentiment,
|
|
8
|
+
TransferSummary,
|
|
9
|
+
BotConfig,
|
|
10
|
+
StreamState,
|
|
11
|
+
} from "../types";
|
|
4
12
|
import { Utils } from "../utils";
|
|
5
13
|
|
|
6
14
|
const MAX_SUGGESTIONS = 50;
|
|
@@ -27,6 +35,8 @@ export interface UseExotelAIAssistReturn {
|
|
|
27
35
|
transcripts: TranscriptLine[];
|
|
28
36
|
/** Latest sentiment reading, or null before the first event. */
|
|
29
37
|
sentiment: Sentiment | null;
|
|
38
|
+
/** Warm-transfer summary from handover-context cache, if present. */
|
|
39
|
+
transferSummary: TransferSummary | null;
|
|
30
40
|
lastError: Error | null;
|
|
31
41
|
connect: () => void;
|
|
32
42
|
disconnect: () => void;
|
|
@@ -47,6 +57,7 @@ export function useExotelAIAssist(params: ExotelAIAssistParams): UseExotelAIAssi
|
|
|
47
57
|
const [suggestions, setSuggestions] = useState<Suggestion[]>([]);
|
|
48
58
|
const [transcripts, setTranscripts] = useState<TranscriptLine[]>([]);
|
|
49
59
|
const [sentiment, setSentiment] = useState<Sentiment | null>(null);
|
|
60
|
+
const [transferSummary, setTransferSummary] = useState<TransferSummary | null>(null);
|
|
50
61
|
const [botConfig, setBotConfig] = useState<BotConfig | null>(null);
|
|
51
62
|
const [lastError, setLastError] = useState<Error | null>(null);
|
|
52
63
|
const paramHash = Utils.hash(params);
|
|
@@ -57,6 +68,7 @@ export function useExotelAIAssist(params: ExotelAIAssistParams): UseExotelAIAssi
|
|
|
57
68
|
setSuggestions([]);
|
|
58
69
|
setTranscripts([]);
|
|
59
70
|
setSentiment(null);
|
|
71
|
+
setTransferSummary(null);
|
|
60
72
|
setBotConfig(null);
|
|
61
73
|
setLastError(null);
|
|
62
74
|
|
|
@@ -69,6 +81,7 @@ export function useExotelAIAssist(params: ExotelAIAssistParams): UseExotelAIAssi
|
|
|
69
81
|
setSuggestions([]);
|
|
70
82
|
setTranscripts([]);
|
|
71
83
|
setSentiment(null);
|
|
84
|
+
setTransferSummary(null);
|
|
72
85
|
}
|
|
73
86
|
});
|
|
74
87
|
ctrl.on("streamState", setStreamState);
|
|
@@ -80,6 +93,7 @@ export function useExotelAIAssist(params: ExotelAIAssistParams): UseExotelAIAssi
|
|
|
80
93
|
}),
|
|
81
94
|
);
|
|
82
95
|
ctrl.on("sentiment", setSentiment);
|
|
96
|
+
ctrl.on("transferSummary", setTransferSummary);
|
|
83
97
|
ctrl.on("transcript", (lines) =>
|
|
84
98
|
setTranscripts((prev) => {
|
|
85
99
|
const map = new Map(prev.map((l) => [l.id, l]));
|
|
@@ -115,5 +129,18 @@ export function useExotelAIAssist(params: ExotelAIAssistParams): UseExotelAIAssi
|
|
|
115
129
|
return sent;
|
|
116
130
|
}, []);
|
|
117
131
|
|
|
118
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
streamState,
|
|
134
|
+
isReady,
|
|
135
|
+
suggestions,
|
|
136
|
+
transcripts,
|
|
137
|
+
sentiment,
|
|
138
|
+
transferSummary,
|
|
139
|
+
botConfig,
|
|
140
|
+
lastError,
|
|
141
|
+
connect,
|
|
142
|
+
disconnect,
|
|
143
|
+
setParams,
|
|
144
|
+
sendSuggestionFeedback,
|
|
145
|
+
};
|
|
119
146
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,4 +12,13 @@ export function unmountExotelAIAssist(container: HTMLElement): void {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export { ExotelAIAssistController } from "./controller";
|
|
15
|
-
export type {
|
|
15
|
+
export type {
|
|
16
|
+
ExotelAIAssistParams,
|
|
17
|
+
Suggestion,
|
|
18
|
+
TranscriptLine,
|
|
19
|
+
Sentiment,
|
|
20
|
+
TransferSummary,
|
|
21
|
+
TransferSummaryValue,
|
|
22
|
+
ConnectionStatus,
|
|
23
|
+
StreamState,
|
|
24
|
+
} from "./types";
|
package/src/react/index.ts
CHANGED
|
@@ -3,7 +3,18 @@ export { ExotelAIAssistProvider, useExotelAIAssistContext } from "../components/
|
|
|
3
3
|
export { useExotelAIAssist } from "../hooks/useExotelAIAssist";
|
|
4
4
|
export { ExotelAIAssistController } from "../controller";
|
|
5
5
|
|
|
6
|
-
export type {
|
|
6
|
+
export type {
|
|
7
|
+
ExotelAIAssistParams,
|
|
8
|
+
Suggestion,
|
|
9
|
+
TranscriptLine,
|
|
10
|
+
Sentiment,
|
|
11
|
+
TransferSummary,
|
|
12
|
+
TransferSummaryValue,
|
|
13
|
+
ConnectionStatus,
|
|
14
|
+
StreamState,
|
|
15
|
+
} from "../types";
|
|
7
16
|
export type { UseExotelAIAssistReturn } from "../hooks/useExotelAIAssist";
|
|
8
17
|
export type { ExotelAIAssistProps } from "../components/ExotelAIAssistApp";
|
|
9
18
|
export type { ExotelAIAssistProviderProps } from "../components/ExotelAIAssistProvider";
|
|
19
|
+
export { TransferSummaryCard } from "../components/key-components/TransferSummaryCard";
|
|
20
|
+
export type { TransferSummaryCardProps } from "../components/key-components/TransferSummaryCard";
|
package/src/styles/index.css
CHANGED
|
@@ -425,6 +425,47 @@
|
|
|
425
425
|
font-weight: 600;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
/* AIA-144 transfer summary card */
|
|
429
|
+
.oa-transfer-summary-card {
|
|
430
|
+
background: #f9f7ff;
|
|
431
|
+
border: 1px solid #ece7f8;
|
|
432
|
+
border-radius: 12px;
|
|
433
|
+
padding: 12px 14px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.oa-transfer-summary-header {
|
|
437
|
+
user-select: none;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.oa-transfer-summary-body,
|
|
441
|
+
.oa-transfer-summary-text {
|
|
442
|
+
text-align: left;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.oa-transfer-summary-badge {
|
|
446
|
+
display: inline-flex;
|
|
447
|
+
align-items: center;
|
|
448
|
+
border-radius: 999px;
|
|
449
|
+
padding: 2px 10px;
|
|
450
|
+
font-size: 13px;
|
|
451
|
+
font-weight: 600;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.oa-transfer-summary-badge--positive {
|
|
455
|
+
background: #dcfce7;
|
|
456
|
+
color: #166534;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.oa-transfer-summary-badge--neutral {
|
|
460
|
+
background: #f3f4f6;
|
|
461
|
+
color: #374151;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.oa-transfer-summary-badge--negative {
|
|
465
|
+
background: #ffe0db;
|
|
466
|
+
color: #9f1239;
|
|
467
|
+
}
|
|
468
|
+
|
|
428
469
|
/* -----------------------------------------------------------------------
|
|
429
470
|
Misc helpers
|
|
430
471
|
----------------------------------------------------------------------- */
|
package/src/types/index.ts
CHANGED
|
@@ -105,7 +105,24 @@ interface WssSuggestionEvent {
|
|
|
105
105
|
value: SuggestionValue;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
/** Value payload for handover / warm-transfer summary events. */
|
|
109
|
+
export interface TransferSummaryValue {
|
|
110
|
+
summary?: string;
|
|
111
|
+
sentiment?: string | number | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Parsed transfer summary shown above the panel tabs. */
|
|
115
|
+
export interface TransferSummary {
|
|
116
|
+
summary: string;
|
|
117
|
+
sentiment: string | null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface WssTransferSummaryEvent {
|
|
121
|
+
event_type: "transfer_summary";
|
|
122
|
+
value: TransferSummaryValue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export type WssEvent = WssTranscriptEvent | WssSentimentEvent | WssSuggestionEvent | WssTransferSummaryEvent;
|
|
109
126
|
|
|
110
127
|
export interface InitialHandshakeResponse {
|
|
111
128
|
type: string;
|
|
@@ -124,6 +141,8 @@ export interface ControllerEvents {
|
|
|
124
141
|
suggestion: (data: Suggestion) => void;
|
|
125
142
|
transcript: (lines: TranscriptLine[]) => void;
|
|
126
143
|
sentiment: (data: Sentiment) => void;
|
|
144
|
+
/** Warm-transfer / handover summary pushed at stream-setup. */
|
|
145
|
+
transferSummary: (data: TransferSummary) => void;
|
|
127
146
|
/** Internal: bot feature-flag config. Consumed by the UI component only. */
|
|
128
147
|
botConfig: (config: BotConfig) => void;
|
|
129
148
|
/**
|