@ai-group/chat-sdk 3.6.7 → 3.6.8

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.
@@ -45,6 +45,47 @@ var combineTextParts = function combineTextParts(parts) {
45
45
  return result;
46
46
  };
47
47
  var DEFAULT_SESSION_EVENTS_POLLING_INTERVAL = 10000;
48
+
49
+ /**
50
+ * 从 event + part 构建非文本 IMessage(functionCall / functionResponse / executableCode 等)。
51
+ */
52
+ var buildNonTextMessage = function buildNonTextMessage(event, part) {
53
+ var role = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "bot";
54
+ var msg = {
55
+ id: v4(),
56
+ author: event.author,
57
+ invocationId: event.invocationId,
58
+ eventId: event.id,
59
+ timestamp: event.timestamp,
60
+ isLike: event.isLike || 0,
61
+ role: role,
62
+ modelCode: event.modelCode,
63
+ usageMetadata: event.usageMetadata,
64
+ finishReason: event.finishReason,
65
+ raw: event
66
+ };
67
+ if (part.inlineData) {
68
+ msg.inlineData = {
69
+ displayName: part.inlineData.displayName,
70
+ data: part.inlineData.data,
71
+ mimeType: part.inlineData.mimeType
72
+ };
73
+ } else if (part.fileData) {
74
+ msg.fileData = [part.fileData];
75
+ } else if (part.functionCall) {
76
+ msg.functionCall = part.functionCall;
77
+ } else if (part.functionResponse) {
78
+ msg.functionResponse = part.functionResponse;
79
+ } else if (part.executableCode) {
80
+ msg.executableCode = part.executableCode;
81
+ } else if (part.codeExecutionResult) {
82
+ msg.codeExecutionResult = part.codeExecutionResult;
83
+ } else if (part.text) {
84
+ msg.text = part.text;
85
+ msg.thought = part.thought;
86
+ }
87
+ return msg;
88
+ };
48
89
  var getMessageEventId = function getMessageEventId(messageItem) {
49
90
  var _messageItem$raw;
50
91
  return messageItem.eventId || ((_messageItem$raw = messageItem.raw) === null || _messageItem$raw === void 0 ? void 0 : _messageItem$raw.id);
@@ -60,6 +101,8 @@ var deduplicateMessages = function deduplicateMessages(messageItems) {
60
101
  seenMessageIds.add(messageItem.id);
61
102
  return true;
62
103
  });
104
+
105
+ // 机器人文本消息去重:同一事件、同一思考类型、同一文件列表的文本消息,如果文本内容包含关系,则保留最长的文本消息,去掉被包含的短文本消息。
63
106
  var botTextGroups = new Map();
