@webless/agent 0.2.15 → 0.3.1

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.
@@ -220,9 +220,8 @@ function clearPersistedAgentSession(visitorSessionId, options) {
220
220
  function isTurnBoundary(event) {
221
221
  return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
222
222
  }
223
- function applyMessageEvent(event, rendered, handlers) {
224
- const step = mapStepLabel(event);
225
- if (step) handlers.onStep?.(step.label, step.detail);
223
+ function applyMessageEvent(event, rendered, handlers, workItems) {
224
+ applyWorkEvent(event, handlers, workItems);
226
225
  if (event.type === "session.failed") {
227
226
  throw new Error(event.data.message || event.data.code);
228
227
  }
@@ -265,13 +264,120 @@ function renderTurn(events) {
265
264
  }
266
265
  return rendered;
267
266
  }
268
- function mapStepLabel(event) {
269
- if (event.type !== "step.started") return null;
270
- const stepIndex = event.data.stepIndex;
271
- return {
272
- label: "Model step running",
273
- detail: typeof stepIndex === "number" ? `Step ${stepIndex + 1}` : void 0
274
- };
267
+ function emitWorkItem(item, handlers, workItems) {
268
+ workItems.set(item.id, item);
269
+ handlers.onWork?.(item);
270
+ handlers.onStep?.(item.label, item.detail);
271
+ }
272
+ function completePlanning(handlers, workItems) {
273
+ const planning = workItems.get("planning");
274
+ if (!planning || planning.state !== "active") return;
275
+ emitWorkItem(
276
+ {
277
+ ...planning,
278
+ detail: "Picked the best way to help",
279
+ state: "completed"
280
+ },
281
+ handlers,
282
+ workItems
283
+ );
284
+ }
285
+ function specialistNameFromInput(input) {
286
+ const message = input.message;
287
+ if (typeof message !== "string") return void 0;
288
+ const match = /^Webless-Agent-Name:\s*(.+)$/imu.exec(message);
289
+ return match?.[1]?.trim() || void 0;
290
+ }
291
+ function requestedWorkItem(action) {
292
+ if (action.kind === "tool-call" && action.toolName === "search_discovery") {
293
+ return {
294
+ id: action.callId,
295
+ kind: "search",
296
+ label: "Search & Discovery",
297
+ detail: "Searching this site",
298
+ state: "active"
299
+ };
300
+ }
301
+ if (action.kind === "subagent-call" || action.kind === "remote-agent-call" || action.kind === "tool-call" && action.toolName === "agent") {
302
+ const name = specialistNameFromInput(action.input) ?? "Specialist";
303
+ return {
304
+ id: action.callId,
305
+ kind: "specialist",
306
+ label: name,
307
+ detail: "Reviewing your question",
308
+ state: "active"
309
+ };
310
+ }
311
+ return null;
312
+ }
313
+ function applyWorkEvent(event, handlers, workItems) {
314
+ if (event.type === "step.started" && workItems.size === 0) {
315
+ emitWorkItem(
316
+ {
317
+ id: "planning",
318
+ kind: "planning",
319
+ label: "Understanding your question",
320
+ state: "active"
321
+ },
322
+ handlers,
323
+ workItems
324
+ );
325
+ return;
326
+ }
327
+ if (event.type === "actions.requested") {
328
+ completePlanning(handlers, workItems);
329
+ for (const action of event.data.actions) {
330
+ const item = requestedWorkItem(action);
331
+ if (item) emitWorkItem(item, handlers, workItems);
332
+ }
333
+ return;
334
+ }
335
+ if (event.type === "subagent.called") {
336
+ completePlanning(handlers, workItems);
337
+ const current2 = workItems.get(event.data.callId);
338
+ if (!current2) {
339
+ emitWorkItem(
340
+ {
341
+ id: event.data.callId,
342
+ kind: "specialist",
343
+ label: event.data.name || "Specialist",
344
+ detail: "Reviewing your question",
345
+ state: "active"
346
+ },
347
+ handlers,
348
+ workItems
349
+ );
350
+ }
351
+ return;
352
+ }
353
+ if (event.type === "subagent.completed") {
354
+ const current2 = workItems.get(event.data.callId);
355
+ if (!current2) return;
356
+ emitWorkItem(
357
+ {
358
+ ...current2,
359
+ detail: "Guidance received",
360
+ state: "completed"
361
+ },
362
+ handlers,
363
+ workItems
364
+ );
365
+ return;
366
+ }
367
+ if (event.type !== "action.result") return;
368
+ const { result, status } = event.data;
369
+ const current = workItems.get(result.callId);
370
+ if (!current) return;
371
+ const failed = status !== "completed" || result.isError === true;
372
+ emitWorkItem(
373
+ {
374
+ ...current,
375
+ detail: failed ? "Couldn\u2019t complete; continuing with available information" : current.kind === "search" ? "Found relevant site content" : "Guidance received",
376
+ state: failed ? "error" : "completed"
377
+ },
378
+ handlers,
379
+ workItems
380
+ );
275
381
  }
276
382
  var AgentSession = class {
277
383
  constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, getUnpublishedPreviewGrant) {
@@ -389,10 +495,11 @@ var AgentSession = class {
389
495
  this.activeResponse = response;
390
496
  let streamIndex = session?.state.streamIndex ?? 0;
391
497
  let rendered = "";
498
+ const workItems = /* @__PURE__ */ new Map();
392
499
  try {
393
500
  for await (const event of response) {
394
501
  if (signal.aborted) break;
395
- rendered = applyMessageEvent(event, rendered, handlers);
502
+ rendered = applyMessageEvent(event, rendered, handlers, workItems);
396
503
  streamIndex += 1;
397
504
  if (session) {
398
505
  savePersistedAgentSession(
@@ -434,6 +541,10 @@ var AgentSession = class {
434
541
  return null;
435
542
  }
436
543
  let rendered = renderTurn(turnEvents);
544
+ const workItems = /* @__PURE__ */ new Map();
545
+ for (const event of turnEvents) {
546
+ applyWorkEvent(event, handlers, workItems);
547
+ }
437
548
  if (rendered.startsWith(initialText)) {
438
549
  const missedText = rendered.slice(initialText.length);
439
550
  if (missedText) handlers.onDelta(missedText);
@@ -468,7 +579,7 @@ var AgentSession = class {
468
579
  let streamIndex = snapshot.session.streamIndex;
469
580
  for await (const event of session.stream({ signal })) {
470
581
  if (signal.aborted) break;
471
- rendered = applyMessageEvent(event, rendered, handlers);
582
+ rendered = applyMessageEvent(event, rendered, handlers, workItems);
472
583
  streamIndex += 1;
473
584
  savePersistedAgentSession(
474
585
  this.visitorSessionId,
@@ -548,6 +659,11 @@ function createAgentClient(options) {
548
659
 
549
660
  // src/runtime/errors.ts
550
661
  import { ClientError as ClientError3 } from "eve/client";
662
+ var TRANSIENT_AGENT_ERROR_MESSAGE = "I couldn\u2019t finish that answer. Please try again.";
663
+ function isTransientRuntimeMessage(message) {
664
+ const normalized = message.trim().toLowerCase();
665
+ return normalized.includes("empty response from runtime") || normalized.includes("failed to fetch") || normalized.includes("networkerror") || normalized.includes("load failed");
666
+ }
551
667
  function formatAgentError(error) {
552
668
  if (error instanceof ClientError3) {
553
669
  if (error.status === 401 && error.code === "index_required") {
@@ -559,13 +675,18 @@ function formatAgentError(error) {
559
675
  if (error.status === 409 && error.code === "session_not_active") {
560
676
  return "Session expired \u2014 send a new message to start again.";
561
677
  }
562
- return error.message || `Runtime error (${error.status})`;
678
+ if (error.status >= 500 || error.message && isTransientRuntimeMessage(error.message)) {
679
+ return TRANSIENT_AGENT_ERROR_MESSAGE;
680
+ }
681
+ return error.message || "This assistant is unavailable right now.";
563
682
  }
564
683
  if (error instanceof DOMException && error.name === "AbortError") {
565
684
  return "";
566
685
  }
567
- if (error instanceof Error) return error.message;
568
- return "Runtime request failed";
686
+ if (error instanceof Error) {
687
+ return isTransientRuntimeMessage(error.message) ? TRANSIENT_AGENT_ERROR_MESSAGE : error.message;
688
+ }
689
+ return TRANSIENT_AGENT_ERROR_MESSAGE;
569
690
  }
570
691
 
571
692
  // src/react/persisted-conversation.ts
@@ -621,57 +742,54 @@ function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
621
742
  }
622
743
 
623
744
  // src/react/hooks/useAgentChat.ts
624
- var GREETING_MESSAGE = {
625
- id: "greeting",
626
- role: "agent",
627
- text: "Hi! I'm connected to the Webless Agent Runtime. Ask anything about your published site index.",
628
- createdAt: 0
629
- };
630
- var INITIAL_STATE = {
631
- phase: "idle",
632
- messages: [GREETING_MESSAGE],
633
- toolSteps: [],
634
- journey: null,
635
- followUps: [],
636
- streamingText: "",
637
- error: null
638
- };
639
- function stateFromConversation(conversation) {
640
- if (!conversation || conversation.messages.length === 0) return INITIAL_STATE;
745
+ var DEFAULT_GREETING = "Hi! I can search this site or bring in a specialist when it helps. What would you like to know?";
746
+ function createInitialState(greeting = DEFAULT_GREETING) {
747
+ return {
748
+ phase: "idle",
749
+ messages: [
750
+ {
751
+ id: "greeting",
752
+ role: "agent",
753
+ text: greeting,
754
+ createdAt: 0
755
+ }
756
+ ],
757
+ toolSteps: [],
758
+ journey: null,
759
+ followUps: [],
760
+ streamingText: "",
761
+ error: null
762
+ };
763
+ }
764
+ function stateFromConversation(conversation, initialState) {
765
+ if (!conversation || conversation.messages.length === 0) return initialState;
641
766
  return {
642
- ...INITIAL_STATE,
767
+ ...initialState,
643
768
  messages: conversation.messages,
644
769
  phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
645
770
  streamingText: conversation.streamingText
646
771
  };
647
772
  }
648
- var STATUS_SEQUENCE = [
649
- { id: "s1", label: "Starting Eve session", ms: 400 },
650
- { id: "s2", label: "Connecting to runtime", ms: 500 }
651
- ];
652
- function delay(ms, signal) {
653
- return new Promise((resolve, reject) => {
654
- const timer = window.setTimeout(resolve, ms);
655
- signal.addEventListener(
656
- "abort",
657
- () => {
658
- window.clearTimeout(timer);
659
- reject(new DOMException("Aborted", "AbortError"));
660
- },
661
- { once: true }
662
- );
663
- });
664
- }
665
- async function runStatusSequence(signal, onStep) {
666
- for (const item of STATUS_SEQUENCE) {
667
- onStep({
668
- id: item.id,
669
- label: item.label,
670
- detail: item.detail,
671
- state: "active"
672
- });
673
- await delay(item.ms, signal);
674
- }
773
+ function upsertToolStep(steps, item) {
774
+ const next = {
775
+ id: item.id,
776
+ kind: item.kind,
777
+ label: item.label,
778
+ state: item.state,
779
+ ...item.detail ? { detail: item.detail } : {}
780
+ };
781
+ const index = steps.findIndex((step) => step.id === item.id);
782
+ if (index < 0) return [...steps, next];
783
+ return steps.map((step, stepIndex) => stepIndex === index ? next : step);
784
+ }
785
+ function completeActivePlanning(steps) {
786
+ return steps.map(
787
+ (step) => step.kind === "planning" && step.state === "active" ? {
788
+ ...step,
789
+ detail: "Prepared a response",
790
+ state: "completed"
791
+ } : step
792
+ );
675
793
  }
676
794
  function useAgentChat({
677
795
  customerId,
@@ -680,8 +798,13 @@ function useAgentChat({
680
798
  version,
681
799
  runtimeOrigin,
682
800
  visitorSessionId,
683
- storageKeyPrefix
801
+ storageKeyPrefix,
802
+ greeting
684
803
  }) {
804
+ const initialState = useMemo(
805
+ () => createInitialState(greeting?.trim() || DEFAULT_GREETING),
806
+ [greeting]
807
+ );
685
808
  const previewGrantProviderRef = useRef(getUnpublishedPreviewGrant);
686
809
  previewGrantProviderRef.current = getUnpublishedPreviewGrant;
687
810
  const resolveUnpublishedPreviewGrant = () => {
@@ -712,7 +835,8 @@ function useAgentChat({
712
835
  );
713
836
  const [state, setState] = useState(
714
837
  () => stateFromConversation(
715
- loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
838
+ loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
839
+ initialState
716
840
  )
717
841
  );
718
842
  const runRef = useRef(null);
@@ -727,7 +851,7 @@ function useAgentChat({
727
851
  storageKeyPrefix: resolvedStorageKeyPrefix
728
852
  })
729
853
  );
730
- const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
854
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
731
855
  const identityRef = useRef(identityKey);
732
856
  useEffect(() => {
733
857
  if (identityRef.current === identityKey) {
@@ -747,13 +871,15 @@ function useAgentChat({
747
871
  });
748
872
  setState(
749
873
  stateFromConversation(
750
- loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
874
+ loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
875
+ initialState
751
876
  )
752
877
  );
753
878
  }, [
754
879
  customerId,
755
880
  identityKey,
756
881
  indexId,
882
+ initialState,
757
883
  runtimeOrigin,
758
884
  resolvedStorageKeyPrefix,
759
885
  version,
@@ -778,8 +904,8 @@ function useAgentChat({
778
904
  runRef.current = null;
779
905
  clientRef.current.reset();
780
906
  clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
781
- setState(INITIAL_STATE);
782
- }, [resolvedStorageKeyPrefix, visitorId]);
907
+ setState(initialState);
908
+ }, [initialState, resolvedStorageKeyPrefix, visitorId]);
783
909
  const runTurn = useCallback(
784
910
  async (input) => {
785
911
  const { controller, initialText = "", resume, visitorText } = input;
@@ -788,35 +914,23 @@ function useAgentChat({
788
914
  try {
789
915
  let streamStarted = Boolean(initialText);
790
916
  let streamed = initialText;
791
- const planningPromise = resume ? Promise.resolve() : runStatusSequence(signal, (step) => {
792
- if (streamStarted || !isActiveRun()) return;
793
- setState((prev) => ({
794
- ...prev,
795
- phase: "running-tools",
796
- toolSteps: [step]
797
- }));
798
- });
799
917
  const handlers = {
800
- onStep: (label, detail) => {
801
- if (streamStarted || !isActiveRun()) return;
918
+ onWork: (item) => {
919
+ if (!isActiveRun()) return;
802
920
  setState((prev) => ({
803
921
  ...prev,
804
- phase: "running-tools",
805
- toolSteps: [
806
- { id: `step-${label}`, label, detail, state: "active" }
807
- ]
922
+ phase: item.state === "active" ? "running-tools" : prev.phase,
923
+ toolSteps: upsertToolStep(prev.toolSteps, item)
808
924
  }));
809
925
  },
810
926
  onDelta: (delta) => {
811
927
  if (!isActiveRun()) return;
812
- void planningPromise.catch(() => {
813
- });
814
928
  if (!streamStarted) {
815
929
  streamStarted = true;
816
930
  setState((prev) => ({
817
931
  ...prev,
818
932
  phase: "streaming",
819
- toolSteps: [],
933
+ toolSteps: completeActivePlanning(prev.toolSteps),
820
934
  streamingText: ""
821
935
  }));
822
936
  }
@@ -844,8 +958,6 @@ function useAgentChat({
844
958
  signal
845
959
  });
846
960
  }
847
- await planningPromise.catch(() => {
848
- });
849
961
  if (!isActiveRun() || finalText === null) return;
850
962
  const agentMessage = {
851
963
  id: `agent-${Date.now()}`,
@@ -857,7 +969,7 @@ function useAgentChat({
857
969
  ...prev,
858
970
  phase: "complete",
859
971
  messages: [...prev.messages, agentMessage],
860
- toolSteps: [],
972
+ toolSteps: completeActivePlanning(prev.toolSteps),
861
973
  streamingText: "",
862
974
  followUps: [],
863
975
  journey: null
@@ -872,7 +984,13 @@ function useAgentChat({
872
984
  setState((prev) => ({
873
985
  ...prev,
874
986
  phase: "complete",
875
- toolSteps: [],
987
+ toolSteps: prev.toolSteps.map(
988
+ (step) => step.state === "active" ? {
989
+ ...step,
990
+ detail: "Couldn\u2019t complete this step",
991
+ state: "error"
992
+ } : step
993
+ ),
876
994
  streamingText: "",
877
995
  error: message
878
996
  }));
@@ -900,7 +1018,12 @@ function useAgentChat({
900
1018
  phase: "thinking",
901
1019
  messages: [...prev.messages, visitorMessage],
902
1020
  toolSteps: [
903
- { id: "s1", label: "Starting Eve session", state: "active" }
1021
+ {
1022
+ id: "planning",
1023
+ kind: "planning",
1024
+ label: "Understanding your question",
1025
+ state: "active"
1026
+ }
904
1027
  ],
905
1028
  journey: null,
906
1029
  followUps: [],
@@ -979,82 +1102,96 @@ function normalizeAgentPlacement(placement) {
979
1102
  };
980
1103
  }
981
1104
 
1105
+ // src/react/types/conversation.ts
1106
+ var defaultAgentRailTheme = {
1107
+ railMaxWidth: "450px",
1108
+ brand: "#6f16ff",
1109
+ brandSoft: "#f3edff",
1110
+ brandDeep: "#12043e",
1111
+ surface: "#ffffff",
1112
+ surfaceMuted: "#f4f6fb",
1113
+ text: "#171b2a",
1114
+ textMuted: "#5a6378",
1115
+ textSubtle: "#8a94a8",
1116
+ border: "rgb(42 51 70 / 0.1)",
1117
+ visitorBubble: "#6f16ff",
1118
+ visitorText: "#ffffff",
1119
+ success: "#18794e",
1120
+ danger: "#c94b63",
1121
+ fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
1122
+ fontDisplay: '"Space Grotesk", sans-serif'
1123
+ };
1124
+
982
1125
  // src/react/components/AgentRail/AgentRail.tsx
983
1126
  import { useEffect as useEffect2, useRef as useRef3 } from "react";
984
1127
 
985
- // src/react/components/ToolTimeline/ToolTimeline.tsx
1128
+ // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
986
1129
  import { jsx, jsxs } from "react/jsx-runtime";
987
- function SpinnerIcon() {
988
- return /* @__PURE__ */ jsxs("svg", { className: "agent-status__spinner", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
989
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "1.5", strokeOpacity: "0.25" }),
990
- /* @__PURE__ */ jsx("path", { d: "M14 8a6 6 0 0 0-6-6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
991
- ] });
992
- }
993
- function getCurrentStep(steps) {
994
- const active = steps.find((step) => step.state === "active");
995
- if (active) return active;
996
- const error = steps.find((step) => step.state === "error");
997
- if (error) return error;
998
- const pending = steps.find((step) => step.state === "pending");
999
- if (pending) return pending;
1000
- return steps.at(-1) ?? null;
1001
- }
1002
- function ToolTimeline({
1003
- steps,
1004
- streamingText,
1005
- showStreaming = false,
1006
- inline = false
1007
- }) {
1008
- if (steps.length === 0 && !(showStreaming && streamingText)) return null;
1009
- const currentStep = getCurrentStep(steps);
1010
- const showStatus = currentStep && currentStep.state !== "completed" && !(showStreaming && streamingText);
1011
- return /* @__PURE__ */ jsxs(
1012
- "div",
1013
- {
1014
- className: `tool-timeline${inline ? " tool-timeline--inline" : ""}`,
1015
- role: "status",
1016
- "aria-live": "polite",
1017
- "aria-label": "Agent progress",
1018
- children: [
1019
- showStatus ? /* @__PURE__ */ jsxs("div", { className: "agent-status", children: [
1020
- 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, {}) }),
1021
- /* @__PURE__ */ jsxs("div", { className: "agent-status__copy", children: [
1022
- /* @__PURE__ */ jsx("span", { className: "agent-status__label", children: currentStep.label }),
1023
- currentStep.detail ? /* @__PURE__ */ jsx("span", { className: "agent-status__detail", children: currentStep.detail }) : null
1024
- ] })
1025
- ] }, currentStep.id) : null,
1026
- showStreaming && streamingText ? /* @__PURE__ */ jsxs("p", { className: "tool-timeline__streaming-text", children: [
1027
- streamingText,
1028
- /* @__PURE__ */ jsx("span", { className: "tool-timeline__cursor", "aria-hidden": "true" })
1029
- ] }) : null
1030
- ]
1031
- }
1130
+ function workSummary(steps) {
1131
+ const active = [...steps].reverse().find((step) => step.state === "active");
1132
+ if (active?.kind === "specialist") return `Working with ${active.label}`;
1133
+ if (active?.kind === "search") return "Searching this site";
1134
+ if (active) return active.label;
1135
+ const hasError = steps.some((step) => step.state === "error");
1136
+ const specialists = steps.filter(
1137
+ (step) => step.kind === "specialist" && step.state === "completed"
1032
1138
  );
1033
- }
1034
-
1035
- // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1036
- import { jsx as jsx2 } from "react/jsx-runtime";
1037
- function AgentActivityBubble({
1038
- steps,
1039
- streamingText,
1040
- showStreaming = false
1041
- }) {
1042
- return /* @__PURE__ */ jsx2("article", { className: "agent-activity-bubble", children: /* @__PURE__ */ jsx2("div", { className: "agent-activity-bubble__shell", children: /* @__PURE__ */ jsx2(
1043
- ToolTimeline,
1044
- {
1045
- steps,
1046
- streamingText,
1047
- showStreaming,
1048
- inline: true
1049
- }
1050
- ) }) });
1139
+ const searched = steps.some(
1140
+ (step) => step.kind === "search" && step.state === "completed"
1141
+ );
1142
+ if (hasError) return "Answered with available information";
1143
+ if (specialists.length > 1)
1144
+ return `Consulted ${specialists.length} specialists`;
1145
+ if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1146
+ if (searched) return "Searched this site";
1147
+ return "Prepared a response";
1148
+ }
1149
+ function AgentActivityBubble({ steps }) {
1150
+ const active = steps.some((step) => step.state === "active");
1151
+ return /* @__PURE__ */ jsxs("article", { className: "agent-activity-bubble", children: [
1152
+ /* @__PURE__ */ jsxs("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1153
+ /* @__PURE__ */ jsx(
1154
+ "span",
1155
+ {
1156
+ className: `agent-activity-bubble__pulse${active ? " is-active" : ""}`,
1157
+ "aria-hidden": "true"
1158
+ }
1159
+ ),
1160
+ workSummary(steps)
1161
+ ] }),
1162
+ /* @__PURE__ */ jsxs("details", { className: "agent-activity-bubble__details", open: active, children: [
1163
+ /* @__PURE__ */ jsx("summary", { children: "Work details" }),
1164
+ /* @__PURE__ */ jsx("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => /* @__PURE__ */ jsxs(
1165
+ "li",
1166
+ {
1167
+ className: "agent-activity-bubble__step",
1168
+ "data-state": step.state,
1169
+ children: [
1170
+ /* @__PURE__ */ jsx(
1171
+ "span",
1172
+ {
1173
+ className: "agent-activity-bubble__step-icon",
1174
+ "aria-hidden": "true",
1175
+ children: step.state === "completed" ? "\u2713" : step.state === "error" ? "!" : ""
1176
+ }
1177
+ ),
1178
+ /* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__step-copy", children: [
1179
+ /* @__PURE__ */ jsx("strong", { children: step.label }),
1180
+ step.detail ? /* @__PURE__ */ jsx("small", { children: step.detail }) : null
1181
+ ] })
1182
+ ]
1183
+ },
1184
+ step.id
1185
+ )) })
1186
+ ] })
1187
+ ] });
1051
1188
  }
1052
1189
 
1053
1190
  // src/react/components/Composer/Composer.tsx
1054
1191
  import { useRef as useRef2, useState as useState2 } from "react";
1055
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1192
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1056
1193
  function SendIcon() {
1057
- 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" }) });
1194
+ return /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M8 12V4M8 4l-3 3M8 4l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1058
1195
  }
1059
1196
  function Composer({
1060
1197
  disabled = false,
@@ -1081,8 +1218,8 @@ function Composer({
1081
1218
  submitCurrent();
1082
1219
  }
1083
1220
  }
1084
- return /* @__PURE__ */ jsx3("form", { className: `composer${variant === "dock" ? " composer--dock" : ""}`, onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2("div", { className: "composer__field", children: [
1085
- /* @__PURE__ */ jsx3(
1221
+ return /* @__PURE__ */ jsx2("form", { className: `composer${variant === "dock" ? " composer--dock" : ""}`, onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2("div", { className: "composer__field", children: [
1222
+ /* @__PURE__ */ jsx2(
1086
1223
  "textarea",
1087
1224
  {
1088
1225
  ref: inputRef,
@@ -1096,21 +1233,21 @@ function Composer({
1096
1233
  onKeyDown: handleKeyDown
1097
1234
  }
1098
1235
  ),
1099
- /* @__PURE__ */ jsx3(
1236
+ /* @__PURE__ */ jsx2(
1100
1237
  "button",
1101
1238
  {
1102
1239
  type: "submit",
1103
1240
  className: "composer__send",
1104
1241
  disabled: disabled || !value.trim(),
1105
1242
  "aria-label": "Send message",
1106
- children: /* @__PURE__ */ jsx3(SendIcon, {})
1243
+ children: /* @__PURE__ */ jsx2(SendIcon, {})
1107
1244
  }
1108
1245
  )
1109
1246
  ] }) });
1110
1247
  }
1111
1248
 
1112
1249
  // src/react/components/FollowUpChips/FollowUpChips.tsx
1113
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1250
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1114
1251
  function FollowUpChips({
1115
1252
  suggestions,
1116
1253
  disabled = false,
@@ -1120,7 +1257,7 @@ function FollowUpChips({
1120
1257
  }) {
1121
1258
  if (suggestions.length === 0) return null;
1122
1259
  if (variant === "dock") {
1123
- return /* @__PURE__ */ jsx4("div", { className: "followups followups--dock", children: /* @__PURE__ */ jsx4("div", { className: "followups__scroll", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx4(
1260
+ return /* @__PURE__ */ jsx3("div", { className: "followups followups--dock", children: /* @__PURE__ */ jsx3("div", { className: "followups__scroll", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx3(
1124
1261
  "button",
1125
1262
  {
1126
1263
  type: "button",
@@ -1133,8 +1270,8 @@ function FollowUpChips({
1133
1270
  )) }) });
1134
1271
  }
1135
1272
  return /* @__PURE__ */ jsxs3("div", { className: "followups", children: [
1136
- /* @__PURE__ */ jsx4("span", { className: "followups__label", children: label }),
1137
- /* @__PURE__ */ jsx4("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx4(
1273
+ /* @__PURE__ */ jsx3("span", { className: "followups__label", children: label }),
1274
+ /* @__PURE__ */ jsx3("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx3(
1138
1275
  "button",
1139
1276
  {
1140
1277
  type: "button",
@@ -1151,12 +1288,12 @@ function FollowUpChips({
1151
1288
  // src/react/components/MessageBubble/MessageBubble.tsx
1152
1289
  import { Streamdown } from "streamdown";
1153
1290
  import "streamdown/styles.css";
1154
- import { jsx as jsx5 } from "react/jsx-runtime";
1291
+ import { jsx as jsx4 } from "react/jsx-runtime";
1155
1292
  function MessageBubble({ message }) {
1156
1293
  if (message.role === "visitor") {
1157
- return /* @__PURE__ */ jsx5("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ jsx5("p", { className: "message-bubble__text", children: message.text }) });
1294
+ return /* @__PURE__ */ jsx4("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ jsx4("p", { className: "message-bubble__text", children: message.text }) });
1158
1295
  }
1159
- return /* @__PURE__ */ jsx5("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ jsx5("div", { className: "message-bubble__text", children: /* @__PURE__ */ jsx5(
1296
+ return /* @__PURE__ */ jsx4("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ jsx4("div", { className: "message-bubble__text", children: /* @__PURE__ */ jsx4(
1160
1297
  Streamdown,
1161
1298
  {
1162
1299
  animated: true,
@@ -1173,12 +1310,20 @@ function MessageBubble({ message }) {
1173
1310
  }
1174
1311
 
1175
1312
  // src/react/components/AgentRail/AgentRail.tsx
1176
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1313
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1177
1314
  function MinimizeIcon() {
1178
- 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" }) });
1315
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
1316
+ "path",
1317
+ {
1318
+ d: "M3.5 8h9",
1319
+ stroke: "currentColor",
1320
+ strokeWidth: "1.7",
1321
+ strokeLinecap: "round"
1322
+ }
1323
+ ) });
1179
1324
  }
1180
1325
  function ExpandIcon() {
1181
- return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1326
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
1182
1327
  "path",
1183
1328
  {
1184
1329
  d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
@@ -1190,7 +1335,7 @@ function ExpandIcon() {
1190
1335
  ) });
1191
1336
  }
1192
1337
  function RestoreIcon() {
1193
- return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1338
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
1194
1339
  "path",
1195
1340
  {
1196
1341
  d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
@@ -1205,6 +1350,7 @@ function AgentRail({
1205
1350
  state,
1206
1351
  theme,
1207
1352
  brandLabel = "Webless Assist",
1353
+ brandLogoUrl,
1208
1354
  poweredByLabel = "Powered by Webless",
1209
1355
  composerPlaceholder = "Ask anything\u2026",
1210
1356
  mobileFullscreen = false,
@@ -1216,10 +1362,31 @@ function AgentRail({
1216
1362
  onFollowUpSelect
1217
1363
  }) {
1218
1364
  const transcriptRef = useRef3(null);
1219
- const railStyle = theme?.railMaxWidth ? { ["--rail-width"]: theme.railMaxWidth, ["--as-rail-max-width"]: theme.railMaxWidth } : void 0;
1365
+ const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
1366
+ const railStyle = {
1367
+ "--rail-width": resolvedTheme.railMaxWidth,
1368
+ "--as-rail-max-width": resolvedTheme.railMaxWidth,
1369
+ "--as-brand": resolvedTheme.brand,
1370
+ "--as-brand-soft": resolvedTheme.brandSoft,
1371
+ "--as-brand-deep": resolvedTheme.brandDeep,
1372
+ "--as-surface": resolvedTheme.surface,
1373
+ "--as-surface-muted": resolvedTheme.surfaceMuted,
1374
+ "--as-text": resolvedTheme.text,
1375
+ "--as-text-muted": resolvedTheme.textMuted,
1376
+ "--as-text-subtle": resolvedTheme.textSubtle,
1377
+ "--as-border": resolvedTheme.border,
1378
+ "--as-visitor-bubble": resolvedTheme.visitorBubble,
1379
+ "--as-visitor-text": resolvedTheme.visitorText,
1380
+ "--as-success": resolvedTheme.success,
1381
+ "--as-danger": resolvedTheme.danger,
1382
+ "--as-font-body": resolvedTheme.fontBody,
1383
+ "--as-font-display": resolvedTheme.fontDisplay
1384
+ };
1220
1385
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
1221
- const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools");
1222
- const hasVisitorMessages2 = state.messages.some((message) => message.role === "visitor");
1386
+ const showActivity = state.toolSteps.length > 0;
1387
+ const hasVisitorMessages2 = state.messages.some(
1388
+ (message) => message.role === "visitor"
1389
+ );
1223
1390
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
1224
1391
  const showDockFollowUps = expanded && showIdleFollowUps;
1225
1392
  const streamingMessage = state.phase === "streaming" && state.streamingText ? {
@@ -1233,7 +1400,13 @@ function AgentRail({
1233
1400
  const node = transcriptRef.current;
1234
1401
  if (!node) return;
1235
1402
  node.scrollTop = node.scrollHeight;
1236
- }, [state.messages, state.toolSteps, state.streamingText, state.followUps, state.journey]);
1403
+ }, [
1404
+ state.messages,
1405
+ state.toolSteps,
1406
+ state.streamingText,
1407
+ state.followUps,
1408
+ state.journey
1409
+ ]);
1237
1410
  return /* @__PURE__ */ jsxs4(
1238
1411
  "aside",
1239
1412
  {
@@ -1241,34 +1414,59 @@ function AgentRail({
1241
1414
  style: railStyle,
1242
1415
  "aria-label": "Agent conversation",
1243
1416
  children: [
1244
- /* @__PURE__ */ jsx6("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs4("div", { className: "agent-rail__brand-row", children: [
1245
- onCollapse ? /* @__PURE__ */ jsx6(
1417
+ /* @__PURE__ */ jsx5("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs4("div", { className: "agent-rail__brand-row", children: [
1418
+ onCollapse ? /* @__PURE__ */ jsx5(
1246
1419
  "button",
1247
1420
  {
1248
1421
  type: "button",
1249
1422
  className: "agent-rail__collapse",
1250
1423
  "aria-label": "Collapse assist",
1251
1424
  onClick: onCollapse,
1252
- children: /* @__PURE__ */ jsx6(MinimizeIcon, {})
1425
+ children: /* @__PURE__ */ jsx5(MinimizeIcon, {})
1253
1426
  }
1254
- ) : 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" }),
1255
- /* @__PURE__ */ jsx6("span", { className: "agent-rail__brand-label", children: brandLabel }),
1256
- onExpandToggle ? /* @__PURE__ */ jsx6(
1427
+ ) : onClose ? /* @__PURE__ */ jsx5(
1428
+ "button",
1429
+ {
1430
+ type: "button",
1431
+ className: "agent-rail__close",
1432
+ "aria-label": "Close agent",
1433
+ onClick: onClose,
1434
+ children: /* @__PURE__ */ jsx5(MinimizeIcon, {})
1435
+ }
1436
+ ) : /* @__PURE__ */ jsx5("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1437
+ /* @__PURE__ */ jsxs4("span", { className: "agent-rail__identity", children: [
1438
+ /* @__PURE__ */ jsxs4("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: [
1439
+ brandLabel.slice(0, 1).toUpperCase(),
1440
+ brandLogoUrl ? /* @__PURE__ */ jsx5(
1441
+ "img",
1442
+ {
1443
+ className: "agent-rail__brand-logo",
1444
+ src: brandLogoUrl,
1445
+ alt: "",
1446
+ onError: (event) => {
1447
+ event.currentTarget.hidden = true;
1448
+ }
1449
+ }
1450
+ ) : null
1451
+ ] }),
1452
+ /* @__PURE__ */ jsx5("span", { className: "agent-rail__brand-label", children: brandLabel })
1453
+ ] }),
1454
+ onExpandToggle ? /* @__PURE__ */ jsx5(
1257
1455
  "button",
1258
1456
  {
1259
1457
  type: "button",
1260
1458
  className: "agent-rail__expand",
1261
1459
  "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
1262
1460
  onClick: onExpandToggle,
1263
- children: expanded ? /* @__PURE__ */ jsx6(RestoreIcon, {}) : /* @__PURE__ */ jsx6(ExpandIcon, {})
1461
+ children: expanded ? /* @__PURE__ */ jsx5(RestoreIcon, {}) : /* @__PURE__ */ jsx5(ExpandIcon, {})
1264
1462
  }
1265
- ) : /* @__PURE__ */ jsx6("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
1463
+ ) : /* @__PURE__ */ jsx5("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
1266
1464
  ] }) }),
1267
1465
  /* @__PURE__ */ jsxs4("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1268
- state.messages.map((message) => /* @__PURE__ */ jsx6(MessageBubble, { message }, message.id)),
1269
- streamingMessage ? /* @__PURE__ */ jsx6(MessageBubble, { message: streamingMessage }) : null,
1270
- showActivity ? /* @__PURE__ */ jsx6(AgentActivityBubble, { steps: state.toolSteps }) : null,
1271
- !expanded && showIdleFollowUps ? /* @__PURE__ */ jsx6("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx6(
1466
+ state.messages.map((message) => /* @__PURE__ */ jsx5(MessageBubble, { message }, message.id)),
1467
+ streamingMessage ? /* @__PURE__ */ jsx5(MessageBubble, { message: streamingMessage }) : null,
1468
+ showActivity ? /* @__PURE__ */ jsx5(AgentActivityBubble, { steps: state.toolSteps }) : null,
1469
+ !expanded && showIdleFollowUps ? /* @__PURE__ */ jsx5("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx5(
1272
1470
  FollowUpChips,
1273
1471
  {
1274
1472
  suggestions: state.followUps,
@@ -1277,10 +1475,17 @@ function AgentRail({
1277
1475
  onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1278
1476
  }
1279
1477
  ) }) : null,
1280
- state.error ? /* @__PURE__ */ jsx6("p", { role: "alert", style: { fontSize: 13, color: "var(--as-danger)", margin: 0 }, children: state.error }) : null
1478
+ state.error ? /* @__PURE__ */ jsx5(
1479
+ "p",
1480
+ {
1481
+ role: "alert",
1482
+ style: { fontSize: 13, color: "var(--as-danger)", margin: 0 },
1483
+ children: state.error
1484
+ }
1485
+ ) : null
1281
1486
  ] }),
1282
1487
  expanded ? /* @__PURE__ */ jsxs4("div", { className: "agent-rail__dock-wrap", children: [
1283
- showDockFollowUps ? /* @__PURE__ */ jsx6("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ jsx6(
1488
+ showDockFollowUps ? /* @__PURE__ */ jsx5("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ jsx5(
1284
1489
  FollowUpChips,
1285
1490
  {
1286
1491
  variant: "dock",
@@ -1289,7 +1494,7 @@ function AgentRail({
1289
1494
  onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1290
1495
  }
1291
1496
  ) }) : null,
1292
- /* @__PURE__ */ jsx6("div", { className: "agent-rail__dock", children: /* @__PURE__ */ jsx6(
1497
+ /* @__PURE__ */ jsx5("div", { className: "agent-rail__dock", children: /* @__PURE__ */ jsx5(
1293
1498
  Composer,
1294
1499
  {
1295
1500
  variant: "dock",
@@ -1299,11 +1504,11 @@ function AgentRail({
1299
1504
  }
1300
1505
  ) }),
1301
1506
  /* @__PURE__ */ jsxs4("div", { className: "agent-rail__footer", children: [
1302
- /* @__PURE__ */ jsx6("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1303
- /* @__PURE__ */ jsx6("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1507
+ /* @__PURE__ */ jsx5("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1508
+ /* @__PURE__ */ jsx5("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1304
1509
  ] })
1305
1510
  ] }) : /* @__PURE__ */ jsxs4("div", { className: "agent-rail__composer-wrap", children: [
1306
- /* @__PURE__ */ jsx6(
1511
+ /* @__PURE__ */ jsx5(
1307
1512
  Composer,
1308
1513
  {
1309
1514
  disabled: isBusy,
@@ -1312,8 +1517,8 @@ function AgentRail({
1312
1517
  }
1313
1518
  ),
1314
1519
  /* @__PURE__ */ jsxs4("div", { className: "agent-rail__footer", children: [
1315
- /* @__PURE__ */ jsx6("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1316
- /* @__PURE__ */ jsx6("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1520
+ /* @__PURE__ */ jsx5("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1521
+ /* @__PURE__ */ jsx5("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1317
1522
  ] })
1318
1523
  ] })
1319
1524
  ]
@@ -1322,22 +1527,67 @@ function AgentRail({
1322
1527
  }
1323
1528
 
1324
1529
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
1325
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1530
+ import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1326
1531
  function SparklesIcon() {
1327
- return /* @__PURE__ */ jsxs5("svg", { className: "assist-edge-tab__sparkles", viewBox: "0 0 18 16", fill: "none", "aria-hidden": "true", children: [
1328
- /* @__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" }),
1329
- /* @__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" }),
1330
- /* @__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" })
1331
- ] });
1532
+ return /* @__PURE__ */ jsxs5(
1533
+ "svg",
1534
+ {
1535
+ className: "assist-edge-tab__sparkles",
1536
+ viewBox: "0 0 18 16",
1537
+ fill: "none",
1538
+ "aria-hidden": "true",
1539
+ children: [
1540
+ /* @__PURE__ */ jsx6(
1541
+ "path",
1542
+ {
1543
+ 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",
1544
+ fill: "currentColor"
1545
+ }
1546
+ ),
1547
+ /* @__PURE__ */ jsx6(
1548
+ "path",
1549
+ {
1550
+ 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",
1551
+ fill: "currentColor"
1552
+ }
1553
+ ),
1554
+ /* @__PURE__ */ jsx6(
1555
+ "path",
1556
+ {
1557
+ 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",
1558
+ fill: "currentColor"
1559
+ }
1560
+ )
1561
+ ]
1562
+ }
1563
+ );
1332
1564
  }
1333
1565
  function ChevronLeftIcon() {
1334
- 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" }) });
1566
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1567
+ "path",
1568
+ {
1569
+ d: "M10 4L6 8l4 4",
1570
+ stroke: "currentColor",
1571
+ strokeWidth: "1.6",
1572
+ strokeLinecap: "round",
1573
+ strokeLinejoin: "round"
1574
+ }
1575
+ ) });
1335
1576
  }
1336
1577
  function ChevronDownIcon() {
1337
- 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" }) });
1578
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1579
+ "path",
1580
+ {
1581
+ d: "M4 6l4 4 4-4",
1582
+ stroke: "currentColor",
1583
+ strokeWidth: "1.6",
1584
+ strokeLinecap: "round",
1585
+ strokeLinejoin: "round"
1586
+ }
1587
+ ) });
1338
1588
  }
1339
1589
  function DragDots() {
1340
- return /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx7("i", {}, index)) });
1590
+ return /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx6("i", {}, index)) });
1341
1591
  }
1342
1592
  var VARIANT_COPY = {
1343
1593
  outline: { label: "Assist", aria: "Open Assist" },
@@ -1350,12 +1600,19 @@ function AssistEdgeTab({
1350
1600
  along,
1351
1601
  inset,
1352
1602
  visible,
1603
+ label,
1604
+ logoUrl,
1605
+ brandColor,
1606
+ fontFamily,
1353
1607
  onOpen
1354
1608
  }) {
1355
1609
  const copy = VARIANT_COPY[variant];
1610
+ const visibleLabel = label?.trim() || copy.label;
1356
1611
  const style = {
1357
1612
  "--tab-along": `${along}%`,
1358
- "--tab-inset": `${inset}px`
1613
+ "--tab-inset": `${inset}px`,
1614
+ ...brandColor ? { "--as-brand": brandColor } : {},
1615
+ ...fontFamily ? { "--as-font-display": fontFamily } : {}
1359
1616
  };
1360
1617
  return /* @__PURE__ */ jsxs5(
1361
1618
  "button",
@@ -1363,25 +1620,51 @@ function AssistEdgeTab({
1363
1620
  type: "button",
1364
1621
  className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
1365
1622
  style,
1366
- "aria-label": copy.aria,
1623
+ "aria-label": `Open ${visibleLabel}`,
1367
1624
  "aria-hidden": !visible,
1368
1625
  tabIndex: visible ? 0 : -1,
1369
1626
  onClick: onOpen,
1370
1627
  children: [
1371
1628
  variant === "outline" ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1372
- /* @__PURE__ */ jsx7(SparklesIcon, {}),
1373
- /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: copy.label }),
1374
- /* @__PURE__ */ jsx7(ChevronDownIcon, {})
1629
+ /* @__PURE__ */ jsxs5("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1630
+ /* @__PURE__ */ jsx6(SparklesIcon, {}),
1631
+ logoUrl ? /* @__PURE__ */ jsx6(
1632
+ "img",
1633
+ {
1634
+ className: "assist-edge-tab__logo",
1635
+ src: logoUrl,
1636
+ alt: "",
1637
+ onError: (event) => {
1638
+ event.currentTarget.hidden = true;
1639
+ }
1640
+ }
1641
+ ) : null
1642
+ ] }),
1643
+ /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1644
+ /* @__PURE__ */ jsx6(ChevronDownIcon, {})
1375
1645
  ] }) : null,
1376
1646
  variant === "ask" ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1377
- /* @__PURE__ */ jsx7(ChevronLeftIcon, {}),
1378
- /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: copy.label }),
1379
- /* @__PURE__ */ jsx7(DragDots, {})
1647
+ /* @__PURE__ */ jsx6(ChevronLeftIcon, {}),
1648
+ /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1649
+ /* @__PURE__ */ jsx6(DragDots, {})
1380
1650
  ] }) : null,
1381
1651
  variant === "fill" ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1382
- /* @__PURE__ */ jsx7(SparklesIcon, {}),
1383
- /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: copy.label }),
1384
- /* @__PURE__ */ jsx7(ChevronLeftIcon, {})
1652
+ /* @__PURE__ */ jsxs5("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1653
+ /* @__PURE__ */ jsx6(SparklesIcon, {}),
1654
+ logoUrl ? /* @__PURE__ */ jsx6(
1655
+ "img",
1656
+ {
1657
+ className: "assist-edge-tab__logo",
1658
+ src: logoUrl,
1659
+ alt: "",
1660
+ onError: (event) => {
1661
+ event.currentTarget.hidden = true;
1662
+ }
1663
+ }
1664
+ ) : null
1665
+ ] }),
1666
+ /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1667
+ /* @__PURE__ */ jsx6(ChevronLeftIcon, {})
1385
1668
  ] }) : null
1386
1669
  ]
1387
1670
  }
@@ -1503,7 +1786,7 @@ function closeAgentPanel(customerId) {
1503
1786
  }
1504
1787
 
1505
1788
  // src/react/components/AgentWidget/AgentWidget.tsx
1506
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1789
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1507
1790
  function AgentWidget({
1508
1791
  indexId,
1509
1792
  customerId,
@@ -1513,7 +1796,8 @@ function AgentWidget({
1513
1796
  placement: placementInput,
1514
1797
  defaultCollapsed = true,
1515
1798
  pageShift = true,
1516
- registerPanelController = false
1799
+ registerPanelController = false,
1800
+ branding
1517
1801
  }) {
1518
1802
  const isMobile = useIsMobile();
1519
1803
  const placement = normalizeAgentPlacement(placementInput);
@@ -1535,8 +1819,27 @@ function AgentWidget({
1535
1819
  getUnpublishedPreviewGrant,
1536
1820
  indexId,
1537
1821
  version,
1538
- runtimeOrigin
1822
+ runtimeOrigin,
1823
+ greeting: branding?.greeting
1539
1824
  });
1825
+ const agentName = branding?.agentName ?? "Webless Guide";
1826
+ const theme = {
1827
+ ...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
1828
+ ...branding?.colors?.primary ? {
1829
+ brand: branding.colors.primary,
1830
+ visitorBubble: branding.colors.primary
1831
+ } : {},
1832
+ ...branding?.colors?.primarySoft ? { brandSoft: branding.colors.primarySoft } : {},
1833
+ ...branding?.colors?.primaryForeground ? { visitorText: branding.colors.primaryForeground } : {},
1834
+ ...branding?.colors?.surface ? { surface: branding.colors.surface } : {},
1835
+ ...branding?.colors?.surfaceMuted ? { surfaceMuted: branding.colors.surfaceMuted } : {},
1836
+ ...branding?.colors?.text ? { text: branding.colors.text } : {},
1837
+ ...branding?.colors?.textMuted ? {
1838
+ textMuted: branding.colors.textMuted,
1839
+ textSubtle: branding.colors.textMuted
1840
+ } : {},
1841
+ ...branding?.colors?.border ? { border: branding.colors.border } : {}
1842
+ };
1540
1843
  const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
1541
1844
  useEffect5(() => {
1542
1845
  if (!registerPanelController) return;
@@ -1554,47 +1857,55 @@ function AgentWidget({
1554
1857
  await submit(message);
1555
1858
  }
1556
1859
  return /* @__PURE__ */ jsxs6("div", { className: "webless-agent-root", children: [
1557
- /* @__PURE__ */ jsx8(
1860
+ /* @__PURE__ */ jsxs6(
1558
1861
  "div",
1559
1862
  {
1560
1863
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
1561
- children: /* @__PURE__ */ jsx8(
1562
- "div",
1563
- {
1564
- ref: railSlotRef,
1565
- className: "webless-agent-root__rail-slot",
1566
- inert: railCollapsed || void 0,
1567
- "aria-hidden": railCollapsed,
1568
- children: /* @__PURE__ */ jsx8(
1569
- AgentRail,
1570
- {
1571
- state: idle ? {
1572
- ...state,
1573
- followUps: createIdleSuggestions()
1574
- } : state,
1575
- mobileFullscreen: isMobile && !railCollapsed,
1576
- expanded: railExpanded,
1577
- onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
1578
- onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1579
- onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1580
- onSubmit: handleSubmit,
1581
- onFollowUpSelect: (label) => void handleSubmit(label)
1582
- }
1583
- )
1584
- }
1585
- )
1864
+ children: [
1865
+ /* @__PURE__ */ jsx7(
1866
+ "div",
1867
+ {
1868
+ ref: railSlotRef,
1869
+ className: "webless-agent-root__rail-slot",
1870
+ inert: railCollapsed || void 0,
1871
+ "aria-hidden": railCollapsed,
1872
+ children: /* @__PURE__ */ jsx7(
1873
+ AgentRail,
1874
+ {
1875
+ theme,
1876
+ brandLabel: agentName,
1877
+ brandLogoUrl: branding?.logoUrl,
1878
+ composerPlaceholder: branding?.composerPlaceholder ?? `Ask ${agentName}\u2026`,
1879
+ poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
1880
+ state: idle ? {
1881
+ ...state,
1882
+ followUps: createIdleSuggestions()
1883
+ } : state,
1884
+ mobileFullscreen: isMobile && !railCollapsed,
1885
+ expanded: railExpanded,
1886
+ onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
1887
+ onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1888
+ onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1889
+ onSubmit: handleSubmit,
1890
+ onFollowUpSelect: (label) => void handleSubmit(label)
1891
+ }
1892
+ )
1893
+ }
1894
+ ),
1895
+ !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ jsx7(
1896
+ "button",
1897
+ {
1898
+ type: "button",
1899
+ className: "webless-agent-root__backdrop",
1900
+ tabIndex: -1,
1901
+ "aria-label": "Close expanded assist",
1902
+ onClick: () => setRailExpanded(false)
1903
+ }
1904
+ ) : null
1905
+ ]
1586
1906
  }
1587
1907
  ),
1588
- !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ jsx8(
1589
- "button",
1590
- {
1591
- type: "button",
1592
- className: "webless-agent-root__backdrop",
1593
- "aria-label": "Close expanded assist",
1594
- onClick: () => setRailExpanded(false)
1595
- }
1596
- ) : null,
1597
- railCollapsed ? /* @__PURE__ */ jsx8(
1908
+ railCollapsed ? /* @__PURE__ */ jsx7(
1598
1909
  AssistEdgeTab,
1599
1910
  {
1600
1911
  variant: placement.variant,
@@ -1602,6 +1913,10 @@ function AgentWidget({
1602
1913
  along: placement.along,
1603
1914
  inset: placement.inset,
1604
1915
  visible: true,
1916
+ label: agentName,
1917
+ logoUrl: branding?.logoUrl,
1918
+ brandColor: branding?.colors?.primary,
1919
+ fontFamily: branding?.fontFamily,
1605
1920
  onOpen: () => setRailCollapsed(false)
1606
1921
  }
1607
1922
  ) : null
@@ -1619,8 +1934,9 @@ export {
1619
1934
  normalizeAgentPlacement,
1620
1935
  openAgentPanel,
1621
1936
  closeAgentPanel,
1937
+ defaultAgentRailTheme,
1622
1938
  AgentRail,
1623
1939
  AssistEdgeTab,
1624
1940
  AgentWidget
1625
1941
  };
1626
- //# sourceMappingURL=chunk-7IR64MFS.js.map
1942
+ //# sourceMappingURL=chunk-MMYSBSHG.js.map