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