@yumiai/chat-widget 0.1.0 → 0.1.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/dist/index.d.cts CHANGED
@@ -139,6 +139,8 @@ interface Round {
139
139
  plan?: TaskPlan;
140
140
  messages: AggregatedMessage[];
141
141
  topPosition?: number;
142
+ /** 本轮次内已收到 agent_end 的 agent_instance_id 集合,与 notification_turns 的 agent start/stop 对齐,用于子 Agent 卡片「已完成」判断 */
143
+ endedAgentInstanceIds?: Set<number>;
142
144
  }
143
145
  type ExecutionStatus = 'idle' | 'running' | 'compressing' | 'completed' | 'error';
144
146
  interface ChatWidgetConfig {
@@ -273,6 +275,8 @@ interface ChildAgentCardProps {
273
275
  config?: ChatWidgetConfig;
274
276
  defaultExpanded?: boolean;
275
277
  parentTaskPurpose?: string;
278
+ /** 本轮次已收到 agent_end 的 agent_instance_id(与 notification_turns 对齐),有则优先据此显示「已完成」 */
279
+ endedAgentInstanceIds?: Set<number>;
276
280
  }
277
281
  interface PlanCardProps {
278
282
  plan: TaskPlan;
@@ -331,6 +335,8 @@ declare function useSSE(options: UseSSEOptions): UseSSEReturn;
331
335
  interface MessageAggregatorState {
332
336
  rounds: Map<string, Round>;
333
337
  currentInteractionId: string | null;
338
+ /** 每轮次已收到 agent_end 的 agent_instance_id,与 notification_turns 的 turn_type=agent_end 对齐 */
339
+ endedAgentInstanceIdsByRound: Map<string, Set<number>>;
334
340
  }
335
341
  interface UseMessageAggregatorReturn {
336
342
  state: MessageAggregatorState;
package/dist/index.d.ts CHANGED
@@ -139,6 +139,8 @@ interface Round {
139
139
  plan?: TaskPlan;
140
140
  messages: AggregatedMessage[];
141
141
  topPosition?: number;
142
+ /** 本轮次内已收到 agent_end 的 agent_instance_id 集合,与 notification_turns 的 agent start/stop 对齐,用于子 Agent 卡片「已完成」判断 */
143
+ endedAgentInstanceIds?: Set<number>;
142
144
  }
143
145
  type ExecutionStatus = 'idle' | 'running' | 'compressing' | 'completed' | 'error';
144
146
  interface ChatWidgetConfig {
@@ -273,6 +275,8 @@ interface ChildAgentCardProps {
273
275
  config?: ChatWidgetConfig;
274
276
  defaultExpanded?: boolean;
275
277
  parentTaskPurpose?: string;
278
+ /** 本轮次已收到 agent_end 的 agent_instance_id(与 notification_turns 对齐),有则优先据此显示「已完成」 */
279
+ endedAgentInstanceIds?: Set<number>;
276
280
  }
277
281
  interface PlanCardProps {
278
282
  plan: TaskPlan;
@@ -331,6 +335,8 @@ declare function useSSE(options: UseSSEOptions): UseSSEReturn;
331
335
  interface MessageAggregatorState {
332
336
  rounds: Map<string, Round>;
333
337
  currentInteractionId: string | null;
338
+ /** 每轮次已收到 agent_end 的 agent_instance_id,与 notification_turns 的 turn_type=agent_end 对齐 */
339
+ endedAgentInstanceIdsByRound: Map<string, Set<number>>;
334
340
  }
335
341
  interface UseMessageAggregatorReturn {
336
342
  state: MessageAggregatorState;
package/dist/index.js CHANGED
@@ -55,7 +55,8 @@ function extractTaskPurposeFromPreview(preview) {
55
55
  function useMessageAggregator() {
56
56
  const [state, setState] = useState({
57
57
  rounds: /* @__PURE__ */ new Map(),
58
- currentInteractionId: null
58
+ currentInteractionId: null,
59
+ endedAgentInstanceIdsByRound: /* @__PURE__ */ new Map()
59
60
  });
60
61
  const [todoMap, setTodoMap] = useState(/* @__PURE__ */ new Map());
61
62
  const todoMapRef = useRef(/* @__PURE__ */ new Map());
@@ -65,6 +66,7 @@ function useMessageAggregator() {
65
66
  const roundIndexRef = useRef(/* @__PURE__ */ new Map());
66
67
  const processedChunksRef = useRef(/* @__PURE__ */ new Set());
67
68
  const callBatchToRoundRef = useRef(/* @__PURE__ */ new Map());
69
+ const endedAgentInstanceIdsByRoundRef = useRef(/* @__PURE__ */ new Map());
68
70
  const parsePlan = useCallback((content) => {
69
71
  try {
70
72
  const parsed = JSON.parse(content);
@@ -206,6 +208,18 @@ function useMessageAggregator() {
206
208
  } else if (!existingRoundForBatch) {
207
209
  callBatchToRoundRef.current.set(call_batch_id, interaction_id);
208
210
  }
211
+ if (msg.notification_type === "agent_end") {
212
+ const iid = interaction_id;
213
+ const aid = msg.agent_instance_id;
214
+ if (iid != null && aid != null) {
215
+ const prev = endedAgentInstanceIdsByRoundRef.current;
216
+ const set = new Set(prev.get(iid) || []);
217
+ set.add(aid);
218
+ endedAgentInstanceIdsByRoundRef.current = new Map(prev).set(iid, set);
219
+ }
220
+ latestInteractionId = interaction_id;
221
+ continue;
222
+ }
209
223
  let round = roundIndexRef.current.get(interaction_id);
210
224
  if (!round) {
211
225
  round = {
@@ -353,9 +367,14 @@ function useMessageAggregator() {
353
367
  }
354
368
  setState(() => {
355
369
  const newRounds = new Map(roundIndexRef.current);
370
+ const endedByRound = endedAgentInstanceIdsByRoundRef.current;
371
+ const endedAgentInstanceIdsByRound = new Map(
372
+ [...endedByRound.entries()].map(([k, v]) => [k, new Set(v)])
373
+ );
356
374
  return {
357
375
  rounds: newRounds,
358
- currentInteractionId: latestInteractionId
376
+ currentInteractionId: latestInteractionId,
377
+ endedAgentInstanceIdsByRound
359
378
  };
360
379
  });
361
380
  if (todoMapChanged) {
@@ -370,8 +389,12 @@ function useMessageAggregator() {
370
389
  }
371
390
  }, [flushUpdates]);
372
391
  const getRoundsList = useCallback(() => {
373
- return Array.from(state.rounds.values()).sort((a, b) => a.index - b.index);
374
- }, [state.rounds]);
392
+ const endedByRound = state.endedAgentInstanceIdsByRound;
393
+ return Array.from(state.rounds.values()).map((r) => ({
394
+ ...r,
395
+ endedAgentInstanceIds: endedByRound.get(r.interactionId) || /* @__PURE__ */ new Set()
396
+ })).sort((a, b) => a.index - b.index);
397
+ }, [state.rounds, state.endedAgentInstanceIdsByRound]);
375
398
  const getCurrentRound = useCallback(() => {
376
399
  if (!state.currentInteractionId) return null;
377
400
  return state.rounds.get(state.currentInteractionId) || null;
@@ -391,7 +414,8 @@ function useMessageAggregator() {
391
414
  newRounds.set(interactionId, round);
392
415
  return {
393
416
  rounds: newRounds,
394
- currentInteractionId: interactionId
417
+ currentInteractionId: interactionId,
418
+ endedAgentInstanceIdsByRound: prevState.endedAgentInstanceIdsByRound
395
419
  };
396
420
  });
397
421
  }, []);
@@ -400,6 +424,7 @@ function useMessageAggregator() {
400
424
  messageIndexRef.current.clear();
401
425
  callBatchToRoundRef.current.clear();
402
426
  processedChunksRef.current.clear();
427
+ endedAgentInstanceIdsByRoundRef.current.clear();
403
428
  const newRounds = /* @__PURE__ */ new Map();
404
429
  for (const round of rounds) {
405
430
  newRounds.set(round.interactionId, round);
@@ -412,7 +437,7 @@ function useMessageAggregator() {
412
437
  }
413
438
  }
414
439
  const lastId = rounds.length > 0 ? rounds[rounds.length - 1].interactionId : null;
415
- setState({ rounds: newRounds, currentInteractionId: lastId });
440
+ setState({ rounds: newRounds, currentInteractionId: lastId, endedAgentInstanceIdsByRound: /* @__PURE__ */ new Map() });
416
441
  }, []);
417
442
  const finalizeRound = useCallback((interactionId) => {
418
443
  const round = roundIndexRef.current.get(interactionId);
@@ -428,9 +453,11 @@ function useMessageAggregator() {
428
453
  }
429
454
  }
430
455
  if (changed) {
456
+ const endedByRound = endedAgentInstanceIdsByRoundRef.current;
431
457
  setState(() => ({
432
458
  rounds: new Map(roundIndexRef.current),
433
- currentInteractionId: interactionId
459
+ currentInteractionId: interactionId,
460
+ endedAgentInstanceIdsByRound: new Map([...endedByRound.entries()].map(([k, v]) => [k, new Set(v)]))
434
461
  }));
435
462
  }
436
463
  }, []);
@@ -440,6 +467,7 @@ function useMessageAggregator() {
440
467
  roundIndexRef.current.clear();
441
468
  processedChunksRef.current.clear();
442
469
  callBatchToRoundRef.current.clear();
470
+ endedAgentInstanceIdsByRoundRef.current.clear();
443
471
  todoMapRef.current.clear();
444
472
  if (rafId.current) {
445
473
  cancelAnimationFrame(rafId.current);
@@ -447,7 +475,8 @@ function useMessageAggregator() {
447
475
  }
448
476
  setState({
449
477
  rounds: /* @__PURE__ */ new Map(),
450
- currentInteractionId: null
478
+ currentInteractionId: null,
479
+ endedAgentInstanceIdsByRound: /* @__PURE__ */ new Map()
451
480
  });
452
481
  setTodoMap(/* @__PURE__ */ new Map());
453
482
  }, []);
@@ -2219,7 +2248,7 @@ var ArtifactCard = ({ artifact, onClick }) => {
2219
2248
  var MessageContent_default = MessageContent;
2220
2249
 
2221
2250
  // src/components/ChildAgentCard.tsx
2222
- import { useState as useState10, useRef as useRef5, useEffect as useEffect3 } from "react";
2251
+ import { useState as useState10, useRef as useRef5, useEffect as useEffect3, useMemo as useMemo4 } from "react";
2223
2252
  import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
2224
2253
  var ChildAgentCard = ({
2225
2254
  message,
@@ -2228,14 +2257,20 @@ var ChildAgentCard = ({
2228
2257
  config,
2229
2258
  onArtifactClick,
2230
2259
  defaultExpanded = false,
2231
- parentTaskPurpose
2260
+ parentTaskPurpose,
2261
+ endedAgentInstanceIds
2232
2262
  }) => {
2233
2263
  const [isExpanded, setIsExpanded] = useState10(defaultExpanded);
2234
2264
  const contentRef = useRef5(null);
2235
- const hasActiveContent = children.some(
2265
+ const allMessages = useMemo4(() => [message, ...children], [message, children]);
2266
+ const hasExplicitEnd = endedAgentInstanceIds != null && endedAgentInstanceIds.has(message.agentInstanceId);
2267
+ const hasActiveContent = allMessages.some(
2236
2268
  (m) => m.contentType === "text" && m.contentChunks.join("").trim()
2237
2269
  );
2238
- const status = hasActiveContent ? "completed" : "running";
2270
+ const hasInProgressTool = allMessages.some(
2271
+ (m) => m.toolPhase === "generating" || m.toolPhase === "executing"
2272
+ );
2273
+ const status = hasExplicitEnd ? "completed" : hasActiveContent && !hasInProgressTool ? "completed" : "running";
2239
2274
  useEffect3(() => {
2240
2275
  if (isExpanded && contentRef.current) {
2241
2276
  contentRef.current.scrollTop = contentRef.current.scrollHeight;
@@ -2711,7 +2746,8 @@ var ChatWidget = ({
2711
2746
  config,
2712
2747
  onArtifactClick: handleArtifactClick,
2713
2748
  defaultExpanded: true,
2714
- parentTaskPurpose: msg.taskPurpose
2749
+ parentTaskPurpose: msg.taskPurpose,
2750
+ endedAgentInstanceIds: round.endedAgentInstanceIds
2715
2751
  },
2716
2752
  `child-${childKey}-${agentInstanceId}`
2717
2753
  )