@webless/agent 0.2.0 → 0.2.7

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
@@ -22,6 +22,7 @@ var embed_exports = {};
22
22
  __export(embed_exports, {
23
23
  DEFAULT_AGENT_PLACEMENT: () => DEFAULT_AGENT_PLACEMENT,
24
24
  bootstrapAgent: () => bootstrapAgent,
25
+ createWeblessAgentPublicApi: () => createWeblessAgentPublicApi,
25
26
  getMountedAgent: () => getMountedAgent,
26
27
  mountAgent: () => mountAgent,
27
28
  normalizeAgentPlacement: () => normalizeAgentPlacement,
@@ -33,7 +34,7 @@ module.exports = __toCommonJS(embed_exports);
33
34
  // src/embed/mount.tsx
34
35
  var import_client4 = require("react-dom/client");
35
36
 
36
- // src/embed/controller.ts
37
+ // src/react/panel-controller.ts
37
38
  var controllers = /* @__PURE__ */ new Map();
38
39
  function registerAgentPanelController(customerId, controller) {
39
40
  controllers.set(customerId, controller);
@@ -48,527 +49,130 @@ function closeAgentPanel(customerId) {
48
49
  controllers.get(customerId)?.close();
49
50
  }
50
51
 
51
- // src/embed/AgentWidget.tsx
52
- var import_react4 = require("react");
52
+ // src/react/components/AgentWidget/AgentWidget.tsx
53
+ var import_react5 = require("react");
53
54
 
54
- // src/react/components/AgentRail/AgentRail.tsx
55
- var import_react2 = require("react");
55
+ // src/react/hooks/useAgentChat.ts
56
+ var import_react = require("react");
56
57
 
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
- }
58
+ // src/runtime/client.ts
59
+ var import_client = require("eve/client");
106
60
 
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
- ) }) });
61
+ // src/runtime/config.ts
62
+ var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
63
+ var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
64
+ function buildEveHost(origin, indexId, version = "published") {
65
+ const base = trimTrailingSlash(origin);
66
+ const searchParams = new URLSearchParams({ indexId: indexId.trim() });
67
+ if (version === "unpublished") {
68
+ searchParams.set("version", version);
69
+ }
70
+ return `${base}?${searchParams.toString()}`;
71
+ }
72
+ function resolveAgentRuntimeConfig(input) {
73
+ const origin = trimTrailingSlash(input.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
74
+ const resolvedIndexId = input.indexId.trim();
75
+ const version = input.version ?? "published";
76
+ return {
77
+ origin,
78
+ indexId: resolvedIndexId,
79
+ version,
80
+ host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId, version) : origin
81
+ };
123
82
  }
