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/src/index.ts CHANGED
@@ -32,7 +32,16 @@ export type {
32
32
  // Markdown types
33
33
  AgentWidgetMarkdownConfig,
34
34
  AgentWidgetMarkdownOptions,
35
- AgentWidgetMarkdownRendererOverrides
35
+ AgentWidgetMarkdownRendererOverrides,
36
+ // Message actions types
37
+ AgentWidgetMessageActionsConfig,
38
+ AgentWidgetMessageFeedback,
39
+ // Client token types
40
+ ClientSession,
41
+ ClientInitResponse,
42
+ ClientChatRequest,
43
+ ClientFeedbackRequest,
44
+ ClientFeedbackType
36
45
  } from "./types";
37
46
 
38
47
  export { initAgentWidgetFn as initAgentWidget };
@@ -67,6 +76,11 @@ export {
67
76
  createRegexJsonParser,
68
77
  createXmlParser
69
78
  } from "./utils/formatting";
79
+ export {
80
+ generateMessageId,
81
+ generateUserMessageId,
82
+ generateAssistantMessageId
83
+ } from "./utils/message-id";
70
84
  export { generateCodeSnippet } from "./utils/code-generators";
71
85
  export type { CodeFormat } from "./utils/code-generators";
72
86
  export type { AgentWidgetInitHandle };
