@copilotz/chat-adapter 0.9.29 → 0.9.31

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/index.js CHANGED
@@ -1732,6 +1732,9 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
1732
1732
  const userContextSeedRef = useRef2(userContextSeed);
1733
1733
  const messagePageInfoRef = useRef2(messagePageInfo);
1734
1734
  const isLoadingOlderMessagesRef = useRef2(isLoadingOlderMessages);
1735
+ const isStreamingRef = useRef2(isStreaming);
1736
+ const threadActivityStatusRef = useRef2(threadActivityStatus);
1737
+ const isRecoveringStreamRef = useRef2(isRecoveringStream);
1735
1738
  const senderOptionsRef = useRef2({
1736
1739
  agents: agentOptions,
1737
1740
  user: userId ? { id: userId, name: userName, avatarUrl: userAvatar } : null,
@@ -1749,6 +1752,9 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
1749
1752
  userContextSeedRef.current = userContextSeed;
1750
1753
  messagePageInfoRef.current = messagePageInfo;
1751
1754
  isLoadingOlderMessagesRef.current = isLoadingOlderMessages;
1755
+ isStreamingRef.current = isStreaming;
1756
+ threadActivityStatusRef.current = threadActivityStatus;
1757
+ isRecoveringStreamRef.current = isRecoveringStream;
1752
1758
  senderOptionsRef.current = {
1753
1759
  agents: agentOptions,
1754
1760
  user: userId ? { id: userId, name: userName, avatarUrl: userAvatar } : null,
@@ -2293,11 +2299,16 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2293
2299
  async (params) => {
2294
2300
  let currentAssistantId = params.assistantMessageId ?? generateId();
2295
2301
  let currentAssistantSender = params.assistantSender;
2302
+ const streamOwnerThreadId = currentThreadIdRef.current;
2303
+ const streamOwnerThreadExternalId = params.threadExternalId ?? currentThreadExternalIdRef.current;
2304
+ const streamStillOwnsVisibleThread = () => streamOwnerThreadId ? currentThreadIdRef.current === streamOwnerThreadId : !streamOwnerThreadExternalId || currentThreadExternalIdRef.current === streamOwnerThreadExternalId || currentThreadIdRef.current === streamOwnerThreadExternalId;
2296
2305
  params.onBeforeStart?.(currentAssistantId);
2297
2306
  let hasStreamProgress = false;
2298
2307
  const updateStreamingMessage = (partial, opts) => {
2308
+ if (!streamStillOwnsVisibleThread()) return;
2299
2309
  if (partial && partial.length > 0) {
2300
2310
  hasStreamProgress = true;
2311
+ setIsRecoveringStream(false);
2301
2312
  }
2302
2313
  const isReasoning = opts?.isReasoning ?? false;
2303
2314
  const nextSender = opts?.agent ? resolveAgentSender(opts.agent, senderOptionsRef.current) : currentAssistantSender;
@@ -2356,6 +2367,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2356
2367
  });
2357
2368
  };
2358
2369
  const finalizeCurrentAssistantBubble = () => {
2370
+ if (!streamStillOwnsVisibleThread()) return;
2359
2371
  setMessages((prev) => {
2360
2372
  const idx = prev.findIndex((m) => m.id === currentAssistantId);
2361
2373
  if (idx < 0) return prev;
@@ -2368,6 +2380,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2368
2380
  };
2369
2381
  const curThreadId = currentThreadIdRef.current;
2370
2382
  const applyLiveToolResultUpdate = (update) => {
2383
+ if (!streamStillOwnsVisibleThread()) return;
2371
2384
  let matched = false;
2372
2385
  setMessages((prev) => {
2373
2386
  const next = applyToolResultUpdateToMessages(prev, update, {
@@ -2382,6 +2395,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2382
2395
  }
2383
2396
  };
2384
2397
  const finalizeActiveAssistantTurn = (finalAnswer) => {
2398
+ if (!streamStillOwnsVisibleThread()) return;
2385
2399
  setMessages((prev) => {
2386
2400
  const currentIdx = prev.findIndex((message2) => message2.id === currentAssistantId && message2.role === "assistant");
2387
2401
  const fallbackIdx = currentIdx >= 0 ? currentIdx : (() => {
@@ -2429,12 +2443,14 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2429
2443
  return null;
2430
2444
  };
2431
2445
  const abortController = new AbortController();
2432
- abortControllerRef.current?.abort();
2446
+ if (!params.preserveActiveRun) {
2447
+ abortControllerRef.current?.abort();
2448
+ recoveryPollGenerationRef.current += 1;
2449
+ setIsRecoveringStream(false);
2450
+ }
2433
2451
  abortControllerRef.current = abortController;
2434
2452
  stopRequestedRef.current = false;
2435
- recoveryPollGenerationRef.current += 1;
2436
2453
  setThreadActivityStatus("running");
2437
- setIsRecoveringStream(false);
2438
2454
  setIsStreaming(true);
2439
2455
  liveToolUpdatesRef.current = [];
2440
2456
  let streamError = null;
@@ -2488,6 +2504,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2488
2504
  agent: raw?.payload?.agent ?? raw?.agent ?? null
2489
2505
  }),
2490
2506
  onMessageEvent: async (event) => {
2507
+ if (!streamStillOwnsVisibleThread()) return;
2491
2508
  const intercepted = applyEventInterceptor(event);
2492
2509
  if (intercepted?.handled) {
2493
2510
  return;
@@ -2596,6 +2613,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2596
2613
  handleStreamMessageEvent(event);
2597
2614
  },
2598
2615
  onAssetEvent: async (payload) => {
2616
+ if (!streamStillOwnsVisibleThread()) return;
2599
2617
  const intercepted = applyEventInterceptor({
2600
2618
  type: "ASSET_CREATED",
2601
2619
  payload
@@ -2615,7 +2633,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2615
2633
  }
2616
2634
  const wasStopped = stopRequestedRef.current || abortController.signal.aborted || isAbortError(streamError);
2617
2635
  let recoveryStarted = false;
2618
- if (!wasStopped) {
2636
+ if (!wasStopped && streamStillOwnsVisibleThread()) {
2619
2637
  try {
2620
2638
  const activityThreadId = await resolveActivityThreadId();
2621
2639
  if (activityThreadId) {
@@ -2635,7 +2653,12 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2635
2653
  }
2636
2654
  }
2637
2655
  }
2638
- abortControllerRef.current = null;
2656
+ if (abortControllerRef.current === abortController) {
2657
+ abortControllerRef.current = null;
2658
+ }
2659
+ if (!streamStillOwnsVisibleThread()) {
2660
+ return currentAssistantId;
2661
+ }
2639
2662
  if (recoveryStarted) {
2640
2663
  return currentAssistantId;
2641
2664
  }
@@ -2654,6 +2677,8 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2654
2677
  const timestamp = nowTs();
2655
2678
  const curThreadId = currentThreadIdRef.current;
2656
2679
  const curThreadExtId = currentThreadExternalIdRef.current;
2680
+ const sendOwnerThreadId = curThreadId;
2681
+ const preserveActiveRun = threadActivityStatusRef.current === "running" || isRecoveringStreamRef.current || isStreamingRef.current;
2657
2682
  const existingThreadId = curThreadId ?? void 0;
2658
2683
  const extMap = threadExternalIdMapRef.current;
2659
2684
  const isPlaceholderThread = existingThreadId ? extMap[existingThreadId] === existingThreadId : false;
@@ -2668,6 +2693,8 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2668
2693
  setCurrentThreadExternalId(effectiveThreadExternalId ?? null);
2669
2694
  }
2670
2695
  const conversationKey = threadIdForSend ?? effectiveThreadExternalId;
2696
+ const sendOwnerThreadExternalId = effectiveThreadExternalId ?? curThreadExtId;
2697
+ const stillShowingSendOwnerThread = () => sendOwnerThreadId ? currentThreadIdRef.current === sendOwnerThreadId : !sendOwnerThreadExternalId || currentThreadExternalIdRef.current === sendOwnerThreadExternalId || currentThreadIdRef.current === sendOwnerThreadExternalId;
2671
2698
  const currentMetadata = threadMetadataMapRef.current[conversationKey];
2672
2699
  const pendingTitle = currentMetadata?.pendingTitle;
2673
2700
  const userMessage = {
@@ -2727,11 +2754,14 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
2727
2754
  agentName: preferredAgentRef.current,
2728
2755
  assistantMessageId: assistantPlaceholder.id,
2729
2756
  assistantSender,
2757
+ preserveActiveRun,
2730
2758
  // Include pending title for new threads
2731
2759
  threadMetadata: pendingTitle ? { name: pendingTitle } : void 0
2732
2760
  });
2733
2761
  await new Promise((r) => setTimeout(r, 1e3));
2734
- await fetchAndSetThreadsState(userId, effectiveThreadExternalId ?? existingThreadId ?? null);
2762
+ if (stillShowingSendOwnerThread()) {
2763
+ await fetchAndSetThreadsState(userId, effectiveThreadExternalId ?? existingThreadId ?? null);
2764
+ }
2735
2765
  } catch (error) {
2736
2766
  if (isAbortError(error)) return;
2737
2767
  console.error("Error sending Copilotz message", error);