vanilla-agent 1.21.0 → 1.23.0
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/README.md +87 -0
- package/dist/index.cjs +28 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +469 -30
- package/dist/index.d.ts +469 -30
- package/dist/index.global.js +60 -56
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +28 -24
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/widget.css +409 -0
- package/package.json +2 -2
- package/src/client.ts +392 -3
- package/src/components/feedback.ts +377 -0
- package/src/components/message-bubble.ts +208 -4
- package/src/components/messages.ts +10 -3
- package/src/defaults.ts +15 -0
- package/src/index.ts +23 -3
- package/src/install.ts +69 -7
- package/src/session.ts +132 -4
- package/src/styles/widget.css +409 -0
- package/src/types.ts +209 -0
- package/src/ui.ts +121 -4
- package/src/utils/message-id.ts +35 -0
package/src/ui.ts
CHANGED
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
AgentWidgetStateEvent,
|
|
12
12
|
AgentWidgetStateSnapshot,
|
|
13
13
|
WidgetLayoutSlot,
|
|
14
|
-
SlotRenderer
|
|
14
|
+
SlotRenderer,
|
|
15
|
+
AgentWidgetMessageFeedback
|
|
15
16
|
} from "./types";
|
|
16
17
|
import { applyThemeVariables } from "./utils/theme";
|
|
17
18
|
import { renderLucideIcon } from "./utils/icons";
|
|
@@ -21,7 +22,7 @@ import { createLauncherButton } from "./components/launcher";
|
|
|
21
22
|
import { createWrapper, buildPanel, buildHeader, buildComposer, attachHeaderToContainer } from "./components/panel";
|
|
22
23
|
import { positionMap } from "./utils/positioning";
|
|
23
24
|
import type { HeaderElements, ComposerElements } from "./components/panel";
|
|
24
|
-
import { MessageTransform } from "./components/message-bubble";
|
|
25
|
+
import { MessageTransform, MessageActionCallbacks } from "./components/message-bubble";
|
|
25
26
|
import { createStandardBubble, createTypingIndicator } from "./components/message-bubble";
|
|
26
27
|
import { createReasoningBubble } from "./components/reasoning-bubble";
|
|
27
28
|
import { createToolBubble } from "./components/tool-bubble";
|
|
@@ -42,6 +43,12 @@ import {
|
|
|
42
43
|
extractComponentDirectiveFromMessage,
|
|
43
44
|
hasComponentDirective
|
|
44
45
|
} from "./utils/component-middleware";
|
|
46
|
+
import {
|
|
47
|
+
createCSATFeedback,
|
|
48
|
+
createNPSFeedback,
|
|
49
|
+
type CSATFeedbackOptions,
|
|
50
|
+
type NPSFeedbackOptions
|
|
51
|
+
} from "./components/feedback";
|
|
45
52
|
|
|
46
53
|
// Default localStorage key for chat history (automatically cleared on clear chat)
|
|
47
54
|
const DEFAULT_CHAT_HISTORY_STORAGE_KEY = "vanilla-agent-chat-history";
|
|
@@ -90,6 +97,11 @@ type Controller = {
|
|
|
90
97
|
isOpen: () => boolean;
|
|
91
98
|
isVoiceActive: () => boolean;
|
|
92
99
|
getState: () => AgentWidgetStateSnapshot;
|
|
100
|
+
// Feedback methods (CSAT/NPS)
|
|
101
|
+
showCSATFeedback: (options?: Partial<CSATFeedbackOptions>) => void;
|
|
102
|
+
showNPSFeedback: (options?: Partial<NPSFeedbackOptions>) => void;
|
|
103
|
+
submitCSATFeedback: (rating: number, comment?: string) => Promise<void>;
|
|
104
|
+
submitNPSFeedback: (rating: number, comment?: string) => Promise<void>;
|
|
93
105
|
};
|
|
94
106
|
|
|
95
107
|
const buildPostprocessor = (
|
|
@@ -228,6 +240,38 @@ export const createAgentExperience = (
|
|
|
228
240
|
let showReasoning = config.features?.showReasoning ?? true;
|
|
229
241
|
let showToolCalls = config.features?.showToolCalls ?? true;
|
|
230
242
|
|
|
243
|
+
// Create message action callbacks that emit events and optionally send to API
|
|
244
|
+
const messageActionCallbacks: MessageActionCallbacks = {
|
|
245
|
+
onCopy: (message: AgentWidgetMessage) => {
|
|
246
|
+
eventBus.emit("message:copy", message);
|
|
247
|
+
// Send copy feedback to API if in client token mode
|
|
248
|
+
if (session?.isClientTokenMode()) {
|
|
249
|
+
session.submitMessageFeedback(message.id, 'copy').catch((error) => {
|
|
250
|
+
if (config.debug) {
|
|
251
|
+
// eslint-disable-next-line no-console
|
|
252
|
+
console.error("[AgentWidget] Failed to submit copy feedback:", error);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Call user-provided callback
|
|
257
|
+
config.messageActions?.onCopy?.(message);
|
|
258
|
+
},
|
|
259
|
+
onFeedback: (feedback: AgentWidgetMessageFeedback) => {
|
|
260
|
+
eventBus.emit("message:feedback", feedback);
|
|
261
|
+
// Send feedback to API if in client token mode
|
|
262
|
+
if (session?.isClientTokenMode()) {
|
|
263
|
+
session.submitMessageFeedback(feedback.messageId, feedback.type).catch((error) => {
|
|
264
|
+
if (config.debug) {
|
|
265
|
+
// eslint-disable-next-line no-console
|
|
266
|
+
console.error("[AgentWidget] Failed to submit feedback:", error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// Call user-provided callback
|
|
271
|
+
config.messageActions?.onFeedback?.(feedback);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
231
275
|
// Get status indicator config
|
|
232
276
|
const statusConfig = config.statusIndicator ?? {};
|
|
233
277
|
const getStatusText = (status: AgentWidgetSessionStatus): string => {
|
|
@@ -877,7 +921,13 @@ export const createAgentExperience = (
|
|
|
877
921
|
bubble = matchingPlugin.renderMessage({
|
|
878
922
|
message,
|
|
879
923
|
defaultRenderer: () => {
|
|
880
|
-
const b = createStandardBubble(
|
|
924
|
+
const b = createStandardBubble(
|
|
925
|
+
message,
|
|
926
|
+
transform,
|
|
927
|
+
messageLayoutConfig,
|
|
928
|
+
config.messageActions,
|
|
929
|
+
messageActionCallbacks
|
|
930
|
+
);
|
|
881
931
|
if (message.role !== "user") {
|
|
882
932
|
enhanceWithForms(b, message, config, session);
|
|
883
933
|
}
|
|
@@ -957,7 +1007,13 @@ export const createAgentExperience = (
|
|
|
957
1007
|
streaming: Boolean(message.streaming)
|
|
958
1008
|
});
|
|
959
1009
|
} else {
|
|
960
|
-
bubble = createStandardBubble(
|
|
1010
|
+
bubble = createStandardBubble(
|
|
1011
|
+
message,
|
|
1012
|
+
transform,
|
|
1013
|
+
messageLayoutConfig,
|
|
1014
|
+
config.messageActions,
|
|
1015
|
+
messageActionCallbacks
|
|
1016
|
+
);
|
|
961
1017
|
}
|
|
962
1018
|
if (message.role !== "user" && bubble) {
|
|
963
1019
|
enhanceWithForms(bubble, message, config, session);
|
|
@@ -2774,6 +2830,67 @@ export const createAgentExperience = (
|
|
|
2774
2830
|
streaming: session.isStreaming()
|
|
2775
2831
|
};
|
|
2776
2832
|
},
|
|
2833
|
+
// Feedback methods (CSAT/NPS)
|
|
2834
|
+
showCSATFeedback(options?: Partial<CSATFeedbackOptions>) {
|
|
2835
|
+
// Auto-open widget if closed and launcher is enabled
|
|
2836
|
+
if (!open && launcherEnabled) {
|
|
2837
|
+
setOpenState(true, "system");
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
// Remove any existing feedback forms
|
|
2841
|
+
const existingFeedback = messagesWrapper.querySelector('.tvw-feedback-container');
|
|
2842
|
+
if (existingFeedback) {
|
|
2843
|
+
existingFeedback.remove();
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
const feedbackEl = createCSATFeedback({
|
|
2847
|
+
onSubmit: async (rating, comment) => {
|
|
2848
|
+
if (session.isClientTokenMode()) {
|
|
2849
|
+
await session.submitCSATFeedback(rating, comment);
|
|
2850
|
+
}
|
|
2851
|
+
options?.onSubmit?.(rating, comment);
|
|
2852
|
+
},
|
|
2853
|
+
onDismiss: options?.onDismiss,
|
|
2854
|
+
...options,
|
|
2855
|
+
});
|
|
2856
|
+
|
|
2857
|
+
// Append to messages area at the bottom
|
|
2858
|
+
messagesWrapper.appendChild(feedbackEl);
|
|
2859
|
+
feedbackEl.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
2860
|
+
},
|
|
2861
|
+
showNPSFeedback(options?: Partial<NPSFeedbackOptions>) {
|
|
2862
|
+
// Auto-open widget if closed and launcher is enabled
|
|
2863
|
+
if (!open && launcherEnabled) {
|
|
2864
|
+
setOpenState(true, "system");
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// Remove any existing feedback forms
|
|
2868
|
+
const existingFeedback = messagesWrapper.querySelector('.tvw-feedback-container');
|
|
2869
|
+
if (existingFeedback) {
|
|
2870
|
+
existingFeedback.remove();
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
const feedbackEl = createNPSFeedback({
|
|
2874
|
+
onSubmit: async (rating, comment) => {
|
|
2875
|
+
if (session.isClientTokenMode()) {
|
|
2876
|
+
await session.submitNPSFeedback(rating, comment);
|
|
2877
|
+
}
|
|
2878
|
+
options?.onSubmit?.(rating, comment);
|
|
2879
|
+
},
|
|
2880
|
+
onDismiss: options?.onDismiss,
|
|
2881
|
+
...options,
|
|
2882
|
+
});
|
|
2883
|
+
|
|
2884
|
+
// Append to messages area at the bottom
|
|
2885
|
+
messagesWrapper.appendChild(feedbackEl);
|
|
2886
|
+
feedbackEl.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
2887
|
+
},
|
|
2888
|
+
async submitCSATFeedback(rating: number, comment?: string): Promise<void> {
|
|
2889
|
+
return session.submitCSATFeedback(rating, comment);
|
|
2890
|
+
},
|
|
2891
|
+
async submitNPSFeedback(rating: number, comment?: string): Promise<void> {
|
|
2892
|
+
return session.submitNPSFeedback(rating, comment);
|
|
2893
|
+
},
|
|
2777
2894
|
destroy() {
|
|
2778
2895
|
destroyCallbacks.forEach((cb) => cb());
|
|
2779
2896
|
wrapper.remove();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ID utilities for client-side message tracking
|
|
3
|
+
* Used for feedback integration with the Travrse API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate a unique message ID for tracking
|
|
8
|
+
* Format: msg_{timestamp_base36}_{random_8chars}
|
|
9
|
+
*/
|
|
10
|
+
export function generateMessageId(): string {
|
|
11
|
+
const timestamp = Date.now().toString(36);
|
|
12
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
13
|
+
return `msg_${timestamp}_${random}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate a unique user message ID
|
|
18
|
+
* Format: usr_{timestamp_base36}_{random_8chars}
|
|
19
|
+
*/
|
|
20
|
+
export function generateUserMessageId(): string {
|
|
21
|
+
const timestamp = Date.now().toString(36);
|
|
22
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
23
|
+
return `usr_${timestamp}_${random}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generate a unique assistant message ID
|
|
28
|
+
* Format: ast_{timestamp_base36}_{random_8chars}
|
|
29
|
+
*/
|
|
30
|
+
export function generateAssistantMessageId(): string {
|
|
31
|
+
const timestamp = Date.now().toString(36);
|
|
32
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
33
|
+
return `ast_${timestamp}_${random}`;
|
|
34
|
+
}
|
|
35
|
+
|