@webless/agent 0.2.0 → 0.2.6

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/embed.cjs CHANGED
@@ -33,7 +33,7 @@ module.exports = __toCommonJS(embed_exports);
33
33
  // src/embed/mount.tsx
34
34
  var import_client4 = require("react-dom/client");
35
35
 
36
- // src/embed/controller.ts
36
+ // src/react/panel-controller.ts
37
37
  var controllers = /* @__PURE__ */ new Map();
38
38
  function registerAgentPanelController(customerId, controller) {
39
39
  controllers.set(customerId, controller);
@@ -48,479 +48,73 @@ function closeAgentPanel(customerId) {
48
48
  controllers.get(customerId)?.close();
49
49
  }
50
50
 
51
- // src/embed/AgentWidget.tsx
52
- var import_react4 = require("react");
51
+ // src/react/components/AgentWidget/AgentWidget.tsx
52
+ var import_react5 = require("react");
53
53
 
54
- // src/react/components/AgentRail/AgentRail.tsx
55
- var import_react2 = require("react");
54
+ // src/react/hooks/useAgentChat.ts
55
+ var import_react = require("react");
56
56
 
57
- // src/react/components/ToolTimeline/ToolTimeline.tsx
58
- var import_jsx_runtime = require("react/jsx-runtime");
59
- function SpinnerIcon() {
60
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "agent-status__spinner", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
61
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "1.5", strokeOpacity: "0.25" }),
62
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 8a6 6 0 0 0-6-6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
63
- ] });
64
- }
65
- function getCurrentStep(steps) {
66
- const active = steps.find((step) => step.state === "active");
67
- if (active) return active;
68
- const error = steps.find((step) => step.state === "error");
69
- if (error) return error;
70
- const pending = steps.find((step) => step.state === "pending");
71
- if (pending) return pending;
72
- return steps.at(-1) ?? null;
73
- }
74
- function ToolTimeline({
75
- steps,
76
- streamingText,
77
- showStreaming = false,
78
- inline = false
79
- }) {
80
- if (steps.length === 0 && !(showStreaming && streamingText)) return null;
81
- const currentStep = getCurrentStep(steps);
82
- const showStatus = currentStep && currentStep.state !== "completed" && !(showStreaming && streamingText);
83
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
84
- "div",
85
- {
86
- className: `tool-timeline${inline ? " tool-timeline--inline" : ""}`,
87
- role: "status",
88
- "aria-live": "polite",
89
- "aria-label": "Agent progress",
90
- children: [
91
- showStatus ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status", children: [
92
- currentStep.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__icon agent-status__icon--error", "aria-hidden": "true", children: "!" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SpinnerIcon, {}) }),
93
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status__copy", children: [
94
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__label", children: currentStep.label }),
95
- currentStep.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__detail", children: currentStep.detail }) : null
96
- ] })
97
- ] }, currentStep.id) : null,
98
- showStreaming && streamingText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "tool-timeline__streaming-text", children: [
99
- streamingText,
100
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" })
101
- ] }) : null
102
- ]
103
- }
104
- );
105
- }
57
+ // src/runtime/client.ts
58
+ var import_client = require("eve/client");
106
59
 
107
- // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
108
- var import_jsx_runtime2 = require("react/jsx-runtime");
109
- function AgentActivityBubble({
110
- steps,
111
- streamingText,
112
- showStreaming = false
113
- }) {
114
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("article", { className: "agent-activity-bubble", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "agent-activity-bubble__shell", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
115
- ToolTimeline,
116
- {
117
- steps,
118
- streamingText,
119
- showStreaming,
120
- inline: true
121
- }
122
- ) }) });
60
+ // src/runtime/config.ts
61
+ var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
62
+ var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
63
+ function buildEveHost(origin, indexId) {
64
+ const base = trimTrailingSlash(origin);
65
+ return `${base}?indexId=${encodeURIComponent(indexId.trim())}`;
66
+ }
67
+ function resolveAgentRuntimeConfig(input) {
68
+ const origin = trimTrailingSlash(input.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
69
+ const resolvedIndexId = input.indexId.trim();
70
+ return {
71
+ origin,
72
+ indexId: resolvedIndexId,
73
+ host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId) : origin
74
+ };
123
75
  }
124
76
 
125
- // src/react/components/Composer/Composer.tsx
126
- var import_react = require("react");
127
- var import_jsx_runtime3 = require("react/jsx-runtime");
128
- function SendIcon() {
129
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M8 12V4M8 4l-3 3M8 4l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
77
+ // src/runtime/session-store.ts
78
+ var DEFAULT_STORAGE_KEY_PREFIX = "webless:agent";
79
+ function normalizeRuntimeOrigin(runtimeOrigin) {
80
+ const trimmed = runtimeOrigin?.trim();
81
+ if (!trimmed) return "_";
82
+ return trimmed.replace(/\/+$/, "");
130
83
  }
131
- function Composer({
132
- disabled = false,
133
- placeholder = "Ask anything\u2026",
134
- variant = "default",
135
- onSubmit
136
- }) {
137
- const [value, setValue] = (0, import_react.useState)("");
138
- const inputRef = (0, import_react.useRef)(null);
139
- function submitCurrent() {
140
- const trimmed = value.trim();
141
- if (!trimmed || disabled) return;
142
- onSubmit?.(trimmed);
143
- setValue("");
144
- inputRef.current?.focus();
145
- }
146
- function handleSubmit(event) {
147
- event.preventDefault();
148
- submitCurrent();
149
- }
150
- function handleKeyDown(event) {
151
- if (event.key === "Enter" && !event.shiftKey) {
152
- event.preventDefault();
153
- submitCurrent();
154
- }
84
+ function buildAgentStorageKeyPrefix(input) {
85
+ const customerId = input.customerId?.trim() || "_";
86
+ const indexId = input.indexId.trim();
87
+ if (!indexId) {
88
+ throw new Error("indexId is required to build agent storage keys.");
155
89
  }
156
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { className: `composer${variant === "dock" ? " composer--dock" : ""}`, onSubmit: handleSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__field", children: [
157
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
158
- "textarea",
159
- {
160
- ref: inputRef,
161
- className: "composer__input",
162
- rows: 1,
163
- value,
164
- placeholder,
165
- disabled,
166
- "aria-label": "Message",
167
- onChange: (event) => setValue(event.target.value),
168
- onKeyDown: handleKeyDown
169
- }
170
- ),
171
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
172
- "button",
173
- {
174
- type: "submit",
175
- className: "composer__send",
176
- disabled: disabled || !value.trim(),
177
- "aria-label": "Send message",
178
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
179
- }
180
- )
181
- ] }) });
90
+ return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
182
91
  }
