@helpai/elements 0.51.0 → 0.51.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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, H as HandshakeResponse, L as Link, S as ServerConfig, b as SiteConfig, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial } from './deployment-oSJ2nv2G.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, H as HandshakeResponse, L as Link, S as ServerConfig, b as SiteConfig, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial } from './deployment-C_jlXJAe.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
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.51.0" : "0.0.0-dev";
32
+ var ELEMENTS_VERSION = true ? "0.51.1" : "0.0.0-dev";
33
33
  var ELEMENTS_VERSION_PARAM = "_ev";
34
34
 
35
35
  // src/i18n/strings.ts
@@ -2454,7 +2454,7 @@ var AgentTransport = class {
2454
2454
  const seenIds = /* @__PURE__ */ new Set();
2455
2455
  const iter = this.runStream(body, ctrl, seenIds);
2456
2456
  const cancel = () => this.cancelStream(ctrl, body.conversationId);
2457
- return { iter, cancel };
2457
+ return { iter, cancel, detach: () => ctrl.abort() };
2458
2458
  }
2459
2459
  /**
2460
2460
  * Re-attach to an in-flight reply on a cold page load / refresh.
@@ -2477,7 +2477,8 @@ var AgentTransport = class {
2477
2477
  const seenIds = /* @__PURE__ */ new Set();
2478
2478
  this.resumeStatus = void 0;
2479
2479
  const iter = this.resumeStreamGen(ctrl, seenIds, true);
2480
- return { iter, cancel: () => ctrl.abort() };
2480
+ const stop = () => ctrl.abort();
2481
+ return { iter, cancel: stop, detach: stop };
2481
2482
  }
2482
2483
  // ---- Stream orchestration ---------------------------------------------
2483
2484
  async *runStream(body, ctrl, seenIds) {
@@ -7620,6 +7621,7 @@ function App({ options, hostElement, bus }) {
7620
7621
  const [agent, setAgent] = useState14(null);
7621
7622
  const [suggestions, setSuggestions] = useState14([]);
7622
7623
  const [activeCancel, setActiveCancel] = useState14(null);
7624
+ const [activeDetach, setActiveDetach] = useState14(null);
7623
7625
  const stringsRef = useRef9(options.strings);
7624
7626
  const [parsedSite, setParsedSite] = useState14(void 0);
7625
7627
  const [parsedBlocks, setParsedBlocks] = useState14(void 0);
@@ -7922,6 +7924,7 @@ function App({ options, hostElement, bus }) {
7922
7924
  messagesSig.value = [...messagesSig.value, bubble];
7923
7925
  setStreaming(true);
7924
7926
  setActiveCancel(() => handle.cancel);
7927
+ setActiveDetach(() => handle.detach);
7925
7928
  resumeActiveRef.current = true;
7926
7929
  const chatTab = chatTabIdRef.current;
7927
7930
  if (chatTab) homeNav.switchTab(chatTab);
@@ -7943,17 +7946,26 @@ function App({ options, hostElement, bus }) {
7943
7946
  }
7944
7947
  } catch (err) {
7945
7948
  if (bubble) {
7946
- bubble.status = "error";
7947
- bubble.errorText = errorMessageFor(err, options.strings);
7948
- feedback.play("error");
7949
+ if (isAbortError(err)) {
7950
+ if (hasNoVisibleAnswer(bubble)) {
7951
+ messagesSig.value = messagesSig.value.filter((m) => m !== bubble);
7952
+ } else {
7953
+ bubble.status = "complete";
7954
+ }
7955
+ } else {
7956
+ bubble.status = "error";
7957
+ bubble.errorText = errorMessageFor(err, options.strings);
7958
+ feedback.play("error");
7959
+ }
7949
7960
  }
7950
- log17.debug("mount resume ended without completing", { err });
7961
+ log17.debug("resume ended without completing", { err });
7951
7962
  } finally {
7952
7963
  resumeBubbleRef.current = null;
7953
7964
  if (bubble) {
7954
7965
  reducer.detach();
7955
7966
  setStreaming(false);
7956
7967
  setActiveCancel(null);
7968
+ setActiveDetach(null);
7957
7969
  messagesSig.value = [...messagesSig.value];
7958
7970
  }
7959
7971
  }
@@ -7961,6 +7973,12 @@ function App({ options, hostElement, bus }) {
7961
7973
  [reducer, messagesSig, feedback, bus, options, homeNav, adoptConversationRebind]
7962
7974
  );
7963
7975
  const resumeHandleRef = useRef9(null);
7976
+ const resumeInFlight = useCallback6(async () => {
7977
+ const handle = transport.resumeStream();
7978
+ resumeHandleRef.current = handle;
7979
+ await runResume(handle);
7980
+ if (transport.resumeStatus === "needs-generation") regenerateDanglingRef.current?.();
7981
+ }, [transport, runResume]);
7964
7982
  useEffect16(() => {
7965
7983
  const persistedConversation = conversationIdSig.value;
7966
7984
  if (persistedConversation) transport.primeIdentity(visitorId, persistedConversation);
@@ -7990,14 +8008,9 @@ function App({ options, hostElement, bus }) {
7990
8008
  const persisted = conversationIdSig.value;
7991
8009
  if (persisted) tasks.push(loadThread(persisted));
7992
8010
  }
7993
- if (conversationIdSig.value) {
7994
- const handle = transport.resumeStream();
7995
- resumeHandleRef.current = handle;
7996
- tasks.push(runResume(handle));
7997
- }
8011
+ if (conversationIdSig.value) tasks.push(resumeInFlight());
7998
8012
  if (tasks.length === 0) return;
7999
8013
  await Promise.allSettled(tasks);
8000
- if (transport.resumeStatus === "needs-generation") regenerateDanglingRef.current?.();
8001
8014
  })();
8002
8015
  }, [activated]);
