@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.
package/dist/react.cjs CHANGED
@@ -335,9 +335,8 @@ function clearPersistedAgentSession(visitorSessionId, options) {
335
335
  function isTurnBoundary(event) {
336
336
  return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
337
337
  }
338
- function applyMessageEvent(event, rendered, handlers) {
339
- const step = mapStepLabel(event);
340
- if (step) handlers.onStep?.(step.label, step.detail);
338
+ function applyMessageEvent(event, rendered, handlers, workItems) {
339
+ applyWorkEvent(event, handlers, workItems);
341
340
  if (event.type === "session.failed") {
342
341
  throw new Error(event.data.message || event.data.code);
343
342
  }
@@ -380,13 +379,120 @@ function renderTurn(events) {
380
379
  }
381
380
  return rendered;
382
381
  }
383
- function mapStepLabel(event) {
384
- if (event.type !== "step.started") return null;
385
- const stepIndex = event.data.stepIndex;
386
- return {
387
- label: "Model step running",
388
- detail: typeof stepIndex === "number" ? `Step ${stepIndex + 1}` : void 0
389
- };
382
+ function emitWorkItem(item, handlers, workItems) {
383
+ workItems.set(item.id, item);
384
+ handlers.onWork?.(item);
385
+ handlers.onStep?.(item.label, item.detail);
386
+ }
387
+ function completePlanning(handlers, workItems) {
388
+ const planning = workItems.get("planning");
389
+ if (!planning || planning.state !== "active") return;
390
+ emitWorkItem(
391
+ {
392
+ ...planning,
393
+ detail: "Picked the best way to help",
394
+ state: "completed"
395
+ },
396
+ handlers,
397
+ workItems
398
+ );
399
+ }
400
+ function specialistNameFromInput(input) {
401
+ const message = input.message;
402
+ if (typeof message !== "string") return void 0;
403
+ const match = /^Webless-Agent-Name:\s*(.+)$/imu.exec(message);
404
+ return match?.[1]?.trim() || void 0;
405
+ }
406
+ function requestedWorkItem(action) {
407
+ if (action.kind === "tool-call" && action.toolName === "search_discovery") {
408
+ return {
409
+ id: action.callId,
410
+ kind: "search",
411
+ label: "Search & Discovery",
412
+ detail: "Searching this site",
413
+ state: "active"
414
+ };
415
+ }
416
+ if (action.kind === "subagent-call" || action.kind === "remote-agent-call" || action.kind === "tool-call" && action.toolName === "agent") {
417
+ const name = specialistNameFromInput(action.input) ?? "Specialist";
418
+ return {
419
+ id: action.callId,
420
+ kind: "specialist",
421
+ label: name,
422
+ detail: "Reviewing your question",
423
+ state: "active"
424
+ };
425
+ }
426
+ return null;
427
+ }
428
+ function applyWorkEvent(event, handlers, workItems) {
429
+ if (event.type === "step.started" && workItems.size === 0) {
430
+ emitWorkItem(
431
+ {
432
+ id: "planning",
433
+ kind: "planning",
434
+ label: "Understanding your question",
435
+ state: "active"
436
+ },
437
+ handlers,
438
+ workItems
439
+ );
440
+ return;
441
+ }
442
+ if (event.type === "actions.requested") {
443
+ completePlanning(handlers, workItems);
444
+ for (const action of event.data.actions) {
445
+ const item = requestedWorkItem(action);
446
+ if (item) emitWorkItem(item, handlers, workItems);
447
+ }
448
+ return;
449
+ }
450
+ if (event.type === "subagent.called") {
451
+ completePlanning(handlers, workItems);
452
+ const current2 = workItems.get(event.data.callId);
453
+ if (!current2) {
454
+ emitWorkItem(
455
+ {
456
+ id: event.data.callId,
457
+ kind: "specialist",
458
+ label: event.data.name || "Specialist",
459
+ detail: "Reviewing your question",
460
+ state: "active"
461
+ },
462
+ handlers,
463
+ workItems
464
+ );
465
+ }
466
+ return;
467
+ }
468
+ if (event.type === "subagent.completed") {
469
+ const current2 = workItems.get(event.data.callId);
470
+ if (!current2) return;
471
+ emitWorkItem(
472
+ {
473
+ ...current2,
474
+ detail: "Guidance received",
475
+ state: "completed"
476
+ },
477
+ handlers,
478
+ workItems
479
+ );
480
+ return;
481
+ }
482
+ if (event.type !== "action.result") return;
483
+ const { result, status } = event.data;
484
+ const current = workItems.get(result.callId);
485
+ if (!current) return;
486
+ const failed = status !== "completed" || result.isError === true;
487
+ emitWorkItem(
488
+ {
489
+ ...current,
490
+ detail: failed ? "Couldn\u2019t complete; continuing with available information" : current.kind === "search" ? "Found relevant site content" : "Guidance received",
491
+ state: failed ? "error" : "completed"
492
+ },
493
+ handlers,
494
+ workItems
495
+ );
390
496
  }
391
497
  var AgentSession = class {
392
498
  constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, getUnpublishedPreviewGrant) {
@@ -504,10 +610,11 @@ var AgentSession = class {
504
610
  this.activeResponse = response;
505
611
  let streamIndex = session?.state.streamIndex ?? 0;
506
612
  let rendered = "";
613
+ const workItems = /* @__PURE__ */ new Map();
507
614
  try {
508
615
  for await (const event of response) {
509
616
  if (signal.aborted) break;
510
- rendered = applyMessageEvent(event, rendered, handlers);
617
+ rendered = applyMessageEvent(event, rendered, handlers, workItems);
511
618
  streamIndex += 1;
512
619
  if (session) {
513
620
  savePersistedAgentSession(
@@ -549,6 +656,10 @@ var AgentSession = class {
549
656
  return null;
550
657
  }
551
658
  let rendered = renderTurn(turnEvents);
659
+ const workItems = /* @__PURE__ */ new Map();
660
+ for (const event of turnEvents) {
661
+ applyWorkEvent(event, handlers, workItems);
662
+ }
552
663
  if (rendered.startsWith(initialText)) {
553
664
  const missedText = rendered.slice(initialText.length);
554
665
  if (missedText) handlers.onDelta(missedText);
@@ -583,7 +694,7 @@ var AgentSession = class {
583
694
  let streamIndex = snapshot.session.streamIndex;
584
695
  for await (const event of session.stream({ signal })) {
585
696
  if (signal.aborted) break;
586
- rendered = applyMessageEvent(event, rendered, handlers);
697
+ rendered = applyMessageEvent(event, rendered, handlers, workItems);
587
698
  streamIndex += 1;
588
699
  savePersistedAgentSession(
589
700
  this.visitorSessionId,
@@ -663,6 +774,11 @@ function createAgentClient(options) {
663
774
 
664
775
  // src/runtime/errors.ts
665
776
  var import_client3 = require("eve/client");
777
+ var TRANSIENT_AGENT_ERROR_MESSAGE = "I couldn\u2019t finish that answer. Please try again.";
778
+ function isTransientRuntimeMessage(message) {
779
+ const normalized = message.trim().toLowerCase();
780
+ return normalized.includes("empty response from runtime") || normalized.includes("failed to fetch") || normalized.includes("networkerror") || normalized.includes("load failed");
781
+ }
666
782
  function formatAgentError(error) {
667
783
  if (error instanceof import_client3.ClientError) {
668
784
  if (error.status === 401 && error.code === "index_required") {
@@ -674,13 +790,18 @@ function formatAgentError(error) {
674
790
  if (error.status === 409 && error.code === "session_not_active") {
675
791
  return "Session expired \u2014 send a new message to start again.";
676
792
  }
677
- return error.message || `Runtime error (${error.status})`;
793
+ if (error.status >= 500 || error.message && isTransientRuntimeMessage(error.message)) {
794
+ return TRANSIENT_AGENT_ERROR_MESSAGE;
795
+ }
796
+ return error.message || "This assistant is unavailable right now.";
678
797
  }
679
798
  if (error instanceof DOMException && error.name === "AbortError") {
680
799
  return "";
681
800
  }
682
- if (error instanceof Error) return error.message;
683
- return "Runtime request failed";
801
+ if (error instanceof Error) {
802
+ return isTransientRuntimeMessage(error.message) ? TRANSIENT_AGENT_ERROR_MESSAGE : error.message;
803
+ }
804
+ return TRANSIENT_AGENT_ERROR_MESSAGE;
684
805
  }
685
806
 
686
807
  // src/react/persisted-conversation.ts
@@ -736,57 +857,54 @@ function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
736
857
  }
737
858
 
738
859
  // src/react/hooks/useAgentChat.ts
739
- var GREETING_MESSAGE = {
740
- id: "greeting",
741
- role: "agent",
742
- text: "Hi! I'm connected to the Webless Agent Runtime. Ask anything about your published site index.",
743
- createdAt: 0
744
- };
745
- var INITIAL_STATE = {
746
- phase: "idle",
747
- messages: [GREETING_MESSAGE],
748
- toolSteps: [],
749
- journey: null,
750
- followUps: [],
751
- streamingText: "",
752
- error: null
753
- };
754
- function stateFromConversation(conversation) {
755
- if (!conversation || conversation.messages.length === 0) return INITIAL_STATE;
860
+ var DEFAULT_GREETING = "Hi! I can search this site or bring in a specialist when it helps. What would you like to know?";
861
+ function createInitialState(greeting = DEFAULT_GREETING) {
862
+ return {
863
+ phase: "idle",
864
+ messages: [
865
+ {
866
+ id: "greeting",
867
+ role: "agent",
868
+ text: greeting,
869
+ createdAt: 0
870
+ }
871
+ ],
872
+ toolSteps: [],
873
+ journey: null,
874
+ followUps: [],
875
+ streamingText: "",
876
+ error: null
877
+ };
878
+ }
879
+ function stateFromConversation(conversation, initialState) {
880
+ if (!conversation || conversation.messages.length === 0) return initialState;
756
881
  return {
757
- ...INITIAL_STATE,
882
+ ...initialState,
758
883
  messages: conversation.messages,
759
884
  phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
760
885
  streamingText: conversation.streamingText
761
886
  };
762
887
  }
763
- var STATUS_SEQUENCE = [
764
- { id: "s1", label: "Starting Eve session", ms: 400 },
765
- { id: "s2", label: "Connecting to runtime", ms: 500 }
766
- ];
767
- function delay(ms, signal) {
768
- return new Promise((resolve, reject) => {
769
- const timer = window.setTimeout(resolve, ms);
770
- signal.addEventListener(
771
- "abort",
772
- () => {
773
- window.clearTimeout(timer);
774
- reject(new DOMException("Aborted", "AbortError"));
775
- },
776
- { once: true }
777
- );
778
- });
779
- }
780
- async function runStatusSequence(signal, onStep) {
781
- for (const item of STATUS_SEQUENCE) {
782
- onStep({
783
- id: item.id,
784
- label: item.label,
785
- detail: item.detail,
786
- state: "active"
787
- });
788
- await delay(item.ms, signal);
789
- }
888
+ function upsertToolStep(steps, item) {
889
+ const next = {
890
+ id: item.id,
891
+ kind: item.kind,
892
+ label: item.label,
893
+ state: item.state,
894
+ ...item.detail ? { detail: item.detail } : {}
895
+ };
896
+ const index = steps.findIndex((step) => step.id === item.id);
897
+ if (index < 0) return [...steps, next];
898
+ return steps.map((step, stepIndex) => stepIndex === index ? next : step);
899
+ }
900
+ function completeActivePlanning(steps) {
901
+ return steps.map(
902
+ (step) => step.kind === "planning" && step.state === "active" ? {
903
+ ...step,
904
+ detail: "Prepared a response",
905
+ state: "completed"
906
+ } : step
907
+ );
790
908
  }
791
909
  function useAgentChat({
792
910
  customerId,
@@ -795,8 +913,13 @@ function useAgentChat({
795
913
  version,
796
914
  runtimeOrigin,
797
915
  visitorSessionId,
798
- storageKeyPrefix
916
+ storageKeyPrefix,
917
+ greeting
799
918
  }) {
919
+ const initialState = (0, import_react2.useMemo)(
920
+ () => createInitialState(greeting?.trim() || DEFAULT_GREETING),
921
+ [greeting]
922
+ );
800
923
  const previewGrantProviderRef = (0, import_react2.useRef)(getUnpublishedPreviewGrant);
801
924
  previewGrantProviderRef.current = getUnpublishedPreviewGrant;
802
925
  const resolveUnpublishedPreviewGrant = () => {
@@ -827,7 +950,8 @@ function useAgentChat({
827
950
  );
828
951
  const [state, setState] = (0, import_react2.useState)(
829
952
  () => stateFromConversation(
830
- loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
953
+ loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
954
+ initialState
831
955
  )
832
956
  );
833
957
  const runRef = (0, import_react2.useRef)(null);
@@ -842,7 +966,7 @@ function useAgentChat({
842
966
  storageKeyPrefix: resolvedStorageKeyPrefix
843
967
  })
844
968
  );
845
- const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
969
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
846
970
  const identityRef = (0, import_react2.useRef)(identityKey);
847
971
  (0, import_react2.useEffect)(() => {
848
972
  if (identityRef.current === identityKey) {
@@ -862,13 +986,15 @@ function useAgentChat({
862
986
  });
863
987
  setState(
864
988
  stateFromConversation(
865
- loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
989
+ loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
990
+ initialState
866
991
  )
867
992
  );
868
993
  }, [
869
994
  customerId,
870
995
  identityKey,
871
996
  indexId,
997
+ initialState,
872
998
  runtimeOrigin,
873
999
  resolvedStorageKeyPrefix,
874
1000
  version,
@@ -893,8 +1019,8 @@ function useAgentChat({
893
1019
  runRef.current = null;
894
1020
  clientRef.current.reset();
895
1021
  clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
896
- setState(INITIAL_STATE);
897
- }, [resolvedStorageKeyPrefix, visitorId]);
1022
+ setState(initialState);
1023
+ }, [initialState, resolvedStorageKeyPrefix, visitorId]);
898
1024
  const runTurn = (0, import_react2.useCallback)(
899
1025
  async (input) => {
900
1026
  const { controller, initialText = "", resume, visitorText } = input;
@@ -903,35 +1029,23 @@ function useAgentChat({
903
1029
  try {
904
1030
  let streamStarted = Boolean(initialText);
905
1031
  let streamed = initialText;
906
- const planningPromise = resume ? Promise.resolve() : runStatusSequence(signal, (step) => {
907
- if (streamStarted || !isActiveRun()) return;
908
- setState((prev) => ({
909
- ...prev,
910
- phase: "running-tools",
911
- toolSteps: [step]
912
- }));
913
- });
914
1032
  const handlers = {
915
- onStep: (label, detail) => {
916
- if (streamStarted || !isActiveRun()) return;
1033
+ onWork: (item) => {
1034
+ if (!isActiveRun()) return;
917
1035
  setState((prev) => ({
918
1036
  ...prev,
919
- phase: "running-tools",
920
- toolSteps: [
921
- { id: `step-${label}`, label, detail, state: "active" }
922
- ]
1037
+ phase: item.state === "active" ? "running-tools" : prev.phase,
1038
+ toolSteps: upsertToolStep(prev.toolSteps, item)
923
1039
  }));
924
1040
  },
925
1041
  onDelta: (delta) => {
926
1042
  if (!isActiveRun()) return;
927
- void planningPromise.catch(() => {
928
- });
929
1043
  if (!streamStarted) {
930
1044
  streamStarted = true;
931
1045
  setState((prev) => ({
932
1046
  ...prev,
933
1047
  phase: "streaming",
934
- toolSteps: [],
1048
+ toolSteps: completeActivePlanning(prev.toolSteps),
935
1049
  streamingText: ""
936
1050
  }));
937
1051
  }
@@ -959,8 +1073,6 @@ function useAgentChat({
959
1073
  signal
960
1074
  });
961
1075
  }
962
- await planningPromise.catch(() => {
963
- });
964
1076
  if (!isActiveRun() || finalText === null) return;
965
1077
  const agentMessage = {
966
1078
  id: `agent-${Date.now()}`,
@@ -972,7 +1084,7 @@ function useAgentChat({
972
1084
  ...prev,
973
1085
  phase: "complete",
974
1086
  messages: [...prev.messages, agentMessage],
975
- toolSteps: [],
1087
+ toolSteps: completeActivePlanning(prev.toolSteps),
976
1088
  streamingText: "",
977
1089
  followUps: [],
978
1090
  journey: null
@@ -987,7 +1099,13 @@ function useAgentChat({
987
1099
  setState((prev) => ({
988
1100
  ...prev,
989
1101
  phase: "complete",
990
- toolSteps: [],
1102
+ toolSteps: prev.toolSteps.map(
1103
+ (step) => step.state === "active" ? {
1104
+ ...step,
1105
+ detail: "Couldn\u2019t complete this step",
1106
+ state: "error"
1107
+ } : step
1108
+ ),
991
1109
  streamingText: "",
992
1110
  error: message
993
1111
  }));
@@ -1015,7 +1133,12 @@ function useAgentChat({
1015
1133
  phase: "thinking",
1016
1134
  messages: [...prev.messages, visitorMessage],
1017
1135
  toolSteps: [
1018
- { id: "s1", label: "Starting Eve session", state: "active" }
1136
+ {
1137
+ id: "planning",
1138
+ kind: "planning",
1139
+ label: "Understanding your question",
1140
+ state: "active"
1141
+ }
1019
1142
  ],
1020
1143
  journey: null,
1021
1144
  followUps: [],
@@ -1122,79 +1245,93 @@ function unregisterAgentPanelController(customerId) {
1122
1245
  // src/react/components/AgentRail/AgentRail.tsx
1123
1246
  var import_react5 = require("react");
1124
1247
 
1125
- // src/react/components/ToolTimeline/ToolTimeline.tsx
1126
- var import_jsx_runtime = require("react/jsx-runtime");
1127
- function SpinnerIcon() {
1128
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "agent-status__spinner", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
1129
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "1.5", strokeOpacity: "0.25" }),
1130
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 8a6 6 0 0 0-6-6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
1131
- ] });
1132
- }
1133
- function getCurrentStep(steps) {
1134
- const active = steps.find((step) => step.state === "active");
1135
- if (active) return active;
1136
- const error = steps.find((step) => step.state === "error");
1137
- if (error) return error;
1138
- const pending = steps.find((step) => step.state === "pending");
1139
- if (pending) return pending;
1140
- return steps.at(-1) ?? null;
1141
- }
1142
- function ToolTimeline({
1143
- steps,
1144
- streamingText,
1145
- showStreaming = false,
1146
- inline = false
1147
- }) {
1148
- if (steps.length === 0 && !(showStreaming && streamingText)) return null;
1149
- const currentStep = getCurrentStep(steps);
1150
- const showStatus = currentStep && currentStep.state !== "completed" && !(showStreaming && streamingText);
1151
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1152
- "div",
1153
- {
1154
- className: `tool-timeline${inline ? " tool-timeline--inline" : ""}`,
1155
- role: "status",
1156
- "aria-live": "polite",
1157
- "aria-label": "Agent progress",
1158
- children: [
1159
- showStatus ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status", children: [
1160
- 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, {}) }),
1161
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-status__copy", children: [
1162
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__label", children: currentStep.label }),
1163
- currentStep.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-status__detail", children: currentStep.detail }) : null
1164
- ] })
1165
- ] }, currentStep.id) : null,
1166
- showStreaming && streamingText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "tool-timeline__streaming-text", children: [
1167
- streamingText,
1168
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tool-timeline__cursor", "aria-hidden": "true" })
1169
- ] }) : null
1170
- ]
1171
- }
1172
- );
1173
- }
1248
+ // src/react/types/conversation.ts
1249
+ var defaultAgentRailTheme = {
1250
+ railMaxWidth: "450px",
1251
+ brand: "#6f16ff",
1252
+ brandSoft: "#f3edff",
1253
+ brandDeep: "#12043e",
1254
+ surface: "#ffffff",
1255
+ surfaceMuted: "#f4f6fb",
1256
+ text: "#171b2a",
1257
+ textMuted: "#5a6378",
1258
+ textSubtle: "#8a94a8",
1259
+ border: "rgb(42 51 70 / 0.1)",
1260
+ visitorBubble: "#6f16ff",
1261
+ visitorText: "#ffffff",
1262
+ success: "#18794e",
1263
+ danger: "#c94b63",
1264
+ fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
1265
+ fontDisplay: '"Space Grotesk", sans-serif'
1266
+ };
1174
1267
 
1175
1268
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1176
- var import_jsx_runtime2 = require("react/jsx-runtime");
1177
- function AgentActivityBubble({
1178
- steps,
1179
- streamingText,
1180
- showStreaming = false
1181
- }) {
1182
- 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)(
1183
- ToolTimeline,
1184
- {
1185
- steps,
1186
- streamingText,
1187
- showStreaming,
1188
- inline: true
1189
- }
1190
- ) }) });
1269
+ var import_jsx_runtime = require("react/jsx-runtime");
1270
+ function workSummary(steps) {
1271
+ const active = [...steps].reverse().find((step) => step.state === "active");
1272
+ if (active?.kind === "specialist") return `Working with ${active.label}`;
1273
+ if (active?.kind === "search") return "Searching this site";
1274
+ if (active) return active.label;
1275
+ const hasError = steps.some((step) => step.state === "error");
1276
+ const specialists = steps.filter(
1277
+ (step) => step.kind === "specialist" && step.state === "completed"
1278
+ );
1279
+ const searched = steps.some(
1280
+ (step) => step.kind === "search" && step.state === "completed"
1281
+ );
1282
+ if (hasError) return "Answered with available information";
1283
+ if (specialists.length > 1)
1284
+ return `Consulted ${specialists.length} specialists`;
1285
+ if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1286
+ if (searched) return "Searched this site";
1287
+ return "Prepared a response";
1288
+ }
1289
+ function AgentActivityBubble({ steps }) {
1290
+ const active = steps.some((step) => step.state === "active");
1291
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1294
+ "span",
1295
+ {
1296
+ className: `agent-activity-bubble__pulse${active ? " is-active" : ""}`,
1297
+ "aria-hidden": "true"
1298
+ }
1299
+ ),
1300
+ workSummary(steps)
1301
+ ] }),
1302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
1303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
1304
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1305
+ "li",
1306
+ {
1307
+ className: "agent-activity-bubble__step",
1308
+ "data-state": step.state,
1309
+ children: [
1310
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1311
+ "span",
1312
+ {
1313
+ className: "agent-activity-bubble__step-icon",
1314
+ "aria-hidden": "true",
1315
+ children: step.state === "completed" ? "\u2713" : step.state === "error" ? "!" : ""
1316
+ }
1317
+ ),
1318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: step.label }),
1320
+ step.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.detail }) : null
1321
+ ] })
1322
+ ]
1323
+ },
1324
+ step.id
1325
+ )) })
1326
+ ] })
1327
+ ] });
1191
1328
  }
