@flowengage/react-chatbot 8.0.13 → 8.0.15

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.
@@ -29482,32 +29482,6 @@ function ik(e) {
29482
29482
  });
29483
29483
  },
29484
29484
  "fwh-fill-form-mvp": async () => "FORM_FILLING_DISABLED: Form filling is not available at this time. Tell the visitor what fields are present (use fwh-list-forms-mvp) and ask them to fill the form themselves.",
29485
- "fwh-save-email-mvp": async (r) => {
29486
- $("fwh-save-email-mvp ▶", r);
29487
- let i = ($O(r)?.email || "").trim();
29488
- if (!i || !i.includes("@")) return "INVALID_EMAIL: The email address is not valid. Ask the visitor to repeat it clearly, or tell them to type it in the field below.";
29489
- if (!n) return "Cannot save email: API base URL not configured.";
29490
- let a = e.chatId || null;
29491
- if (!a) return "Cannot save email: no active chat session.";
29492
- try {
29493
- let e = await (await fetch(`${n}/api/voice/save-visitor-email`, {
29494
- method: "POST",
29495
- headers: { "Content-Type": "application/json" },
29496
- body: JSON.stringify({
29497
- chatId: a,
29498
- email: i,
29499
- siteId: t
29500
- })
29501
- })).json();
29502
- if (!e.success) return `Failed to save email: ${e.error || "unknown error"}`;
29503
- try {
29504
- typeof window < "u" && window.dispatchEvent(new CustomEvent("fe:voice-email-saved", { detail: { email: i } }));
29505
- } catch {}
29506
- return $("fwh-save-email-mvp ✓", i), `EMAIL_SAVED: ${i}. Now answer the visitor's original question.`;
29507
- } catch (e) {
29508
- return $("fwh-save-email-mvp ✖", e.message), `Failed to save email: ${e.message}`;
29509
- }
29510
- },
29511
29485
  "fwh-click-button-mvp": async (e) => {
29512
29486
  let t = $O(e), n = (t.button_text ?? t.buttonText ?? t.text ?? "").trim();
29513
29487
  if ($("fwh-click-button-mvp ▶", n), !n) return "Missing button_text: pass the visible label, e.g. Conduct Audit.";
@@ -29970,21 +29944,17 @@ function hk({ copy: e, orbColors: t, primaryColor: n, onVoice: r, onText: i, onC
29970
29944
  ]
29971
29945
  });
29972
29946
  }