8003
8016
  const formsLocaleRef = useRef9(null);
@@ -8069,6 +8082,7 @@ function App({ options, hostElement, bus }) {
8069
8082
  setStreaming(true);
8070
8083
  const handle = transport.sendMessage(body);
8071
8084
  setActiveCancel(() => handle.cancel);
8085
+ setActiveDetach(() => handle.detach);
8072
8086
  try {
8073
8087
  for await (const evt of handle.iter) {
8074
8088
  if (adoptConversationRebind(evt.chunk)) continue;
@@ -8109,6 +8123,7 @@ function App({ options, hostElement, bus }) {
8109
8123
  reducer.detach();
8110
8124
  setStreaming(false);
8111
8125
  setActiveCancel(null);
8126
+ setActiveDetach(null);
8112
8127
  messagesSig.value = [...messagesSig.value];
8113
8128
  }
8114
8129
  },
@@ -8356,7 +8371,7 @@ function App({ options, hostElement, bus }) {
8356
8371
  }
8357
8372
  log17.info("clear \u2192 new conversation", { wasStreaming: streaming });
8358
8373
  bus.emit("clear", void 0);
8359
- if (streaming) activeCancel?.();
8374
+ if (streaming) activeDetach?.();
8360
8375
  batch(() => {
8361
8376
  messagesSig.value = [];
8362
8377
  conversationIdSig.value = void 0;
@@ -8365,7 +8380,7 @@ function App({ options, hostElement, bus }) {
8365
8380
  setCanSend(true);
8366
8381
  setFormMarkers([]);
8367
8382
  void runHandshake({ newConversation: true });
8368
- }, [streaming, activeCancel, messagesSig, conversationIdSig, persistence, runHandshake, bus, canSend, playWelcome]);
8383
+ }, [streaming, activeDetach, messagesSig, conversationIdSig, persistence, runHandshake, bus, canSend, playWelcome]);
8369
8384
  const handleNewChat = useCallback6(() => {
8370
8385
  handleClear();
8371
8386
  setView("chat");
@@ -8442,8 +8457,9 @@ function App({ options, hostElement, bus }) {
8442
8457
  );
8443
8458
  const handleSelectHistoryConversation = useCallback6(
8444
8459
  async (targetConversationId) => {
8445
- log17.info("selectConversation", { conversationId: targetConversationId });
8460
+ log17.info("selectConversation", { conversationId: targetConversationId, wasStreaming: streaming });
8446
8461
  bus.emit("selectConversation", { conversationId: targetConversationId });
8462
+ if (streaming) activeDetach?.();
8447
8463
  try {
8448
8464
  const [res, markers] = await Promise.all([
8449
8465
  transport.loadConversation(targetConversationId),
@@ -8456,10 +8472,12 @@ function App({ options, hostElement, bus }) {
8456
8472
  });
8457
8473
  setFormMarkers(markers);
8458
8474
  persistence.saveConversationId(res.conversationId);
8475
+ transport.primeIdentity(visitorId, res.conversationId);
8459
8476
  if (res.agent) setAgent(res.agent);
8460
8477
  setCanSend(res.canContinue);
8461
8478
  setSuggestions(res.suggestions ?? []);
8462
8479
  setView("chat");
8480
+ void resumeInFlight();
8463
8481
  await transport.markRead(targetConversationId);
8464
8482
  refreshUnread();
8465
8483
  } catch (err) {
@@ -8476,7 +8494,11 @@ function App({ options, hostElement, bus }) {
8476
8494
  persistence,
8477
8495
  refreshUnread,
8478
8496
  fetchFormMarkers,
8479
- withWelcomeRows
8497
+ withWelcomeRows,
8498
+ streaming,
8499
+ activeDetach,
8500
+ visitorId,
8501
+ resumeInFlight
8480
8502
  ]
8481
8503
  );
8482
8504
  const pendingOpenFormRef = useRef9(null);
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  ],
81
81
  "type": "module",
82
82
  "types": "./index.d.ts",
83
- "version": "0.51.0"
83
+ "version": "0.51.1"
84
84
  }
package/schema.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, E as Endpoints, H as HandshakeResponse, L as Link, P as PAGE_AREA_SUGGESTIONS, f as PageContext, S as ServerConfig, b as SiteConfig, U as UserContext, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial, g as assetSchema, h as blocksConfigSchema, i as connectionConfigPartialSchema, j as connectionConfigSchema, k as cssColorSchema, l as cssLengthSchema, m as endpointsSchema, n as handshakeResponseSchema, o as linkSchema, p as localeSchema, q as pageContextSchema, s as serverConfigSchema, r as siteConfigSchema, u as userContextSchema, t as uuid7Schema, w as widgetConfigPartialSchema, v as widgetConfigSchema, x as widgetSettingsPartialSchema, y as widgetSettingsSchema } from './deployment-oSJ2nv2G.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, E as Endpoints, H as HandshakeResponse, L as Link, P as PAGE_AREA_SUGGESTIONS, f as PageContext, S as ServerConfig, b as SiteConfig, U as UserContext, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial, g as assetSchema, h as blocksConfigSchema, i as connectionConfigPartialSchema, j as connectionConfigSchema, k as cssColorSchema, l as cssLengthSchema, m as endpointsSchema, n as handshakeResponseSchema, o as linkSchema, p as localeSchema, q as pageContextSchema, s as serverConfigSchema, r as siteConfigSchema, u as userContextSchema, t as uuid7Schema, w as widgetConfigPartialSchema, v as widgetConfigSchema, x as widgetSettingsPartialSchema, y as widgetSettingsSchema } from './deployment-C_jlXJAe.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  /**
@@ -56,9 +56,9 @@ declare const presentationSchema: z.ZodObject<{
56
56
  inset: z.ZodOptional<z.ZodString>;
57
57
  initialSize: z.ZodDefault<z.ZodEnum<{
58
58
  fullscreen: "fullscreen";
59
- normal: "normal";
60
59
  expanded: "expanded";
61
60
  auto: "auto";
61
+ normal: "normal";
62
62
  }>>;
63
63
  autoSizeBreakpoint: z.ZodDefault<z.ZodNumber>;
64
64
  }, z.core.$loose>>;
@@ -240,9 +240,9 @@ type LauncherOptions = z.infer<typeof launcherOptionsSchema>;
240
240
 
241
241
  declare const initialSizeSchema: z.ZodEnum<{
242
242
  fullscreen: "fullscreen";
243
- normal: "normal";
244
243
  expanded: "expanded";
245
244
  auto: "auto";
245
+ normal: "normal";
246
246
  }>;
247
247
  declare const resizeOptionsSchema: z.ZodObject<{
248
248
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -269,9 +269,9 @@ declare const sizeOptionsSchema: z.ZodObject<{
269
269
  inset: z.ZodOptional<z.ZodString>;
270
270
  initialSize: z.ZodDefault<z.ZodEnum<{
271
271
  fullscreen: "fullscreen";
272
- normal: "normal";
273
272
  expanded: "expanded";
274
273
  auto: "auto";
274
+ normal: "normal";
275
275
  }>>;
276
276
  autoSizeBreakpoint: z.ZodDefault<z.ZodNumber>;
277
277
  }, z.core.$loose>;
@@ -323,40 +323,40 @@ type FeatureFlags = z.infer<typeof featureFlagsSchema>;
323
323
  */
324
324
 
325
325
  declare const actionNameSchema: z.ZodEnum<{
326
+ close: "close";
326
327
  expand: "expand";
327
328
  fullscreen: "fullscreen";
328
- close: "close";
329
- language: "language";
329
+ clear: "clear";
330
330
  theme: "theme";
331
+ language: "language";
331
332
  textSize: "textSize";
332
333
  history: "history";
333
- clear: "clear";
334
334
  sound: "sound";
335
335
  }>;
336
336
  type ActionName = z.infer<typeof actionNameSchema>;
337
337
  declare const headerActionsSchema: z.ZodArray<z.ZodEnum<{
338
+ close: "close";
338
339
  expand: "expand";
339
340
  fullscreen: "fullscreen";
340
- close: "close";
341
- language: "language";
341
+ clear: "clear";
342
342
  theme: "theme";
343
+ language: "language";
343
344
  textSize: "textSize";
344
345
  history: "history";
345
- clear: "clear";
346
346
  sound: "sound";
347
347
  }>>;
348
348
  type HeaderActions = z.infer<typeof headerActionsSchema>;
349
349
  /** Section wrapper — `actions` list wrapped under `header` in the dashboard form. */
350
350
  declare const headerSchema: z.ZodObject<{
351
351
  actions: z.ZodOptional<z.ZodArray<z.ZodEnum<{
352
+ close: "close";
352
353
  expand: "expand";
353
354
  fullscreen: "fullscreen";
354
- close: "close";
355
- language: "language";
355
+ clear: "clear";
356
356
  theme: "theme";
357
+ language: "language";
357
358
  textSize: "textSize";
358
359
  history: "history";
359
- clear: "clear";
360
360
  sound: "sound";
361
361
  }>>>;
362
362
  }, z.core.$loose>;
@@ -372,33 +372,33 @@ type HeaderOptions = z.infer<typeof headerSchema>;
372
372
  */
373
373
 
374
374
  declare const feedbackEventSchema: z.ZodEnum<{
375
+ voiceStart: "voiceStart";
376
+ voiceStop: "voiceStop";
375
377
  error: "error";
376
378
  messageReceived: "messageReceived";
377
379
  messageSent: "messageSent";
378
- voiceStart: "voiceStart";
379
- voiceStop: "voiceStop";
380
380
  }>;
381
381
  type FeedbackEvent = z.infer<typeof feedbackEventSchema>;
382
382
  declare const soundOptionsSchema: z.ZodObject<{
383
383
  enabled: z.ZodDefault<z.ZodBoolean>;
384
384
  volume: z.ZodDefault<z.ZodNumber>;
385
385
  events: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
386
+ voiceStart: "voiceStart";
387
+ voiceStop: "voiceStop";
386
388
  error: "error";
387
389
  messageReceived: "messageReceived";
388
390
  messageSent: "messageSent";
389
- voiceStart: "voiceStart";
390
- voiceStop: "voiceStop";
391
391
  }> & z.core.$partial, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
392
392
  }, z.core.$loose>;
393
393
  type SoundOptions = z.infer<typeof soundOptionsSchema>;
394
394
  declare const hapticsOptionsSchema: z.ZodObject<{
395
395
  enabled: z.ZodDefault<z.ZodBoolean>;
396
396
  events: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
397
+ voiceStart: "voiceStart";
398
+ voiceStop: "voiceStop";
397
399
  error: "error";
398
400
  messageReceived: "messageReceived";
399
401
  messageSent: "messageSent";
400
- voiceStart: "voiceStart";
401
- voiceStop: "voiceStop";
402
402
  }> & z.core.$partial, z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodNumber>]>>>;
403
403
  }, z.core.$loose>;
