@mrrocket/sdk 0.22.0 → 0.23.0

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/messenger.js CHANGED
@@ -19,6 +19,7 @@ var Messenger = ({ position, title, greeting, askEmail, label, launcher, help, u
19
19
  const who = useRef({ distinctId: ctx.distinctId, user: ctx.user, userHash: ctx.userHash });
20
20
  who.current = { distinctId: ctx.distinctId, user: ctx.user, userHash: ctx.userHash };
21
21
  const { apiKey, host, embedded } = ctx;
22
+ useEffect(() => handle?.refresh(), [ctx.distinctId, ctx.userHash]);
22
23
  useEffect(() => {
23
24
  if (embedded || !apiKey) return;
24
25
  let gone = false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/messenger.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport type { MessengerCorners, MessengerPosition } from \"./api-client\";\nimport { useCtx } from \"./context\";\nimport type { MessengerHandle, MessengerView } from \"./messenger-ui\";\nimport type { Theme } from \"./ui\";\n\n/**\n * The messenger, after Featurebase's: a launcher and a panel with Home, Messages (a chat with the product's team) and\n * Help (the product's published help articles). A visitor's messages become a Support conversation (channel \"chat\")\n * in MRRocket's inbox, where the owner answers or the Customer experience agent does (Support › Automation › AI\n * answers). A new answer plays a short sound and shows a preview above the closed launcher.\n *\n * The panel is plain DOM (messenger-ui.ts, shared with the script tag), loaded with a dynamic import after the page\n * rendered. The visitor is the provider's id; a person the site identified carries their name and email.\n */\n\nlet handle: MessengerHandle | null = null;\n/** A call made before the panel loaded runs once it has. */\nlet waiting: ((h: MessengerHandle) => void) | null = null;\nconst withHandle = (fn: (h: MessengerHandle) => void) => {\n if (handle) fn(handle);\n else waiting = fn;\n};\n\n/** Open (on Home, a tab or an article), close or toggle the messenger from your own button (or mark any element `data-ck-messenger`). */\nexport const useMessenger = () => ({\n open: (view?: MessengerView | { article: string }) => withHandle((h) => h.open(view)),\n close: () => handle?.close(),\n toggle: () => withHandle((h) => h.toggle()),\n});\n\nexport interface MessengerProps {\n /** A corner, the middle of the top or bottom edge, or the middle of a side (default \"bottom-right\"). */\n position?: MessengerPosition;\n /** The team's name beside its mark on Home (default: the product's name). */\n title?: string;\n /** The big line on Home and the first line of an empty chat. */\n greeting?: string;\n /** Ask anonymous visitors for an email (optional for them). Default true. */\n askEmail?: boolean;\n /** Text on the launcher; empty shows the icon only. */\n label?: string;\n /** Show the round launcher (default true). False: open it with `useMessenger()` or `data-ck-messenger`. */\n launcher?: boolean;\n /** The Help tab with your published help articles (default true; hidden while there are none). */\n help?: boolean;\n /** The Updates tab with your published changelog (default true; hidden while there are none). */\n updates?: boolean;\n /** A short sound when an answer arrives (default true). */\n sound?: boolean;\n /** A preview of a new answer above the closed launcher (default true). */\n preview?: boolean;\n theme?: Theme;\n accent?: string;\n /** \"round\" (default), \"soft\" or \"square\". */\n corners?: MessengerCorners;\n /** Your site's font (default) or \"system\". */\n font?: \"site\" | \"system\";\n /** Home's top: \"plain\" (default) or \"color\" (the accent). */\n header?: \"plain\" | \"color\";\n}\n\n/**\n * A floating launcher that opens the messenger. Put it once inside `<CockpitProvider>`:\n *\n * <Messenger title=\"Acme\" greeting=\"Hi! How can we help?\" />\n */\nexport const Messenger = ({ position, title, greeting, askEmail, label, launcher, help, updates, sound, preview, theme, accent, corners, font, header }: MessengerProps) => {\n const ctx = useCtx();\n // The panel reads the visitor at each request, so a later identify or reset needs no remount.\n const who = useRef({ distinctId: ctx.distinctId, user: ctx.user, userHash: ctx.userHash });\n who.current = { distinctId: ctx.distinctId, user: ctx.user, userHash: ctx.userHash };\n const { apiKey, host, embedded } = ctx;\n\n useEffect(() => {\n if (embedded || !apiKey) return;\n let gone = false;\n let mine: MessengerHandle | null = null;\n void import(\"./messenger-ui\").then(({ mountMessenger }) => {\n if (gone) return;\n mine = mountMessenger({\n host,\n apiKey,\n distinctId: () => who.current.distinctId,\n user: () => who.current.user as { id?: string; name?: string; email?: string } | undefined,\n userHash: () => who.current.userHash,\n launcher,\n position,\n title,\n greeting,\n askEmail,\n label,\n help,\n updates,\n sound,\n preview,\n theme,\n accent,\n corners,\n font,\n header,\n });\n handle = mine;\n waiting?.(mine);\n waiting = null;\n });\n const click = (e: MouseEvent) => {\n if ((e.target as HTMLElement | null)?.closest(\"[data-ck-messenger]\")) {\n e.preventDefault();\n withHandle((h) => h.open());\n }\n };\n document.addEventListener(\"click\", click);\n return () => {\n gone = true;\n document.removeEventListener(\"click\", click);\n mine?.destroy();\n if (handle === mine) handle = null;\n };\n }, [apiKey, host, embedded, launcher, position, title, greeting, askEmail, label, help, updates, sound, preview, theme, accent, corners, font, header]);\n\n return null;\n};\n"],"mappings":";;;AAEA,SAAS,WAAW,cAAc;AAElC,SAAS,cAAc;AAcvB,IAAI,SAAiC;AAErC,IAAI,UAAiD;AACrD,IAAM,aAAa,CAAC,OAAqC;AACrD,MAAI,OAAQ,IAAG,MAAM;AAAA,MAChB,WAAU;AACnB;AAGO,IAAM,eAAe,OAAO;AAAA,EAC/B,MAAM,CAAC,SAA+C,WAAW,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,EACpF,OAAO,MAAM,QAAQ,MAAM;AAAA,EAC3B,QAAQ,MAAM,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;AAC9C;AAsCO,IAAM,YAAY,CAAC,EAAE,UAAU,OAAO,UAAU,UAAU,OAAO,UAAU,MAAM,SAAS,OAAO,SAAS,OAAO,QAAQ,SAAS,MAAM,OAAO,MAAsB;AACxK,QAAM,MAAM,OAAO;AAEnB,QAAM,MAAM,OAAO,EAAE,YAAY,IAAI,YAAY,MAAM,IAAI,MAAM,UAAU,IAAI,SAAS,CAAC;AACzF,MAAI,UAAU,EAAE,YAAY,IAAI,YAAY,MAAM,IAAI,MAAM,UAAU,IAAI,SAAS;AACnF,QAAM,EAAE,QAAQ,MAAM,SAAS,IAAI;AAEnC,YAAU,MAAM;AACZ,QAAI,YAAY,CAAC,OAAQ;AACzB,QAAI,OAAO;AACX,QAAI,OAA+B;AACnC,SAAK,OAAO,mBAAgB,EAAE,KAAK,CAAC,EAAE,eAAe,MAAM;AACvD,UAAI,KAAM;AACV,aAAO,eAAe;AAAA,QAClB;AAAA,QACA;AAAA,QACA,YAAY,MAAM,IAAI,QAAQ;AAAA,QAC9B,MAAM,MAAM,IAAI,QAAQ;AAAA,QACxB,UAAU,MAAM,IAAI,QAAQ;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ,CAAC;AACD,eAAS;AACT,gBAAU,IAAI;AACd,gBAAU;AAAA,IACd,CAAC;AACD,UAAM,QAAQ,CAAC,MAAkB;AAC7B,UAAK,EAAE,QAA+B,QAAQ,qBAAqB,GAAG;AAClE,UAAE,eAAe;AACjB,mBAAW,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,MAC9B;AAAA,IACJ;AACA,aAAS,iBAAiB,SAAS,KAAK;AACxC,WAAO,MAAM;AACT,aAAO;AACP,eAAS,oBAAoB,SAAS,KAAK;AAC3C,YAAM,QAAQ;AACd,UAAI,WAAW,KAAM,UAAS;AAAA,IAClC;AAAA,EACJ,GAAG,CAAC,QAAQ,MAAM,UAAU,UAAU,UAAU,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS,OAAO,SAAS,OAAO,QAAQ,SAAS,MAAM,MAAM,CAAC;AAEtJ,SAAO;AACX;","names":[]}
1
+ {"version":3,"sources":["../src/messenger.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport type { MessengerCorners, MessengerPosition } from \"./api-client\";\nimport { useCtx } from \"./context\";\nimport type { MessengerHandle, MessengerView } from \"./messenger-ui\";\nimport type { Theme } from \"./ui\";\n\n/**\n * The messenger, after Featurebase's: a launcher and a panel with Home, Messages (a chat with the product's team) and\n * Help (the product's published help articles). A visitor's messages become a Support conversation (channel \"chat\")\n * in MRRocket's inbox, where the owner answers or the Customer experience agent does (Support › Automation › AI\n * answers). A new answer plays a short sound and shows a preview above the closed launcher.\n *\n * The panel is plain DOM (messenger-ui.ts, shared with the script tag), loaded with a dynamic import after the page\n * rendered. The visitor is the provider's id; a person the site identified carries their name and email.\n */\n\nlet handle: MessengerHandle | null = null;\n/** A call made before the panel loaded runs once it has. */\nlet waiting: ((h: MessengerHandle) => void) | null = null;\nconst withHandle = (fn: (h: MessengerHandle) => void) => {\n if (handle) fn(handle);\n else waiting = fn;\n};\n\n/** Open (on Home, a tab or an article), close or toggle the messenger from your own button (or mark any element `data-ck-messenger`). */\nexport const useMessenger = () => ({\n open: (view?: MessengerView | { article: string }) => withHandle((h) => h.open(view)),\n close: () => handle?.close(),\n toggle: () => withHandle((h) => h.toggle()),\n});\n\nexport interface MessengerProps {\n /** A corner, the middle of the top or bottom edge, or the middle of a side (default \"bottom-right\"). */\n position?: MessengerPosition;\n /** The team's name beside its mark on Home (default: the product's name). */\n title?: string;\n /** The big line on Home and the first line of an empty chat. */\n greeting?: string;\n /** Ask anonymous visitors for an email (optional for them). Default true. */\n askEmail?: boolean;\n /** Text on the launcher; empty shows the icon only. */\n label?: string;\n /** Show the round launcher (default true). False: open it with `useMessenger()` or `data-ck-messenger`. */\n launcher?: boolean;\n /** The Help tab with your published help articles (default true; hidden while there are none). */\n help?: boolean;\n /** The Updates tab with your published changelog (default true; hidden while there are none). */\n updates?: boolean;\n /** A short sound when an answer arrives (default true). */\n sound?: boolean;\n /** A preview of a new answer above the closed launcher (default true). */\n preview?: boolean;\n theme?: Theme;\n accent?: string;\n /** \"round\" (default), \"soft\" or \"square\". */\n corners?: MessengerCorners;\n /** Your site's font (default) or \"system\". */\n font?: \"site\" | \"system\";\n /** Home's top: \"plain\" (default) or \"color\" (the accent). */\n header?: \"plain\" | \"color\";\n}\n\n/**\n * A floating launcher that opens the messenger. Put it once inside `<CockpitProvider>`:\n *\n * <Messenger title=\"Acme\" greeting=\"Hi! How can we help?\" />\n */\nexport const Messenger = ({ position, title, greeting, askEmail, label, launcher, help, updates, sound, preview, theme, accent, corners, font, header }: MessengerProps) => {\n const ctx = useCtx();\n // The panel reads the visitor at each request, so a later identify or reset needs no remount.\n const who = useRef({ distinctId: ctx.distinctId, user: ctx.user, userHash: ctx.userHash });\n who.current = { distinctId: ctx.distinctId, user: ctx.user, userHash: ctx.userHash };\n const { apiKey, host, embedded } = ctx;\n // Whoever signs in after the page loaded gets their conversations now, not at the panel's next slow read.\n useEffect(() => handle?.refresh(), [ctx.distinctId, ctx.userHash]);\n\n useEffect(() => {\n if (embedded || !apiKey) return;\n let gone = false;\n let mine: MessengerHandle | null = null;\n void import(\"./messenger-ui\").then(({ mountMessenger }) => {\n if (gone) return;\n mine = mountMessenger({\n host,\n apiKey,\n distinctId: () => who.current.distinctId,\n user: () => who.current.user as { id?: string; name?: string; email?: string } | undefined,\n userHash: () => who.current.userHash,\n launcher,\n position,\n title,\n greeting,\n askEmail,\n label,\n help,\n updates,\n sound,\n preview,\n theme,\n accent,\n corners,\n font,\n header,\n });\n handle = mine;\n waiting?.(mine);\n waiting = null;\n });\n const click = (e: MouseEvent) => {\n if ((e.target as HTMLElement | null)?.closest(\"[data-ck-messenger]\")) {\n e.preventDefault();\n withHandle((h) => h.open());\n }\n };\n document.addEventListener(\"click\", click);\n return () => {\n gone = true;\n document.removeEventListener(\"click\", click);\n mine?.destroy();\n if (handle === mine) handle = null;\n };\n }, [apiKey, host, embedded, launcher, position, title, greeting, askEmail, label, help, updates, sound, preview, theme, accent, corners, font, header]);\n\n return null;\n};\n"],"mappings":";;;AAEA,SAAS,WAAW,cAAc;AAElC,SAAS,cAAc;AAcvB,IAAI,SAAiC;AAErC,IAAI,UAAiD;AACrD,IAAM,aAAa,CAAC,OAAqC;AACrD,MAAI,OAAQ,IAAG,MAAM;AAAA,MAChB,WAAU;AACnB;AAGO,IAAM,eAAe,OAAO;AAAA,EAC/B,MAAM,CAAC,SAA+C,WAAW,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,EACpF,OAAO,MAAM,QAAQ,MAAM;AAAA,EAC3B,QAAQ,MAAM,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;AAC9C;AAsCO,IAAM,YAAY,CAAC,EAAE,UAAU,OAAO,UAAU,UAAU,OAAO,UAAU,MAAM,SAAS,OAAO,SAAS,OAAO,QAAQ,SAAS,MAAM,OAAO,MAAsB;AACxK,QAAM,MAAM,OAAO;AAEnB,QAAM,MAAM,OAAO,EAAE,YAAY,IAAI,YAAY,MAAM,IAAI,MAAM,UAAU,IAAI,SAAS,CAAC;AACzF,MAAI,UAAU,EAAE,YAAY,IAAI,YAAY,MAAM,IAAI,MAAM,UAAU,IAAI,SAAS;AACnF,QAAM,EAAE,QAAQ,MAAM,SAAS,IAAI;AAEnC,YAAU,MAAM,QAAQ,QAAQ,GAAG,CAAC,IAAI,YAAY,IAAI,QAAQ,CAAC;AAEjE,YAAU,MAAM;AACZ,QAAI,YAAY,CAAC,OAAQ;AACzB,QAAI,OAAO;AACX,QAAI,OAA+B;AACnC,SAAK,OAAO,mBAAgB,EAAE,KAAK,CAAC,EAAE,eAAe,MAAM;AACvD,UAAI,KAAM;AACV,aAAO,eAAe;AAAA,QAClB;AAAA,QACA;AAAA,QACA,YAAY,MAAM,IAAI,QAAQ;AAAA,QAC9B,MAAM,MAAM,IAAI,QAAQ;AAAA,QACxB,UAAU,MAAM,IAAI,QAAQ;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ,CAAC;AACD,eAAS;AACT,gBAAU,IAAI;AACd,gBAAU;AAAA,IACd,CAAC;AACD,UAAM,QAAQ,CAAC,MAAkB;AAC7B,UAAK,EAAE,QAA+B,QAAQ,qBAAqB,GAAG;AAClE,UAAE,eAAe;AACjB,mBAAW,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,MAC9B;AAAA,IACJ;AACA,aAAS,iBAAiB,SAAS,KAAK;AACxC,WAAO,MAAM;AACT,aAAO;AACP,eAAS,oBAAoB,SAAS,KAAK;AAC3C,YAAM,QAAQ;AACd,UAAI,WAAW,KAAM,UAAS;AAAA,IAClC;AAAA,EACJ,GAAG,CAAC,QAAQ,MAAM,UAAU,UAAU,UAAU,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS,OAAO,SAAS,OAAO,QAAQ,SAAS,MAAM,MAAM,CAAC;AAEtJ,SAAO;AACX;","names":[]}
@@ -261,7 +261,10 @@ declare const createApi: (c: Conn) => {
261
261
  completed: boolean;
262
262
  seconds: number;
263
263
  }) => Promise<unknown>;
264
- /** One chat by id, else the visitor's newest; `conversations` lists them all. */
264
+ /**
265
+ * One chat by id, else the visitor's newest; `conversations` lists them all. `aiLabel`: the messenger says "AI agent"
266
+ * beside an answer's name, so the answer comes without its AI notice line (from 0.22.0).
267
+ */
265
268
  chat: (signal?: AbortSignal, conversation?: string) => Promise<ChatThread>;
266
269
  /**
267
270
  * A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). `conversation` is
@@ -826,7 +829,7 @@ interface PaywallHost {
826
829
 
827
830
  type VariantId = string;
828
831
  /** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
829
- declare const SDK_VERSION = "0.22.0";
832
+ declare const SDK_VERSION = "0.23.0";
830
833
  /** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
831
834
  interface VariantConfig {
832
835
  id: VariantId;
@@ -261,7 +261,10 @@ declare const createApi: (c: Conn) => {
261
261
  completed: boolean;
262
262
  seconds: number;
263
263
  }) => Promise<unknown>;
264
- /** One chat by id, else the visitor's newest; `conversations` lists them all. */
264
+ /**
265
+ * One chat by id, else the visitor's newest; `conversations` lists them all. `aiLabel`: the messenger says "AI agent"
266
+ * beside an answer's name, so the answer comes without its AI notice line (from 0.22.0).
267
+ */
265
268
  chat: (signal?: AbortSignal, conversation?: string) => Promise<ChatThread>;
266
269
  /**
267
270
  * A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). `conversation` is
@@ -826,7 +829,7 @@ interface PaywallHost {
826
829
 
827
830
  type VariantId = string;
828
831
  /** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
829
- declare const SDK_VERSION = "0.22.0";
832
+ declare const SDK_VERSION = "0.23.0";
830
833
  /** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
831
834
  interface VariantConfig {
832
835
  id: VariantId;
@@ -114,7 +114,7 @@ var onLocale = (fn) => {
114
114
  };
115
115
 
116
116
  // src/core.ts
117
- var SDK_VERSION = "0.22.0";
117
+ var SDK_VERSION = "0.23.0";
118
118
  var propOf = (person, name) => {
119
119
  const want = name.trim().toLowerCase();
120
120
  for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
@@ -156,6 +156,7 @@ var flagFor = (key, config, distinctId, person = {}) => {
156
156
  const flag = config?.flags?.find((f) => f.key === key);
157
157
  return flag ? evaluateFlag(flag, distinctId, person) : false;
158
158
  };
159
+ var CONSENT_OFF_EVENT = "$consent_off";
159
160
  var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
160
161
  var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
161
162
  var utmFrom = (get) => {
@@ -1195,6 +1196,11 @@ var inkOn = (hex) => {
1195
1196
  });
1196
1197
  return 0.2126 * r + 0.7152 * g2 + 0.0722 * b > 0.45 ? "#111214" : "#ffffff";
1197
1198
  };
1199
+ var stillThere = (t2, chats) => (m) => {
1200
+ if (m.chat === t2.conversation?.id) return t2.messages.some((x) => x.id === m.id);
1201
+ const last = chats.find((c) => c.id === m.chat)?.last;
1202
+ return !!last && m.at <= last.at;
1203
+ };
1198
1204
  var ApiError = class extends Error {
1199
1205
  constructor(message, status) {
1200
1206
  super(message);
@@ -1248,13 +1254,16 @@ var createApi = (c) => {
1248
1254
  survey: (url, signal) => call(`${base}/surveys?${qs({ ...me, url })}`, void 0, signal),
1249
1255
  seen: (id) => post(`/surveys/${encodeURIComponent(id)}/seen`, { user: void 0 }),
1250
1256
  respond: (id, r) => post(`/surveys/${encodeURIComponent(id)}/respond`, r),
1251
- /** One chat by id, else the visitor's newest; `conversations` lists them all. */
1252
- chat: (signal, conversation) => call(`${base}/messages?${qs({ ...me, conversation })}`, void 0, signal),
1257
+ /**
1258
+ * One chat by id, else the visitor's newest; `conversations` lists them all. `aiLabel`: the messenger says "AI agent"
1259
+ * beside an answer's name, so the answer comes without its AI notice line (from 0.22.0).
1260
+ */
1261
+ chat: (signal, conversation) => call(`${base}/messages?${qs({ ...me, conversation, aiLabel: "1" })}`, void 0, signal),
1253
1262
  /**
1254
1263
  * A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). `conversation` is
1255
1264
  * the chat it answers, or "new" to start one; without it, the running chat or else a new one.
1256
1265
  */
1257
- sendChat: (m) => post("/messages", m),
1266
+ sendChat: (m) => post("/messages", { ...m, aiLabel: true }),
1258
1267
  /** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
1259
1268
  uploadChatFile: (file, signal) => {
1260
1269
  const form = new FormData();
@@ -1859,7 +1868,7 @@ var createClient = (options, deps) => {
1859
1868
  }
1860
1869
  };
1861
1870
  const sendQueue = async () => {
1862
- if (!queue.length || consent === "off" || !apiKey) return;
1871
+ if (!queue.length || !apiKey || consent === "off" && queue.some((e) => e.event !== CONSENT_OFF_EVENT)) return;
1863
1872
  if (now() < backoffUntil) return;
1864
1873
  const snapshot = queue.slice();
1865
1874
  const requests = batchRequests(apiKey, snapshot, sdk, hashOf(sendId()));
@@ -2283,7 +2292,8 @@ var createClient = (options, deps) => {
2283
2292
  consent = next;
2284
2293
  if (next === "off") {
2285
2294
  recorder?.drop();
2286
- queue = [];
2295
+ queue = identified && before !== "off" ? [{ event: CONSENT_OFF_EVENT, distinctId: identified, ts: Date.now() }] : [];
2296
+ if (queue.length) void flush();
2287
2297
  session = null;
2288
2298
  errorQueue.length = 0;
2289
2299
  void Promise.all(Object.values(KEYS).map(remove));
@@ -3576,7 +3586,7 @@ var POLL_QUIET_MS = 6e4;
3576
3586
  var WAITING_FOR_MS = 10 * 6e4;
3577
3587
  var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
3578
3588
  var whoLabel = (from) => from === "visitor" ? t("You") : from === "ai" ? t("AI agent") : t("Team");
3579
- var who = (from, agentName) => from === "ai" && agentName?.trim() || whoLabel(from);
3589
+ var who = (from, agentName) => from === "ai" && agentName?.trim() ? `${agentName.trim()} \xB7 ${t("AI agent")}` : whoLabel(from);
3580
3590
  var msgView = "home";
3581
3591
  var msgChat = null;
3582
3592
  var messengerOpen = opener();
@@ -3694,7 +3704,7 @@ var useChat = (client, o, open, view) => {
3694
3704
  if (fresh.length && o.haptics !== false) buzz();
3695
3705
  const shown2 = shownChat(at.current, all);
3696
3706
  const counted = fresh.filter((m) => !watch.current || m.chat !== shown2);
3697
- setUnseen((u) => [...u.filter((m) => all.some((c) => c.id === m.chat)), ...counted]);
3707
+ setUnseen((u) => [...u.filter(stillThere(t2, all)), ...counted]);
3698
3708
  list.current = all;
3699
3709
  setChats(all);
3700
3710
  setHasEmail(t2.hasEmail);