1192
1329
 
1193
1330
  // src/react/components/Composer/Composer.tsx
1194
1331
  var import_react4 = require("react");
1195
- var import_jsx_runtime3 = require("react/jsx-runtime");
1332
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1196
1333
  function SendIcon() {
1197
- 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" }) });
1334
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 12V4M8 4l-3 3M8 4l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1198
1335
  }
1199
1336
  function Composer({
1200
1337
  disabled = false,
@@ -1221,8 +1358,8 @@ function Composer({
1221
1358
  submitCurrent();
1222
1359
  }
1223
1360
  }
1224
- 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: [
1225
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1361
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("form", { className: `composer${variant === "dock" ? " composer--dock" : ""}`, onSubmit: handleSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__field", children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1226
1363
  "textarea",
1227
1364
  {
1228
1365
  ref: inputRef,
@@ -1236,21 +1373,21 @@ function Composer({
1236
1373
  onKeyDown: handleKeyDown
1237
1374
  }
1238
1375
  ),
1239
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1376
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1240
1377
  "button",
1241
1378
  {
1242
1379
  type: "submit",
1243
1380
  className: "composer__send",
1244
1381
  disabled: disabled || !value.trim(),
1245
1382
  "aria-label": "Send message",
1246
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
1383
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
1247
1384
  }
1248
1385
  )
1249
1386
  ] }) });