29973
- function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i, apiBaseUrl: a, chatId: s, historyRef: l, onMessageSync: u, voiceEndCounter: d, websiteName: f, visitorEmail: h }) {
29947
+ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i, apiBaseUrl: a, chatId: s, historyRef: l, onMessageSync: u, voiceEndCounter: d, websiteName: f, voiceFirstMessage: h }) {
29974
29948
  c(() => (console.log("[FlowEngage:VoiceDebug] 🔵 VoiceMode MOUNTED"), () => console.log("[FlowEngage:VoiceDebug] 🔴 VoiceMode UNMOUNTED")), []);
29975
- let [v, y] = m("idle"), [b, x] = m(!1), [S, C] = m(!1), [w, T] = m(!1), [ee, te] = m(""), [ne, re] = m(""), E = p(null), ie = p(""), ae = p(async () => {}), oe = p(!1), D = p(!1), se = p(!1), ce = p(!1), le = p("minimized"), ue = p(null), de = p(null), fe = p(null), pe = p(0), me = p(!1), he = p("idle"), ge = p(h);
29976
- c(() => {
29977
- ge.current = h;
29978
- }, [h]);
29979
- let _e = p(!1), ve = p(null), ye = p(!1), be = p(!1), xe = p(null), Se = p(null), Ce = p(!1), we = o((e = "minimized") => {
29949
+ let [v, y] = m("idle"), [b, x] = m(!1), [S, C] = m(!1), w = p(null), T = p(""), ee = p(async () => {}), te = p(!1), ne = p(!1), re = p(!1), E = p(!1), ie = p("minimized"), ae = p(null), oe = p(null), D = p(null), se = p(0), ce = p(!1), le = p("idle"), ue = p(!1), de = p(!1), fe = p(null), pe = p(null), me = p(!1), he = o((e = "minimized") => {
29980
29950
  console.warn("[FlowEngage:VoiceDebug] 🛑 requestSessionShutdown called", {
29981
29951
  destination: e,
29982
29952
  stack: (/* @__PURE__ */ Error()).stack?.split("\n").slice(1, 4).join(" | ")
29983
- }), le.current = e, ce.current = !0, queueMicrotask(() => {
29984
- oe.current || (oe.current = !0, ae.current?.());
29953
+ }), ie.current = e, E.current = !0, queueMicrotask(() => {
29954
+ te.current || (te.current = !0, ee.current?.());
29985
29955
  });
29986
- }, [l]), Te = o(async (e) => {
29987
- ce.current = !0;
29956
+ }, [l]), ge = o(async (e) => {
29957
+ E.current = !0;
29988
29958
  try {
29989
29959
  Bn();
29990
29960
  } catch {}
@@ -29992,9 +29962,9 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
29992
29962
  Ve(i);
29993
29963
  } catch {}
29994
29964
  try {
29995
- await E.current?.endSession?.();
29965
+ await w.current?.endSession?.();
29996
29966
  } catch {}
29997
- let t = ue.current, n = de.current;
29967
+ let t = ae.current, n = oe.current;
29998
29968
  if (t && s && a) {
29999
29969
  let e = n ? Math.round((Date.now() - n) / 1e3) : 0;
30000
29970
  fetch(`${a}/api/voice/save-chat-recording`, {
@@ -30008,7 +29978,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30008
29978
  })
30009
29979
  }).catch((e) => console.warn("[FlowEngage] save-chat-recording failed:", e.message));
30010
29980
  }
30011
- if (ue.current = null, de.current = null, E.current = null, y("idle"), x(!1), T(!1), ve.current &&= (clearTimeout(ve.current), null), oe.current = !1, Ir(), i) try {
29981
+ if (ae.current = null, oe.current = null, w.current = null, y("idle"), x(!1), te.current = !1, Ir(), i) try {
30012
29982
  je(i);
30013
29983
  } catch {}
30014
29984
  typeof u == "function" && await u(l.current), e?.();
@@ -30019,10 +29989,10 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30019
29989
  s,
30020
29990
  a
30021
29991
  ]);
30022
- ae.current = () => Te(le.current === "chat" ? t : n), c(() => {
30023
- D.current = b;
29992
+ return ee.current = () => ge(ie.current === "chat" ? t : n), c(() => {
29993
+ ne.current = b;
30024
29994
  }, [b]), c(() => {
30025
- d && we("chat");
29995
+ d && he("chat");
30026
29996
  }, [d]), c(() => {
30027
29997
  let e = !1, o = null, c = null, d = null;
30028
29998
  return (async () => {
@@ -30032,13 +30002,13 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30032
30002
  }
30033
30003
  try {
30034
30004
  if ((await navigator.mediaDevices.getUserMedia({ audio: !0 })).getTracks().forEach((e) => e.stop()), e) return;
30035
- l.current = [], ie.current = "", oe.current = !1, se.current = !1, ce.current = !1, le.current = "minimized", Fr({ onBeforeNavigate: () => {
30005
+ l.current = [], T.current = "", te.current = !1, re.current = !1, E.current = !1, ie.current = "minimized", Fr({ onBeforeNavigate: () => {
30036
30006
  if (i && l.current.length > 0) try {
30037
30007
  let e = SO(l.current);
30038
30008
  e && O(i, e);
30039
30009
  } catch {}
30040
30010
  if (!or()) {
30041
- let e = ue.current, t = de.current;
30011
+ let e = ae.current, t = oe.current;
30042
30012
  if (e && s && a) try {
30043
30013
  let n = t ? Math.round((Date.now() - t) / 1e3) : 0, r = JSON.stringify({
30044
30014
  chatId: s,
@@ -30051,7 +30021,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30051
30021
  headers: { "Content-Type": "application/json" },
30052
30022
  body: r,
30053
30023
  keepalive: !0
30054
- }).catch(() => {}), ue.current = null, de.current = null;
30024
+ }).catch(() => {}), ae.current = null, oe.current = null;
30055
30025
  } catch {}
30056
30026
  }
30057
30027
  Ne();
@@ -30060,17 +30030,17 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30060
30030
  siteId: i || null,
30061
30031
  apiBaseUrl: a || null,
30062
30032
  chatId: s || null,
30063
- getLastUserVoiceText: () => ie.current,
30033
+ getLastUserVoiceText: () => T.current,
30064
30034
  requestEndSession: () => {
30065
- me.current = !0;
30035
+ ce.current = !0;
30066
30036
  }
30067
- })), n = i ? Ae(i) : null, u = !!n?.transcript, p = i ? Ve(i) : null, m = !!p, h = typeof window < "u" ? window.location.pathname : "/", g = typeof document < "u" ? document.title : "", _ = h.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i, "") || "/", v = _ === "/" || _ === "", b = null;
30037
+ })), n = i ? Ae(i) : null, u = !!n?.transcript, p = i ? Ve(i) : null, m = !!p, g = typeof window < "u" ? window.location.pathname : "/", _ = typeof document < "u" ? document.title : "", v = g.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i, "") || "/", b = v === "/" || v === "", S = null;
30068
30038
  try {
30069
30039
  let e = await fetch(`${a}/api/voice/signed-url?siteId=${encodeURIComponent(i || "")}`, {
30070
30040
  method: "GET",
30071
30041
  headers: { "Content-Type": "application/json" }
30072
30042
  });
30073
- if (e.ok) b = (await e.json()).signedUrl || null, b && console.log("[FlowEngage] ✅ Using signed ElevenLabs URL");
30043
+ if (e.ok) S = (await e.json()).signedUrl || null, S && console.log("[FlowEngage] ✅ Using signed ElevenLabs URL");
30074
30044
  else if (e.status === 429) {
30075
30045
  let t = await e.json().catch(() => ({}));
30076
30046
  throw Error(t.message || "Too many voice sessions started. Please wait a minute before trying again.");
@@ -30079,55 +30049,55 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30079
30049
  if (e.message && e.message.includes("Too many voice sessions")) throw e;
30080
30050
  console.warn("[FlowEngage] ⚠️ Signed URL fetch error:", e.message);
30081
30051
  }
30082
- let S = {
30052
+ let C = {
30083
30053
  clientTools: t,
30084
30054
  connectionType: "websocket",
30085
30055
  minSpeechDurationMs: 50,
30086
- ...b ? { signedUrl: b } : { agentId: r }
30087
- }, C = (h.split("/").filter(Boolean).pop() || "").replace(/[-_]/g, " ").replace(/\b\w/g, (e) => e.toUpperCase()), w = "";
30088
- g && g.includes("|") && (w = g.split("|").map((e) => e.trim()).filter(Boolean).reduce((e, t) => e.length <= t.length ? e : t));
30089
- let ee = w || C || "this page";
30090
- if (b) if (m) {
30091
- let e = p?.pageName || ee;
30092
- S.overrides = { agent: { firstMessage: `I have navigated you on ${e} page. Do you want to know about any specific section or want a complete overview?` } }, console.log(`[FlowEngage] 🔄 Agent-nav resume: firstMessage set for "${e}" (${h})`);
30093
- } else if (u) S.overrides = { agent: { firstMessage: "I'm still here. Go ahead!" } };
30056
+ ...S ? { signedUrl: S } : { agentId: r }
30057
+ }, ee = (g.split("/").filter(Boolean).pop() || "").replace(/[-_]/g, " ").replace(/\b\w/g, (e) => e.toUpperCase()), D = "";
30058
+ _ && _.includes("|") && (D = _.split("|").map((e) => e.trim()).filter(Boolean).reduce((e, t) => e.length <= t.length ? e : t));
30059
+ let ge = D || ee || "this page";
30060
+ if (S) if (m) {
30061
+ let e = p?.pageName || ge;
30062
+ C.overrides = { agent: { firstMessage: `I have navigated you on ${e} page. Do you want to know about any specific section or want a complete overview?` } }, console.log(`[FlowEngage] 🔄 Agent-nav resume: firstMessage set for "${e}" (${g})`);
30063
+ } else if (u) C.overrides = { agent: { firstMessage: "I'm still here. Go ahead!" } };
30094
30064
  else {
30095
30065
  let e = f || "";
30096
- S.overrides = { agent: { firstMessage: v ? e ? `Hi, welcome to ${e}!` : "Hi, welcome!" : `Welcome to the ${ee} page!` } }, v || console.log(`[FlowEngage] 🏠 Page-context override: "${ee}" for ${h}`);
30066
+ C.overrides = { agent: { firstMessage: h || (b ? e ? `Hi, welcome to ${e}!` : "Hi, welcome!" : `Welcome to the ${ge} page!`) } }, b || console.log(`[FlowEngage] 🏠 Page-context override: "${ge}" for ${g}`);
30097
30067
  }
30098
- else u && (S.overrides = { agent: { firstMessage: "" } });
30099
- let te = !1, ne = m ? `NAVIGATION COMPLETE — The exact phrase "I have navigated you on [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript ? `Prior transcript:\n${n.transcript}\n` : "(no prior transcript)"}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.` : null, re = !1, ae = !m && !u ? "Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead." : null, fe = await ED.startSession({
30100
- ...S,
30068
+ else u && (C.overrides = { agent: { firstMessage: "" } });
30069
+ let _e = !1, ve = m ? `NAVIGATION COMPLETE — The exact phrase "I have navigated you on [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript ? `Prior transcript:\n${n.transcript}\n` : "(no prior transcript)"}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.` : null, ye = !1, be = !m && !u ? "Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead." : null, xe = await ED.startSession({
30070
+ ...C,
30101
30071
  onConnect: ({ conversationId: e }) => {
30102
- ue.current = e, de.current = Date.now(), console.log("[FlowEngage] 🎙️ AI chat session connected:", e);
30072
+ ae.current = e, oe.current = Date.now(), console.log("[FlowEngage] 🎙️ AI chat session connected:", e);
30103
30073
  },
30104
30074
  onUnhandledClientToolCall: (e) => console.error("[FlowEngage] Unhandled tool:", e?.tool_name),
30105
30075
  onModeChange: ({ mode: t }) => {
30106
30076
  if (!e) {
30107
30077
  if (t === "speaking") {
30108
- Se.current &&= (clearTimeout(Se.current), null), y("speaking"), he.current = "speaking", ye.current = !0, Ce.current = !1, xe.current &&= (clearTimeout(xe.current), null);
30078
+ pe.current &&= (clearTimeout(pe.current), null), y("speaking"), le.current = "speaking", ue.current = !0, me.current = !1, fe.current &&= (clearTimeout(fe.current), null);
30109
30079
  return;
30110
30080
  }
30111
- if (he.current = "listening", me.current) {
30112
- me.current = !1, we();
30081
+ if (le.current = "listening", ce.current) {
30082
+ ce.current = !1, he();
30113
30083
  return;
30114
30084
  }
30115
- if (ne && !te) {
30116
- te = !0;
30085
+ if (ve && !_e) {
30086
+ _e = !0;
30117
30087
  try {
30118
- E.current?.sendContextualUpdate?.(ne);
30088
+ w.current?.sendContextualUpdate?.(ve);
30119
30089
  } catch {}
30120
30090
  }
30121
- if (ae && !re) {
30122
- re = !0;
30091
+ if (be && !ye) {
30092
+ ye = !0;
30123
30093
  try {
30124
- E.current?.sendContextualUpdate?.(ae);
30094
+ w.current?.sendContextualUpdate?.(be);
30125
30095
  } catch {}
30126
30096
  }
30127
- Se.current && clearTimeout(Se.current), Se.current = setTimeout(() => {
30128
- e || he.current !== "speaking" && (Ce.current || (Se.current = null, he.current = "listening", y("listening"), ye.current && be.current && !D.current && (xe.current && clearTimeout(xe.current), xe.current = setTimeout(() => {
30129
- if (!e && he.current === "listening" && !D.current) try {
30130
- E.current?.sendContextualUpdate?.("The visitor has been silent for a while. Ask one short, natural question to re-engage — for example: 'Is there anything specific you'd like to know more about?' or 'What else can I help you with today?' Keep it to one sentence.");
30097
+ pe.current && clearTimeout(pe.current), pe.current = setTimeout(() => {
30098
+ e || le.current !== "speaking" && (me.current || (pe.current = null, le.current = "listening", y("listening"), ue.current && de.current && !ne.current && (fe.current && clearTimeout(fe.current), fe.current = setTimeout(() => {
30099
+ if (!e && le.current === "listening" && !ne.current) try {
30100
+ w.current?.sendContextualUpdate?.("The visitor has been silent for a while. Ask one short, natural question to re-engage — for example: 'Is there anything specific you'd like to know more about?' or 'What else can I help you with today?' Keep it to one sentence.");
30131
30101
  } catch {}
30132
30102
  }, 15e3))));
30133
30103
  }, 1200);
@@ -30138,22 +30108,10 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30138
30108
  let n = t.role === "user" ? "user" : "bot", r = t.message || t.text || t.content || "";
30139
30109
  if (r) {
30140
30110
  if (n === "user") {
30141
- ie.current = r, Ce.current = !0, be.current = !0, Se.current &&= (clearTimeout(Se.current), null), xe.current &&= (clearTimeout(xe.current), null);
30111
+ T.current = r, me.current = !0, de.current = !0, pe.current &&= (clearTimeout(pe.current), null), fe.current &&= (clearTimeout(fe.current), null);
30142
30112
  try {
30143
30113
  Bn();
30144
30114
  } catch {}
30145
- if (!ge.current && !_e.current) {
30146
- _e.current = !0;
30147
- try {
30148
- E.current?.sendContextualUpdate?.("COLLECT_EMAIL: Before answering the visitor's question, you MUST first ask for their email address. Say: 'Before I answer, could I get your email address? You can also type it in the field below.' Do NOT answer their question yet — wait for the email first.");
30149
- } catch {}
30150
- T(!0), ve.current && clearTimeout(ve.current), ve.current = setTimeout(() => {
30151
- ve.current = null;
30152
- try {
30153
- E.current?.sendContextualUpdate?.("The visitor has not provided their email for 1 minute and appears to have left. Say exactly: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' Then immediately call the fwh-end-session-mvp tool to end the voice session.");
30154
- } catch {}
30155
- }, 6e4);
30156
- }
30157
30115
  }
30158
30116
  if (l.current = [...l.current, {
30159
30117
  sender: n,
@@ -30164,50 +30122,50 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30164
30122
  e && O(i, e);
30165
30123
  } catch {}
30166
30124
  if (fk(n, r)) {
30167
- me.current = !0;
30125
+ ce.current = !0;
30168
30126
  return;
30169
30127
  }
30170
- if (D.current && n === "bot" && /are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(r)) if (pe.current += 1, pe.current === 2) try {
30171
- E.current?.sendContextualUpdate?.("IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.");
30128
+ if (ne.current && n === "bot" && /are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(r)) if (se.current += 1, se.current === 2) try {
30129
+ w.current?.sendContextualUpdate?.("IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.");
30172
30130
  } catch {}
30173
- else pe.current >= 5 && we("minimized");
30131
+ else se.current >= 5 && he("minimized");
30174
30132
  }
30175
30133
  },
30176
30134
  onError: (t) => {
30177
- if (!e && !ce.current && !se.current && !Re()) {
30178
- se.current = !0, console.error("[FlowEngageVoiceCall] session:error", {
30135
+ if (!e && !E.current && !re.current && !Re()) {
30136
+ re.current = !0, console.error("[FlowEngageVoiceCall] session:error", {
30179
30137
  message: t?.message,
30180
30138
  name: t?.name,
30181
30139
  raw: t
30182
30140
  });
30183
30141
  try {
30184
- E.current?.setMicMuted?.(!0);
30142
+ w.current?.setMicMuted?.(!0);
30185
30143
  } catch {}
30186
- x(!0), y("error"), we("chat");
30144
+ x(!0), y("error"), he("chat");
30187
30145
  }
30188
30146
  },
30189
30147
  onDisconnect: (t) => {
30190
- if (e || ce.current || se.current || Re()) return;
30148
+ if (e || E.current || re.current || Re()) return;
30191
30149
  let n = l.current?.length || 0;
30192
30150
  console.warn("[FlowEngageVoiceCall] session:disconnected", {
30193
30151
  historyCount: n,
30194
30152
  reason: t?.reason,
30195
30153
  closeCode: t?.context?.code,
30196
30154
  closeReason: t?.context?.reason || t?.message,
30197
- lastUserText: ie.current || "",
30198
- wasMuted: D.current
30199
- }), se.current = !0;
30155
+ lastUserText: T.current || "",
30156
+ wasMuted: ne.current
30157
+ }), re.current = !0;
30200
30158
  try {
30201
- E.current?.setMicMuted?.(!0);
30159
+ w.current?.setMicMuted?.(!0);
30202
30160
  } catch {}
30203
- x(!0), we("chat");
30161
+ x(!0), he("chat");
30204
30162
  }
30205
30163
  });
30206
30164
  if (e) {
30207
- console.warn("[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately"), fe.endSession().catch(() => {});
30165
+ console.warn("[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately"), xe.endSession().catch(() => {});
30208
30166
  return;
30209
30167
  }
30210
- o = fe, E.current = fe, y("listening");
30168
+ o = xe, w.current = xe, y("listening");
30211
30169
  try {
30212
30170
  let e = typeof window < "u" ? window.location.href : "", r = "[]";
30213
30171
  try {
@@ -30219,7 +30177,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30219
30177
  n.length || (n = Array.from(document.body.querySelectorAll("h1, h2, h3")).filter((t) => !t.closest(e)).map((e) => e.textContent.replace(/\s+/g, " ").trim()).filter((e) => e.length > 2 && e.length < 200)), n.length && (a = [...new Set(n)].slice(0, 15).join(" | "));
30220
30178
  } catch {}
30221
30179
  let o = u && !m ? `RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript || ""}\n\n` : "";
30222
- fe.sendContextualUpdate(`${o}SESSION_CONTEXT (NOT a tool result — do NOT treat as CURRENT_PAGE): Visitor is on: ${e} | Approx headings for awareness only: ${a}. SiteId: ${i || "unknown"}. Forms: ${r}. The visitor is ALREADY on this page — do NOT offer to navigate there. WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. The approximate headings above are for routing awareness ONLY — they are NOT precise enough for fwh-highlight-mvp. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`);
30180
+ xe.sendContextualUpdate(`${o}SESSION_CONTEXT (NOT a tool result — do NOT treat as CURRENT_PAGE): Visitor is on: ${e} | Approx headings for awareness only: ${a}. SiteId: ${i || "unknown"}. Forms: ${r}. The visitor is ALREADY on this page — do NOT offer to navigate there. WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. The approximate headings above are for routing awareness ONLY — they are NOT precise enough for fwh-highlight-mvp. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`);
30223
30181
  } catch {}
30224
30182
  if (typeof window < "u") {
30225
30183
  let t = window.location.pathname, n = () => {
@@ -30248,7 +30206,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30248
30206
  ts: Date.now()
30249
30207
  });
30250
30208
  try {
30251
- E.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${l}" page (${i}). Page title: "${a}". Sections: ${JSON.stringify(o)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${l} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call flowengage_navigate, do NOT call flowengage_highlight, the visitor will navigate themselves if needed. IF I want to hear about this page: call flowengage_highlight for sections of "${l}" only.]`);
30209
+ w.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${l}" page (${i}). Page title: "${a}". Sections: ${JSON.stringify(o)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${l} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call flowengage_navigate, do NOT call flowengage_highlight, the visitor will navigate themselves if needed. IF I want to hear about this page: call flowengage_highlight for sections of "${l}" only.]`);
30252
30210
  } catch {}
30253
30211
  };
30254
30212
  c = window.history.pushState.bind(window.history), window.history.pushState = (...e) => {
@@ -30256,7 +30214,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30256
30214
  }, d = () => setTimeout(n, 50), window.addEventListener("popstate", d);
30257
30215
  }
30258
30216
  } catch (r) {
30259
- e || (console.error("[FlowEngage] startSession failed", r), r.message && r.message.includes("Too many voice sessions") ? (alert(r.message), n && n("minimized")) : (y("error"), le.current = "chat", window.setTimeout(() => {
30217
+ e || (console.error("[FlowEngage] startSession failed", r), r.message && r.message.includes("Too many voice sessions") ? (alert(r.message), n && n("minimized")) : (y("error"), ie.current = "chat", window.setTimeout(() => {
30260
30218
  e || t?.();
30261
30219
  }, 0)));
30262
30220
  }
@@ -30265,15 +30223,15 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30265
30223
  agentId: r,
30266
30224
  siteId: i,
30267
30225
  apiBaseUrl: a
30268
- }), e = !0, ce.current = !0;
30226
+ }), e = !0, E.current = !0;
30269
30227
  try {
30270
30228
  Bn();
30271
30229
  } catch {}
30272
30230
  if (i) try {
30273
30231
  Ve(i);
30274
30232
  } catch {}
30275
- typeof window < "u" && (window.__fe_user_nav_during_speech = null), Ir(), xe.current &&= (clearTimeout(xe.current), null), Se.current &&= (clearTimeout(Se.current), null), ye.current = !1, Ce.current = !1, he.current = "idle", ve.current &&= (clearTimeout(ve.current), null), c && typeof window < "u" && (window.history.pushState = c), d && typeof window < "u" && window.removeEventListener("popstate", d);
30276
- let t = o || E.current, n = ue.current, f = de.current;
30233
+ typeof window < "u" && (window.__fe_user_nav_during_speech = null), Ir(), fe.current &&= (clearTimeout(fe.current), null), pe.current &&= (clearTimeout(pe.current), null), ue.current = !1, me.current = !1, le.current = "idle", c && typeof window < "u" && (window.history.pushState = c), d && typeof window < "u" && window.removeEventListener("popstate", d);
30234
+ let t = o || w.current, n = ae.current, f = oe.current;
30277
30235
  if (t?.endSession?.().catch(() => {}), n && s && a) {
30278
30236
  let e = f ? Math.round((Date.now() - f) / 1e3) : 0, t = JSON.stringify({
30279
30237
  chatId: s,
@@ -30286,7 +30244,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30286
30244
  headers: { "Content-Type": "application/json" },
30287
30245
  body: t,
30288
30246
  keepalive: !0
30289
- }).catch(() => {}), ue.current = null, de.current = null;
30247
+ }).catch(() => {}), ae.current = null, oe.current = null;
30290
30248
  }
30291
30249
  typeof u == "function" && l.current.length > 0 && u(l.current).catch?.(() => {});
30292
30250
  };
@@ -30294,50 +30252,16 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30294
30252
  r,
30295
30253
  i,
30296
30254
  a,
30297
- we
30255
+ he
30298
30256
  ]), c(() => {
30299
30257
  if (!S) return;
30300
30258
  let e = (e) => {
30301
30259
  let t = e.composedPath?.()?.[0] || e.target;
30302
- fe.current && !fe.current.contains(t) && C(!1);
30260
+ D.current && !D.current.contains(t) && C(!1);
30303
30261
  };
30304
30262
  return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
30305
- }, [S]), c(() => {
30306
- let e = (e) => {
30307
- ve.current &&= (clearTimeout(ve.current), null), e?.detail?.email && (ge.current = e.detail.email), T(!1), te(""), re("");
30308
- };
30309
- return window.addEventListener("fe:voice-email-saved", e), () => window.removeEventListener("fe:voice-email-saved", e);
30310
- }, []);
30311
- let Ee = async () => {
30312
- let e = ee.trim();
30313
- if (!e || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)) {
30314
- re("Please enter a valid email address.");
30315
- return;
30316
- }
30317
- re("");
30318
- try {
30319
- if (!(await (await fetch(`${a}/api/voice/save-visitor-email`, {
30320
- method: "POST",
30321
- headers: { "Content-Type": "application/json" },
30322
- body: JSON.stringify({
30323
- chatId: s,
30324
- email: e,
30325
- siteId: i
30326
- })
30327
- })).json()).success) {
30328
- re("Could not save email. Please try again.");
30329
- return;
30330
- }
30331
- window.dispatchEvent(new CustomEvent("fe:voice-email-saved", { detail: { email: e } }));
30332
- try {
30333
- E.current?.sendContextualUpdate?.(`EMAIL_SAVED: ${e}. The visitor has typed their email in the widget. Now answer their original question.`);
30334
- } catch {}
30335
- } catch {
30336
- re("Could not save email. Please try again.");
30337
- }
30338
- };
30339
- return /* @__PURE__ */ _("div", {
30340
- ref: fe,
30263
+ }, [S]), /* @__PURE__ */ _("div", {
30264
+ ref: D,
30341
30265
  style: {
30342
30266
  position: "relative",
30343
30267
  display: "flex",
@@ -30398,82 +30322,6 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30398
30322
  },
30399
30323
  children: "Could not start voice session."
30400
30324
  }),
30401
- w && /* @__PURE__ */ _("div", {
30402
- style: {
30403
- background: "rgba(15,22,36,0.92)",
30404
- backdropFilter: "blur(12px)",
30405
- WebkitBackdropFilter: "blur(12px)",
30406
- border: "1px solid rgba(255,255,255,0.15)",
30407
- borderRadius: "12px",
30408
- padding: "10px 12px",
30409
- display: "flex",
30410
- flexDirection: "column",
30411
- gap: "6px",
30412
- width: "240px"
30413
- },
30414
- children: [
30415
- /* @__PURE__ */ g("span", {
30416
- style: {
30417
- color: "rgba(255,255,255,0.7)",
30418
- fontSize: "11px",
30419
- fontWeight: 500
30420
- },
30421
- children: "Type your email to continue"
30422
- }),
30423
- /* @__PURE__ */ _("div", {
30424
- style: {
30425
- display: "flex",
30426
- gap: "6px",
30427
- alignItems: "center"
30428
- },
30429
- children: [/* @__PURE__ */ g("input", {
30430
- type: "email",
30431
- value: ee,
30432
- onChange: (e) => {
30433
- te(e.target.value), re("");
30434
- },
30435
- onKeyDown: (e) => {
30436
- e.key === "Enter" && Ee();
30437
- },
30438
- placeholder: "you@example.com",
30439
- style: {
30440
- flex: 1,
30441
- background: "rgba(255,255,255,0.08)",
30442
- border: "1px solid rgba(255,255,255,0.2)",
30443
- borderRadius: "8px",
30444
- color: "white",
30445
- fontSize: "13px",
30446
- padding: "6px 10px",
30447
- outline: "none"
30448
- }
30449
- }), /* @__PURE__ */ g("button", {
30450
- onClick: Ee,
30451
- style: {
30452
- width: "30px",
30453
- height: "30px",
30454
- borderRadius: "8px",
30455
- background: "#2563eb",
30456
- border: "none",
30457
- color: "white",
30458
- fontSize: "16px",
30459
- cursor: "pointer",
30460
- display: "flex",
30461
- alignItems: "center",
30462
- justifyContent: "center",
30463
- flexShrink: 0
30464
- },
30465
- children: "→"
30466
- })]
30467
- }),
30468
- ne && /* @__PURE__ */ g("span", {
30469
- style: {
30470
- color: "#f87171",
30471
- fontSize: "11px"
30472
- },
30473
- children: ne
30474
- })
30475
- ]
30476
- }),
30477
30325
  /* @__PURE__ */ _("div", {
30478
30326
  style: {
30479
30327
  display: "flex",
@@ -30482,16 +30330,16 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30482
30330
  },
30483
30331
  children: [/* @__PURE__ */ g("button", {
30484
30332
  onClick: async () => {
30485
- if (E.current) try {
30333
+ if (w.current) try {
30486
30334
  if (b) {
30487
- pe.current = 0, await E.current.setMicMuted?.(!1), x(!1);
30335
+ se.current = 0, await w.current.setMicMuted?.(!1), x(!1);
30488
30336
  try {
30489
- E.current.sendContextualUpdate?.("The visitor has unmuted. Resume normal interaction and wait for them to speak.");
30337
+ w.current.sendContextualUpdate?.("The visitor has unmuted. Resume normal interaction and wait for them to speak.");
30490
30338
  } catch {}
30491
30339
  } else {
30492
- pe.current = 0, await E.current.setMicMuted?.(!0), x(!0);
30340
+ se.current = 0, await w.current.setMicMuted?.(!0), x(!0);
30493
30341
  try {
30494
- E.current.sendContextualUpdate?.("The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.");
30342
+ w.current.sendContextualUpdate?.("The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.");
30495
30343
  } catch {}
30496
30344
  }
30497
30345
  } catch {}
@@ -30590,7 +30438,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30590
30438
  children: [
30591
30439
  /* @__PURE__ */ _("button", {
30592
30440
  onClick: () => {
30593
- C(!1), le.current = "chat", Te(t);
30441
+ C(!1), ie.current = "chat", ge(t);
30594
30442
  },
30595
30443
  style: {
30596
30444
  display: "flex",
@@ -30619,7 +30467,7 @@ function gk({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
30619
30467
  }),
30620
30468
  /* @__PURE__ */ _("button", {
30621
30469
  onClick: () => {
30622
- C(!1), le.current = "minimized", Te(n);
30470
+ C(!1), ie.current = "minimized", ge(n);
30623
30471
  },
30624
30472
  style: {
30625
30473
  display: "flex",
@@ -31547,13 +31395,7 @@ function bk() {
31547
31395
  } catch {}
31548
31396
  }, []), c(() => {
31549
31397
  Ee && M !== "voice_mode" && M !== "text_chat" && M !== "call_screen" && (ue?.(!1), dt("text_chat"));
31550
- }, [Ee]), c(() => {
31551
- let e = (e) => {
31552
- let t = e?.detail?.email;
31553
- t && typeof le == "function" && le(t);
31554
- };
31555
- return window.addEventListener("fe:voice-email-saved", e), () => window.removeEventListener("fe:voice-email-saved", e);
31556
- }, [le]);
31398
+ }, [Ee]);
31557
31399
  let Oe = t?.widgetSettings || {}, ke = n?.widgetSettings || {}, O = {
31558
31400
  ...Oe.branding || {},
31559
31401
  ...ke.branding || {}
@@ -31967,7 +31809,7 @@ function bk() {
31967
31809
  onMessageSync: oe,
31968
31810
  voiceEndCounter: De,
31969
31811
  websiteName: t?.websiteName,
31970
- visitorEmail: h
31812
+ voiceFirstMessage: Oe.voiceFirstMessage || null
31971
31813
  }, "voice"),
31972
31814
  M === "call_screen" && /* @__PURE__ */ g(bO, {
31973
31815
  incomingCall: w,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowengage/react-chatbot",
3
- "version": "8.0.13",
3
+ "version": "8.0.15",
4
4
  "description": "Embeddable AI chat widget for React — powered by FlowEngage. Drop it in with a single siteId.",
5
5
  "keywords": [
6
6
  "chatbot",