404
404
  type HapticsOptions = z.infer<typeof hapticsOptionsSchema>;
@@ -407,21 +407,21 @@ declare const feedbackSchema: z.ZodObject<{
407
407
  enabled: z.ZodDefault<z.ZodBoolean>;
408
408
  volume: z.ZodDefault<z.ZodNumber>;
409
409
  events: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
410
+ voiceStart: "voiceStart";
411
+ voiceStop: "voiceStop";
410
412
  error: "error";
411
413
  messageReceived: "messageReceived";
412
414
  messageSent: "messageSent";
413
- voiceStart: "voiceStart";
414
- voiceStop: "voiceStop";
415
415
  }> & z.core.$partial, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
416
416
  }, z.core.$loose>>;
417
417
  haptics: z.ZodOptional<z.ZodObject<{
418
418
  enabled: z.ZodDefault<z.ZodBoolean>;
419
419
  events: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
420
+ voiceStart: "voiceStart";
421
+ voiceStop: "voiceStop";
420
422
  error: "error";
421
423
  messageReceived: "messageReceived";
422
424
  messageSent: "messageSent";
423
- voiceStart: "voiceStart";
424
- voiceStop: "voiceStop";
425
425
  }> & z.core.$partial, z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodNumber>]>>>;
426
426
  }, z.core.$loose>>;