1250
1387
  }
1251
1388
 
1252
1389
  // src/react/components/FollowUpChips/FollowUpChips.tsx
1253
- var import_jsx_runtime4 = require("react/jsx-runtime");
1390
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1254
1391
  function FollowUpChips({
1255
1392
  suggestions,
1256
1393
  disabled = false,
@@ -1260,7 +1397,7 @@ function FollowUpChips({
1260
1397
  }) {
1261
1398
  if (suggestions.length === 0) return null;
1262
1399
  if (variant === "dock") {
1263
- 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)(
1400
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "followups followups--dock", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "followups__scroll", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1264
1401
  "button",
1265
1402
  {
1266
1403
  type: "button",
@@ -1272,9 +1409,9 @@ function FollowUpChips({
1272
1409
  suggestion.id
1273
1410
  )) }) });
1274
1411
  }
1275
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "followups", children: [
1276
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "followups__label", children: label }),
1277
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1412
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "followups", children: [
1413
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "followups__label", children: label }),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "followups__list", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1278
1415
  "button",
1279
1416
  {
1280
1417
  type: "button",
@@ -1291,12 +1428,12 @@ function FollowUpChips({
1291
1428
  // src/react/components/MessageBubble/MessageBubble.tsx
1292
1429
  var import_streamdown = require("streamdown");
1293
1430
  var import_styles = require("streamdown/styles.css");
1294
- var import_jsx_runtime5 = require("react/jsx-runtime");
1431
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1295
1432
  function MessageBubble({ message }) {
1296
1433
  if (message.role === "visitor") {
1297
- 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 }) });
1434
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "message-bubble__text", children: message.text }) });
1298
1435
  }
