@ai-group/chat-sdk 3.6.7 → 3.6.9

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