@@ -120,8 +134,14 @@ export type {
120
134
  export {
121
135
  createStandardBubble,
122
136
  createBubbleWithLayout,
123
- createTypingIndicator
137
+ createTypingIndicator,
138
+ createMessageActions
124
139
  } from "./components/message-bubble";
125
- export type { MessageTransform } from "./components/message-bubble";
140
+ export type { MessageTransform, MessageActionCallbacks } from "./components/message-bubble";
141
+ export {
142
+ createCSATFeedback,
143
+ createNPSFeedback
144
+ } from "./components/feedback";
145
+ export type { CSATFeedbackOptions, NPSFeedbackOptions } from "./components/feedback";
126
146
 
127
147
  export default initAgentWidgetFn;
package/src/install.ts CHANGED
@@ -12,6 +12,10 @@ interface SiteAgentInstallConfig {
12
12
  target?: string | HTMLElement;
13
13
  config?: any;
14
14
  autoInit?: boolean;
15
+ // Client token mode options (can also be set via data attributes)
16
+ clientToken?: string;
17
+ flowId?: string;
18
+ apiUrl?: string;
15
19
  }
16
20
 
17
21
  declare global {
@@ -30,7 +34,45 @@ declare global {
30
34
  }
31
35
  (window as any).__siteAgentInstallerLoaded = true;
32
36
 
33
- const config: SiteAgentInstallConfig = window.siteAgentConfig || {};
37
+ /**
38
+ * Read configuration from data attributes on the current script tag.
39
+ * Supports: data-travrse-token, data-flow-id, data-api-url
40
+ */
41
+ const getConfigFromScript = (): Partial<SiteAgentInstallConfig> => {
42
+ // Try to get the current script element
43
+ const script = document.currentScript as HTMLScriptElement | null;
44
+ if (!script) return {};
45
+
46
+ const scriptConfig: Partial<SiteAgentInstallConfig> = {};
47
+
48
+ // Client token from data attribute (primary method for client token mode)
49
+ const token = script.getAttribute('data-travrse-token');
50
+ if (token) {
51
+ scriptConfig.clientToken = token;
52
+ }
53
+
54
+ // Optional flow ID
55
+ const flowId = script.getAttribute('data-flow-id');
56
+ if (flowId) {
57
+ scriptConfig.flowId = flowId;
58
+ }
59
+
60
+ // Optional API URL override
61
+ const apiUrl = script.getAttribute('data-api-url');
62
+ if (apiUrl) {
63
+ scriptConfig.apiUrl = apiUrl;
64
+ }
65
+
66
+ return scriptConfig;
67
+ };
68
+
69
+ // Get config from script attributes (must be called synchronously during script execution)
70
+ const scriptConfig = getConfigFromScript();
71
+
72
+ // Merge script attributes with window config (script attributes take precedence)
73
+ const windowConfig: SiteAgentInstallConfig = window.siteAgentConfig || {};
74
+ const config: SiteAgentInstallConfig = { ...windowConfig, ...scriptConfig };
75
+
34
76
  const version = config.version || "latest";
35
77
  const cdn = config.cdn || "jsdelivr";
36
78
  const autoInit = config.autoInit !== false; // Default to true
@@ -113,14 +155,27 @@ declare global {
113
155
  }
114
156
 
115
157
  const target = config.target || "body";
116
- // Merge apiUrl from top-level config into widget config if present
158
+ // Merge top-level config options into widget config
117
159
  const widgetConfig = { ...config.config };
118
- if ((config as any).apiUrl && !widgetConfig.apiUrl) {
119
- widgetConfig.apiUrl = (config as any).apiUrl;
160
+
161
+ // Merge apiUrl from top-level config into widget config if present
162
+ if (config.apiUrl && !widgetConfig.apiUrl) {
163
+ widgetConfig.apiUrl = config.apiUrl;
164
+ }
165
+
166
+ // Merge clientToken from top-level config into widget config if present
167
+ if (config.clientToken && !widgetConfig.clientToken) {
168
+ widgetConfig.clientToken = config.clientToken;
169
+ }
170
+
171
+ // Merge flowId from top-level config into widget config if present
172
+ if (config.flowId && !widgetConfig.flowId) {
173
+ widgetConfig.flowId = config.flowId;
120
174
  }
121
175
 
122
- // Only initialize if config is provided
123
- if (!widgetConfig.apiUrl && Object.keys(widgetConfig).length === 0) {
176
+ // Only initialize if we have either apiUrl OR clientToken (or other config)
177
+ const hasApiConfig = widgetConfig.apiUrl || widgetConfig.clientToken;
178
+ if (!hasApiConfig && Object.keys(widgetConfig).length === 0) {
124
179
  return;
125
180
  }
126
181
 
@@ -140,7 +195,14 @@ declare global {
140
195
  await loadCSS();
141
196
  await loadJS();
142
197
 
143
- if (autoInit && (config.config || (config as any).apiUrl)) {
198
+ // Auto-init if we have config OR apiUrl OR clientToken
199
+ const shouldAutoInit = autoInit && (
200
+ config.config ||
201
+ config.apiUrl ||
202
+ config.clientToken
203
+ );
204
+
205
+ if (shouldAutoInit) {
144
206
  // Wait a tick to ensure AgentWidget is fully initialized
145
207
  setTimeout(initWidget, 0);
146
208
  }
package/src/session.ts CHANGED
@@ -2,8 +2,13 @@ import { AgentWidgetClient } from "./client";
2
2
  import {
3
3
  AgentWidgetConfig,
4
4
  AgentWidgetEvent,
5
- AgentWidgetMessage
5
+ AgentWidgetMessage,
6
+ ClientSession
6
7
  } from "./types";
8
+ import {
9
+ generateUserMessageId,
10
+ generateAssistantMessageId
11
+ } from "./utils/message-id";
7
12
 
8
13
  export type AgentWidgetSessionStatus =
9
14
  | "idle"
@@ -25,6 +30,9 @@ export class AgentWidgetSession {
25
30
  private streaming = false;
26
31
  private abortController: AbortController | null = null;
27
32
  private sequenceCounter = Date.now();
33
+
34
+ // Client token session management
35
+ private clientSession: ClientSession | null = null;
28
36
 
29
37
  constructor(
30
38
  private config: AgentWidgetConfig = {},
@@ -43,6 +51,121 @@ export class AgentWidgetSession {
43
51
  this.callbacks.onStatusChanged(this.status);
44
52
  }
45
53
 
54
+ /**
55
+ * Check if running in client token mode
56
+ */
57
+ public isClientTokenMode(): boolean {
58
+ return this.client.isClientTokenMode();
59
+ }
60
+
61
+ /**
62
+ * Initialize the client session (for client token mode).
63
+ * This is called automatically on first message, but can be called
64
+ * explicitly to pre-initialize the session and get config from server.
65
+ */
66
+ public async initClientSession(): Promise<ClientSession | null> {
67
+ if (!this.isClientTokenMode()) {
68
+ return null;
69
+ }
70
+
71
+ try {
72
+ const session = await this.client.initSession();
73
+ this.setClientSession(session);
74
+ return session;
75
+ } catch (error) {
76
+ this.callbacks.onError?.(
77
+ error instanceof Error ? error : new Error(String(error))
78
+ );
79
+ return null;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Set the client session after initialization
85
+ */
86
+ public setClientSession(session: ClientSession): void {
87
+ this.clientSession = session;
88
+
89
+ // Optionally add welcome message from session config
90
+ if (session.config.welcomeMessage && this.messages.length === 0) {
91
+ const welcomeMessage: AgentWidgetMessage = {
92
+ id: `welcome-${Date.now()}`,
93
+ role: "assistant",
94
+ content: session.config.welcomeMessage,
95
+ createdAt: new Date().toISOString(),
96
+ sequence: this.nextSequence()
97
+ };
98
+ this.appendMessage(welcomeMessage);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Get current client session
104
+ */
105
+ public getClientSession(): ClientSession | null {
106
+ return this.clientSession ?? this.client.getClientSession();
107
+ }
108
+
109
+ /**
110
+ * Check if session is valid and not expired
111
+ */
112
+ public isSessionValid(): boolean {
113
+ const session = this.getClientSession();
114
+ if (!session) return false;
115
+ return new Date() < session.expiresAt;
116
+ }
117
+
118
+ /**
119
+ * Clear session (on expiry or error)
120
+ */
121
+ public clearClientSession(): void {
122
+ this.clientSession = null;
123
+ this.client.clearClientSession();
124
+ }
125
+
126
+ /**
127
+ * Get the underlying client instance (for advanced use cases like feedback)
128
+ */
129
+ public getClient(): AgentWidgetClient {
130
+ return this.client;
131
+ }
132
+
133
+ /**
134
+ * Submit message feedback (upvote, downvote, or copy) to the API.
135
+ * Only available in client token mode.
136
+ *
137
+ * @param messageId - The ID of the message to provide feedback for
138
+ * @param type - The feedback type: 'upvote', 'downvote', or 'copy'
139
+ */
140
+ public async submitMessageFeedback(
141
+ messageId: string,
142
+ type: 'upvote' | 'downvote' | 'copy'
143
+ ): Promise<void> {
144
+ return this.client.submitMessageFeedback(messageId, type);
145
+ }
146
+
147
+ /**
148
+ * Submit CSAT (Customer Satisfaction) feedback to the API.
149
+ * Only available in client token mode.
150
+ *
151
+ * @param rating - Rating from 1 to 5
152
+ * @param comment - Optional comment
153
+ */
154
+ public async submitCSATFeedback(rating: number, comment?: string): Promise<void> {
155
+ return this.client.submitCSATFeedback(rating, comment);
156
+ }
157
+
158
+ /**
159
+ * Submit NPS (Net Promoter Score) feedback to the API.
160
+ * Only available in client token mode.
161
+ *
162
+ * @param rating - Rating from 0 to 10
163
+ * @param comment - Optional comment
164
+ */
165
+ public async submitNPSFeedback(rating: number, comment?: string): Promise<void> {
166
+ return this.client.submitNPSFeedback(rating, comment);
167
+ }
168
+
46
169
  public updateConfig(next: AgentWidgetConfig) {
47
170
  this.config = { ...this.config, ...next };
48
171
  this.client = new AgentWidgetClient(this.config);
@@ -70,8 +193,12 @@ export class AgentWidgetSession {
70
193
 
71
194
  this.abortController?.abort();
72
195
 
196
+ // Generate IDs for both user message and expected assistant response
197
+ const userMessageId = generateUserMessageId();
198
+ const assistantMessageId = generateAssistantMessageId();
199
+
73
200
  const userMessage: AgentWidgetMessage = {
74
- id: `user-${Date.now()}`,
201
+ id: userMessageId,
75
202
  role: "user",
76
203
  content: input,
77
204
  createdAt: new Date().toISOString(),
@@ -91,13 +218,14 @@ export class AgentWidgetSession {
91
218
  await this.client.dispatch(
92
219
  {
93
220
  messages: snapshot,
94
- signal: controller.signal
221
+ signal: controller.signal,
222
+ assistantMessageId // Pass expected assistant message ID for tracking
95
223
  },
96
224
  this.handleEvent
97
225
  );
98
226
  } catch (error) {
99
227
  const fallback: AgentWidgetMessage = {
100
- id: `assistant-${Date.now()}`,
228
+ id: assistantMessageId, // Use the pre-generated ID for fallback too
101
229
  role: "assistant",
102
230
  createdAt: new Date().toISOString(),
103
231
  content: