vanilla-agent 1.21.0 → 1.22.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 +24 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +247 -3
- package/dist/index.d.ts +247 -3
- package/dist/index.global.js +43 -43
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +23 -23
- 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 +185 -0
- package/package.json +1 -1
- package/src/client.ts +215 -1
- 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 +11 -3
- package/src/install.ts +69 -7
- package/src/session.ts +77 -1
- package/src/styles/widget.css +185 -0
- package/src/types.ts +184 -0
- package/src/ui.ts +27 -4
|
@@ -3,8 +3,11 @@ import {
|
|
|
3
3
|
AgentWidgetMessage,
|
|
4
4
|
AgentWidgetMessageLayoutConfig,
|
|
5
5
|
AgentWidgetAvatarConfig,
|
|
6
|
-
AgentWidgetTimestampConfig
|
|
6
|
+
AgentWidgetTimestampConfig,
|
|
7
|
+
AgentWidgetMessageActionsConfig,
|
|
8
|
+
AgentWidgetMessageFeedback
|
|
7
9
|
} from "../types";
|
|
10
|
+
import { renderLucideIcon } from "../utils/icons";
|
|
8
11
|
|
|
9
12
|
export type MessageTransform = (context: {
|
|
10
13
|
text: string;
|
|
@@ -13,6 +16,11 @@ export type MessageTransform = (context: {
|
|
|
13
16
|
raw?: string;
|
|
14
17
|
}) => string;
|
|
15
18
|
|
|
19
|
+
export type MessageActionCallbacks = {
|
|
20
|
+
onCopy?: (message: AgentWidgetMessage) => void;
|
|
21
|
+
onFeedback?: (feedback: AgentWidgetMessageFeedback) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
16
24
|
// Create typing indicator element
|
|
17
25
|
export const createTypingIndicator = (): HTMLElement => {
|
|
18
26
|
const container = document.createElement("div");
|
|
@@ -204,6 +212,185 @@ const getBubbleClasses = (
|
|
|
204
212
|
return baseClasses;
|
|
205
213
|
};
|
|
206
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Create message action buttons (copy, upvote, downvote)
|
|
217
|
+
*/
|
|
218
|
+
export const createMessageActions = (
|
|
219
|
+
message: AgentWidgetMessage,
|
|
220
|
+
actionsConfig: AgentWidgetMessageActionsConfig,
|
|
221
|
+
callbacks?: MessageActionCallbacks
|
|
222
|
+
): HTMLElement => {
|
|
223
|
+
const showCopy = actionsConfig.showCopy ?? true;
|
|
224
|
+
const showUpvote = actionsConfig.showUpvote ?? true;
|
|
225
|
+
const showDownvote = actionsConfig.showDownvote ?? true;
|
|
226
|
+
const visibility = actionsConfig.visibility ?? "hover";
|
|
227
|
+
const align = actionsConfig.align ?? "right";
|
|
228
|
+
const layout = actionsConfig.layout ?? "pill-inside";
|
|
229
|
+
|
|
230
|
+
// Map alignment to CSS class
|
|
231
|
+
const alignClass = {
|
|
232
|
+
left: "tvw-message-actions-left",
|
|
233
|
+
center: "tvw-message-actions-center",
|
|
234
|
+
right: "tvw-message-actions-right",
|
|
235
|
+
}[align];
|
|
236
|
+
|
|
237
|
+
// Map layout to CSS class
|
|
238
|
+
const layoutClass = {
|
|
239
|
+
"pill-inside": "tvw-message-actions-pill",
|
|
240
|
+
"row-inside": "tvw-message-actions-row",
|
|
241
|
+
}[layout];
|
|
242
|
+
|
|
243
|
+
const container = createElement(
|
|
244
|
+
"div",
|
|
245
|
+
`tvw-message-actions tvw-flex tvw-items-center tvw-gap-1 tvw-mt-2 ${alignClass} ${layoutClass} ${
|
|
246
|
+
visibility === "hover" ? "tvw-message-actions-hover" : ""
|
|
247
|
+
}`
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Track vote state for this message
|
|
251
|
+
let currentVote: "upvote" | "downvote" | null = null;
|
|
252
|
+
|
|
253
|
+
const createActionButton = (
|
|
254
|
+
iconName: string,
|
|
255
|
+
label: string,
|
|
256
|
+
onClick: () => void,
|
|
257
|
+
dataAction?: string
|
|
258
|
+
): HTMLButtonElement => {
|
|
259
|
+
const button = document.createElement("button");
|
|
260
|
+
button.className = "tvw-message-action-btn";
|
|
261
|
+
button.setAttribute("aria-label", label);
|
|
262
|
+
button.setAttribute("title", label);
|
|
263
|
+
if (dataAction) {
|
|
264
|
+
button.setAttribute("data-action", dataAction);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const icon = renderLucideIcon(iconName, 14, "currentColor", 2);
|
|
268
|
+
if (icon) {
|
|
269
|
+
button.appendChild(icon);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
button.addEventListener("click", (e) => {
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
e.stopPropagation();
|
|
275
|
+
onClick();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
return button;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Copy button
|
|
282
|
+
if (showCopy) {
|
|
283
|
+
const copyButton = createActionButton("copy", "Copy message", () => {
|
|
284
|
+
// Copy to clipboard
|
|
285
|
+
const textToCopy = message.content || "";
|
|
286
|
+
navigator.clipboard.writeText(textToCopy).then(() => {
|
|
287
|
+
// Show success feedback - swap icon temporarily
|
|
288
|
+
copyButton.classList.add("tvw-message-action-success");
|
|
289
|
+
const checkIcon = renderLucideIcon("check", 14, "currentColor", 2);
|
|
290
|
+
if (checkIcon) {
|
|
291
|
+
copyButton.innerHTML = "";
|
|
292
|
+
copyButton.appendChild(checkIcon);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Restore original icon after 2 seconds
|
|
296
|
+
setTimeout(() => {
|
|
297
|
+
copyButton.classList.remove("tvw-message-action-success");
|
|
298
|
+
const originalIcon = renderLucideIcon("copy", 14, "currentColor", 2);
|
|
299
|
+
if (originalIcon) {
|
|
300
|
+
copyButton.innerHTML = "";
|
|
301
|
+
copyButton.appendChild(originalIcon);
|
|
302
|
+
}
|
|
303
|
+
}, 2000);
|
|
304
|
+
}).catch((err) => {
|
|
305
|
+
if (typeof console !== "undefined") {
|
|
306
|
+
console.error("[AgentWidget] Failed to copy message:", err);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Trigger callback
|
|
311
|
+
if (callbacks?.onCopy) {
|
|
312
|
+
callbacks.onCopy(message);
|
|
313
|
+
}
|
|
314
|
+
if (actionsConfig.onCopy) {
|
|
315
|
+
actionsConfig.onCopy(message);
|
|
316
|
+
}
|
|
317
|
+
}, "copy");
|
|
318
|
+
container.appendChild(copyButton);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Upvote button
|
|
322
|
+
if (showUpvote) {
|
|
323
|
+
const upvoteButton = createActionButton("thumbs-up", "Upvote", () => {
|
|
324
|
+
const wasActive = currentVote === "upvote";
|
|
325
|
+
|
|
326
|
+
// Toggle state
|
|
327
|
+
if (wasActive) {
|
|
328
|
+
currentVote = null;
|
|
329
|
+
upvoteButton.classList.remove("tvw-message-action-active");
|
|
330
|
+
} else {
|
|
331
|
+
// Remove downvote if active
|
|
332
|
+
const downvoteBtn = container.querySelector('[data-action="downvote"]');
|
|
333
|
+
if (downvoteBtn) {
|
|
334
|
+
downvoteBtn.classList.remove("tvw-message-action-active");
|
|
335
|
+
}
|
|
336
|
+
currentVote = "upvote";
|
|
337
|
+
upvoteButton.classList.add("tvw-message-action-active");
|
|
338
|
+
|
|
339
|
+
// Trigger feedback
|
|
340
|
+
const feedback: AgentWidgetMessageFeedback = {
|
|
341
|
+
type: "upvote",
|
|
342
|
+
messageId: message.id,
|
|
343
|
+
message
|
|
344
|
+
};
|
|
345
|
+
if (callbacks?.onFeedback) {
|
|
346
|
+
callbacks.onFeedback(feedback);
|
|
347
|
+
}
|
|
348
|
+
if (actionsConfig.onFeedback) {
|
|
349
|
+
actionsConfig.onFeedback(feedback);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}, "upvote");
|
|
353
|
+
container.appendChild(upvoteButton);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Downvote button
|
|
357
|
+
if (showDownvote) {
|
|
358
|
+
const downvoteButton = createActionButton("thumbs-down", "Downvote", () => {
|
|
359
|
+
const wasActive = currentVote === "downvote";
|
|
360
|
+
|
|
361
|
+
// Toggle state
|
|
362
|
+
if (wasActive) {
|
|
363
|
+
currentVote = null;
|
|
364
|
+
downvoteButton.classList.remove("tvw-message-action-active");
|
|
365
|
+
} else {
|
|
366
|
+
// Remove upvote if active
|
|
367
|
+
const upvoteBtn = container.querySelector('[data-action="upvote"]');
|
|
368
|
+
if (upvoteBtn) {
|
|
369
|
+
upvoteBtn.classList.remove("tvw-message-action-active");
|
|
370
|
+
}
|
|
371
|
+
currentVote = "downvote";
|
|
372
|
+
downvoteButton.classList.add("tvw-message-action-active");
|
|
373
|
+
|
|
374
|
+
// Trigger feedback
|
|
375
|
+
const feedback: AgentWidgetMessageFeedback = {
|
|
376
|
+
type: "downvote",
|
|
377
|
+
messageId: message.id,
|
|
378
|
+
message
|
|
379
|
+
};
|
|
380
|
+
if (callbacks?.onFeedback) {
|
|
381
|
+
callbacks.onFeedback(feedback);
|
|
382
|
+
}
|
|
383
|
+
if (actionsConfig.onFeedback) {
|
|
384
|
+
actionsConfig.onFeedback(feedback);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}, "downvote");
|
|
388
|
+
container.appendChild(downvoteButton);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return container;
|
|
392
|
+
};
|
|
393
|
+
|
|
207
394
|
/**
|
|
208
395
|
* Create standard message bubble
|
|
209
396
|
* Supports layout configuration for avatars, timestamps, and visual presets
|
|
@@ -211,7 +398,9 @@ const getBubbleClasses = (
|
|
|
211
398
|
export const createStandardBubble = (
|
|
212
399
|
message: AgentWidgetMessage,
|
|
213
400
|
transform: MessageTransform,
|
|
214
|
-
layoutConfig?: AgentWidgetMessageLayoutConfig
|
|
401
|
+
layoutConfig?: AgentWidgetMessageLayoutConfig,
|
|
402
|
+
actionsConfig?: AgentWidgetMessageActionsConfig,
|
|
403
|
+
actionCallbacks?: MessageActionCallbacks
|
|
215
404
|
): HTMLElement => {
|
|
216
405
|
const config = layoutConfig ?? {};
|
|
217
406
|
const layout = config.layout ?? "bubble";
|
|
@@ -259,6 +448,19 @@ export const createStandardBubble = (
|
|
|
259
448
|
}
|
|
260
449
|
}
|
|
261
450
|
|
|
451
|
+
// Add message actions for assistant messages (only when not streaming and has content)
|
|
452
|
+
const shouldShowActions =
|
|
453
|
+
message.role === "assistant" &&
|
|
454
|
+
!message.streaming &&
|
|
455
|
+
message.content &&
|
|
456
|
+
message.content.trim() &&
|
|
457
|
+
actionsConfig?.enabled !== false;
|
|
458
|
+
|
|
459
|
+
if (shouldShowActions && actionsConfig) {
|
|
460
|
+
const actions = createMessageActions(message, actionsConfig, actionCallbacks);
|
|
461
|
+
bubble.appendChild(actions);
|
|
462
|
+
}
|
|
463
|
+
|
|
262
464
|
// If no avatar needed, return bubble directly
|
|
263
465
|
if (!showAvatar || message.role === "system") {
|
|
264
466
|
return bubble;
|
|
@@ -292,7 +494,9 @@ export const createStandardBubble = (
|
|
|
292
494
|
export const createBubbleWithLayout = (
|
|
293
495
|
message: AgentWidgetMessage,
|
|
294
496
|
transform: MessageTransform,
|
|
295
|
-
layoutConfig?: AgentWidgetMessageLayoutConfig
|
|
497
|
+
layoutConfig?: AgentWidgetMessageLayoutConfig,
|
|
498
|
+
actionsConfig?: AgentWidgetMessageActionsConfig,
|
|
499
|
+
actionCallbacks?: MessageActionCallbacks
|
|
296
500
|
): HTMLElement => {
|
|
297
501
|
const config = layoutConfig ?? {};
|
|
298
502
|
|
|
@@ -314,5 +518,5 @@ export const createBubbleWithLayout = (
|
|
|
314
518
|
}
|
|
315
519
|
|
|
316
520
|
// Fall back to standard bubble
|
|
317
|
-
return createStandardBubble(message, transform, layoutConfig);
|
|
521
|
+
return createStandardBubble(message, transform, layoutConfig, actionsConfig, actionCallbacks);
|
|
318
522
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createElement, createFragment } from "../utils/dom";
|
|
2
2
|
import { AgentWidgetMessage, AgentWidgetConfig } from "../types";
|
|
3
|
-
import { MessageTransform } from "./message-bubble";
|
|
3
|
+
import { MessageTransform, MessageActionCallbacks } from "./message-bubble";
|
|
4
4
|
import { createStandardBubble } from "./message-bubble";
|
|
5
5
|
import { createReasoningBubble } from "./reasoning-bubble";
|
|
6
6
|
import { createToolBubble } from "./tool-bubble";
|
|
@@ -11,7 +11,8 @@ export const renderMessages = (
|
|
|
11
11
|
transform: MessageTransform,
|
|
12
12
|
showReasoning: boolean,
|
|
13
13
|
showToolCalls: boolean,
|
|
14
|
-
config?: AgentWidgetConfig
|
|
14
|
+
config?: AgentWidgetConfig,
|
|
15
|
+
actionCallbacks?: MessageActionCallbacks
|
|
15
16
|
) => {
|
|
16
17
|
container.innerHTML = "";
|
|
17
18
|
const fragment = createFragment();
|
|
@@ -25,7 +26,13 @@ export const renderMessages = (
|
|
|
25
26
|
if (!showToolCalls) return;
|
|
26
27
|
bubble = createToolBubble(message, config);
|
|
27
28
|
} else {
|
|
28
|
-
bubble = createStandardBubble(
|
|
29
|
+
bubble = createStandardBubble(
|
|
30
|
+
message,
|
|
31
|
+
transform,
|
|
32
|
+
config?.layout?.messages,
|
|
33
|
+
config?.messageActions,
|
|
34
|
+
actionCallbacks
|
|
35
|
+
);
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
const wrapper = createElement("div", "tvw-flex");
|
package/src/defaults.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { AgentWidgetConfig } from "./types";
|
|
|
6
6
|
*/
|
|
7
7
|
export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
|
|
8
8
|
apiUrl: "http://localhost:43111/api/chat/dispatch",
|
|
9
|
+
// Client token mode defaults (optional, only used when clientToken is set)
|
|
10
|
+
clientToken: undefined,
|
|
9
11
|
theme: {
|
|
10
12
|
primary: "#111827",
|
|
11
13
|
accent: "#1d4ed8",
|
|
@@ -168,6 +170,15 @@ export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
|
|
|
168
170
|
},
|
|
169
171
|
disableDefaultStyles: false,
|
|
170
172
|
},
|
|
173
|
+
messageActions: {
|
|
174
|
+
enabled: true,
|
|
175
|
+
showCopy: true,
|
|
176
|
+
showUpvote: false, // Requires backend - disabled by default
|
|
177
|
+
showDownvote: false, // Requires backend - disabled by default
|
|
178
|
+
visibility: "hover",
|
|
179
|
+
align: "right",
|
|
180
|
+
layout: "pill-inside",
|
|
181
|
+
},
|
|
171
182
|
debug: false,
|
|
172
183
|
};
|
|
173
184
|
|
|
@@ -252,5 +263,9 @@ export function mergeWithDefaults(
|
|
|
252
263
|
...config.markdown?.options,
|
|
253
264
|
},
|
|
254
265
|
},
|
|
266
|
+
messageActions: {
|
|
267
|
+
...DEFAULT_WIDGET_CONFIG.messageActions,
|
|
268
|
+
...config.messageActions,
|
|
269
|
+
},
|
|
255
270
|
};
|
|
256
271
|
}
|
package/src/index.ts
CHANGED
|
@@ -32,7 +32,14 @@ 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
|
|
36
43
|
} from "./types";
|
|
37
44
|
|
|
38
45
|
export { initAgentWidgetFn as initAgentWidget };
|
|
@@ -120,8 +127,9 @@ export type {
|
|
|
120
127
|
export {
|
|
121
128
|
createStandardBubble,
|
|
122
129
|
createBubbleWithLayout,
|
|
123
|
-
createTypingIndicator
|
|
130
|
+
createTypingIndicator,
|
|
131
|
+
createMessageActions
|
|
124
132
|
} from "./components/message-bubble";
|
|
125
|
-
export type { MessageTransform } from "./components/message-bubble";
|
|
133
|
+
export type { MessageTransform, MessageActionCallbacks } from "./components/message-bubble";
|
|
126
134
|
|
|
127
135
|
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
|
-
|
|
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
|
|
158
|
+
// Merge top-level config options into widget config
|
|
117
159
|
const widgetConfig = { ...config.config };
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
123
|
-
|
|
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
|
-
|
|
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,7 +2,8 @@ import { AgentWidgetClient } from "./client";
|
|
|
2
2
|
import {
|
|
3
3
|
AgentWidgetConfig,
|
|
4
4
|
AgentWidgetEvent,
|
|
5
|
-
AgentWidgetMessage
|
|
5
|
+
AgentWidgetMessage,
|
|
6
|
+
ClientSession
|
|
6
7
|
} from "./types";
|
|
7
8
|
|
|
8
9
|
export type AgentWidgetSessionStatus =
|
|
@@ -25,6 +26,9 @@ export class AgentWidgetSession {
|
|
|
25
26
|
private streaming = false;
|
|
26
27
|
private abortController: AbortController | null = null;
|
|
27
28
|
private sequenceCounter = Date.now();
|
|
29
|
+
|
|
30
|
+
// Client token session management
|
|
31
|
+
private clientSession: ClientSession | null = null;
|
|
28
32
|
|
|
29
33
|
constructor(
|
|
30
34
|
private config: AgentWidgetConfig = {},
|
|
@@ -43,6 +47,78 @@ export class AgentWidgetSession {
|
|
|
43
47
|
this.callbacks.onStatusChanged(this.status);
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Check if running in client token mode
|
|
52
|
+
*/
|
|
53
|
+
public isClientTokenMode(): boolean {
|
|
54
|
+
return this.client.isClientTokenMode();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the client session (for client token mode).
|
|
59
|
+
* This is called automatically on first message, but can be called
|
|
60
|
+
* explicitly to pre-initialize the session and get config from server.
|
|
61
|
+
*/
|
|
62
|
+
public async initClientSession(): Promise<ClientSession | null> {
|
|
63
|
+
if (!this.isClientTokenMode()) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const session = await this.client.initSession();
|
|
69
|
+
this.setClientSession(session);
|
|
70
|
+
return session;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this.callbacks.onError?.(
|
|
73
|
+
error instanceof Error ? error : new Error(String(error))
|
|
74
|
+
);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set the client session after initialization
|
|
81
|
+
*/
|
|
82
|
+
public setClientSession(session: ClientSession): void {
|
|
83
|
+
this.clientSession = session;
|
|
84
|
+
|
|
85
|
+
// Optionally add welcome message from session config
|
|
86
|
+
if (session.config.welcomeMessage && this.messages.length === 0) {
|
|
87
|
+
const welcomeMessage: AgentWidgetMessage = {
|
|
88
|
+
id: `welcome-${Date.now()}`,
|
|
89
|
+
role: "assistant",
|
|
90
|
+
content: session.config.welcomeMessage,
|
|
91
|
+
createdAt: new Date().toISOString(),
|
|
92
|
+
sequence: this.nextSequence()
|
|
93
|
+
};
|
|
94
|
+
this.appendMessage(welcomeMessage);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get current client session
|
|
100
|
+
*/
|
|
101
|
+
public getClientSession(): ClientSession | null {
|
|
102
|
+
return this.clientSession ?? this.client.getClientSession();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check if session is valid and not expired
|
|
107
|
+
*/
|
|
108
|
+
public isSessionValid(): boolean {
|
|
109
|
+
const session = this.getClientSession();
|
|
110
|
+
if (!session) return false;
|
|
111
|
+
return new Date() < session.expiresAt;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Clear session (on expiry or error)
|
|
116
|
+
*/
|
|
117
|
+
public clearClientSession(): void {
|
|
118
|
+
this.clientSession = null;
|
|
119
|
+
this.client.clearClientSession();
|
|
120
|
+
}
|
|
121
|
+
|
|
46
122
|
public updateConfig(next: AgentWidgetConfig) {
|
|
47
123
|
this.config = { ...this.config, ...next };
|
|
48
124
|
this.client = new AgentWidgetClient(this.config);
|