427
427
  }, z.core.$loose>;
@@ -856,18 +856,18 @@ type I18nOptions = z.infer<typeof i18nSchema>;
856
856
  */
857
857
 
858
858
  declare const moduleLayoutSchema: z.ZodEnum<{
859
+ home: "home";
859
860
  chat: "chat";
860
861
  help: "help";
861
- home: "home";
862
862
  news: "news";
863
863
  }>;
864
864
  type ModuleLayout = z.infer<typeof moduleLayoutSchema>;
865
865
  declare const moduleSchema: z.ZodObject<{
866
866
  label: z.ZodString;
867
867
  layout: z.ZodEnum<{
868
+ home: "home";
868
869
  chat: "chat";
869
870
  help: "help";
870
- home: "home";
871
871
  news: "news";
872
872
  }>;
873
873
  contentTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -893,9 +893,9 @@ type ModuleOptions = z.infer<typeof moduleSchema>;
893
893
  declare const modulesSchema: z.ZodArray<z.ZodObject<{
894
894
  label: z.ZodString;
895
895
  layout: z.ZodEnum<{
896
+ home: "home";
896
897
  chat: "chat";
897
898
  help: "help";
898
- home: "home";
899
899
  news: "news";
900
900
  }>;
901
901
  contentTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
package/web-component.mjs CHANGED
@@ -1794,7 +1794,7 @@ function createAuth(opts) {
1794
1794
  }
1795
1795
 
1796
1796
  // src/core/version.ts
1797
- var ELEMENTS_VERSION = true ? "0.51.0" : "0.0.0-dev";
1797
+ var ELEMENTS_VERSION = true ? "0.51.1" : "0.0.0-dev";
1798
1798
  var ELEMENTS_VERSION_PARAM = "_ev";
1799
1799
 
1800
1800
  // src/stream/types.ts
@@ -2413,7 +2413,7 @@ var AgentTransport = class {
2413
2413
  const seenIds = /* @__PURE__ */ new Set();
2414
2414
  const iter = this.runStream(body, ctrl, seenIds);
2415
2415
  const cancel = () => this.cancelStream(ctrl, body.conversationId);
2416
- return { iter, cancel };
2416
+ return { iter, cancel, detach: () => ctrl.abort() };
2417
2417
  }
2418
2418
  /**
2419
2419
  * Re-attach to an in-flight reply on a cold page load / refresh.
@@ -2436,7 +2436,8 @@ var AgentTransport = class {
2436
2436
  const seenIds = /* @__PURE__ */ new Set();
2437
2437
  this.resumeStatus = void 0;
2438
2438
  const iter = this.resumeStreamGen(ctrl, seenIds, true);
2439
- return { iter, cancel: () => ctrl.abort() };
2439
+ const stop = () => ctrl.abort();
2440
+ return { iter, cancel: stop, detach: stop };
2440
2441
  }
2441
2442
  // ---- Stream orchestration ---------------------------------------------
2442
2443
  async *runStream(body, ctrl, seenIds) {
@@ -7579,6 +7580,7 @@ function App({ options, hostElement, bus }) {
7579
7580
  const [agent, setAgent] = useState14(null);
7580
7581
  const [suggestions, setSuggestions] = useState14([]);
7581
7582
  const [activeCancel, setActiveCancel] = useState14(null);
7583
+ const [activeDetach, setActiveDetach] = useState14(null);
7582
7584
  const stringsRef = useRef9(options.strings);
7583
7585
  const [parsedSite, setParsedSite] = useState14(void 0);
7584
7586
  const [parsedBlocks, setParsedBlocks] = useState14(void 0);
@@ -7881,6 +7883,7 @@ function App({ options, hostElement, bus }) {
7881
7883
  messagesSig.value = [...messagesSig.value, bubble];
7882
7884
  setStreaming(true);
7883
7885
  setActiveCancel(() => handle.cancel);
7886
+ setActiveDetach(() => handle.detach);
7884
7887
  resumeActiveRef.current = true;
7885
7888
  const chatTab = chatTabIdRef.current;
7886
7889
  if (chatTab) homeNav.switchTab(chatTab);
@@ -7902,17 +7905,26 @@ function App({ options, hostElement, bus }) {
7902
7905
  }
7903
7906
  } catch (err) {
7904
7907
  if (bubble) {
7905
- bubble.status = "error";
7906
- bubble.errorText = errorMessageFor(err, options.strings);
7907
- feedback.play("error");
7908
+ if (isAbortError(err)) {
7909
+ if (hasNoVisibleAnswer(bubble)) {
7910
+ messagesSig.value = messagesSig.value.filter((m) => m !== bubble);
7911
+ } else {
7912
+ bubble.status = "complete";
7913
+ }
7914
+ } else {
7915
+ bubble.status = "error";
7916
+ bubble.errorText = errorMessageFor(err, options.strings);
7917
+ feedback.play("error");
7918
+ }
7908
7919
  }
7909
- log16.debug("mount resume ended without completing", { err });
7920
+ log16.debug("resume ended without completing", { err });
7910
7921
  } finally {
7911
7922
  resumeBubbleRef.current = null;
7912
7923
  if (bubble) {
7913
7924
  reducer.detach();
7914
7925
  setStreaming(false);
7915
7926
  setActiveCancel(null);
7927
+ setActiveDetach(null);
7916
7928
  messagesSig.value = [...messagesSig.value];
7917
7929
  }
7918
7930
  }
@@ -7920,6 +7932,12 @@ function App({ options, hostElement, bus }) {
7920
7932
  [reducer, messagesSig, feedback, bus, options, homeNav, adoptConversationRebind]
7921
7933
  );
7922
7934
  const resumeHandleRef = useRef9(null);
7935
+ const resumeInFlight = useCallback6(async () => {
7936
+ const handle = transport.resumeStream();
7937
+ resumeHandleRef.current = handle;
7938
+ await runResume(handle);
7939
+ if (transport.resumeStatus === "needs-generation") regenerateDanglingRef.current?.();
7940
+ }, [transport, runResume]);
7923
7941
  useEffect16(() => {
7924
7942
  const persistedConversation = conversationIdSig.value;
7925
7943
  if (persistedConversation) transport.primeIdentity(visitorId, persistedConversation);
@@ -7949,14 +7967,9 @@ function App({ options, hostElement, bus }) {
7949
7967
  const persisted = conversationIdSig.value;
7950
7968
  if (persisted) tasks.push(loadThread(persisted));
7951
7969
  }
7952
- if (conversationIdSig.value) {
7953
- const handle = transport.resumeStream();
7954
- resumeHandleRef.current = handle;
7955
- tasks.push(runResume(handle));
7956
- }
7970
+ if (conversationIdSig.value) tasks.push(resumeInFlight());
7957
7971
  if (tasks.length === 0) return;
7958
7972
  await Promise.allSettled(tasks);
7959
- if (transport.resumeStatus === "needs-generation") regenerateDanglingRef.current?.();
7960
7973
  })();
7961
7974
  }, [activated]);
7962
7975
  const formsLocaleRef = useRef9(null);
@@ -8028,6 +8041,7 @@ function App({ options, hostElement, bus }) {
8028
8041
  setStreaming(true);
8029
8042
  const handle = transport.sendMessage(body);
8030
8043
  setActiveCancel(() => handle.cancel);
8044
+ setActiveDetach(() => handle.detach);
8031
8045
  try {
8032
8046
  for await (const evt of handle.iter) {
8033
8047
  if (adoptConversationRebind(evt.chunk)) continue;
@@ -8068,6 +8082,7 @@ function App({ options, hostElement, bus }) {
8068
8082
  reducer.detach();
8069
8083
  setStreaming(false);
8070
8084
  setActiveCancel(null);
8085
+ setActiveDetach(null);
8071
8086
  messagesSig.value = [...messagesSig.value];
8072
8087
  }
8073
8088
  },
@@ -8315,7 +8330,7 @@ function App({ options, hostElement, bus }) {
8315
8330
  }
8316
8331
  log16.info("clear \u2192 new conversation", { wasStreaming: streaming });
8317
8332
  bus.emit("clear", void 0);
8318
- if (streaming) activeCancel?.();
8333
+ if (streaming) activeDetach?.();
8319
8334
  batch(() => {
8320
8335
  messagesSig.value = [];
8321
8336
  conversationIdSig.value = void 0;
@@ -8324,7 +8339,7 @@ function App({ options, hostElement, bus }) {
8324
8339
  setCanSend(true);
8325
8340
  setFormMarkers([]);
8326
8341
  void runHandshake({ newConversation: true });
8327
- }, [streaming, activeCancel, messagesSig, conversationIdSig, persistence, runHandshake, bus, canSend, playWelcome]);
8342
+ }, [streaming, activeDetach, messagesSig, conversationIdSig, persistence, runHandshake, bus, canSend, playWelcome]);
8328
8343
  const handleNewChat = useCallback6(() => {
8329
8344
  handleClear();
8330
8345
  setView("chat");
@@ -8401,8 +8416,9 @@ function App({ options, hostElement, bus }) {
8401
8416
  );
8402
8417
  const handleSelectHistoryConversation = useCallback6(
8403
8418
  async (targetConversationId) => {
8404
- log16.info("selectConversation", { conversationId: targetConversationId });
8419
+ log16.info("selectConversation", { conversationId: targetConversationId, wasStreaming: streaming });
8405
8420
  bus.emit("selectConversation", { conversationId: targetConversationId });
8421
+ if (streaming) activeDetach?.();
8406
8422
  try {
8407
8423
  const [res, markers] = await Promise.all([
8408
8424
  transport.loadConversation(targetConversationId),
@@ -8415,10 +8431,12 @@ function App({ options, hostElement, bus }) {
8415
8431
  });
8416
8432
  setFormMarkers(markers);
8417
8433
  persistence.saveConversationId(res.conversationId);
8434
+ transport.primeIdentity(visitorId, res.conversationId);
8418
8435
  if (res.agent) setAgent(res.agent);
8419
8436
  setCanSend(res.canContinue);
8420
8437
  setSuggestions(res.suggestions ?? []);
8421
8438
  setView("chat");
8439
+ void resumeInFlight();
8422
8440
  await transport.markRead(targetConversationId);
8423
8441
  refreshUnread();
8424
8442
  } catch (err) {
@@ -8435,7 +8453,11 @@ function App({ options, hostElement, bus }) {
8435
8453
  persistence,
8436
8454
  refreshUnread,
8437
8455
  fetchFormMarkers,
8438
- withWelcomeRows
8456
+ withWelcomeRows,
8457
+ streaming,
8458
+ activeDetach,
8459
+ visitorId,
8460
+ resumeInFlight
8439
8461
  ]
8440
8462
  );
8441
8463
  const pendingOpenFormRef = useRef9(null);