183
-
184
- // src/react/components/FollowUpChips/FollowUpChips.tsx
185
- var import_jsx_runtime4 = require("react/jsx-runtime");
186
- function FollowUpChips({
187
- suggestions,
188
- disabled = false,
189
- label = "Follow up",
190
- variant = "default",
191
- onSelect
192
- }) {
193
- if (suggestions.length === 0) return null;
194
- if (variant === "dock") {
195
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups followups--dock", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups__scroll", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
196
- "button",
197
- {
198
- type: "button",
199
- className: "followups__chip followups__chip--dock",
200
- disabled,
201
- onClick: () => onSelect?.(suggestion),
202
- children: suggestion.label
203
- },
204
- suggestion.id
205
- )) }) });
92
+ function createSessionId() {
93
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
94
+ return crypto.randomUUID();
206
95
  }
207
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "followups", children: [
208
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "followups__label", children: label }),
209
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
210
- "button",
211
- {
212
- type: "button",
213
- className: "followups__chip",
214
- disabled,
215
- onClick: () => onSelect?.(suggestion),
216
- children: suggestion.label
217
- },
218
- suggestion.id
219
- )) })
220
- ] });
96
+ return `sess_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
221
97
  }
222
-
223
- // src/react/components/MessageBubble/MessageBubble.tsx
224
- var import_jsx_runtime5 = require("react/jsx-runtime");
225
- function MessageBubble({ message }) {
226
- if (message.role === "visitor") {
227
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "message-bubble__text", children: message.text }) });
228
- }
229
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "message-bubble__text", children: [
230
- message.text,
231
- message.streaming ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" }) : null
232
- ] }) });
98
+ function resolvePrefix(options) {
99
+ return options?.storageKeyPrefix?.trim() || DEFAULT_STORAGE_KEY_PREFIX;
233
100
  }
234
-
235
- // src/react/components/AgentRail/AgentRail.tsx
236
- var import_jsx_runtime6 = require("react/jsx-runtime");
237
- function MinimizeIcon() {
238
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M3.5 8h9", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
101
+ function getOrCreateVisitorSessionId(options) {
102
+ const prefix = resolvePrefix(options);
103
+ const visitorKey = `${prefix}:visitorSessionId`;
104
+ if (typeof sessionStorage === "undefined") {
105
+ return createSessionId();
106
+ }
107
+ const existing = sessionStorage.getItem(visitorKey)?.trim();
108
+ if (existing) return existing;
109
+ const next = createSessionId();
110
+ sessionStorage.setItem(visitorKey, next);
111
+ return next;
239
112
  }
240
- function ExpandIcon() {
241
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
242
- "path",
243
- {
244
- d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
245
- stroke: "currentColor",
246
- strokeWidth: "1.5",
247
- strokeLinecap: "round",
248
- strokeLinejoin: "round"
249
- }
250
- ) });
113
+ function runtimeSessionIdKey(visitorSessionId, prefix) {
114
+ return `${prefix}:eve:${visitorSessionId}:sessionId`;
251
115
  }
252
- function RestoreIcon() {
253
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
254
- "path",
255
- {
256
- d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
257
- stroke: "currentColor",
258
- strokeWidth: "1.5",
259
- strokeLinecap: "round",
260
- strokeLinejoin: "round"
261
- }
262
- ) });
263
- }
264
- function AgentRail({
265
- state,
266
- theme,
267
- brandLabel = "Webless Assist",
268
- poweredByLabel = "Powered by Webless",
269
- composerPlaceholder = "Ask anything\u2026",
270
- mobileFullscreen = false,
271
- expanded = false,
272
- onCollapse,
273
- onClose,
274
- onExpandToggle,
275
- onSubmit,
276
- onFollowUpSelect
277
- }) {
278
- const transcriptRef = (0, import_react2.useRef)(null);
279
- const railStyle = theme?.railMaxWidth ? { ["--rail-width"]: theme.railMaxWidth, ["--as-rail-max-width"]: theme.railMaxWidth } : void 0;
280
- const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
281
- const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
282
- const showStreamingInActivity = state.phase === "streaming" && Boolean(state.streamingText);
283
- const hasVisitorMessages2 = state.messages.some((message) => message.role === "visitor");
284
- const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
285
- const showCompleteFollowUps = state.phase === "complete" && state.followUps.length > 0 && hasVisitorMessages2;
286
- const showDockFollowUps = expanded && (showIdleFollowUps || showCompleteFollowUps);
287
- (0, import_react2.useEffect)(() => {
288
- const node = transcriptRef.current;
289
- if (!node) return;
290
- node.scrollTop = node.scrollHeight;
291
- }, [state.messages, state.toolSteps, state.streamingText, state.followUps, state.journey]);
292
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
293
- "aside",
294
- {
295
- className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
296
- style: railStyle,
297
- "aria-label": "Agent conversation",
298
- children: [
299
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__brand-row", children: [
300
- onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
301
- "button",
302
- {
303
- type: "button",
304
- className: "agent-rail__collapse",
305
- "aria-label": "Collapse assist",
306
- onClick: onCollapse,
307
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
308
- }
309
- ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "agent-rail__close", "aria-label": "Close agent", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
310
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel }),
311
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
312
- "button",
313
- {
314
- type: "button",
315
- className: "agent-rail__expand",
316
- "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
317
- onClick: onExpandToggle,
318
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
319
- }
320
- ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
321
- ] }) }),
322
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
323
- state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message }, message.id)),
324
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
325
- AgentActivityBubble,
326
- {
327
- steps: state.toolSteps,
328
- streamingText: state.streamingText,
329
- showStreaming: showStreamingInActivity
330
- }
331
- ) : null,
332
- !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
333
- FollowUpChips,
334
- {
335
- suggestions: state.followUps,
336
- disabled: isBusy,
337
- label: "Try asking",
338
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
339
- }
340
- ) }) : null,
341
- !expanded && showCompleteFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
342
- FollowUpChips,
343
- {
344
- suggestions: state.followUps,
345
- disabled: isBusy,
346
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
347
- }
348
- ) }) : null,
349
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", style: { fontSize: 13, color: "var(--as-danger)", margin: 0 }, children: state.error }) : null
350
- ] }),
351
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
352
- showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
353
- FollowUpChips,
354
- {
355
- variant: "dock",
356
- suggestions: state.followUps,
357
- disabled: isBusy,
358
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
359
- }
360
- ) }) : null,
361
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
362
- Composer,
363
- {
364
- variant: "dock",
365
- disabled: isBusy,
366
- placeholder: composerPlaceholder,
367
- onSubmit
368
- }
369
- ) }),
370
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
371
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
372
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
373
- ] })
374
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
375
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
376
- Composer,
377
- {
378
- disabled: isBusy,
379
- placeholder: composerPlaceholder,
380
- onSubmit
381
- }
382
- ),
383
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
384
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
385
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
386
- ] })
387
- ] })
388
- ]
389
- }
390
- );
391
- }
392
-
393
- // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
394
- var import_jsx_runtime7 = require("react/jsx-runtime");
395
- function SparklesIcon() {
396
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "assist-edge-tab__sparkles", viewBox: "0 0 18 16", fill: "none", "aria-hidden": "true", children: [
397
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 1.2l.95 2.7 2.85.05-2.25 1.75.8 2.75L8 6.7 5.65 8.45l.8-2.75L4.2 3.95l2.85-.05L8 1.2z", fill: "currentColor" }),
398
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M14.2 6.4l.55 1.55 1.65.03-1.3 1 .46 1.58-1.36-1-1.36 1 .46-1.58-1.3-1 1.65-.03.55-1.55z", fill: "currentColor" }),
399
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M3.1 9.1l.4 1.15 1.22.02-.96.74.34 1.17-1-.74-1 .74.34-1.17-.96-.74 1.22-.02.4-1.15z", fill: "currentColor" })
400
- ] });
401
- }
402
- function ChevronLeftIcon() {
403
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M10 4L6 8l4 4", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
404
- }
405
- function ChevronDownIcon() {
406
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
407
- }
408
- function DragDots() {
409
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", {}, index)) });
410
- }
411
- var VARIANT_COPY = {
412
- outline: { label: "Assist", aria: "Open Assist" },
413
- ask: { label: "Ask anything", aria: "Ask anything" },
414
- fill: { label: "Assist", aria: "Open Assist" }
415
- };
416
- function AssistEdgeTab({
417
- variant,
418
- side,
419
- along,
420
- inset,
421
- visible,
422
- onOpen
423
- }) {
424
- const copy = VARIANT_COPY[variant];
425
- const style = {
426
- "--tab-along": `${along}%`,
427
- "--tab-inset": `${inset}px`
428
- };
429
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
430
- "button",
431
- {
432
- type: "button",
433
- className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
434
- style,
435
- "aria-label": copy.aria,
436
- "aria-hidden": !visible,
437
- tabIndex: visible ? 0 : -1,
438
- onClick: onOpen,
439
- children: [
440
- variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
441
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
442
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
443
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDownIcon, {})
444
- ] }) : null,
445
- variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
446
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}),
447
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
448
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DragDots, {})
449
- ] }) : null,
450
- variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
451
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
452
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
453
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {})
454
- ] }) : null
455
- ]
456
- }
457
- );
458
- }
459
-
460
- // src/react/hooks/useAgentChat.ts
461
- var import_react3 = require("react");
462
-
463
- // src/runtime/client.ts
464
- var import_client = require("eve/client");
465
-
466
- // src/runtime/config.ts
467
- var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
468
- var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
469
- function buildEveHost(origin, indexId) {
470
- const base = trimTrailingSlash(origin);
471
- return `${base}?indexId=${encodeURIComponent(indexId.trim())}`;
472
- }
473
- function resolveAgentRuntimeConfig(input) {
474
- const origin = trimTrailingSlash(input.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
475
- const resolvedIndexId = input.indexId.trim();
476
- return {
477
- origin,
478
- indexId: resolvedIndexId,
479
- host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId) : origin
480
- };
481
- }
482
-
483
- // src/runtime/session-store.ts
484
- var DEFAULT_STORAGE_KEY_PREFIX = "webless:agent";
485
- function normalizeRuntimeOrigin(runtimeOrigin) {
486
- const trimmed = runtimeOrigin?.trim();
487
- if (!trimmed) return "_";
488
- return trimmed.replace(/\/+$/, "");
489
- }
490
- function buildAgentStorageKeyPrefix(input) {
491
- const customerId = input.customerId?.trim() || "_";
492
- const indexId = input.indexId.trim();
493
- if (!indexId) {
494
- throw new Error("indexId is required to build agent storage keys.");
495
- }
496
- return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
497
- }
498
- function createSessionId() {
499
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
500
- return crypto.randomUUID();
501
- }
502
- return `sess_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
503
- }
504
- function resolvePrefix(options) {
505
- return options?.storageKeyPrefix?.trim() || DEFAULT_STORAGE_KEY_PREFIX;
506
- }
507
- function getOrCreateVisitorSessionId(options) {
508
- const prefix = resolvePrefix(options);
509
- const visitorKey = `${prefix}:visitorSessionId`;
510
- if (typeof sessionStorage === "undefined") {
511
- return createSessionId();
512
- }
513
- const existing = sessionStorage.getItem(visitorKey)?.trim();
514
- if (existing) return existing;
515
- const next = createSessionId();
516
- sessionStorage.setItem(visitorKey, next);
517
- return next;
518
- }
519
- function runtimeSessionIdKey(visitorSessionId, prefix) {
520
- return `${prefix}:eve:${visitorSessionId}:sessionId`;
521
- }
522
- function runtimeStreamIndexKey(visitorSessionId, prefix) {
523
- return `${prefix}:eve:${visitorSessionId}:streamIndex`;
116
+ function runtimeStreamIndexKey(visitorSessionId, prefix) {
117
+ return `${prefix}:eve:${visitorSessionId}:streamIndex`;
524
118
  }
525
119
  function loadPersistedAgentSession(visitorSessionId, options) {
526
120
  if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
@@ -795,17 +389,17 @@ function useAgentChat({
795
389
  visitorSessionId,
796
390
  storageKeyPrefix
797
391
  }) {
798
- const [state, setState] = (0, import_react3.useState)(INITIAL_STATE);
799
- const runRef = (0, import_react3.useRef)(null);
800
- const resolvedStorageKeyPrefix = (0, import_react3.useMemo)(
392
+ const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
393
+ const runRef = (0, import_react.useRef)(null);
394
+ const resolvedStorageKeyPrefix = (0, import_react.useMemo)(
801
395
  () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, runtimeOrigin }),
802
396
  [customerId, indexId, runtimeOrigin, storageKeyPrefix]
803
397
  );
804
- const visitorId = (0, import_react3.useMemo)(
398
+ const visitorId = (0, import_react.useMemo)(
805
399
  () => visitorSessionId?.trim() || getOrCreateVisitorSessionId({ storageKeyPrefix: resolvedStorageKeyPrefix }),
806
400
  [resolvedStorageKeyPrefix, visitorSessionId]
807
401
  );
808
- const clientRef = (0, import_react3.useRef)(
402
+ const clientRef = (0, import_react.useRef)(
809
403
  createAgentClient({
810
404
  customerId,
811
405
  indexId,
@@ -814,9 +408,9 @@ function useAgentChat({
814
408
  storageKeyPrefix: resolvedStorageKeyPrefix
815
409
  })
816
410
  );
817
- const identityRef = (0, import_react3.useRef)(null);
411
+ const identityRef = (0, import_react.useRef)(null);
818
412
  const identityKey = `${customerId ?? ""}|${indexId}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
819
- (0, import_react3.useEffect)(() => {
413
+ (0, import_react.useEffect)(() => {
820
414
  if (identityRef.current === null) {
821
415
  identityRef.current = identityKey;
822
416
  return;
@@ -837,13 +431,13 @@ function useAgentChat({
837
431
  });
838
432
  setState(INITIAL_STATE);
839
433
  }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]);
840
- const reset = (0, import_react3.useCallback)(() => {
434
+ const reset = (0, import_react.useCallback)(() => {
841
435
  runRef.current?.abort();
842
436
  runRef.current = null;
843
437
  clientRef.current.reset();
844
438
  setState(INITIAL_STATE);
845
439
  }, []);
846
- const submit = (0, import_react3.useCallback)(
440
+ const submit = (0, import_react.useCallback)(
847
441
  async (visitorText) => {
848
442
  runRef.current?.abort();
849
443
  clientRef.current.cancelActive();
@@ -905,108 +499,542 @@ function useAgentChat({
905
499
  if (!isActiveRun()) return;
906
500
  streamStarted = true;
907
501
  }
908
- }
909
- });
910
- await planningPromise.catch(() => {
911
- });
912
- if (!isActiveRun()) return;
913
- const agentMessage = {
914
- id: `agent-${Date.now()}`,
915
- role: "agent",
916
- text: finalText,
917
- createdAt: Date.now()
918
- };
919
- setState((prev) => ({
920
- ...prev,
921
- phase: "complete",
922
- messages: [...prev.messages, agentMessage],
923
- toolSteps: [],
924
- streamingText: "",
925
- followUps: DEFAULT_FOLLOW_UPS,
926
- journey: null
927
- }));
928
- } catch (error) {
929
- if (error instanceof DOMException && error.name === "AbortError") return;
930
- if (!isActiveRun()) return;
931
- const message = formatAgentError(error);
932
- if (!message) return;
933
- setState((prev) => ({
934
- ...prev,
935
- phase: "complete",
936
- toolSteps: [],
937
- streamingText: "",
938
- error: message
939
- }));
940
- }
941
- },
942
- [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]
502
+ }
503
+ });
504
+ await planningPromise.catch(() => {
505
+ });
506
+ if (!isActiveRun()) return;
507
+ const agentMessage = {
508
+ id: `agent-${Date.now()}`,
509
+ role: "agent",
510
+ text: finalText,
511
+ createdAt: Date.now()
512
+ };
513
+ setState((prev) => ({
514
+ ...prev,
515
+ phase: "complete",
516
+ messages: [...prev.messages, agentMessage],
517
+ toolSteps: [],
518
+ streamingText: "",
519
+ followUps: DEFAULT_FOLLOW_UPS,
520
+ journey: null
521
+ }));
522
+ } catch (error) {
523
+ if (error instanceof DOMException && error.name === "AbortError") return;
524
+ if (!isActiveRun()) return;
525
+ const message = formatAgentError(error);
526
+ if (!message) return;
527
+ setState((prev) => ({
528
+ ...prev,
529
+ phase: "complete",
530
+ toolSteps: [],
531
+ streamingText: "",
532
+ error: message
533
+ }));
534
+ }
535
+ },
536
+ [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]
537
+ );
538
+ (0, import_react.useEffect)(() => {
539
+ return () => {
540
+ runRef.current?.abort();
541
+ clientRef.current.cancelActive();
542
+ };
543
+ }, []);
544
+ return {
545
+ state,
546
+ reset,
547
+ submit,
548
+ visitorSessionId: visitorId,
549
+ sessionId: clientRef.current.getActiveSessionId()
550
+ };
551
+ }
552
+ function hasVisitorMessages(messages) {
553
+ return messages.some((message) => message.role === "visitor");
554
+ }
555
+ function createIdleSuggestions() {
556
+ return [
557
+ { id: "idle-1", label: "Hello \u2014 what can you do?" },
558
+ { id: "idle-2", label: "Help me evaluate this product" },
559
+ { id: "idle-3", label: "What should I ask you?" }
560
+ ];
561
+ }
562
+
563
+ // src/react/hooks/useIsMobile.ts
564
+ var import_react2 = require("react");
565
+ function useIsMobile(breakpoint = 767) {
566
+ const [isMobile, setIsMobile] = (0, import_react2.useState)(
567
+ () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
568
+ );
569
+ (0, import_react2.useEffect)(() => {
570
+ const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
571
+ const onChange = () => setIsMobile(media.matches);
572
+ onChange();
573
+ media.addEventListener("change", onChange);
574
+ return () => media.removeEventListener("change", onChange);
575
+ }, [breakpoint]);
576
+ return isMobile;
577
+ }
578
+
579
+ // src/react/placement.ts
580
+ var DEFAULT_AGENT_PLACEMENT = {
581
+ side: "right",
582
+ along: 50,
583
+ inset: 0,
584
+ variant: "outline"
585
+ };
586
+ function normalizeAgentPlacement(placement) {
587
+ const along = placement?.along ?? DEFAULT_AGENT_PLACEMENT.along;
588
+ return {
589
+ side: placement?.side ?? DEFAULT_AGENT_PLACEMENT.side,
590
+ along: Math.min(100, Math.max(0, along)),
591
+ inset: Math.max(0, placement?.inset ?? DEFAULT_AGENT_PLACEMENT.inset),
592
+ variant: placement?.variant ?? DEFAULT_AGENT_PLACEMENT.variant
593
+ };
594
+ }
595
+
596
+ // src/react/components/AgentRail/AgentRail.tsx
597
+ var import_react4 = require("react");
598
+
599
+ // src/react/components/ToolTimeline/ToolTimeline.tsx
600
+ var import_jsx_runtime = require("react/jsx-runtime");
601
+ function SpinnerIcon() {
602
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "agent-status__spinner", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
603
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "1.5", strokeOpacity: "0.25" }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 8a6 6 0 0 0-6-6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
605
+ ] });
606
+ }
607
+ function getCurrentStep(steps) {
608
+ const active = steps.find((step) => step.state === "active");
609
+ if (active) return active;
610
+ const error = steps.find((step) => step.state === "error");
611
+ if (error) return error;
612
+ const pending = steps.find((step) => step.state === "pending");
613
+ if (pending) return pending;
614
+ return steps.at(-1) ?? null;
615
+ }
616
+ function ToolTimeline({
617
+ steps,
618
+ streamingText,
619
+ showStreaming = false,
620
+ inline = false
621
+ }) {
622
+ if (steps.length === 0 && !(showStreaming && streamingText)) return null;
623
+ const currentStep = getCurrentStep(steps);
624
+ const showStatus = currentStep && currentStep.state !== "completed" && !(showStreaming && streamingText);
625
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
626
+ "div",
627
+ {
628
+ className: `tool-timeline${inline ? " tool-timeline--inline" : ""}`,
629
+ role: "status",
630
+ "aria-live": "polite",
631
+ "aria-label": "Agent progress",
632
+ children: [
633
+ showStatus ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status", children: [
634
+ currentStep.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__icon agent-status__icon--error", "aria-hidden": "true", children: "!" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SpinnerIcon, {}) }),
635
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status__copy", children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__label", children: currentStep.label }),
637
+ currentStep.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__detail", children: currentStep.detail }) : null
638
+ ] })
639
+ ] }, currentStep.id) : null,
640
+ showStreaming && streamingText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "tool-timeline__streaming-text", children: [
641
+ streamingText,
642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" })
643
+ ] }) : null
644
+ ]
645
+ }
646
+ );
647
+ }
648
+
649
+ // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
650
+ var import_jsx_runtime2 = require("react/jsx-runtime");
651
+ function AgentActivityBubble({
652
+ steps,
653
+ streamingText,
654
+ showStreaming = false
655
+ }) {
656
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("article", { className: "agent-activity-bubble", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "agent-activity-bubble__shell", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
657
+ ToolTimeline,
658
+ {
659
+ steps,
660
+ streamingText,
661
+ showStreaming,
662
+ inline: true
663
+ }
664
+ ) }) });
665
+ }
666
+
667
+ // src/react/components/Composer/Composer.tsx
668
+ var import_react3 = require("react");
669
+ var import_jsx_runtime3 = require("react/jsx-runtime");
670
+ function SendIcon() {
671
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M8 12V4M8 4l-3 3M8 4l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
672
+ }
673
+ function Composer({
674
+ disabled = false,
675
+ placeholder = "Ask anything\u2026",
676
+ variant = "default",
677
+ onSubmit
678
+ }) {
679
+ const [value, setValue] = (0, import_react3.useState)("");
680
+ const inputRef = (0, import_react3.useRef)(null);
681
+ function submitCurrent() {
682
+ const trimmed = value.trim();
683
+ if (!trimmed || disabled) return;
684
+ onSubmit?.(trimmed);
685
+ setValue("");
686
+ inputRef.current?.focus();
687
+ }
688
+ function handleSubmit(event) {
689
+ event.preventDefault();
690
+ submitCurrent();
691
+ }
692
+ function handleKeyDown(event) {
693
+ if (event.key === "Enter" && !event.shiftKey) {
694
+ event.preventDefault();
695
+ submitCurrent();
696
+ }
697
+ }
698
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { className: `composer${variant === "dock" ? " composer--dock" : ""}`, onSubmit: handleSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__field", children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
700
+ "textarea",
701
+ {
702
+ ref: inputRef,
703
+ className: "composer__input",
704
+ rows: 1,
705
+ value,
706
+ placeholder,
707
+ disabled,
708
+ "aria-label": "Message",
709
+ onChange: (event) => setValue(event.target.value),
710
+ onKeyDown: handleKeyDown
711
+ }
712
+ ),
713
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
714
+ "button",
715
+ {
716
+ type: "submit",
717
+ className: "composer__send",
718
+ disabled: disabled || !value.trim(),
719
+ "aria-label": "Send message",
720
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
721
+ }
722
+ )
723
+ ] }) });
724
+ }
725
+
726
+ // src/react/components/FollowUpChips/FollowUpChips.tsx
727
+ var import_jsx_runtime4 = require("react/jsx-runtime");
728
+ function FollowUpChips({
729
+ suggestions,
730
+ disabled = false,
731
+ label = "Follow up",
732
+ variant = "default",
733
+ onSelect
734
+ }) {
735
+ if (suggestions.length === 0) return null;
736
+ if (variant === "dock") {
737
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups followups--dock", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups__scroll", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
738
+ "button",
739
+ {
740
+ type: "button",
741
+ className: "followups__chip followups__chip--dock",
742
+ disabled,
743
+ onClick: () => onSelect?.(suggestion),
744
+ children: suggestion.label
745
+ },
746
+ suggestion.id
747
+ )) }) });
748
+ }
749
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "followups", children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "followups__label", children: label }),
751
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
752
+ "button",
753
+ {
754
+ type: "button",
755
+ className: "followups__chip",
756
+ disabled,
757
+ onClick: () => onSelect?.(suggestion),
758
+ children: suggestion.label
759
+ },
760
+ suggestion.id
761
+ )) })
762
+ ] });
763
+ }
764
+
765
+ // src/react/components/MessageBubble/MessageBubble.tsx
766
+ var import_jsx_runtime5 = require("react/jsx-runtime");
767
+ function MessageBubble({ message }) {
768
+ if (message.role === "visitor") {
769
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "message-bubble__text", children: message.text }) });
770
+ }
771
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "message-bubble__text", children: [
772
+ message.text,
773
+ message.streaming ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" }) : null
774
+ ] }) });
775
+ }
776
+
777
+ // src/react/components/AgentRail/AgentRail.tsx
778
+ var import_jsx_runtime6 = require("react/jsx-runtime");
779
+ function MinimizeIcon() {
780
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M3.5 8h9", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
781
+ }
782
+ function ExpandIcon() {
783
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
784
+ "path",
785
+ {
786
+ d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
787
+ stroke: "currentColor",
788
+ strokeWidth: "1.5",
789
+ strokeLinecap: "round",
790
+ strokeLinejoin: "round"
791
+ }
792
+ ) });
793
+ }
794
+ function RestoreIcon() {
795
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
796
+ "path",
797
+ {
798
+ d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
799
+ stroke: "currentColor",
800
+ strokeWidth: "1.5",
801
+ strokeLinecap: "round",
802
+ strokeLinejoin: "round"
803
+ }
804
+ ) });
805
+ }
806
+ function AgentRail({
807
+ state,
808
+ theme,
809
+ brandLabel = "Webless Assist",
810
+ poweredByLabel = "Powered by Webless",
811
+ composerPlaceholder = "Ask anything\u2026",
812
+ mobileFullscreen = false,
813
+ expanded = false,
814
+ onCollapse,
815
+ onClose,
816
+ onExpandToggle,
817
+ onSubmit,
818
+ onFollowUpSelect
819
+ }) {
820
+ const transcriptRef = (0, import_react4.useRef)(null);
821
+ const railStyle = theme?.railMaxWidth ? { ["--rail-width"]: theme.railMaxWidth, ["--as-rail-max-width"]: theme.railMaxWidth } : void 0;
822
+ const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
823
+ const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
824
+ const showStreamingInActivity = state.phase === "streaming" && Boolean(state.streamingText);
825
+ const hasVisitorMessages2 = state.messages.some((message) => message.role === "visitor");
826
+ const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
827
+ const showCompleteFollowUps = state.phase === "complete" && state.followUps.length > 0 && hasVisitorMessages2;
828
+ const showDockFollowUps = expanded && (showIdleFollowUps || showCompleteFollowUps);
829
+ (0, import_react4.useEffect)(() => {
830
+ const node = transcriptRef.current;
831
+ if (!node) return;
832
+ node.scrollTop = node.scrollHeight;
833
+ }, [state.messages, state.toolSteps, state.streamingText, state.followUps, state.journey]);
834
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
835
+ "aside",
836
+ {
837
+ className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
838
+ style: railStyle,
839
+ "aria-label": "Agent conversation",
840
+ children: [
841
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__brand-row", children: [
842
+ onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
843
+ "button",
844
+ {
845
+ type: "button",
846
+ className: "agent-rail__collapse",
847
+ "aria-label": "Collapse assist",
848
+ onClick: onCollapse,
849
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
850
+ }
851
+ ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "agent-rail__close", "aria-label": "Close agent", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel }),
853
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
854
+ "button",
855
+ {
856
+ type: "button",
857
+ className: "agent-rail__expand",
858
+ "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
859
+ onClick: onExpandToggle,
860
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
861
+ }
862
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
863
+ ] }) }),
864
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
865
+ state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message }, message.id)),
866
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
867
+ AgentActivityBubble,
868
+ {
869
+ steps: state.toolSteps,
870
+ streamingText: state.streamingText,
871
+ showStreaming: showStreamingInActivity
872
+ }
873
+ ) : null,
874
+ !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
875
+ FollowUpChips,
876
+ {
877
+ suggestions: state.followUps,
878
+ disabled: isBusy,
879
+ label: "Try asking",
880
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
881
+ }
882
+ ) }) : null,
883
+ !expanded && showCompleteFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
884
+ FollowUpChips,
885
+ {
886
+ suggestions: state.followUps,
887
+ disabled: isBusy,
888
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
889
+ }
890
+ ) }) : null,
891
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", style: { fontSize: 13, color: "var(--as-danger)", margin: 0 }, children: state.error }) : null
892
+ ] }),
893
+ expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
894
+ showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
895
+ FollowUpChips,
896
+ {
897
+ variant: "dock",
898
+ suggestions: state.followUps,
899
+ disabled: isBusy,
900
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
901
+ }
902
+ ) }) : null,
903
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
904
+ Composer,
905
+ {
906
+ variant: "dock",
907
+ disabled: isBusy,
908
+ placeholder: composerPlaceholder,
909
+ onSubmit
910
+ }
911
+ ) }),
912
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
913
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
914
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
915
+ ] })
916
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
917
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
918
+ Composer,
919
+ {
920
+ disabled: isBusy,
921
+ placeholder: composerPlaceholder,
922
+ onSubmit
923
+ }
924
+ ),
925
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
928
+ ] })
929
+ ] })
930
+ ]
931
+ }
943
932
  );
944
- (0, import_react3.useEffect)(() => {
945
- return () => {
946
- runRef.current?.abort();
947
- clientRef.current.cancelActive();
948
- };
949
- }, []);
950
- return {
951
- state,
952
- reset,
953
- submit,
954
- visitorSessionId: visitorId,
955
- sessionId: clientRef.current.getActiveSessionId()
956
- };
957
933
  }
958
- function hasVisitorMessages(messages) {
959
- return messages.some((message) => message.role === "visitor");
934
+
935
+ // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
936
+ var import_jsx_runtime7 = require("react/jsx-runtime");
937
+ function SparklesIcon() {
938
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "assist-edge-tab__sparkles", viewBox: "0 0 18 16", fill: "none", "aria-hidden": "true", children: [
939
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 1.2l.95 2.7 2.85.05-2.25 1.75.8 2.75L8 6.7 5.65 8.45l.8-2.75L4.2 3.95l2.85-.05L8 1.2z", fill: "currentColor" }),
940
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M14.2 6.4l.55 1.55 1.65.03-1.3 1 .46 1.58-1.36-1-1.36 1 .46-1.58-1.3-1 1.65-.03.55-1.55z", fill: "currentColor" }),
941
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M3.1 9.1l.4 1.15 1.22.02-.96.74.34 1.17-1-.74-1 .74.34-1.17-.96-.74 1.22-.02.4-1.15z", fill: "currentColor" })
942
+ ] });
960
943
  }
961
- function createIdleSuggestions() {
962
- return [
963
- { id: "idle-1", label: "Hello \u2014 what can you do?" },
964
- { id: "idle-2", label: "Help me evaluate this product" },
965
- { id: "idle-3", label: "What should I ask you?" }
966
- ];
944
+ function ChevronLeftIcon() {
945
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M10 4L6 8l4 4", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
967
946
  }
968
-
969
- // src/embed/AgentWidget.tsx
970
- var import_jsx_runtime8 = require("react/jsx-runtime");
971
- function useIsMobile(breakpoint = 767) {
972
- const [isMobile, setIsMobile] = (0, import_react4.useState)(
973
- () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
947
+ function ChevronDownIcon() {
948
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
949
+ }
950
+ function DragDots() {
951
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", {}, index)) });
952
+ }
953
+ var VARIANT_COPY = {
954
+ outline: { label: "Assist", aria: "Open Assist" },
955
+ ask: { label: "Ask anything", aria: "Ask anything" },
956
+ fill: { label: "Assist", aria: "Open Assist" }
957
+ };
958
+ function AssistEdgeTab({
959
+ variant,
960
+ side,
961
+ along,
962
+ inset,
963
+ visible,
964
+ onOpen
965
+ }) {
966
+ const copy = VARIANT_COPY[variant];
967
+ const style = {
968
+ "--tab-along": `${along}%`,
969
+ "--tab-inset": `${inset}px`
970
+ };
971
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
972
+ "button",
973
+ {
974
+ type: "button",
975
+ className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
976
+ style,
977
+ "aria-label": copy.aria,
978
+ "aria-hidden": !visible,
979
+ tabIndex: visible ? 0 : -1,
980
+ onClick: onOpen,
981
+ children: [
982
+ variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
984
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
985
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDownIcon, {})
986
+ ] }) : null,
987
+ variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
988
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}),
989
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DragDots, {})
991
+ ] }) : null,
992
+ variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
993
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
994
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {})
996
+ ] }) : null
997
+ ]
998
+ }
974
999
  );
975
- (0, import_react4.useEffect)(() => {
976
- const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
977
- const onChange = () => setIsMobile(media.matches);
978
- onChange();
979
- media.addEventListener("change", onChange);
980
- return () => media.removeEventListener("change", onChange);
981
- }, [breakpoint]);
982
- return isMobile;
983
1000
  }
984
- function AgentWidget({ manifest }) {
1001
+
1002
+ // src/react/components/AgentWidget/AgentWidget.tsx
1003
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1004
+ function AgentWidget({
1005
+ indexId,
1006
+ customerId,
1007
+ runtimeOrigin,
1008
+ placement: placementInput,
1009
+ defaultCollapsed = true,
1010
+ registerPanelController = false
1011
+ }) {
985
1012
  const isMobile = useIsMobile();
986
- const [railCollapsed, setRailCollapsed] = (0, import_react4.useState)(true);
987
- const [railExpanded, setRailExpanded] = (0, import_react4.useState)(false);
1013
+ const placement = normalizeAgentPlacement(placementInput);
1014
+ const [railCollapsed, setRailCollapsed] = (0, import_react5.useState)(defaultCollapsed);
1015
+ const [railExpanded, setRailExpanded] = (0, import_react5.useState)(false);
988
1016
  const { state, submit } = useAgentChat({
989
- customerId: manifest.customerId,
990
- indexId: manifest.indexId,
991
- runtimeOrigin: manifest.runtimeOrigin
1017
+ customerId,
1018
+ indexId,
1019
+ runtimeOrigin
992
1020
  });
993
1021
  const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
994
- const placement = manifest.placement;
995
- (0, import_react4.useEffect)(() => {
996
- registerAgentPanelController(manifest.customerId, {
1022
+ (0, import_react5.useEffect)(() => {
1023
+ if (!registerPanelController) return;
1024
+ registerAgentPanelController(customerId, {
997
1025
  open: () => setRailCollapsed(false),
998
1026
  close: () => {
999
1027
  setRailCollapsed(true);
1000
1028
  setRailExpanded(false);
1001
1029
  }
1002
1030
  });
1003
- return () => unregisterAgentPanelController(manifest.customerId);
1004
- }, [manifest.customerId]);
1031
+ return () => unregisterAgentPanelController(customerId);
1032
+ }, [customerId, registerPanelController]);
1005
1033
  async function handleSubmit(message) {
1006
1034
  if (isMobile) setRailCollapsed(false);
1007
1035
  await submit(message);
1008
1036
  }
1009
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1037
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
1010
1038
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1011
1039
  "div",
1012
1040
  {
@@ -1060,22 +1088,23 @@ function AgentWidget({ manifest }) {
1060
1088
  ] });
1061
1089
  }
1062
1090
 
1063
- // src/embed/manifest.ts
1064
- var DEFAULT_AGENT_PLACEMENT = {
1065
- side: "right",
1066
- along: 50,
1067
- inset: 0,
1068
- variant: "outline"
1069
- };
1070
- function normalizeAgentPlacement(placement) {
1071
- const along = placement?.along ?? DEFAULT_AGENT_PLACEMENT.along;
1072
- return {
1073
- side: placement?.side ?? DEFAULT_AGENT_PLACEMENT.side,
1074
- along: Math.min(100, Math.max(0, along)),
1075
- inset: Math.max(0, placement?.inset ?? DEFAULT_AGENT_PLACEMENT.inset),
1076
- variant: placement?.variant ?? DEFAULT_AGENT_PLACEMENT.variant
1077
- };
1091
+ // src/embed/AgentWidget.tsx
1092
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1093
+ function AgentWidget2({ manifest }) {
1094
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1095
+ AgentWidget,
1096
+ {
1097
+ indexId: manifest.indexId,
1098
+ customerId: manifest.customerId,
1099
+ runtimeOrigin: manifest.runtimeOrigin,
1100
+ placement: manifest.placement,
1101
+ defaultCollapsed: true,
1102
+ registerPanelController: true
1103
+ }
1104
+ );
1078
1105
  }
1106
+
1107
+ // src/embed/manifest.ts
1079
1108
  function normalizeAgentTagManifest(manifest) {
1080
1109
  const indexId = manifest.indexId.trim();
1081
1110
  if (!indexId) {
@@ -1103,7 +1132,7 @@ function normalizeAgentTagManifest(manifest) {
1103
1132
  }
1104
1133
 
1105
1134
  // src/embed/mount.tsx
1106
- var import_jsx_runtime9 = require("react/jsx-runtime");
1135
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1107
1136
  var mountedHandles = /* @__PURE__ */ new Map();
1108
1137
  var latestCustomerId = null;
1109
1138
  function resolveMountHost(manifest, script) {
@@ -1119,7 +1148,6 @@ function resolveMountHost(manifest, script) {
1119
1148
  }
1120
1149
  function createHost(customerId) {
1121
1150
  const host = document.createElement("div");
1122
- host.className = "webless-agent-root";
1123
1151
  host.dataset.weblessAgentCustomerId = customerId;
1124
1152
  host.setAttribute("data-webless-agent", customerId);
1125
1153
  return host;
@@ -1134,7 +1162,7 @@ function mountAgent(input) {
1134
1162
  const host = createHost(manifest.customerId);
1135
1163
  mountTarget.append(host);
1136
1164
  const root = (0, import_client4.createRoot)(host);
1137
- root.render(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AgentWidget, { manifest }));
1165
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentWidget2, { manifest }));
1138
1166
  const handle = {
1139
1167
  customerId: manifest.customerId,
1140
1168
  manifest,