@helpai/elements 0.61.3 → 0.61.5

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/index.mjs CHANGED
@@ -29,7 +29,7 @@ var BRAND = {
29
29
  };
30
30
 
31
31
  // src/core/version.ts
32
- var ELEMENTS_VERSION = true ? "0.61.3" : "0.0.0-dev";
32
+ var ELEMENTS_VERSION = true ? "0.61.5" : "0.0.0-dev";
33
33
  var ELEMENTS_VERSION_PARAM = "_ev";
34
34
 
35
35
  // src/i18n/strings.ts
@@ -897,7 +897,7 @@ function resolveInitial() {
897
897
  }
898
898
  } catch {
899
899
  }
900
- if (window[GLOBAL_FLAG_KEY]) return 4;
900
+ if (Reflect.get(window, GLOBAL_FLAG_KEY)) return 4;
901
901
  return 2;
902
902
  }
903
903
  function parseToIndex(input) {
@@ -2254,15 +2254,6 @@ function retryAfterMs(headers) {
2254
2254
  const ms = Number.isFinite(secs) ? secs * 1e3 : Date.parse(raw) - Date.now();
2255
2255
  return Number.isFinite(ms) && ms >= 0 ? Math.min(ms, 3e4) : null;
2256
2256
  }
2257
- function followupsFromMessages(messages) {
2258
- for (let i = messages.length - 1; i >= 0; i--) {
2259
- const m = messages[i];
2260
- if (!m || m.role !== "assistant") continue;
2261
- const part = m.parts.find((p36) => p36.type === "data-suggestions");
2262
- return part?.type === "data-suggestions" ? parseSuggestions(part.data) : void 0;
2263
- }
2264
- return void 0;
2265
- }
2266
2257
  function sleep(ms, signal7) {
2267
2258
  return new Promise((resolve) => {
2268
2259
  if (signal7?.aborted) return resolve();
@@ -2509,10 +2500,10 @@ var AgentTransport = class {
2509
2500
  canContinue: res.canContinue ?? true,
2510
2501
  messages,
2511
2502
  agent: res.agent,
2512
- // The latest turn's model follow-ups are persisted ON the assistant message (a `data-suggestions`
2513
- // part), not in a top-level field — read them off there, falling back to any server-provided
2514
- // top-level `suggestions` (e.g. resume chips) when the last turn carried none.
2515
- suggestions: followupsFromMessages(messages) ?? res.suggestions
2503
+ // Top-level STARTER chips (resume / new-chat), not turn-tied. A turn's own model follow-ups ride
2504
+ // ON its assistant message (`data-suggestions` part), lifted onto `message.suggestions` by
2505
+ // `fromWireMessage`; the App prefers the last turn's and falls back to these starters.
2506
+ suggestions: res.suggestions
2516
2507
  };
2517
2508
  }
2518
2509
  /**
@@ -3014,9 +3005,11 @@ function fromWireMessage(w) {
3014
3005
  }
3015
3006
  return null;
3016
3007
  }).filter((p36) => p36 !== null);
3008
+ const suggestionsPart = w.parts.find((p36) => p36.type === "data-suggestions");
3017
3009
  return {
3018
3010
  id: w.id,
3019
3011
  role: w.role,
3012
+ suggestions: suggestionsPart?.type === "data-suggestions" ? parseSuggestions(suggestionsPart.data) : void 0,
3020
3013
  // On reload the wire `id` IS the backend's id — keep it as `serverMessageId`
3021
3014
  // too so a HITL re-POST after a reload still references the backend's message.
3022
3015
  serverMessageId: w.id,
@@ -3239,6 +3232,7 @@ var StreamReducer = class {
3239
3232
  case "data-conversation-rebind":
3240
3233
  return;
3241
3234
  case "data-suggestions":
3235
+ m.suggestions = parseSuggestions(chunk.data);
3242
3236
  return;
3243
3237
  default: {
3244
3238
  const _exhaustive = chunk;
@@ -4415,8 +4409,7 @@ function createLocalVoice(locale) {
4415
4409
  };
4416
4410
  }
4417
4411
  function getSpeechRecognitionCtor() {
4418
- const g = globalThis;
4419
- return g.SpeechRecognition ?? g.webkitSpeechRecognition;
4412
+ return window.SpeechRecognition ?? window.webkitSpeechRecognition;
4420
4413
  }
4421
4414
  var CANDIDATE_MIME_TYPES = ["audio/webm;codecs=opus", "audio/webm", "audio/ogg;codecs=opus", "audio/mp4"];
4422
4415
  function createServerVoice(transcribe) {
@@ -8306,7 +8299,8 @@ function App({ options, hostElement, bus }) {
8306
8299
  if (loaded.length || tail.length) {
8307
8300
  messagesSig.value = [...withWelcomeRows(loaded, markers), ...tail];
8308
8301
  setCanSend(chat.canContinue ?? true);
8309
- setSuggestions(chat.suggestions ?? []);
8302
+ const lastTurn = loaded.findLast((m) => m.role === "assistant" && !m.ephemeral);
8303
+ setSuggestions(lastTurn?.suggestions ?? chat.suggestions ?? []);
8310
8304
  } else {
8311
8305
  messagesSig.value = withWelcomeRows([], markers);
8312
8306
  }
@@ -8440,11 +8434,10 @@ function App({ options, hostElement, bus }) {
8440
8434
  useEffect16(() => {
8441
8435
  if (effectiveLocale !== activeLocale) setActiveLocale(effectiveLocale);
8442
8436
  }, [effectiveLocale, activeLocale]);
8443
- const captureSuggestions = useCallback6((chunk) => {
8444
- if (chunk.type !== "data-suggestions") return false;
8445
- setSuggestions(parseSuggestions(chunk.data));
8446
- return true;
8447
- }, []);
8437
+ const settleSuggestions = useCallback6(
8438
+ (turn, starters = []) => setSuggestions(turn.suggestions ?? starters),
8439
+ []
8440
+ );
8448
8441
  const adoptConversationRebind = useCallback6(
8449
8442
  (chunk) => {
8450
8443
  if (chunk.type !== "data-conversation-rebind") return false;
@@ -8466,7 +8459,7 @@ function App({ options, hostElement, bus }) {
8466
8459
  let bubble = null;
8467
8460
  try {
8468
8461
  for await (const evt of handle.iter) {
8469
- if (adoptConversationRebind(evt.chunk) || captureSuggestions(evt.chunk)) continue;
8462
+ if (adoptConversationRebind(evt.chunk)) continue;
8470
8463
  if (!bubble) {
8471
8464
  bubble = makeAssistantMessage();
8472
8465
  resumeBubbleRef.current = bubble;
@@ -8492,6 +8485,7 @@ function App({ options, hostElement, bus }) {
8492
8485
  bubble.status = "complete";
8493
8486
  feedback.play("messageReceived");
8494
8487
  emitMessage(bus, options, "assistant", assistantText(bubble));
8488
+ settleSuggestions(bubble);
8495
8489
  }
8496
8490
  }
8497
8491
  } catch (err) {
@@ -8636,7 +8630,7 @@ function App({ options, hostElement, bus }) {
8636
8630
  setActiveDetach(() => handle.detach);
8637
8631
  try {
8638
8632
  for await (const evt of handle.iter) {
8639
- if (adoptConversationRebind(evt.chunk) || captureSuggestions(evt.chunk)) continue;
8633
+ if (adoptConversationRebind(evt.chunk)) continue;
8640
8634
  reducer.apply(evt.chunk);
8641
8635
  if (evt.chunk.type === "finish" && evt.chunk.canContinue === false) {
8642
8636
  setCanSend(false);
@@ -8655,6 +8649,7 @@ function App({ options, hostElement, bus }) {
8655
8649
  assistantMsg.status = "complete";
8656
8650
  feedback.play("messageReceived");
8657
8651
  emitMessage(bus, options, "assistant", assistantText(assistantMsg));
8652
+ settleSuggestions(assistantMsg);
8658
8653
  }
8659
8654
  } catch (error) {
8660
8655
  if (isAbortError(error)) {
@@ -9073,7 +9068,8 @@ function App({ options, hostElement, bus }) {
9073
9068
  transport.primeIdentity(visitorId, res.conversationId);
9074
9069
  if (res.agent) setAgent(res.agent);
9075
9070
  setCanSend(res.canContinue);
9076
- setSuggestions(res.suggestions ?? []);
9071
+ const lastTurn = hydrated.findLast((m) => m.role === "assistant" && !m.ephemeral);
9072
+ setSuggestions(lastTurn?.suggestions ?? res.suggestions ?? []);
9077
9073
  setView("chat");
9078
9074
  void resumeInFlight();
9079
9075
  await transport.markRead(targetConversationId);
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  ],
81
81
  "type": "module",
82
82
  "types": "./index.d.ts",
83
- "version": "0.61.3"
83
+ "version": "0.61.5"
84
84
  }
package/schema.mjs CHANGED
@@ -864,9 +864,7 @@ function widgetSettingsSchemaForMode(mode) {
864
864
  const ordered = [...MODE_EXTRAS[mode], ...COMMON_SECTIONS];
865
865
  const picks = {};
866
866
  for (const k of ordered) picks[k] = true;
867
- return widgetSettingsSchema.pick(
868
- picks
869
- );
867
+ return widgetSettingsSchema.pick(picks);
870
868
  }
871
869
  var ALL_MODES = ["floating", "inline", "standalone", "page", "modal", "drawer"];
872
870
 
package/web-component.mjs CHANGED
@@ -893,7 +893,7 @@ function resolveInitial() {
893
893
  }
894
894
  } catch {
895
895
  }
896
- if (window[GLOBAL_FLAG_KEY]) return 4;
896
+ if (Reflect.get(window, GLOBAL_FLAG_KEY)) return 4;
897
897
  return 2;
898
898
  }
899
899
  function parseToIndex(input) {
@@ -1930,7 +1930,7 @@ function createAuth(opts) {
1930
1930
  }
1931
1931
 
1932
1932
  // src/core/version.ts
1933
- var ELEMENTS_VERSION = true ? "0.61.3" : "0.0.0-dev";
1933
+ var ELEMENTS_VERSION = true ? "0.61.5" : "0.0.0-dev";
1934
1934
  var ELEMENTS_VERSION_PARAM = "_ev";
1935
1935
 
1936
1936
  // src/stream/types.ts
@@ -2213,15 +2213,6 @@ function retryAfterMs(headers) {
2213
2213
  const ms = Number.isFinite(secs) ? secs * 1e3 : Date.parse(raw) - Date.now();
2214
2214
  return Number.isFinite(ms) && ms >= 0 ? Math.min(ms, 3e4) : null;
2215
2215
  }
2216
- function followupsFromMessages(messages) {
2217
- for (let i = messages.length - 1; i >= 0; i--) {
2218
- const m = messages[i];
2219
- if (!m || m.role !== "assistant") continue;
2220
- const part = m.parts.find((p36) => p36.type === "data-suggestions");
2221
- return part?.type === "data-suggestions" ? parseSuggestions(part.data) : void 0;
2222
- }
2223
- return void 0;
2224
- }
2225
2216
  function sleep(ms, signal7) {
2226
2217
  return new Promise((resolve) => {
2227
2218
  if (signal7?.aborted) return resolve();
@@ -2468,10 +2459,10 @@ var AgentTransport = class {
2468
2459
  canContinue: res.canContinue ?? true,
2469
2460
  messages,
2470
2461
  agent: res.agent,
2471
- // The latest turn's model follow-ups are persisted ON the assistant message (a `data-suggestions`
2472
- // part), not in a top-level field — read them off there, falling back to any server-provided
2473
- // top-level `suggestions` (e.g. resume chips) when the last turn carried none.
2474
- suggestions: followupsFromMessages(messages) ?? res.suggestions
2462
+ // Top-level STARTER chips (resume / new-chat), not turn-tied. A turn's own model follow-ups ride
2463
+ // ON its assistant message (`data-suggestions` part), lifted onto `message.suggestions` by
2464
+ // `fromWireMessage`; the App prefers the last turn's and falls back to these starters.
2465
+ suggestions: res.suggestions
2475
2466
  };
2476
2467
  }
2477
2468
  /**
@@ -2973,9 +2964,11 @@ function fromWireMessage(w) {
2973
2964
  }
2974
2965
  return null;
2975
2966
  }).filter((p36) => p36 !== null);
2967
+ const suggestionsPart = w.parts.find((p36) => p36.type === "data-suggestions");
2976
2968
  return {
2977
2969
  id: w.id,
2978
2970
  role: w.role,
2971
+ suggestions: suggestionsPart?.type === "data-suggestions" ? parseSuggestions(suggestionsPart.data) : void 0,
2979
2972
  // On reload the wire `id` IS the backend's id — keep it as `serverMessageId`
2980
2973
  // too so a HITL re-POST after a reload still references the backend's message.
2981
2974
  serverMessageId: w.id,
@@ -3198,6 +3191,7 @@ var StreamReducer = class {
3198
3191
  case "data-conversation-rebind":
3199
3192
  return;
3200
3193
  case "data-suggestions":
3194
+ m.suggestions = parseSuggestions(chunk.data);
3201
3195
  return;
3202
3196
  default: {
3203
3197
  const _exhaustive = chunk;
@@ -4374,8 +4368,7 @@ function createLocalVoice(locale) {
4374
4368
  };
4375
4369
  }
4376
4370
  function getSpeechRecognitionCtor() {
4377
- const g = globalThis;
4378
- return g.SpeechRecognition ?? g.webkitSpeechRecognition;
4371
+ return window.SpeechRecognition ?? window.webkitSpeechRecognition;
4379
4372
  }
4380
4373
  var CANDIDATE_MIME_TYPES = ["audio/webm;codecs=opus", "audio/webm", "audio/ogg;codecs=opus", "audio/mp4"];
4381
4374
  function createServerVoice(transcribe) {
@@ -8265,7 +8258,8 @@ function App({ options, hostElement, bus }) {
8265
8258
  if (loaded.length || tail.length) {
8266
8259
  messagesSig.value = [...withWelcomeRows(loaded, markers), ...tail];
8267
8260
  setCanSend(chat.canContinue ?? true);
8268
- setSuggestions(chat.suggestions ?? []);
8261
+ const lastTurn = loaded.findLast((m) => m.role === "assistant" && !m.ephemeral);
8262
+ setSuggestions(lastTurn?.suggestions ?? chat.suggestions ?? []);
8269
8263
  } else {
8270
8264
  messagesSig.value = withWelcomeRows([], markers);
8271
8265
  }
@@ -8399,11 +8393,10 @@ function App({ options, hostElement, bus }) {
8399
8393
  useEffect16(() => {
8400
8394
  if (effectiveLocale !== activeLocale) setActiveLocale(effectiveLocale);
8401
8395
  }, [effectiveLocale, activeLocale]);
8402
- const captureSuggestions = useCallback6((chunk) => {
8403
- if (chunk.type !== "data-suggestions") return false;
8404
- setSuggestions(parseSuggestions(chunk.data));
8405
- return true;
8406
- }, []);
8396
+ const settleSuggestions = useCallback6(
8397
+ (turn, starters = []) => setSuggestions(turn.suggestions ?? starters),
8398
+ []
8399
+ );
8407
8400
  const adoptConversationRebind = useCallback6(
8408
8401
  (chunk) => {
8409
8402
  if (chunk.type !== "data-conversation-rebind") return false;
@@ -8425,7 +8418,7 @@ function App({ options, hostElement, bus }) {
8425
8418
  let bubble = null;
8426
8419
  try {
8427
8420
  for await (const evt of handle.iter) {
8428
- if (adoptConversationRebind(evt.chunk) || captureSuggestions(evt.chunk)) continue;
8421
+ if (adoptConversationRebind(evt.chunk)) continue;
8429
8422
  if (!bubble) {
8430
8423
  bubble = makeAssistantMessage();
8431
8424
  resumeBubbleRef.current = bubble;
@@ -8451,6 +8444,7 @@ function App({ options, hostElement, bus }) {
8451
8444
  bubble.status = "complete";
8452
8445
  feedback.play("messageReceived");
8453
8446
  emitMessage(bus, options, "assistant", assistantText(bubble));
8447
+ settleSuggestions(bubble);
8454
8448
  }
8455
8449
  }
8456
8450
  } catch (err) {
@@ -8595,7 +8589,7 @@ function App({ options, hostElement, bus }) {
8595
8589
  setActiveDetach(() => handle.detach);
8596
8590
  try {
8597
8591
  for await (const evt of handle.iter) {
8598
- if (adoptConversationRebind(evt.chunk) || captureSuggestions(evt.chunk)) continue;
8592
+ if (adoptConversationRebind(evt.chunk)) continue;
8599
8593
  reducer.apply(evt.chunk);
8600
8594
  if (evt.chunk.type === "finish" && evt.chunk.canContinue === false) {
8601
8595
  setCanSend(false);
@@ -8614,6 +8608,7 @@ function App({ options, hostElement, bus }) {
8614
8608
  assistantMsg.status = "complete";
8615
8609
  feedback.play("messageReceived");
8616
8610
  emitMessage(bus, options, "assistant", assistantText(assistantMsg));
8611
+ settleSuggestions(assistantMsg);
8617
8612
  }
8618
8613
  } catch (error) {
8619
8614
  if (isAbortError(error)) {
@@ -9032,7 +9027,8 @@ function App({ options, hostElement, bus }) {
9032
9027
  transport.primeIdentity(visitorId, res.conversationId);
9033
9028
  if (res.agent) setAgent(res.agent);
9034
9029
  setCanSend(res.canContinue);
9035
- setSuggestions(res.suggestions ?? []);
9030
+ const lastTurn = hydrated.findLast((m) => m.role === "assistant" && !m.ephemeral);
9031
+ setSuggestions(lastTurn?.suggestions ?? res.suggestions ?? []);
9036
9032
  setView("chat");
9037
9033
  void resumeInFlight();
9038
9034
  await transport.markRead(targetConversationId);