64
107
  uniqueIdMessages.forEach(function (messageItem) {
65
108
  var eventId = getMessageEventId(messageItem);
@@ -107,6 +150,10 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
107
150
  if (item.author === "user" && item.content.parts.some(function (part) {
108
151
  return part.task;
109
152
  })) return;
153
+ // functionResponse 的 user 事件仅用于配对 functionCall,不作为独立消息展示
154
+ if (item.content.role === "user" && item.content.parts.every(function (part) {
155
+ return !part || part.functionResponse;
156
+ })) return;
110
157
  var role = (item.content.role || "").toLowerCase() === "user" ? "user" : "bot";
111
158
  var parts = item.content.parts.filter(function (part) {
112
159
  if (!part) return false;
@@ -122,9 +169,13 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
122
169
  var otherParts = parts.filter(function (part) {
123
170
  return !part.text && !part.errorMessage && !part.inlineData && !part.fileData;
124
171
  });
172
+
173
+ // 使用确定性 ID:同一服务端事件 + 同一 part 类型始终生成相同 ID,
174
+ // 确保多次 mapSessionMessages 调用产生的消息可以被 id 去重正确识别。
175
+ var partCounter = 0;
125
176
  if (textParts.length > 0 || fileDataParts.length > 0) {
126
177
  var messageItem = {
127
- id: v4(),
178
+ id: "session-event:".concat(item.id, ":").concat(partCounter++),
128
179
  author: item.author,
129
180
  invocationId: item.invocationId,
130
181
  eventId: item.id,
@@ -165,7 +216,7 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
165
216
  }
166
217
  otherParts.forEach(function (part) {
167
218
  var messageItem = {
168
- id: v4(),
219
+ id: "session-event:".concat(item.id, ":").concat(partCounter++),
169
220
  author: item.author,
170
221
  invocationId: item.invocationId,
171
222
  eventId: item.id,
@@ -195,62 +246,54 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
195
246
  });
196
247
  return mapped;
197
248
  };
198
- var getComparableMessageContent = function getComparableMessageContent(messageItem) {
199
- var _messageItem$text;
200
- return JSON.stringify({
201
- role: messageItem.role,
202
- text: ((_messageItem$text = messageItem.text) === null || _messageItem$text === void 0 ? void 0 : _messageItem$text.replace(/\r\n/g, "\n").trim()) || "",
203
- thought: Boolean(messageItem.thought),
204
- inlineData: messageItem.inlineData || null,
205
- fileData: messageItem.fileData || null,
206
- functionCall: messageItem.functionCall || null,
207
- functionResponse: messageItem.functionResponse || null,
208
- executableCode: messageItem.executableCode || null,
209
- codeExecutionResult: messageItem.codeExecutionResult || null
210
- });
211
- };
212
- var isStreamMessageWithoutServerEventId = function isStreamMessageWithoutServerEventId(messageItem) {
213
- var eventId = getMessageEventId(messageItem);
214
- return Boolean(messageItem.invocationId) && (!eventId || eventId === messageItem.invocationId);
215
- };
216
- var insertMissingSessionEvents = function insertMissingSessionEvents(currentMessages, sessionEvents) {
249
+ var insertMissingSessionEvents = function insertMissingSessionEvents(currentMessages, sessionEvents, removedInvocationIds) {
217
250
  var renderedEventIds = new Set(currentMessages.map(getMessageEventId).filter(Boolean));
218
- var unmatchedStreamMessageIndexes = new Set(currentMessages.map(function (messageItem, index) {
219
- return isStreamMessageWithoutServerEventId(messageItem) ? index : -1;
220
- }).filter(function (index) {
221
- return index >= 0;
222
- }));
223
- var missingEvents = sessionEvents.filter(function (event) {
251
+
252
+ // 从所有 sessionEvents 中提取 functionResponse,配对到 currentMessages 中缺少响应的 functionCall
253
+ // (functionResponse 事件的 role 为 user,不应作为独立消息展示,仅用于补齐工具调用状态)
254
+ var nextMessages = _toConsumableArray(currentMessages);
255
+ var fnResMap = new Map();
256
+ sessionEvents.forEach(function (event) {
224
257
  var _event$content, _event$content2;
225
- var parts = (_event$content = event.content) === null || _event$content === void 0 ? void 0 : _event$content.parts;
226
- var canRender = Boolean(event.id) && ((_event$content2 = event.content) === null || _event$content2 === void 0 ? void 0 : _event$content2.role) === "model" && Array.isArray(parts) && parts.length > 0;
258
+ if (((_event$content = event.content) === null || _event$content === void 0 ? void 0 : _event$content.role) !== "user" || !Array.isArray((_event$content2 = event.content) === null || _event$content2 === void 0 ? void 0 : _event$content2.parts)) return;
259
+ event.content.parts.forEach(function (p) {
260
+ var _p$functionResponse;
261
+ if ((_p$functionResponse = p.functionResponse) !== null && _p$functionResponse !== void 0 && _p$functionResponse.id) {
262
+ fnResMap.set(p.functionResponse.id, p.functionResponse);
263
+ }
264
+ });
265
+ });
266
+ if (fnResMap.size > 0) {
267
+ var paired = false;
268
+ nextMessages = nextMessages.map(function (msg) {
269
+ if (msg.functionCall && !msg.functionResponse) {
270
+ var callId = msg.functionCall.id;
271
+ if (callId && fnResMap.has(callId)) {
272
+ paired = true;
273
+ return _objectSpread(_objectSpread({}, msg), {}, {
274
+ functionResponse: fnResMap.get(callId)
275
+ });
276
+ }
277
+ }
278
+ return msg;
279
+ });
280
+ if (!paired) nextMessages = currentMessages;
281
+ }
282
+ var missingEvents = sessionEvents.filter(function (event) {
283
+ var _event$content3, _event$content4;
284
+ var parts = (_event$content3 = event.content) === null || _event$content3 === void 0 ? void 0 : _event$content3.parts;
285
+ var canRender = Boolean(event.id) && ((_event$content4 = event.content) === null || _event$content4 === void 0 ? void 0 : _event$content4.role) === "model" && Array.isArray(parts) && parts.length > 0;
227
286
  if (!canRender || renderedEventIds.has(event.id)) return false;
228
287
 
229
- // 新版 SSE 包装数据没有事件 id,只能临时使用 invocationId。
230
- // invocationId + 完整渲染内容做一次性身份对齐,避免会话轮询重复插入;
231
- // 同 invocation 下内容不同或后续出现的事件,仍然按独立 event.id 补入。
232
- var eventMessages = mapSessionMessages([event]);
233
- var matchedIndexes = [];
234
- var isRenderedByStream = eventMessages.length > 0 && eventMessages.every(function (eventMessage) {
235
- var comparableContent = getComparableMessageContent(eventMessage);
236
- var matchedIndex = currentMessages.findIndex(function (currentMessage, index) {
237
- return unmatchedStreamMessageIndexes.has(index) && !matchedIndexes.includes(index) && currentMessage.invocationId === eventMessage.invocationId && getComparableMessageContent(currentMessage) === comparableContent;
238
- });
239
- if (matchedIndex < 0) return false;
240
- matchedIndexes.push(matchedIndex);
241
- return true;
242
- });
243
- if (isRenderedByStream) {
244
- matchedIndexes.forEach(function (index) {
245
- return unmatchedStreamMessageIndexes.delete(index);
246
- });
288
+ // reChat 截断后,忽略被删除的 invocationId 对应的旧 bot 消息
289
+ if (removedInvocationIds && removedInvocationIds.size > 0 && event.invocationId && removedInvocationIds.has(event.invocationId)) {
247
290
  return false;
248
291
  }
249
292
  return true;
250
293
  });
251
- if (missingEvents.length === 0) return currentMessages;
294
+ if (missingEvents.length === 0) return nextMessages;
252
295
  var missingMessages = mapSessionMessages(missingEvents);
253
- if (missingMessages.length === 0) return currentMessages;
296
+ if (missingMessages.length === 0) return nextMessages;
254
297
  var eventOrder = new Map(sessionEvents.map(function (event, index) {
255
298
  return [event.id, index];
256
299
  }));
@@ -267,7 +310,8 @@ var insertMissingSessionEvents = function insertMissingSessionEvents(currentMess
267
310
  }
268
311
  return Number.POSITIVE_INFINITY;
269
312
  };
270
- var nextMessages = _toConsumableArray(currentMessages);
313
+
314
+ // nextMessages 已在上方 functionResponse 配对阶段创建,此处直接复用
271
315
  missingMessages.forEach(function (messageItem) {
272
316
  var targetOrder = getMessageOrder(messageItem);
273
317
  var insertIndex = nextMessages.findIndex(function (currentMessage) {
@@ -290,7 +334,7 @@ function useADKChat(_ref2) {
290
334
  _ref2$config = _ref2.config,
291
335
  config = _ref2$config === void 0 ? {} : _ref2$config,
292
336
  _ref2$type = _ref2.type,
293
- type = _ref2$type === void 0 ? "agentDebug" : _ref2$type,
337
+ _type = _ref2$type === void 0 ? "agentDebug" : _ref2$type,
294
338
  _ref2$enabled = _ref2.enabled,
295
339
  enabled = _ref2$enabled === void 0 ? true : _ref2$enabled,
296
340
  strategies = _ref2.strategies,
@@ -366,11 +410,11 @@ function useADKChat(_ref2) {
366
410
  messages = _useState20[0],
367
411
  setMessages = _useState20[1];
368
412
  var pendingSessionEventsRef = useRef(null);
369
- var sessionEventsFlushTimerRef = useRef(null);
370
- var sessionEventsSyncingRef = useRef(false);
371
413
  var sessionDetailLoadingRef = useRef(false);
372
414
  // 用户自定义业务参数 (stateDelta),每次发送请求时携带
373
415
  var stateDeltaRef = useRef(undefined);
416
+ // reChat 截断消息时记录被删除的 invocationId,防止轮询把旧 bot 回复补回
417
+ var reChatRemovedInvocationIdsRef = useRef(new Set());
374
418
 
375
419
  // ===== 多会话后台连接管理 =====
376
420
  var backgroundRef = useRef(new Map());
@@ -378,6 +422,7 @@ function useADKChat(_ref2) {
378
422
  messagesRef.current = messages;
379
423
  var currentSessionIdRef = useRef(currentSessionId);
380
424
  currentSessionIdRef.current = currentSessionId;
425
+ // 合并消息
381
426
  var mergedMessages = useMemo(function () {
382
427
  var fnResMap = {};
383
428
  var uniqueMessages = deduplicateMessages(messages);
@@ -399,33 +444,23 @@ function useADKChat(_ref2) {
399
444
  }, [messages]);
400
445
  var textMsgRef = useRef(null);
401
446
  var eventDataRef = useRef(new Map());
402
- var cancelSessionEventsFlush = useCallback(function () {
403
- if (sessionEventsFlushTimerRef.current === null) return;
404
- clearTimeout(sessionEventsFlushTimerRef.current);
405
- sessionEventsFlushTimerRef.current = null;
406
- }, []);
407
447
  var flushPendingSessionEvents = useCallback(function () {
408
448
  var pending = pendingSessionEventsRef.current;
409
- var replyInProgress = loadingRef.current || sendingRef.current || textMsgRef.current !== null;
449
+ // 仅在用户正在发送或流式文本拼接时跳过 flush,
450
+ // loading 状态不应阻塞轮询补入(异步任务完成的消息需要在 loading 期间也能补入)
451
+ var replyInProgress = sendingRef.current || textMsgRef.current !== null;
410
452
  if (!pending || replyInProgress || pending.sessionId !== currentSessionIdRef.current) return;
411
- var previewMessages = insertMissingSessionEvents(messagesRef.current, pending.events);
412
453
  pendingSessionEventsRef.current = null;
413
- if (previewMessages === messagesRef.current) return;
414
- sessionEventsSyncingRef.current = true;
415
- setLoading(true);
416
- sessionEventsFlushTimerRef.current = setTimeout(function () {
417
- sessionEventsFlushTimerRef.current = null;
418
- if (pending.sessionId === currentSessionIdRef.current) {
419
- setMessages(function (prev) {
420
- var nextMessages = insertMissingSessionEvents(prev, pending.events);
421
- messagesRef.current = nextMessages;
422
- return nextMessages;
423
- });
424
- }
425
- sessionEventsSyncingRef.current = false;
426
- setLoading(false);
427
- }, 0);
428
- }, [setLoading]);
454
+
455
+ // 使用 functional update 在同一个 setMessages 回调中完成 diff + 写入,
456
+ // 避免双重调用和 loading 闪烁。仅在有实际新消息时才触发 loading 状态。
457
+ setMessages(function (prev) {
458
+ var nextMessages = insertMissingSessionEvents(prev, pending.events, reChatRemovedInvocationIdsRef.current);
459
+ if (nextMessages === prev) return prev; // 无变化,不触发 re-render
460
+ messagesRef.current = nextMessages;
461
+ return nextMessages;
462
+ });
463
+ }, []);
429
464
  var syncSessionEvents = useCallback(function (events, targetSessionId) {
430
465
  if (targetSessionId !== currentSessionIdRef.current) return;
431
466
  pendingSessionEventsRef.current = {
@@ -436,12 +471,7 @@ function useADKChat(_ref2) {
436
471
  }, [flushPendingSessionEvents]);
437
472
  useEffect(function () {
438
473
  pendingSessionEventsRef.current = null;
439
- cancelSessionEventsFlush();
440
- if (sessionEventsSyncingRef.current) {
441
- sessionEventsSyncingRef.current = false;
442
- setLoading(false);
443
- }
444
- }, [cancelSessionEventsFlush, currentSessionId, setLoading]);
474
+ }, [currentSessionId]);
445
475
  useEffect(function () {
446
476
  if (!loading) flushPendingSessionEvents();
447
477
  }, [flushPendingSessionEvents, loading]);
@@ -458,21 +488,21 @@ function useADKChat(_ref2) {
458
488
  }, []);
459
489
 
460
490
  // 初始化开场白预设置问题
461
- var insertSuggestedQuestions = function insertSuggestedQuestions() {
491
+ var insertSuggestedQuestions = useCallback(function () {
462
492
  var suggested_questions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
463
493
  if (!(suggested_questions !== null && suggested_questions !== void 0 && suggested_questions.length)) return;
464
494
  // 无消息内容 且 有建议问题时
465
- if (messages.length === 0 && suggested_questions.length) {
466
- setSuggestedQuestions(suggested_questions);
467
- }
468
- };
495
+ setSuggestedQuestions(function (prev) {
496
+ return messages.length === 0 ? suggested_questions : prev;
497
+ });
498
+ }, [messages.length]);
469
499
 
470
500
  // 初始化开场白
471
- var insertPrologue = function insertPrologue(prologue) {
472
- if (prologue) {
473
- setPrologue(prologue);
501
+ var insertPrologue = useCallback(function (prologueText) {
502
+ if (prologueText) {
503
+ setPrologue(prologueText);
474
504
  }
475
- };
505
+ }, []);
476
506
 
477
507
  // 更新消息
478
508
  var updateMessage = useCallback(function (msg) {
@@ -539,48 +569,15 @@ function useADKChat(_ref2) {
539
569
 
540
570
  // 存储消息
541
571
  var storeMessage = useCallback(function (part, event, role) {
542
- var msg = {
543
- id: v4(),
544
- author: event.author,
545
- invocationId: event.invocationId,
546
- eventId: event.id,
547
- timestamp: event.timestamp,
548
- isLike: event.isLike || 0,
549
- role: role,
550
- modelCode: event.modelCode,
551
- usageMetadata: event.usageMetadata,
552
- finishReason: event.finishReason,
553
- raw: event
554
- };
555
- if (part) {
556
- if (part.inlineData) {
557
- msg.inlineData = {
558
- displayName: part.inlineData.displayName,
559
- data: part.inlineData.data,
560
- mimeType: part.inlineData.mimeType
561
- };
562
- } else if (part.text) {
563
- var _event$groundingMetad;
564
- msg.text = part.text;
565
- msg.thought = part.thought;
566
- if (event !== null && event !== void 0 && (_event$groundingMetad = event.groundingMetadata) !== null && _event$groundingMetad !== void 0 && (_event$groundingMetad = _event$groundingMetad.searchEntryPoint) !== null && _event$groundingMetad !== void 0 && _event$groundingMetad.renderedContent) {
567
- msg.renderedContent = event.groundingMetadata.searchEntryPoint.renderedContent;
568
- }
569
- } else if (part.fileData) {
570
- msg.fileData = [part.fileData];
571
- } else if (part.functionCall) {
572
- msg.functionCall = part.functionCall;
573
- } else if (part.functionResponse) {
574
- msg.functionResponse = part.functionResponse;
575
- } else if (part.executableCode) {
576
- msg.executableCode = part.executableCode;
577
- } else if (part.codeExecutionResult) {
578
- msg.codeExecutionResult = part.codeExecutionResult;
579
- }
580
- }
581
- if (part && Object.keys(part).length > 0) {
582
- insertMessage(msg);
572
+ var _event$groundingMetad;
573
+ if (!part || Object.keys(part).length === 0) return;
574
+ var msg = buildNonTextMessage(event, part, role);
575
+
576
+ // groundingMetadata 仅在前台 SSE 场景需要
577
+ if (part.text && event !== null && event !== void 0 && (_event$groundingMetad = event.groundingMetadata) !== null && _event$groundingMetad !== void 0 && (_event$groundingMetad = _event$groundingMetad.searchEntryPoint) !== null && _event$groundingMetad !== void 0 && _event$groundingMetad.renderedContent) {
578
+ msg.renderedContent = event.groundingMetadata.searchEntryPoint.renderedContent;
583
579
  }
580
+ insertMessage(msg);
584
581
  }, [insertMessage]);
585
582
 
586
583
  // 处理消息部分(关键更新)
@@ -639,9 +636,9 @@ function useADKChat(_ref2) {
639
636
 
640
637
  // 处理建议类消息
641
638
  var processFollowupPart = useCallback(function (event) {
642
- var _event$content3;
639
+ var _event$content5;
643
640
  // 获取建议类消息的文本内容
644
- var part = (event === null || event === void 0 || (_event$content3 = event.content) === null || _event$content3 === void 0 || (_event$content3 = _event$content3.parts) === null || _event$content3 === void 0 ? void 0 : _event$content3[0]) || null;
641
+ var part = (event === null || event === void 0 || (_event$content5 = event.content) === null || _event$content5 === void 0 || (_event$content5 = _event$content5.parts) === null || _event$content5 === void 0 ? void 0 : _event$content5[0]) || null;
645
642
  var text = (part === null || part === void 0 ? void 0 : part.text) || "";
646
643
  storeEvents(part, event);
647
644
  if (text) {
@@ -649,7 +646,7 @@ function useADKChat(_ref2) {
649
646
  return [].concat(_toConsumableArray(prev), [text]);
650
647
  });
651
648
  }
652
- }, [storeEvents, insertMessage]);
649
+ }, [storeEvents]);
653
650
 
654
651
  // 处理错误消息
655
652
  var processErrorMessage = useCallback(function (chunkJson) {
@@ -795,20 +792,7 @@ function useADKChat(_ref2) {
795
792
  }
796
793
  } else {
797
794
  bgTextMsg = null;
798
- var msg = {
799
- id: v4(),
800
- author: chunkJson.author,
801
- invocationId: chunkJson.invocationId,
802
- eventId: chunkJson.id,
803
- timestamp: chunkJson.timestamp,
804
- role: "bot"
805
- };
806
- if (part.functionCall) msg.functionCall = part.functionCall;else if (part.functionResponse) msg.functionResponse = part.functionResponse;else if (part.executableCode) msg.executableCode = part.executableCode;else if (part.codeExecutionResult) msg.codeExecutionResult = part.codeExecutionResult;else {
807
- msg.text = part.text;
808
- msg.thought = part.thought;
809
- if (part.fileData) msg.fileData = [part.fileData];
810
- }
811
- bg.messages = [].concat(_toConsumableArray(bg.messages), [msg]);
795
+ bg.messages = [].concat(_toConsumableArray(bg.messages), [buildNonTextMessage(chunkJson, part)]);
812
796
  }
813
797
  }
814
798
  } catch (err) {
@@ -843,7 +827,7 @@ function useADKChat(_ref2) {
843
827
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
844
828
  var _chunkJson2;
845
829
  var _part = _step3.value;
846
- // partial为 true 时忽略该条消息文本
830
+ // partial为 false 时忽略该条消息文本
847
831
  processPart(chunkJson, _part, ((_chunkJson2 = chunkJson) === null || _chunkJson2 === void 0 ? void 0 : _chunkJson2.partial) === false);
848
832
  }
849
833
  } catch (err) {
@@ -865,6 +849,12 @@ function useADKChat(_ref2) {
865
849
  bg.loading = false;
866
850
  bg.textMsgRef = null;
867
851
  }
852
+ // 如果该后台会话已被恢复到前台(backgroundRef 中已删除),
853
+ // 需要更新前台 loading 状态
854
+ if (!backgroundRef.current.has(targetSessionId) && currentSessionIdRef.current === targetSessionId) {
855
+ setLoading(false);
856
+ textMsgRef.current = null;
857
+ }
868
858
  } else {
869
859
  setLoading(false);
870
860
  if (textMsgRef.current) {
@@ -872,6 +862,7 @@ function useADKChat(_ref2) {
872
862
  onMessage === null || onMessage === void 0 || onMessage(((_textMsgRef$current = textMsgRef.current) === null || _textMsgRef$current === void 0 ? void 0 : _textMsgRef$current.text) || "", textMsgRef.current);
873
863
  }
874
864
  textMsgRef.current = null;
865
+ ctrl.current = null;
875
866
  }
876
867
  resolve();
877
868
  },
@@ -880,8 +871,14 @@ function useADKChat(_ref2) {
880
871
  if (isBackground) {
881
872
  var bg = backgroundRef.current.get(targetSessionId);
882
873
  if (bg) bg.loading = false;
874
+ // 同上:后台会话已恢复到前台时同步 loading 状态
875
+ if (!backgroundRef.current.has(targetSessionId) && currentSessionIdRef.current === targetSessionId) {
876
+ setLoading(false);
877
+ ctrl.current = null;
878
+ }
883
879
  } else {
884
880
  setLoading(false);
881
+ ctrl.current = null;
885
882
  }
886
883
  resolve();
887
884
  console.error("EventSource failed:", error);
@@ -889,7 +886,7 @@ function useADKChat(_ref2) {
889
886
  }
890
887
  });
891
888
  });
892
- }, [type, currentSessionId, processActionArtifact, processErrorMessage, processPart, updateLastBotEventMeta]);
889
+ }, [processActionArtifact, processErrorMessage, processPart, updateLastBotEventMeta]);
893
890
 
894
891
  // 发送会话
895
892
  var startChat = /*#__PURE__*/function () {
@@ -914,6 +911,8 @@ function useADKChat(_ref2) {
914
911
  sendingRef.current = true;
915
912
  // 清空建议问题
916
913
  setSuggestedQuestions([]);
914
+ // 正常发送时重置 reChat 删除标记
915
+ reChatRemovedInvocationIdsRef.current = new Set();
917
916
  // 发送消息
918
917
  setMessages(function (prev) {
919
918
  var temp = _toConsumableArray(prev);
@@ -950,8 +949,8 @@ function useADKChat(_ref2) {
950
949
  }
951
950
  return temp;
952
951
  });
953
- _context2.prev = 8;
954
- _context2.next = 11;
952
+ _context2.prev = 9;
953
+ _context2.next = 12;
955
954
  return sendMessageRequest(_objectSpread({
956
955
  stream: true,
957
956
  sessionId: currentSessionId,
@@ -976,18 +975,18 @@ function useADKChat(_ref2) {
976
975
  }, stateDeltaRef.current !== undefined && {
977
976
  stateDelta: stateDeltaRef.current
978
977
  }));
979
- case 11:
980
- _context2.prev = 11;
978
+ case 12:
979
+ _context2.prev = 12;
981
980
  sendingRef.current = false;
982
- return _context2.finish(11);
983
- case 14:
981
+ return _context2.finish(12);
982
+ case 15:
984
983
  // 获取聊天记录
985
984
  initAppConversations();
986
- case 15:
985
+ case 16:
987
986
  case "end":
988
987
  return _context2.stop();
989
988
  }
990
- }, _callee2, null, [[8,, 11, 14]]);
989
+ }, _callee2, null, [[9,, 12, 15]]);
991
990
  }));
992
991
  return function startChat(_x) {
993
992
  return _ref5.apply(this, arguments);
@@ -997,7 +996,7 @@ function useADKChat(_ref2) {
997
996
  // 重新发送会话
998
997
  var reChat = /*#__PURE__*/function () {
999
998
  var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
1000
- var lastUserIndex, newMessages, lastUserMessage, _lastUserMessage$text, text, _lastUserMessage$file, fileData;
999
+ var lastUserIndex, newMessages, removedInvocationIds, i, msg, lastUserMessage, _lastUserMessage$text, text, _lastUserMessage$file, fileData;
1001
1000
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
1002
1001
  while (1) switch (_context3.prev = _context3.next) {
1003
1002
  case 0:
@@ -1030,10 +1029,18 @@ function useADKChat(_ref2) {
1030
1029
  case 10:
1031
1030
  newMessages = messages.slice(0, lastUserIndex + 1);
1032
1031
  setMessages(newMessages);
1032
+
1033
+ // 收集被截断的 bot 消息的 invocationId,轮询时忽略这些旧回复
1034
+ removedInvocationIds = new Set();
1035
+ for (i = lastUserIndex + 1; i < messages.length; i++) {
1036
+ msg = messages[i];
1037
+ if (msg.invocationId) removedInvocationIds.add(msg.invocationId);
1038
+ }
1039
+ reChatRemovedInvocationIdsRef.current = removedInvocationIds;
1033
1040
  lastUserMessage = newMessages[lastUserIndex];
1034
1041
  _lastUserMessage$text = lastUserMessage.text, text = _lastUserMessage$text === void 0 ? "" : _lastUserMessage$text, _lastUserMessage$file = lastUserMessage.fileData, fileData = _lastUserMessage$file === void 0 ? [] : _lastUserMessage$file;
1035
- _context3.prev = 14;
1036
- _context3.next = 17;
1042
+ _context3.prev = 17;
1043
+ _context3.next = 20;
1037
1044
  return sendMessageRequest(_objectSpread({
1038
1045
  stream: true,
1039
1046
  sessionId: currentSessionId,
@@ -1053,15 +1060,15 @@ function useADKChat(_ref2) {
1053
1060
  }, stateDeltaRef.current !== undefined && {
1054
1061
  stateDelta: stateDeltaRef.current
1055
1062
  }));
1056
- case 17:
1057
- _context3.prev = 17;
1058
- sendingRef.current = false;
1059
- return _context3.finish(17);
1060
1063
  case 20:
1064
+ _context3.prev = 20;
1065
+ sendingRef.current = false;
1066
+ return _context3.finish(20);
1067
+ case 23:
1061
1068
  case "end":
1062
1069
  return _context3.stop();
1063
1070
  }
1064
- }, _callee3, null, [[14,, 17, 20]]);
1071
+ }, _callee3, null, [[17,, 20, 23]]);
1065
1072
  }));
1066
1073
  return function reChat() {
1067
1074
  return _ref6.apply(this, arguments);
@@ -1097,19 +1104,19 @@ function useADKChat(_ref2) {
1097
1104
  var stopChat = useCallback(function () {
1098
1105
  var _ctrl$current;
1099
1106
  pendingSessionEventsRef.current = null;
1100
- cancelSessionEventsFlush();
1101
- sessionEventsSyncingRef.current = false;
1102
1107
  (_ctrl$current = ctrl.current) === null || _ctrl$current === void 0 || _ctrl$current.abort();
1103
1108
  ctrl.current = null;
1104
1109
  setLoading(false);
1105
1110
  textMsgRef.current = null;
1106
- }, [cancelSessionEventsFlush, setLoading]);
1111
+ }, [setLoading]);
1107
1112
 
1108
1113
  // 清除/重置会话
1109
1114
  var clearChat = function clearChat() {
1110
1115
  var _appInfo$onboardingIn, _appInfo$onboardingIn2;
1111
1116
  var newSessionId = v4();
1112
1117
  setCurrentSessionId(newSessionId);
1118
+ // 清除 reChat 删除标记
1119
+ reChatRemovedInvocationIdsRef.current = new Set();
1113
1120
  // 重置聊天内容
1114
1121
  stopChat();
1115
1122
  setPrologue((appInfo === null || appInfo === void 0 || (_appInfo$onboardingIn = appInfo.onboardingInfo) === null || _appInfo$onboardingIn === void 0 ? void 0 : _appInfo$onboardingIn.prologue) || "");
@@ -1129,12 +1136,12 @@ function useADKChat(_ref2) {
1129
1136
  // 设置当前会话详情
1130
1137
  var setCurrentSession = /*#__PURE__*/function () {
1131
1138
  var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(sessionId) {
1132
- var prevSessionId, bg, _yield$fetchSessionDe, data, result, _appInfo$onboardingIn3, _appInfo$onboardingIn4;
1139
+ var prevSessionId, bg, isSseStillActive, _yield$fetchSessionDe, data, result, _appInfo$onboardingIn3, _appInfo$onboardingIn4;
1133
1140
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
1134
1141
  while (1) switch (_context4.prev = _context4.next) {
1135
1142
  case 0:
1136
1143
  if (!sessionId) {
1137
- _context4.next = 34;
1144
+ _context4.next = 35;
1138
1145
  break;
1139
1146
  }
1140
1147
  if (!(sessionId === currentSessionId)) {
@@ -1144,11 +1151,8 @@ function useADKChat(_ref2) {
1144
1151
  return _context4.abrupt("return");
1145
1152
  case 3:
1146
1153
  pendingSessionEventsRef.current = null;
1147
- cancelSessionEventsFlush();
1148
- if (sessionEventsSyncingRef.current) {
1149
- sessionEventsSyncingRef.current = false;
1150
- setLoading(false);
1151
- }
1154
+ // 切换会话时重置 reChat 删除标记
1155
+ reChatRemovedInvocationIdsRef.current = new Set();
1152
1156
 
1153
1157
  // 1. 将当前会话挂起到后台(不中断 SSE 连接)
1154
1158
  prevSessionId = currentSessionId;
@@ -1176,31 +1180,35 @@ function useADKChat(_ref2) {
1176
1180
  // 2. 尝试从后台恢复目标会话
1177
1181
  bg = backgroundRef.current.get(sessionId);
1178
1182
  if (!bg) {
1179
- _context4.next = 18;
1183
+ _context4.next = 19;
1180
1184
  break;
1181
1185
  }
1182
1186
  setMessages(bg.messages);
1183
- setLoading(bg.loading);
1187
+ messagesRef.current = bg.messages;
1184
1188
  textMsgRef.current = bg.textMsgRef;
1185
- ctrl.current = bg.loading ? bg.ctrl : null;
1189
+ // 仅当 SSE 连接还在运行时才恢复 loading 状态,
1190
+ // 避免 SSE 已关闭但 bg 快照中 loading 仍为 true 导致 sender 永久卡住
1191
+ isSseStillActive = bg.loading && !bg.ctrl.signal.aborted;
1192
+ setLoading(isSseStillActive);
1193
+ ctrl.current = isSseStillActive ? bg.ctrl : null;
1186
1194
  setCurrentSessionId(sessionId);
1187
1195
  setInitialized(true);
1188
1196
  backgroundRef.current.delete(sessionId);
1189
1197
  return _context4.abrupt("return");
1190
- case 18:
1198
+ case 19:
1191
1199
  // 3. 后台没有目标 session,走 API 加载(原有逻辑)
1192
1200
  setInitialized(false);
1193
1201
  setCurrentSessionId(sessionId);
1194
1202
  sessionDetailLoadingRef.current = true;
1195
- _context4.prev = 21;
1196
- _context4.next = 24;
1203
+ _context4.prev = 22;
1204
+ _context4.next = 25;
1197
1205
  return fetchSessionDetail({
1198
1206
  url: url,
1199
1207
  appNo: appNo,
1200
1208
  sessionId: sessionId,
1201
1209
  token: tokenRef.current
1202
1210
  });
1203
- case 24:
1211
+ case 25:
1204
1212
  _yield$fetchSessionDe = _context4.sent;
1205
1213
  data = _yield$fetchSessionDe.data;
1206
1214
  result = _yield$fetchSessionDe.result;
@@ -1212,21 +1220,21 @@ function useADKChat(_ref2) {
1212
1220
  setSuggestedQuestions((appInfo === null || appInfo === void 0 || (_appInfo$onboardingIn4 = appInfo.onboardingInfo) === null || _appInfo$onboardingIn4 === void 0 ? void 0 : _appInfo$onboardingIn4.suggested_questions) || []);
1213
1221
  }
1214
1222
  }
1215
- case 28:
1216
- _context4.prev = 28;
1223
+ case 29:
1224
+ _context4.prev = 29;
1217
1225
  sessionDetailLoadingRef.current = false;
1218
1226
  setInitialized(true);
1219
- return _context4.finish(28);
1220
- case 32:
1221
- _context4.next = 35;
1227
+ return _context4.finish(29);
1228
+ case 33:
1229
+ _context4.next = 36;
1222
1230
  break;
1223
- case 34:
1224
- setCurrentSessionId(v4());
1225
1231
  case 35:
1232
+ setCurrentSessionId(v4());
1233
+ case 36:
1226
1234
  case "end":
1227
1235
  return _context4.stop();
1228
1236
  }
1229
- }, _callee4, null, [[21,, 28, 32]]);
1237
+ }, _callee4, null, [[22,, 29, 33]]);
1230
1238
  }));
1231
1239
  return function setCurrentSession(_x2) {
1232
1240
  return _ref7.apply(this, arguments);
@@ -1561,7 +1569,7 @@ function useADKChat(_ref2) {
1561
1569
  return _regeneratorRuntime().wrap(function _callee10$(_context10) {
1562
1570
  while (1) switch (_context10.prev = _context10.next) {
1563
1571
  case 0:
1564
- if (!(requesting || sessionDetailLoadingRef.current)) {
1572
+ if (!(disposed || requesting || sessionDetailLoadingRef.current)) {
1565
1573
  _context10.next = 2;
1566
1574
  break;
1567
1575
  }
@@ -1602,7 +1610,8 @@ function useADKChat(_ref2) {
1602
1610
  return _ref13.apply(this, arguments);
1603
1611
  };
1604
1612
  }();
1605
- void poll();
1613
+
1614
+ // 不立即执行首次 poll,避免与 initAppConversations / setCurrentSession / fetchSessionDetail 竞争。首次数据由初始化流程加载,轮询仅负责后续增量同步。
1606
1615
  var timer = window.setInterval(poll, sessionEventsPollingInterval);
1607
1616
  return function () {
1608
1617
  disposed = true;
@@ -1614,7 +1623,6 @@ function useADKChat(_ref2) {
1614
1623
  useEffect(function () {
1615
1624
  return function () {
1616
1625
  var _ctrl$current2;
1617
- cancelSessionEventsFlush();
1618
1626
  (_ctrl$current2 = ctrl.current) === null || _ctrl$current2 === void 0 || _ctrl$current2.abort();
1619
1627
  backgroundRef.current.forEach(function (bg) {
1620
1628
  var _bg$ctrl;
@@ -1622,7 +1630,7 @@ function useADKChat(_ref2) {
1622
1630
  });
1623
1631
  backgroundRef.current.clear();
1624
1632
  };
1625
- }, [cancelSessionEventsFlush]);
1633
+ }, []);
1626
1634
  return {
1627
1635
  appInfo: appInfo,
1628
1636
  startChat: startChat,