124
83
 
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" }) });
84
+ // src/runtime/session-store.ts
85
+ var DEFAULT_STORAGE_KEY_PREFIX = "webless:agent";
86
+ function normalizeRuntimeOrigin(runtimeOrigin) {
87
+ const trimmed = runtimeOrigin?.trim();
88
+ if (!trimmed) return "_";
89
+ return trimmed.replace(/\/+$/, "");
130
90
  }
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
- }
91
+ function buildAgentStorageKeyPrefix(input) {
92
+ const customerId = input.customerId?.trim() || "_";
93
+ const indexId = input.indexId.trim();
94
+ if (!indexId) {
95
+ throw new Error("indexId is required to build agent storage keys.");
155
96
  }
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
- ] }) });
97
+ const versionSuffix = input.version === "unpublished" ? ":unpublished" : "";
98
+ return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}${versionSuffix}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
182
99
  }
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
- )) }) });
100
+ function createSessionId() {
101
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
102
+ return crypto.randomUUID();
206
103
  }
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
- ] });
104
+ return `sess_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
221
105
  }
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 }) });
106
+ function resolvePrefix(options) {
107
+ return options?.storageKeyPrefix?.trim() || DEFAULT_STORAGE_KEY_PREFIX;
108
+ }
109
+ function getOrCreateVisitorSessionId(options) {
110
+ const prefix = resolvePrefix(options);
111
+ const visitorKey = `${prefix}:visitorSessionId`;
112
+ if (typeof sessionStorage === "undefined") {
113
+ return createSessionId();
228
114
  }
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
- ] }) });
115
+ const existing = sessionStorage.getItem(visitorKey)?.trim();
116
+ if (existing) return existing;
117
+ const next = createSessionId();
118
+ sessionStorage.setItem(visitorKey, next);
119
+ return next;
233
120
  }
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" }) });
121
+ function runtimeSessionIdKey(visitorSessionId, prefix) {
122
+ return `${prefix}:eve:${visitorSessionId}:sessionId`;
239
123
  }
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
- ) });
124
+ function runtimeStreamIndexKey(visitorSessionId, prefix) {
125
+ return `${prefix}:eve:${visitorSessionId}:streamIndex`;
251
126
  }
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
- ) });
127
+ function loadPersistedAgentSession(visitorSessionId, options) {
128
+ if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
129
+ const prefix = resolvePrefix(options);
130
+ const sessionId = sessionStorage.getItem(runtimeSessionIdKey(visitorSessionId, prefix))?.trim();
131
+ if (!sessionId) return null;
132
+ const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
133
+ const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
134
+ return {
135
+ sessionId,
136
+ streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0
137
+ };
263
138
  }
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
- }
139
+ function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
140
+ if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !sessionId.trim()) {
141
+ return;
142
+ }
143
+ const prefix = resolvePrefix(options);
144
+ sessionStorage.setItem(runtimeSessionIdKey(visitorSessionId, prefix), sessionId);
145
+ sessionStorage.setItem(
146
+ runtimeStreamIndexKey(visitorSessionId, prefix),
147
+ String(Math.max(0, streamIndex))
390
148
  );
391
149
  }
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
- );
150
+ function clearPersistedAgentSession(visitorSessionId, options) {
151
+ if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
152
+ const prefix = resolvePrefix(options);
153
+ sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
154
+ sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
458
155
  }
459
156
 
460
- // src/react/hooks/useAgentChat.ts
461
- var import_react3 = require("react");
462
-
463
157
  // 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();
158
+ function mapStepLabel(event) {
159
+ if (event.type !== "step.started") return null;
160
+ const stepIndex = event.data.stepIndex;
476
161
  return {
477
- origin,
478
- indexId: resolvedIndexId,
479
- host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId) : origin
162
+ label: "Model step running",
163
+ detail: typeof stepIndex === "number" ? `Step ${stepIndex + 1}` : void 0
480
164
  };
481
165
  }
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`;
524
- }
525
- function loadPersistedAgentSession(visitorSessionId, options) {
526
- if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
527
- const prefix = resolvePrefix(options);
528
- const sessionId = sessionStorage.getItem(runtimeSessionIdKey(visitorSessionId, prefix))?.trim();
529
- if (!sessionId) return null;
530
- const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
531
- const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
532
- return {
533
- sessionId,
534
- streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0
535
- };
536
- }
537
- function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
538
- if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !sessionId.trim()) {
539
- return;
540
- }
541
- const prefix = resolvePrefix(options);
542
- sessionStorage.setItem(runtimeSessionIdKey(visitorSessionId, prefix), sessionId);
543
- sessionStorage.setItem(
544
- runtimeStreamIndexKey(visitorSessionId, prefix),
545
- String(Math.max(0, streamIndex))
546
- );
547
- }
548
- function clearPersistedAgentSession(visitorSessionId, options) {
549
- if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
550
- const prefix = resolvePrefix(options);
551
- sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
552
- sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
553
- }
554
-
555
- // src/runtime/client.ts
556
- function mapStepLabel(event) {
557
- if (event.type !== "step.started") return null;
558
- const stepIndex = event.data.stepIndex;
559
- return {
560
- label: "Model step running",
561
- detail: typeof stepIndex === "number" ? `Step ${stepIndex + 1}` : void 0
562
- };
563
- }
564
- var AgentSession = class {
565
- constructor(indexId, runtimeOrigin, visitorSessionId, storeOptions) {
566
- this.indexId = indexId;
567
- this.runtimeOrigin = runtimeOrigin;
568
- this.visitorSessionId = visitorSessionId;
569
- this.storeOptions = storeOptions;
166
+ var AgentSession = class {
167
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions) {
168
+ this.indexId = indexId;
169
+ this.version = version;
170
+ this.runtimeOrigin = runtimeOrigin;
171
+ this.visitorSessionId = visitorSessionId;
172
+ this.storeOptions = storeOptions;
570
173
  }
571
174
  indexId;
175
+ version;
572
176
  runtimeOrigin;
573
177
  visitorSessionId;
574
178
  storeOptions;
@@ -599,6 +203,7 @@ var AgentSession = class {
599
203
  ensureClient() {
600
204
  const config = resolveAgentRuntimeConfig({
601
205
  indexId: this.indexId,
206
+ version: this.version,
602
207
  runtimeOrigin: this.runtimeOrigin
603
208
  });
604
209
  if (!config.indexId) {
@@ -698,17 +303,20 @@ function createAgentClient(options) {
698
303
  throw new Error("indexId is required.");
699
304
  }
700
305
  const runtimeOrigin = options.runtimeOrigin?.trim() || resolveAgentRuntimeConfig({ indexId }).origin;
306
+ const version = options.version ?? "published";
701
307
  const storeOptions = {
702
308
  storageKeyPrefix: options.storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
703
309
  customerId: options.customerId,
704
310
  indexId,
311
+ version,
705
312
  runtimeOrigin
706
313
  })
707
314
  };
708
315
  const visitorSessionId = options.visitorSessionId?.trim() || getOrCreateVisitorSessionId(storeOptions);
709
- const session = new AgentSession(indexId, runtimeOrigin, visitorSessionId, storeOptions);
316
+ const session = new AgentSession(indexId, version, runtimeOrigin, visitorSessionId, storeOptions);
710
317
  return {
711
318
  indexId,
319
+ version,
712
320
  runtimeOrigin,
713
321
  visitorSessionId,
714
322
  sendTurn: (message, sendOptions) => session.sendTurn(
@@ -791,32 +399,34 @@ async function runStatusSequence(signal, onStep) {
791
399
  function useAgentChat({
792
400
  customerId,
793
401
  indexId,
402
+ version,
794
403
  runtimeOrigin,
795
404
  visitorSessionId,
796
405
  storageKeyPrefix
797
406
  }) {
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)(
801
- () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, runtimeOrigin }),
802
- [customerId, indexId, runtimeOrigin, storageKeyPrefix]
407
+ const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
408
+ const runRef = (0, import_react.useRef)(null);
409
+ const resolvedStorageKeyPrefix = (0, import_react.useMemo)(
410
+ () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, version, runtimeOrigin }),
411
+ [customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
803
412
  );
804
- const visitorId = (0, import_react3.useMemo)(
413
+ const visitorId = (0, import_react.useMemo)(
805
414
  () => visitorSessionId?.trim() || getOrCreateVisitorSessionId({ storageKeyPrefix: resolvedStorageKeyPrefix }),
806
415
  [resolvedStorageKeyPrefix, visitorSessionId]
807
416
  );
808
- const clientRef = (0, import_react3.useRef)(
417
+ const clientRef = (0, import_react.useRef)(
809
418
  createAgentClient({
810
419
  customerId,
811
420
  indexId,
421
+ version,
812
422
  runtimeOrigin,
813
423
  visitorSessionId: visitorId,
814
424
  storageKeyPrefix: resolvedStorageKeyPrefix
815
425
  })
816
426
  );
817
- const identityRef = (0, import_react3.useRef)(null);
818
- const identityKey = `${customerId ?? ""}|${indexId}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
819
- (0, import_react3.useEffect)(() => {
427
+ const identityRef = (0, import_react.useRef)(null);
428
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
429
+ (0, import_react.useEffect)(() => {
820
430
  if (identityRef.current === null) {
821
431
  identityRef.current = identityKey;
822
432
  return;
@@ -831,19 +441,20 @@ function useAgentChat({
831
441
  clientRef.current = createAgentClient({
832
442
  customerId,
833
443
  indexId,
444
+ version,
834
445
  runtimeOrigin,
835
446
  visitorSessionId: visitorId,
836
447
  storageKeyPrefix: resolvedStorageKeyPrefix
837
448
  });
838
449
  setState(INITIAL_STATE);
839
- }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]);
840
- const reset = (0, import_react3.useCallback)(() => {
450
+ }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]);
451
+ const reset = (0, import_react.useCallback)(() => {
841
452
  runRef.current?.abort();
842
453
  runRef.current = null;
843
454
  clientRef.current.reset();
844
455
  setState(INITIAL_STATE);
845
456
  }, []);
846
- const submit = (0, import_react3.useCallback)(
457
+ const submit = (0, import_react.useCallback)(
847
458
  async (visitorText) => {
848
459
  runRef.current?.abort();
849
460
  clientRef.current.cancelActive();
@@ -905,108 +516,544 @@ function useAgentChat({
905
516
  if (!isActiveRun()) return;
906
517
  streamStarted = true;
907
518
  }
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]
519
+ }
520
+ });
521
+ await planningPromise.catch(() => {
522
+ });
523
+ if (!isActiveRun()) return;
524
+ const agentMessage = {
525
+ id: `agent-${Date.now()}`,
526
+ role: "agent",
527
+ text: finalText,
528
+ createdAt: Date.now()
529
+ };
530
+ setState((prev) => ({
531
+ ...prev,
532
+ phase: "complete",
533
+ messages: [...prev.messages, agentMessage],
534
+ toolSteps: [],
535
+ streamingText: "",
536
+ followUps: DEFAULT_FOLLOW_UPS,
537
+ journey: null
538
+ }));
539
+ } catch (error) {
540
+ if (error instanceof DOMException && error.name === "AbortError") return;
541
+ if (!isActiveRun()) return;
542
+ const message = formatAgentError(error);
543
+ if (!message) return;
544
+ setState((prev) => ({
545
+ ...prev,
546
+ phase: "complete",
547
+ toolSteps: [],
548
+ streamingText: "",
549
+ error: message
550
+ }));
551
+ }
552
+ },
553
+ [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]
554
+ );
555
+ (0, import_react.useEffect)(() => {
556
+ return () => {
557
+ runRef.current?.abort();
558
+ clientRef.current.cancelActive();
559
+ };
560
+ }, []);
561
+ return {
562
+ state,
563
+ reset,
564
+ submit,
565
+ visitorSessionId: visitorId,
566
+ sessionId: clientRef.current.getActiveSessionId()
567
+ };
568
+ }
569
+ function hasVisitorMessages(messages) {
570
+ return messages.some((message) => message.role === "visitor");
571
+ }
572
+ function createIdleSuggestions() {
573
+ return [
574
+ { id: "idle-1", label: "Hello \u2014 what can you do?" },
575
+ { id: "idle-2", label: "Help me evaluate this product" },
576
+ { id: "idle-3", label: "What should I ask you?" }
577
+ ];
578
+ }
579
+
580
+ // src/react/hooks/useIsMobile.ts
581
+ var import_react2 = require("react");
582
+ function useIsMobile(breakpoint = 767) {
583
+ const [isMobile, setIsMobile] = (0, import_react2.useState)(
584
+ () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
585
+ );
586
+ (0, import_react2.useEffect)(() => {
587
+ const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
588
+ const onChange = () => setIsMobile(media.matches);
589
+ onChange();
590
+ media.addEventListener("change", onChange);
591
+ return () => media.removeEventListener("change", onChange);
592
+ }, [breakpoint]);
593
+ return isMobile;
594
+ }
595
+
596
+ // src/react/placement.ts
597
+ var DEFAULT_AGENT_PLACEMENT = {
598
+ side: "right",
599
+ along: 50,
600
+ inset: 0,
601
+ variant: "outline"
602
+ };
603
+ function normalizeAgentPlacement(placement) {
604
+ const along = placement?.along ?? DEFAULT_AGENT_PLACEMENT.along;
605
+ return {
606
+ side: placement?.side ?? DEFAULT_AGENT_PLACEMENT.side,
607
+ along: Math.min(100, Math.max(0, along)),
608
+ inset: Math.max(0, placement?.inset ?? DEFAULT_AGENT_PLACEMENT.inset),
609
+ variant: placement?.variant ?? DEFAULT_AGENT_PLACEMENT.variant
610
+ };
611
+ }
612
+
613
+ // src/react/components/AgentRail/AgentRail.tsx
614
+ var import_react4 = require("react");
615
+
616
+ // src/react/components/ToolTimeline/ToolTimeline.tsx
617
+ var import_jsx_runtime = require("react/jsx-runtime");
618
+ function SpinnerIcon() {
619
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "agent-status__spinner", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
620
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "1.5", strokeOpacity: "0.25" }),
621
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 8a6 6 0 0 0-6-6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
622
+ ] });
623
+ }
624
+ function getCurrentStep(steps) {
625
+ const active = steps.find((step) => step.state === "active");
626
+ if (active) return active;
627
+ const error = steps.find((step) => step.state === "error");
628
+ if (error) return error;
629
+ const pending = steps.find((step) => step.state === "pending");
630
+ if (pending) return pending;
631
+ return steps.at(-1) ?? null;
632
+ }
633
+ function ToolTimeline({
634
+ steps,
635
+ streamingText,
636
+ showStreaming = false,
637
+ inline = false
638
+ }) {
639
+ if (steps.length === 0 && !(showStreaming && streamingText)) return null;
640
+ const currentStep = getCurrentStep(steps);
641
+ const showStatus = currentStep && currentStep.state !== "completed" && !(showStreaming && streamingText);
642
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
643
+ "div",
644
+ {
645
+ className: `tool-timeline${inline ? " tool-timeline--inline" : ""}`,
646
+ role: "status",
647
+ "aria-live": "polite",
648
+ "aria-label": "Agent progress",
649
+ children: [
650
+ showStatus ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status", children: [
651
+ 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, {}) }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status__copy", children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__label", children: currentStep.label }),
654
+ currentStep.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__detail", children: currentStep.detail }) : null
655
+ ] })
656
+ ] }, currentStep.id) : null,
657
+ showStreaming && streamingText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "tool-timeline__streaming-text", children: [
658
+ streamingText,
659
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" })
660
+ ] }) : null
661
+ ]
662
+ }
663
+ );
664
+ }
665
+
666
+ // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
667
+ var import_jsx_runtime2 = require("react/jsx-runtime");
668
+ function AgentActivityBubble({
669
+ steps,
670
+ streamingText,
671
+ showStreaming = false
672
+ }) {
673
+ 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)(
674
+ ToolTimeline,
675
+ {
676
+ steps,
677
+ streamingText,
678
+ showStreaming,
679
+ inline: true
680
+ }
681
+ ) }) });
682
+ }
683
+
684
+ // src/react/components/Composer/Composer.tsx
685
+ var import_react3 = require("react");
686
+ var import_jsx_runtime3 = require("react/jsx-runtime");
687
+ function SendIcon() {
688
+ 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" }) });
689
+ }
690
+ function Composer({
691
+ disabled = false,
692
+ placeholder = "Ask anything\u2026",
693
+ variant = "default",
694
+ onSubmit
695
+ }) {
696
+ const [value, setValue] = (0, import_react3.useState)("");
697
+ const inputRef = (0, import_react3.useRef)(null);
698
+ function submitCurrent() {
699
+ const trimmed = value.trim();
700
+ if (!trimmed || disabled) return;
701
+ onSubmit?.(trimmed);
702
+ setValue("");
703
+ inputRef.current?.focus();
704
+ }
705
+ function handleSubmit(event) {
706
+ event.preventDefault();
707
+ submitCurrent();
708
+ }
709
+ function handleKeyDown(event) {
710
+ if (event.key === "Enter" && !event.shiftKey) {
711
+ event.preventDefault();
712
+ submitCurrent();
713
+ }
714
+ }
715
+ 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: [
716
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
717
+ "textarea",
718
+ {
719
+ ref: inputRef,
720
+ className: "composer__input",
721
+ rows: 1,
722
+ value,
723
+ placeholder,
724
+ disabled,
725
+ "aria-label": "Message",
726
+ onChange: (event) => setValue(event.target.value),
727
+ onKeyDown: handleKeyDown
728
+ }
729
+ ),
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
731
+ "button",
732
+ {
733
+ type: "submit",
734
+ className: "composer__send",
735
+ disabled: disabled || !value.trim(),
736
+ "aria-label": "Send message",
737
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
738
+ }
739
+ )
740
+ ] }) });
741
+ }
742
+
743
+ // src/react/components/FollowUpChips/FollowUpChips.tsx
744
+ var import_jsx_runtime4 = require("react/jsx-runtime");
745
+ function FollowUpChips({
746
+ suggestions,
747
+ disabled = false,
748
+ label = "Follow up",
749
+ variant = "default",
750
+ onSelect
751
+ }) {
752
+ if (suggestions.length === 0) return null;
753
+ if (variant === "dock") {
754
+ 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)(
755
+ "button",
756
+ {
757
+ type: "button",
758
+ className: "followups__chip followups__chip--dock",
759
+ disabled,
760
+ onClick: () => onSelect?.(suggestion),
761
+ children: suggestion.label
762
+ },
763
+ suggestion.id
764
+ )) }) });
765
+ }
766
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "followups", children: [
767
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "followups__label", children: label }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
769
+ "button",
770
+ {
771
+ type: "button",
772
+ className: "followups__chip",
773
+ disabled,
774
+ onClick: () => onSelect?.(suggestion),
775
+ children: suggestion.label
776
+ },
777
+ suggestion.id
778
+ )) })
779
+ ] });
780
+ }
781
+
782
+ // src/react/components/MessageBubble/MessageBubble.tsx
783
+ var import_jsx_runtime5 = require("react/jsx-runtime");
784
+ function MessageBubble({ message }) {
785
+ if (message.role === "visitor") {
786
+ 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 }) });
787
+ }
788
+ 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: [
789
+ message.text,
790
+ message.streaming ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" }) : null
791
+ ] }) });
792
+ }
793
+
794
+ // src/react/components/AgentRail/AgentRail.tsx
795
+ var import_jsx_runtime6 = require("react/jsx-runtime");
796
+ function MinimizeIcon() {
797
+ 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" }) });
798
+ }
799
+ function ExpandIcon() {
800
+ 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)(
801
+ "path",
802
+ {
803
+ d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
804
+ stroke: "currentColor",
805
+ strokeWidth: "1.5",
806
+ strokeLinecap: "round",
807
+ strokeLinejoin: "round"
808
+ }
809
+ ) });
810
+ }
811
+ function RestoreIcon() {
812
+ 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)(
813
+ "path",
814
+ {
815
+ d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
816
+ stroke: "currentColor",
817
+ strokeWidth: "1.5",
818
+ strokeLinecap: "round",
819
+ strokeLinejoin: "round"
820
+ }
821
+ ) });
822
+ }
823
+ function AgentRail({
824
+ state,
825
+ theme,
826
+ brandLabel = "Webless Assist",
827
+ poweredByLabel = "Powered by Webless",
828
+ composerPlaceholder = "Ask anything\u2026",
829
+ mobileFullscreen = false,
830
+ expanded = false,
831
+ onCollapse,
832
+ onClose,
833
+ onExpandToggle,
834
+ onSubmit,
835
+ onFollowUpSelect
836
+ }) {
837
+ const transcriptRef = (0, import_react4.useRef)(null);
838
+ const railStyle = theme?.railMaxWidth ? { ["--rail-width"]: theme.railMaxWidth, ["--as-rail-max-width"]: theme.railMaxWidth } : void 0;
839
+ const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
840
+ const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
841
+ const showStreamingInActivity = state.phase === "streaming" && Boolean(state.streamingText);
842
+ const hasVisitorMessages2 = state.messages.some((message) => message.role === "visitor");
843
+ const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
844
+ const showCompleteFollowUps = state.phase === "complete" && state.followUps.length > 0 && hasVisitorMessages2;
845
+ const showDockFollowUps = expanded && (showIdleFollowUps || showCompleteFollowUps);
846
+ (0, import_react4.useEffect)(() => {
847
+ const node = transcriptRef.current;
848
+ if (!node) return;
849
+ node.scrollTop = node.scrollHeight;
850
+ }, [state.messages, state.toolSteps, state.streamingText, state.followUps, state.journey]);
851
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
852
+ "aside",
853
+ {
854
+ className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
855
+ style: railStyle,
856
+ "aria-label": "Agent conversation",
857
+ children: [
858
+ /* @__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: [
859
+ onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
860
+ "button",
861
+ {
862
+ type: "button",
863
+ className: "agent-rail__collapse",
864
+ "aria-label": "Collapse assist",
865
+ onClick: onCollapse,
866
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
867
+ }
868
+ ) : 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" }),
869
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel }),
870
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
871
+ "button",
872
+ {
873
+ type: "button",
874
+ className: "agent-rail__expand",
875
+ "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
876
+ onClick: onExpandToggle,
877
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
878
+ }
879
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
880
+ ] }) }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
882
+ state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message }, message.id)),
883
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
884
+ AgentActivityBubble,
885
+ {
886
+ steps: state.toolSteps,
887
+ streamingText: state.streamingText,
888
+ showStreaming: showStreamingInActivity
889
+ }
890
+ ) : null,
891
+ !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
892
+ FollowUpChips,
893
+ {
894
+ suggestions: state.followUps,
895
+ disabled: isBusy,
896
+ label: "Try asking",
897
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
898
+ }
899
+ ) }) : null,
900
+ !expanded && showCompleteFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
901
+ FollowUpChips,
902
+ {
903
+ suggestions: state.followUps,
904
+ disabled: isBusy,
905
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
906
+ }
907
+ ) }) : null,
908
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", style: { fontSize: 13, color: "var(--as-danger)", margin: 0 }, children: state.error }) : null
909
+ ] }),
910
+ expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
911
+ showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
912
+ FollowUpChips,
913
+ {
914
+ variant: "dock",
915
+ suggestions: state.followUps,
916
+ disabled: isBusy,
917
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
918
+ }
919
+ ) }) : null,
920
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
921
+ Composer,
922
+ {
923
+ variant: "dock",
924
+ disabled: isBusy,
925
+ placeholder: composerPlaceholder,
926
+ onSubmit
927
+ }
928
+ ) }),
929
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
930
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
932
+ ] })
933
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
934
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
935
+ Composer,
936
+ {
937
+ disabled: isBusy,
938
+ placeholder: composerPlaceholder,
939
+ onSubmit
940
+ }
941
+ ),
942
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
943
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
944
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
945
+ ] })
946
+ ] })
947
+ ]
948
+ }
943
949
  );
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
950
  }
958
- function hasVisitorMessages(messages) {
959
- return messages.some((message) => message.role === "visitor");
951
+
952
+ // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
953
+ var import_jsx_runtime7 = require("react/jsx-runtime");
954
+ function SparklesIcon() {
955
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "assist-edge-tab__sparkles", viewBox: "0 0 18 16", fill: "none", "aria-hidden": "true", children: [
956
+ /* @__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" }),
957
+ /* @__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" }),
958
+ /* @__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" })
959
+ ] });
960
960
  }
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
- ];
961
+ function ChevronLeftIcon() {
962
+ 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
963
  }
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
964
+ function ChevronDownIcon() {
965
+ 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" }) });
966
+ }
967
+ function DragDots() {
968
+ 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)) });
969
+ }
970
+ var VARIANT_COPY = {
971
+ outline: { label: "Assist", aria: "Open Assist" },
972
+ ask: { label: "Ask anything", aria: "Ask anything" },
973
+ fill: { label: "Assist", aria: "Open Assist" }
974
+ };
975
+ function AssistEdgeTab({
976
+ variant,
977
+ side,
978
+ along,
979
+ inset,
980
+ visible,
981
+ onOpen
982
+ }) {
983
+ const copy = VARIANT_COPY[variant];
984
+ const style = {
985
+ "--tab-along": `${along}%`,
986
+ "--tab-inset": `${inset}px`
987
+ };
988
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
989
+ "button",
990
+ {
991
+ type: "button",
992
+ className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
993
+ style,
994
+ "aria-label": copy.aria,
995
+ "aria-hidden": !visible,
996
+ tabIndex: visible ? 0 : -1,
997
+ onClick: onOpen,
998
+ children: [
999
+ variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1000
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
1001
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
1002
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDownIcon, {})
1003
+ ] }) : null,
1004
+ variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1005
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}),
1006
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
1007
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DragDots, {})
1008
+ ] }) : null,
1009
+ variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {})
1013
+ ] }) : null
1014
+ ]
1015
+ }
974
1016
  );
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
1017
  }
984
- function AgentWidget({ manifest }) {
1018
+
1019
+ // src/react/components/AgentWidget/AgentWidget.tsx
1020
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1021
+ function AgentWidget({
1022
+ indexId,
1023
+ customerId,
1024
+ version,
1025
+ runtimeOrigin,
1026
+ placement: placementInput,
1027
+ defaultCollapsed = true,
1028
+ registerPanelController = false
1029
+ }) {
985
1030
  const isMobile = useIsMobile();
986
- const [railCollapsed, setRailCollapsed] = (0, import_react4.useState)(true);
987
- const [railExpanded, setRailExpanded] = (0, import_react4.useState)(false);
1031
+ const placement = normalizeAgentPlacement(placementInput);
1032
+ const [railCollapsed, setRailCollapsed] = (0, import_react5.useState)(defaultCollapsed);
1033
+ const [railExpanded, setRailExpanded] = (0, import_react5.useState)(false);
988
1034
  const { state, submit } = useAgentChat({
989
- customerId: manifest.customerId,
990
- indexId: manifest.indexId,
991
- runtimeOrigin: manifest.runtimeOrigin
1035
+ customerId,
1036
+ indexId,
1037
+ version,
1038
+ runtimeOrigin
992
1039
  });
993
1040
  const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
994
- const placement = manifest.placement;
995
- (0, import_react4.useEffect)(() => {
996
- registerAgentPanelController(manifest.customerId, {
1041
+ (0, import_react5.useEffect)(() => {
1042
+ if (!registerPanelController) return;
1043
+ registerAgentPanelController(customerId, {
997
1044
  open: () => setRailCollapsed(false),
998
1045
  close: () => {
999
1046
  setRailCollapsed(true);
1000
1047
  setRailExpanded(false);
1001
1048
  }
1002
1049
  });
1003
- return () => unregisterAgentPanelController(manifest.customerId);
1004
- }, [manifest.customerId]);
1050
+ return () => unregisterAgentPanelController(customerId);
1051
+ }, [customerId, registerPanelController]);
1005
1052
  async function handleSubmit(message) {
1006
1053
  if (isMobile) setRailCollapsed(false);
1007
1054
  await submit(message);
1008
1055
  }
1009
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1056
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
1010
1057
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1011
1058
  "div",
1012
1059
  {
@@ -1060,22 +1107,24 @@ function AgentWidget({ manifest }) {
1060
1107
  ] });
1061
1108
  }
1062
1109
 
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
- };
1110
+ // src/embed/AgentWidget.tsx
1111
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1112
+ function AgentWidget2({ manifest }) {
1113
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1114
+ AgentWidget,
1115
+ {
1116
+ indexId: manifest.indexId,
1117
+ customerId: manifest.customerId,
1118
+ version: manifest.version,
1119
+ runtimeOrigin: manifest.runtimeOrigin,
1120
+ placement: manifest.placement,
1121
+ defaultCollapsed: true,
1122
+ registerPanelController: true
1123
+ }
1124
+ );
1078
1125
  }
1126
+
1127
+ // src/embed/manifest.ts
1079
1128
  function normalizeAgentTagManifest(manifest) {
1080
1129
  const indexId = manifest.indexId.trim();
1081
1130
  if (!indexId) {
@@ -1085,15 +1134,20 @@ function normalizeAgentTagManifest(manifest) {
1085
1134
  if (!customerId) {
1086
1135
  throw new Error("Agent manifest requires customerId.");
1087
1136
  }
1137
+ const version = manifest.version ?? "published";
1138
+ if (version !== "published" && version !== "unpublished") {
1139
+ throw new Error("Agent manifest version must be published or unpublished.");
1140
+ }
1088
1141
  const runtime = resolveAgentRuntimeConfig({
1089
1142
  indexId,
1143
+ version,
1090
1144
  runtimeOrigin: manifest.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN
1091
1145
  });
1092
1146
  return {
1093
1147
  customerId,
1094
1148
  indexId,
1095
1149
  runtimeOrigin: runtime.origin,
1096
- version: manifest.version?.trim() || "published",
1150
+ version,
1097
1151
  mount: {
1098
1152
  selector: manifest.mount?.selector?.trim(),
1099
1153
  strategy: manifest.mount?.strategy ?? "body"
@@ -1103,7 +1157,7 @@ function normalizeAgentTagManifest(manifest) {
1103
1157
  }
1104
1158
 
1105
1159
  // src/embed/mount.tsx
1106
- var import_jsx_runtime9 = require("react/jsx-runtime");
1160
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1107
1161
  var mountedHandles = /* @__PURE__ */ new Map();
1108
1162
  var latestCustomerId = null;
1109
1163
  function resolveMountHost(manifest, script) {
@@ -1119,7 +1173,6 @@ function resolveMountHost(manifest, script) {
1119
1173
  }
1120
1174
  function createHost(customerId) {
1121
1175
  const host = document.createElement("div");
1122
- host.className = "webless-agent-root";
1123
1176
  host.dataset.weblessAgentCustomerId = customerId;
1124
1177
  host.setAttribute("data-webless-agent", customerId);
1125
1178
  return host;
@@ -1134,7 +1187,7 @@ function mountAgent(input) {
1134
1187
  const host = createHost(manifest.customerId);
1135
1188
  mountTarget.append(host);
1136
1189
  const root = (0, import_client4.createRoot)(host);
1137
- root.render(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AgentWidget, { manifest }));
1190
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentWidget2, { manifest }));
1138
1191
  const handle = {
1139
1192
  customerId: manifest.customerId,
1140
1193
  manifest,
@@ -1175,10 +1228,54 @@ function getMountedAgent(customerId) {
1175
1228
  function bootstrapAgent(manifest, script) {
1176
1229
  return mountAgent({ manifest, script });
1177
1230
  }
1231
+
1232
+ // src/embed/iife-bootstrap.ts
1233
+ function createWeblessAgentPublicApi(manifest) {
1234
+ const script = document.currentScript instanceof HTMLScriptElement ? document.currentScript : null;
1235
+ function mount(options = {}) {
1236
+ return mountAgent({
1237
+ manifest: {
1238
+ ...manifest,
1239
+ ...options,
1240
+ customerId: manifest.customerId
1241
+ },
1242
+ script
1243
+ });
1244
+ }
1245
+ function unmount(customerId) {
1246
+ return unmountAgent(customerId ?? manifest.customerId);
1247
+ }
1248
+ function open(customerId) {
1249
+ getMountedAgent(customerId ?? manifest.customerId)?.open();
1250
+ }
1251
+ function close(customerId) {
1252
+ getMountedAgent(customerId ?? manifest.customerId)?.close();
1253
+ }
1254
+ function get(customerId) {
1255
+ return getMountedAgent(customerId ?? manifest.customerId);
1256
+ }
1257
+ const api = {
1258
+ mount,
1259
+ unmount,
1260
+ open,
1261
+ close,
1262
+ get
1263
+ };
1264
+ window.WeblessAgent = api;
1265
+ void (async () => {
1266
+ try {
1267
+ mount();
1268
+ } catch (error) {
1269
+ console.error("[Webless] Agent mount failed", error);
1270
+ }
1271
+ })();
1272
+ return api;
1273
+ }
1178
1274
  // Annotate the CommonJS export names for ESM import in node:
1179
1275
  0 && (module.exports = {
1180
1276
  DEFAULT_AGENT_PLACEMENT,
1181
1277
  bootstrapAgent,
1278
+ createWeblessAgentPublicApi,
1182
1279
  getMountedAgent,
1183
1280
  mountAgent,
1184
1281
  normalizeAgentPlacement,