1299
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1436
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1300
1437
  import_streamdown.Streamdown,
1301
1438
  {
1302
1439
  animated: true,
@@ -1313,12 +1450,20 @@ function MessageBubble({ message }) {
1313
1450
  }
1314
1451
 
1315
1452
  // src/react/components/AgentRail/AgentRail.tsx
1316
- var import_jsx_runtime6 = require("react/jsx-runtime");
1453
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1317
1454
  function MinimizeIcon() {
1318
- 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" }) });
1455
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1456
+ "path",
1457
+ {
1458
+ d: "M3.5 8h9",
1459
+ stroke: "currentColor",
1460
+ strokeWidth: "1.7",
1461
+ strokeLinecap: "round"
1462
+ }
1463
+ ) });
1319
1464
  }
1320
1465
  function ExpandIcon() {
1321
- 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)(
1466
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1322
1467
  "path",
1323
1468
  {
1324
1469
  d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
@@ -1330,7 +1475,7 @@ function ExpandIcon() {
1330
1475
  ) });
1331
1476
  }
1332
1477
  function RestoreIcon() {
1333
- 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)(
1478
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1334
1479
  "path",
1335
1480
  {
1336
1481
  d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
@@ -1345,6 +1490,7 @@ function AgentRail({
1345
1490
  state,
1346
1491
  theme,
1347
1492
  brandLabel = "Webless Assist",
1493
+ brandLogoUrl,
1348
1494
  poweredByLabel = "Powered by Webless",
1349
1495
  composerPlaceholder = "Ask anything\u2026",
1350
1496
  mobileFullscreen = false,
@@ -1356,10 +1502,31 @@ function AgentRail({
1356
1502
  onFollowUpSelect
1357
1503
  }) {
1358
1504
  const transcriptRef = (0, import_react5.useRef)(null);
1359
- const railStyle = theme?.railMaxWidth ? { ["--rail-width"]: theme.railMaxWidth, ["--as-rail-max-width"]: theme.railMaxWidth } : void 0;
1505
+ const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
1506
+ const railStyle = {
1507
+ "--rail-width": resolvedTheme.railMaxWidth,
1508
+ "--as-rail-max-width": resolvedTheme.railMaxWidth,
1509
+ "--as-brand": resolvedTheme.brand,
1510
+ "--as-brand-soft": resolvedTheme.brandSoft,
1511
+ "--as-brand-deep": resolvedTheme.brandDeep,
1512
+ "--as-surface": resolvedTheme.surface,
1513
+ "--as-surface-muted": resolvedTheme.surfaceMuted,
1514
+ "--as-text": resolvedTheme.text,
1515
+ "--as-text-muted": resolvedTheme.textMuted,
1516
+ "--as-text-subtle": resolvedTheme.textSubtle,
1517
+ "--as-border": resolvedTheme.border,
1518
+ "--as-visitor-bubble": resolvedTheme.visitorBubble,
1519
+ "--as-visitor-text": resolvedTheme.visitorText,
1520
+ "--as-success": resolvedTheme.success,
1521
+ "--as-danger": resolvedTheme.danger,
1522
+ "--as-font-body": resolvedTheme.fontBody,
1523
+ "--as-font-display": resolvedTheme.fontDisplay
1524
+ };
1360
1525
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
1361
- const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools");
1362
- const hasVisitorMessages2 = state.messages.some((message) => message.role === "visitor");
1526
+ const showActivity = state.toolSteps.length > 0;
1527
+ const hasVisitorMessages2 = state.messages.some(
1528
+ (message) => message.role === "visitor"
1529
+ );
1363
1530
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
1364
1531
  const showDockFollowUps = expanded && showIdleFollowUps;
1365
1532
  const streamingMessage = state.phase === "streaming" && state.streamingText ? {
@@ -1373,42 +1540,73 @@ function AgentRail({
1373
1540
  const node = transcriptRef.current;
1374
1541
  if (!node) return;
1375
1542
  node.scrollTop = node.scrollHeight;
1376
- }, [state.messages, state.toolSteps, state.streamingText, state.followUps, state.journey]);
1377
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1543
+ }, [
1544
+ state.messages,
1545
+ state.toolSteps,
1546
+ state.streamingText,
1547
+ state.followUps,
1548
+ state.journey
1549
+ ]);
1550
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1378
1551
  "aside",
1379
1552
  {
1380
1553
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
1381
1554
  style: railStyle,
1382
1555
  "aria-label": "Agent conversation",
1383
1556
  children: [
1384
- /* @__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: [
1385
- onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1557
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__brand-row", children: [
1558
+ onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1386
1559
  "button",
1387
1560
  {
1388
1561
  type: "button",
1389
1562
  className: "agent-rail__collapse",
1390
1563
  "aria-label": "Collapse assist",
1391
1564
  onClick: onCollapse,
1392
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
1565
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
1566
+ }
1567
+ ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1568
+ "button",
1569
+ {
1570
+ type: "button",
1571
+ className: "agent-rail__close",
1572
+ "aria-label": "Close agent",
1573
+ onClick: onClose,
1574
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
1393
1575
  }
1394
- ) : 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" }),
1395
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel }),
1396
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1576
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1577
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__identity", children: [
1578
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: [
1579
+ brandLabel.slice(0, 1).toUpperCase(),
1580
+ brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1581
+ "img",
1582
+ {
1583
+ className: "agent-rail__brand-logo",
1584
+ src: brandLogoUrl,
1585
+ alt: "",
1586
+ onError: (event) => {
1587
+ event.currentTarget.hidden = true;
1588
+ }
1589
+ }
1590
+ ) : null
1591
+ ] }),
1592
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
1593
+ ] }),
1594
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1397
1595
  "button",
1398
1596
  {
1399
1597
  type: "button",
1400
1598
  className: "agent-rail__expand",
1401
1599
  "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
1402
1600
  onClick: onExpandToggle,
1403
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
1601
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1404
1602
  }
1405
- ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
1603
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
1406
1604
  ] }) }),
1407
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1408
- state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message }, message.id)),
1409
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message: streamingMessage }) : null,
1410
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AgentActivityBubble, { steps: state.toolSteps }) : null,
1411
- !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1605
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1606
+ state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1607
+ streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1608
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AgentActivityBubble, { steps: state.toolSteps }) : null,
1609
+ !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1412
1610
  FollowUpChips,
1413
1611
  {
1414
1612
  suggestions: state.followUps,
@@ -1417,10 +1615,17 @@ function AgentRail({
1417
1615
  onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1418
1616
  }
1419
1617
  ) }) : null,
1420
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", style: { fontSize: 13, color: "var(--as-danger)", margin: 0 }, children: state.error }) : null
1618
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1619
+ "p",
1620
+ {
1621
+ role: "alert",
1622
+ style: { fontSize: 13, color: "var(--as-danger)", margin: 0 },
1623
+ children: state.error
1624
+ }
1625
+ ) : null
1421
1626
  ] }),
1422
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
1423
- showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1627
+ expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
1628
+ showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1424
1629
  FollowUpChips,
1425
1630
  {
1426
1631
  variant: "dock",
@@ -1429,7 +1634,7 @@ function AgentRail({
1429
1634
  onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1430
1635
  }
1431
1636
  ) }) : null,
1432
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1637
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1433
1638
  Composer,
1434
1639
  {
1435
1640
  variant: "dock",
@@ -1438,12 +1643,12 @@ function AgentRail({
1438
1643
  onSubmit
1439
1644
  }
1440
1645
  ) }),
1441
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
1442
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1443
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1646
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1647
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1444
1649
  ] })
1445
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1446
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1650
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1651
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1447
1652
  Composer,
1448
1653
  {
1449
1654
  disabled: isBusy,
@@ -1451,9 +1656,9 @@ function AgentRail({
1451
1656
  onSubmit
1452
1657
  }
1453
1658
  ),
1454
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__footer", children: [
1455
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1456
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1659
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1660
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1661
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1457
1662
  ] })
1458
1663
  ] })
1459
1664
  ]
@@ -1462,22 +1667,67 @@ function AgentRail({
1462
1667
  }
1463
1668
 
1464
1669
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
1465
- var import_jsx_runtime7 = require("react/jsx-runtime");
1670
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1466
1671
  function SparklesIcon() {
1467
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "assist-edge-tab__sparkles", viewBox: "0 0 18 16", fill: "none", "aria-hidden": "true", children: [
1468
- /* @__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" }),
1469
- /* @__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" }),
1470
- /* @__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" })
1471
- ] });
1672
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1673
+ "svg",
1674
+ {
1675
+ className: "assist-edge-tab__sparkles",
1676
+ viewBox: "0 0 18 16",
1677
+ fill: "none",
1678
+ "aria-hidden": "true",
1679
+ children: [
1680
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1681
+ "path",
1682
+ {
1683
+ 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",
1684
+ fill: "currentColor"
1685
+ }
1686
+ ),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1688
+ "path",
1689
+ {
1690
+ 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",
1691
+ fill: "currentColor"
1692
+ }
1693
+ ),
1694
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1695
+ "path",
1696
+ {
1697
+ 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",
1698
+ fill: "currentColor"
1699
+ }
1700
+ )
1701
+ ]
1702
+ }
1703
+ );
1472
1704
  }
1473
1705
  function ChevronLeftIcon() {
1474
- 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" }) });
1706
+ 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)(
1707
+ "path",
1708
+ {
1709
+ d: "M10 4L6 8l4 4",
1710
+ stroke: "currentColor",
1711
+ strokeWidth: "1.6",
1712
+ strokeLinecap: "round",
1713
+ strokeLinejoin: "round"
1714
+ }
1715
+ ) });
1475
1716
  }
1476
1717
  function ChevronDownIcon() {
1477
- 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" }) });
1718
+ 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)(
1719
+ "path",
1720
+ {
1721
+ d: "M4 6l4 4 4-4",
1722
+ stroke: "currentColor",
1723
+ strokeWidth: "1.6",
1724
+ strokeLinecap: "round",
1725
+ strokeLinejoin: "round"
1726
+ }
1727
+ ) });
1478
1728
  }
1479
1729
  function DragDots() {
1480
- 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)) });
1730
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("i", {}, index)) });
1481
1731
  }
1482
1732
  var VARIANT_COPY = {
1483
1733
  outline: { label: "Assist", aria: "Open Assist" },
@@ -1490,38 +1740,71 @@ function AssistEdgeTab({
1490
1740
  along,
1491
1741
  inset,
1492
1742
  visible,
1743
+ label,
1744
+ logoUrl,
1745
+ brandColor,
1746
+ fontFamily,
1493
1747
  onOpen
1494
1748
  }) {
1495
1749
  const copy = VARIANT_COPY[variant];
1750
+ const visibleLabel = label?.trim() || copy.label;
1496
1751
  const style = {
1497
1752
  "--tab-along": `${along}%`,
1498
- "--tab-inset": `${inset}px`
1753
+ "--tab-inset": `${inset}px`,
1754
+ ...brandColor ? { "--as-brand": brandColor } : {},
1755
+ ...fontFamily ? { "--as-font-display": fontFamily } : {}
1499
1756
  };
1500
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1757
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1501
1758
  "button",
1502
1759
  {
1503
1760
  type: "button",
1504
1761
  className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
1505
1762
  style,
1506
- "aria-label": copy.aria,
1763
+ "aria-label": `Open ${visibleLabel}`,
1507
1764
  "aria-hidden": !visible,
1508
1765
  tabIndex: visible ? 0 : -1,
1509
1766
  onClick: onOpen,
1510
1767
  children: [
1511
- variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1512
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
1513
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
1514
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDownIcon, {})
1768
+ variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1769
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1770
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1771
+ logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1772
+ "img",
1773
+ {
1774
+ className: "assist-edge-tab__logo",
1775
+ src: logoUrl,
1776
+ alt: "",
1777
+ onError: (event) => {
1778
+ event.currentTarget.hidden = true;
1779
+ }
1780
+ }
1781
+ ) : null
1782
+ ] }),
1783
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1784
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronDownIcon, {})
1515
1785
  ] }) : null,
1516
- variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1517
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}),
1518
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
1519
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DragDots, {})
1786
+ variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1787
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronLeftIcon, {}),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1789
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DragDots, {})
1520
1790
  ] }) : null,
1521
- variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1522
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {}),
1523
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: copy.label }),
1524
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {})
1791
+ variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1792
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1793
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1794
+ logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1795
+ "img",
1796
+ {
1797
+ className: "assist-edge-tab__logo",
1798
+ src: logoUrl,
1799
+ alt: "",
1800
+ onError: (event) => {
1801
+ event.currentTarget.hidden = true;
1802
+ }
1803
+ }
1804
+ ) : null
1805
+ ] }),
1806
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1807
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronLeftIcon, {})
1525
1808
  ] }) : null
1526
1809
  ]
1527
1810
  }
@@ -1529,7 +1812,7 @@ function AssistEdgeTab({
1529
1812
  }
1530
1813
 
1531
1814
  // src/react/components/AgentWidget/AgentWidget.tsx
1532
- var import_jsx_runtime8 = require("react/jsx-runtime");
1815
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1533
1816
  function AgentWidget({
1534
1817
  indexId,
1535
1818
  customerId,
@@ -1539,7 +1822,8 @@ function AgentWidget({
1539
1822
  placement: placementInput,
1540
1823
  defaultCollapsed = true,
1541
1824
  pageShift = true,
1542
- registerPanelController = false
1825
+ registerPanelController = false,
1826
+ branding
1543
1827
  }) {
1544
1828
  const isMobile = useIsMobile();
1545
1829
  const placement = normalizeAgentPlacement(placementInput);
@@ -1561,8 +1845,27 @@ function AgentWidget({
1561
1845
  getUnpublishedPreviewGrant,
1562
1846
  indexId,
1563
1847
  version,
1564
- runtimeOrigin
1848
+ runtimeOrigin,
1849
+ greeting: branding?.greeting
1565
1850
  });
1851
+ const agentName = branding?.agentName ?? "Webless Guide";
1852
+ const theme = {
1853
+ ...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
1854
+ ...branding?.colors?.primary ? {
1855
+ brand: branding.colors.primary,
1856
+ visitorBubble: branding.colors.primary
1857
+ } : {},
1858
+ ...branding?.colors?.primarySoft ? { brandSoft: branding.colors.primarySoft } : {},
1859
+ ...branding?.colors?.primaryForeground ? { visitorText: branding.colors.primaryForeground } : {},
1860
+ ...branding?.colors?.surface ? { surface: branding.colors.surface } : {},
1861
+ ...branding?.colors?.surfaceMuted ? { surfaceMuted: branding.colors.surfaceMuted } : {},
1862
+ ...branding?.colors?.text ? { text: branding.colors.text } : {},
1863
+ ...branding?.colors?.textMuted ? {
1864
+ textMuted: branding.colors.textMuted,
1865
+ textSubtle: branding.colors.textMuted
1866
+ } : {},
1867
+ ...branding?.colors?.border ? { border: branding.colors.border } : {}
1868
+ };
1566
1869
  const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
1567
1870
  (0, import_react6.useEffect)(() => {
1568
1871
  if (!registerPanelController) return;
@@ -1579,48 +1882,56 @@ function AgentWidget({
1579
1882
  if (isMobile) setRailCollapsed(false);
1580
1883
  await submit(message);
1581
1884
  }
1582
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
1583
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1885
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "webless-agent-root", children: [
1886
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1584
1887
  "div",
1585
1888
  {
1586
1889
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
1587
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1588
- "div",
1589
- {
1590
- ref: railSlotRef,
1591
- className: "webless-agent-root__rail-slot",
1592
- inert: railCollapsed || void 0,
1593
- "aria-hidden": railCollapsed,
1594
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1595
- AgentRail,
1596
- {
1597
- state: idle ? {
1598
- ...state,
1599
- followUps: createIdleSuggestions()
1600
- } : state,
1601
- mobileFullscreen: isMobile && !railCollapsed,
1602
- expanded: railExpanded,
1603
- onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
1604
- onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1605
- onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1606
- onSubmit: handleSubmit,
1607
- onFollowUpSelect: (label) => void handleSubmit(label)
1608
- }
1609
- )
1610
- }
1611
- )
1890
+ children: [
1891
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1892
+ "div",
1893
+ {
1894
+ ref: railSlotRef,
1895
+ className: "webless-agent-root__rail-slot",
1896
+ inert: railCollapsed || void 0,
1897
+ "aria-hidden": railCollapsed,
1898
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1899
+ AgentRail,
1900
+ {
1901
+ theme,
1902
+ brandLabel: agentName,
1903
+ brandLogoUrl: branding?.logoUrl,
1904
+ composerPlaceholder: branding?.composerPlaceholder ?? `Ask ${agentName}\u2026`,
1905
+ poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
1906
+ state: idle ? {
1907
+ ...state,
1908
+ followUps: createIdleSuggestions()
1909
+ } : state,
1910
+ mobileFullscreen: isMobile && !railCollapsed,
1911
+ expanded: railExpanded,
1912
+ onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
1913
+ onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1914
+ onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1915
+ onSubmit: handleSubmit,
1916
+ onFollowUpSelect: (label) => void handleSubmit(label)
1917
+ }
1918
+ )
1919
+ }
1920
+ ),
1921
+ !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1922
+ "button",
1923
+ {
1924
+ type: "button",
1925
+ className: "webless-agent-root__backdrop",
1926
+ tabIndex: -1,
1927
+ "aria-label": "Close expanded assist",
1928
+ onClick: () => setRailExpanded(false)
1929
+ }
1930
+ ) : null
1931
+ ]
1612
1932
  }
1613
1933
  ),
1614
- !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1615
- "button",
1616
- {
1617
- type: "button",
1618
- className: "webless-agent-root__backdrop",
1619
- "aria-label": "Close expanded assist",
1620
- onClick: () => setRailExpanded(false)
1621
- }
1622
- ) : null,
1623
- railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1934
+ railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1624
1935
  AssistEdgeTab,
1625
1936
  {
1626
1937
  variant: placement.variant,
@@ -1628,29 +1939,15 @@ function AgentWidget({
1628
1939
  along: placement.along,
1629
1940
  inset: placement.inset,
1630
1941
  visible: true,
1942
+ label: agentName,
1943
+ logoUrl: branding?.logoUrl,
1944
+ brandColor: branding?.colors?.primary,
1945
+ fontFamily: branding?.fontFamily,
1631
1946
  onOpen: () => setRailCollapsed(false)
1632
1947
  }
1633
1948
  ) : null
1634
1949
  ] });
1635
1950
  }
1636
-
1637
- // src/react/types/conversation.ts
1638
- var defaultAgentRailTheme = {
1639
- railMaxWidth: "450px",
1640
- brand: "#6f16ff",
1641
- brandSoft: "#f3edff",
1642
- brandDeep: "#12043e",
1643
- surface: "#ffffff",
1644
- surfaceMuted: "#f4f6fb",
1645
- text: "#171b2a",
1646
- textMuted: "#5a6378",
1647
- textSubtle: "#8a94a8",
1648
- border: "rgb(42 51 70 / 0.1)",
1649
- visitorBubble: "#6f16ff",
1650
- visitorText: "#ffffff",
1651
- success: "#18794e",
1652
- danger: "#c94b63"
1653
- };
1654
1951
  // Annotate the CommonJS export names for ESM import in node:
1655
1952
  0 && (module.exports = {
1656
1953
  AgentRail,