@blade-hq/agent-kit 0.5.5 → 0.5.7

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.
Files changed (34) hide show
  1. package/dist/{SkillStatusBar-BtvQmvxZ.d.ts → SkillStatusBar-Dlf-_G5d.d.ts} +2 -2
  2. package/dist/{blade-client-CqWTKQhP.d.ts → blade-client-7VANnJfr.d.ts} +6 -3
  3. package/dist/{chunk-Q6CSM5DE.js → chunk-ETHPRRT2.js} +11 -4
  4. package/dist/chunk-ETHPRRT2.js.map +1 -0
  5. package/dist/{chunk-557R7QZV.js → chunk-GIE2Q2MB.js} +28 -16
  6. package/dist/chunk-GIE2Q2MB.js.map +1 -0
  7. package/dist/{chunk-S6EPGDAL.js → chunk-K5EE7X2D.js} +152 -73
  8. package/dist/chunk-K5EE7X2D.js.map +1 -0
  9. package/dist/{chunk-NEI66DW6.js → chunk-STCTXRMJ.js} +2 -2
  10. package/dist/{chunk-HWOVPFWG.js → chunk-UM7G65GH.js} +116 -20
  11. package/dist/chunk-UM7G65GH.js.map +1 -0
  12. package/dist/{chunk-3ZEWA2YM.js → chunk-X3S36RR2.js} +2 -2
  13. package/dist/client/index.d.ts +111 -4
  14. package/dist/client/index.js +1 -1
  15. package/dist/react/api/vibe-coding.d.ts +3 -3
  16. package/dist/react/api/vibe-coding.js +2 -2
  17. package/dist/react/components/chat/index.d.ts +6 -5
  18. package/dist/react/components/chat/index.js +5 -5
  19. package/dist/react/components/plan/index.js +3 -3
  20. package/dist/react/components/session/index.d.ts +1 -1
  21. package/dist/react/components/session/index.js +3 -3
  22. package/dist/react/components/workspace/index.js +3 -3
  23. package/dist/react/index.d.ts +177 -9
  24. package/dist/react/index.js +469 -6
  25. package/dist/react/index.js.map +1 -1
  26. package/dist/{session-ADRevzHD.d.ts → session-BuaeCsMC.d.ts} +62 -2
  27. package/dist/style.css +1 -1
  28. package/package.json +1 -1
  29. package/dist/chunk-557R7QZV.js.map +0 -1
  30. package/dist/chunk-HWOVPFWG.js.map +0 -1
  31. package/dist/chunk-Q6CSM5DE.js.map +0 -1
  32. package/dist/chunk-S6EPGDAL.js.map +0 -1
  33. /package/dist/{chunk-NEI66DW6.js.map → chunk-STCTXRMJ.js.map} +0 -0
  34. /package/dist/{chunk-3ZEWA2YM.js.map → chunk-X3S36RR2.js.map} +0 -0
@@ -8,13 +8,13 @@ import {
8
8
  getCodeLanguageFromFilename,
9
9
  parseAskUserQuestion,
10
10
  useHighlightedCodeHtml
11
- } from "./chunk-557R7QZV.js";
11
+ } from "./chunk-GIE2Q2MB.js";
12
12
  import {
13
13
  Collapsible,
14
14
  CollapsibleContent,
15
15
  CollapsibleTrigger,
16
16
  resolveSessionFilePreviewTarget
17
- } from "./chunk-3ZEWA2YM.js";
17
+ } from "./chunk-X3S36RR2.js";
18
18
  import {
19
19
  buildMessageContent,
20
20
  buildToolPreviewKey,
@@ -57,7 +57,7 @@ import {
57
57
  useUiBridgeStore,
58
58
  useUiStore,
59
59
  writeFile
60
- } from "./chunk-HWOVPFWG.js";
60
+ } from "./chunk-UM7G65GH.js";
61
61
  import {
62
62
  registerBridgeIframe,
63
63
  tapBridgeEvent
@@ -66,7 +66,7 @@ import {
66
66
  ModelOption,
67
67
  ModelsConfig,
68
68
  ModelsResource
69
- } from "./chunk-Q6CSM5DE.js";
69
+ } from "./chunk-ETHPRRT2.js";
70
70
  import {
71
71
  cn,
72
72
  copyToClipboard
@@ -77,7 +77,7 @@ import {
77
77
 
78
78
  // src/react/components/chat/ChatView.tsx
79
79
  import { Eye } from "lucide-react";
80
- import { useCallback as useCallback13 } from "react";
80
+ import { useCallback as useCallback13, useMemo as useMemo18 } from "react";
81
81
 
82
82
  // src/react/hooks/use-chat.ts
83
83
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -2121,11 +2121,22 @@ function isImageAttachment(attachment) {
2121
2121
  function buildSubmitPayload(text, attachments, pendingContexts, onBeforeSend) {
2122
2122
  const validAttachments = attachments.filter((attachment) => attachment.status !== "failed");
2123
2123
  const payload = buildMessageContent(text, validAttachments);
2124
- const contextParts = pendingContexts.map((context) => ({
2125
- type: "text",
2126
- text: `[\u4E0A\u4E0B\u6587: ${context.label}]
2124
+ const contextParts = pendingContexts.flatMap((context) => {
2125
+ const parts = [
2126
+ {
2127
+ type: "text",
2128
+ text: `[\u4E0A\u4E0B\u6587: ${context.label}]
2127
2129
  ${context.content}`
2128
- }));
2130
+ }
2131
+ ];
2132
+ if (context.imageUrl) {
2133
+ parts.push({
2134
+ type: "image_url",
2135
+ image_url: { url: context.imageUrl }
2136
+ });
2137
+ }
2138
+ return parts;
2139
+ });
2129
2140
  const mergedPayload = contextParts.length === 0 ? payload : typeof payload === "string" ? payload.trim() ? [...contextParts, { type: "text", text: payload }] : contextParts : [...contextParts, ...payload];
2130
2141
  return onBeforeSend ? onBeforeSend(mergedPayload) : mergedPayload;
2131
2142
  }
@@ -2430,12 +2441,23 @@ function ComposerContextPill({
2430
2441
  id,
2431
2442
  label,
2432
2443
  content,
2444
+ imageUrl,
2433
2445
  onRemove
2434
2446
  }) {
2435
2447
  const [showDetail, setShowDetail] = useState8(false);
2436
2448
  const tokenK = formatTokenK(content);
2437
2449
  return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2438
2450
  /* @__PURE__ */ jsxs8("div", { className: "flex shrink-0 items-center gap-1.5 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--accent))] px-2.5 py-1 text-[11px] text-[hsl(var(--foreground))]", children: [
2451
+ imageUrl ? /* @__PURE__ */ jsx9(
2452
+ "button",
2453
+ {
2454
+ type: "button",
2455
+ onClick: () => setShowDetail(true),
2456
+ className: "h-6 w-6 overflow-hidden rounded-md border border-[hsl(var(--border))] bg-white",
2457
+ title: "\u67E5\u770B\u9009\u4E2D\u533A\u57DF\u622A\u56FE",
2458
+ children: /* @__PURE__ */ jsx9("img", { src: imageUrl, alt: "\u9009\u4E2D\u533A\u57DF\u622A\u56FE", className: "h-full w-full object-cover" })
2459
+ }
2460
+ ) : null,
2439
2461
  /* @__PURE__ */ jsx9(
2440
2462
  "button",
2441
2463
  {
@@ -2481,7 +2503,10 @@ function ComposerContextPill({
2481
2503
  onKeyDown: (e) => e.stopPropagation(),
2482
2504
  children: [
2483
2505
  /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between border-b border-[hsl(var(--border))] px-6 py-3", children: [
2484
- /* @__PURE__ */ jsx9("h4", { className: "text-xs font-semibold text-[hsl(var(--foreground))]", children: label }),
2506
+ /* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
2507
+ /* @__PURE__ */ jsx9("h4", { className: "truncate text-xs font-semibold text-[hsl(var(--foreground))]", children: label }),
2508
+ imageUrl ? /* @__PURE__ */ jsx9("p", { className: "mt-0.5 text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u9644\u52A0\u9009\u4E2D\u533A\u57DF\u622A\u56FE\uFF0C\u975E\u591A\u6A21\u6001\u6A21\u578B\u4F1A\u81EA\u52A8\u5FFD\u7565\u56FE\u7247\u3002" }) : null
2509
+ ] }),
2485
2510
  /* @__PURE__ */ jsx9(
2486
2511
  "button",
2487
2512
  {
@@ -2492,7 +2517,10 @@ function ComposerContextPill({
2492
2517
  }
2493
2518
  )
2494
2519
  ] }),
2495
- /* @__PURE__ */ jsx9("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children: /* @__PURE__ */ jsx9("pre", { className: "whitespace-pre-wrap break-words font-mono text-[11px] leading-[1.6] text-[hsl(var(--foreground)/0.85)]", children: content }) })
2520
+ /* @__PURE__ */ jsxs8("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children: [
2521
+ imageUrl ? /* @__PURE__ */ jsx9("div", { className: "mb-4 rounded-xl border border-[hsl(var(--border))] bg-white p-2", children: /* @__PURE__ */ jsx9("img", { src: imageUrl, alt: "\u9009\u4E2D\u533A\u57DF\u622A\u56FE", className: "max-h-72 max-w-full rounded-lg object-contain" }) }) : null,
2522
+ /* @__PURE__ */ jsx9("pre", { className: "whitespace-pre-wrap break-words font-mono text-[11px] leading-[1.6] text-[hsl(var(--foreground)/0.85)]", children: content })
2523
+ ] })
2496
2524
  ]
2497
2525
  }
2498
2526
  )
@@ -3387,6 +3415,7 @@ function ChatInput({
3387
3415
  id: context.id,
3388
3416
  label: context.label,
3389
3417
  content: context.content,
3418
+ imageUrl: context.imageUrl,
3390
3419
  onRemove: (contextId) => {
3391
3420
  if (!activeSessionId) {
3392
3421
  return;
@@ -4184,7 +4213,7 @@ function useStickToBottomContext() {
4184
4213
 
4185
4214
  // src/react/components/chat/AssistantTurnBlock.tsx
4186
4215
  import { AlertCircle, BookOpen, Check as Check4, ChevronRight as ChevronRight5 } from "lucide-react";
4187
- import { useCallback as useCallback11, useEffect as useEffect14, useMemo as useMemo15, useRef as useRef11, useState as useState18 } from "react";
4216
+ import { memo as memo3, useCallback as useCallback11, useEffect as useEffect14, useMemo as useMemo15, useRef as useRef11, useState as useState18 } from "react";
4188
4217
 
4189
4218
  // src/react/routes.ts
4190
4219
  var MEMORIES_ROUTE = "/memories";
@@ -5670,6 +5699,8 @@ function ToolCallBlock({
5670
5699
  ) : null
5671
5700
  ] }),
5672
5701
  expanded && /* @__PURE__ */ jsx20("div", { className: "ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: Renderer ? /* @__PURE__ */ jsx20(Renderer, { toolCall, sessionId: resolvedSessionId }) : /* @__PURE__ */ jsxs16(Fragment5, { children: [
5702
+ /* @__PURE__ */ jsx20("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
5703
+ /* @__PURE__ */ jsx20("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
5673
5704
  /* @__PURE__ */ jsx20("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
5674
5705
  /* @__PURE__ */ jsx20("pre", { className: "overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatArgs(toolCall.arguments) }),
5675
5706
  toolCall.result != null && /* @__PURE__ */ jsxs16(Fragment5, { children: [
@@ -6414,6 +6445,19 @@ function safeParseDescription(argsStr) {
6414
6445
  return "\u5B50\u667A\u80FD\u4F53";
6415
6446
  }
6416
6447
  }
6448
+ function safeParsePrompt(argsStr) {
6449
+ try {
6450
+ const prompt = JSON.parse(argsStr)?.prompt ?? "";
6451
+ const marker = "# \u4F60\u7684\u4EFB\u52A1";
6452
+ const idx = prompt.indexOf(marker);
6453
+ if (idx >= 0) {
6454
+ return prompt.slice(idx + marker.length).trim();
6455
+ }
6456
+ return prompt;
6457
+ } catch {
6458
+ return "";
6459
+ }
6460
+ }
6417
6461
  function stripFileExtension(fileName) {
6418
6462
  return fileName.replace(/\.[^.]+$/, "");
6419
6463
  }
@@ -6654,33 +6698,42 @@ function AgentLoopBlock({ toolCall, sessionId, reasoning }) {
6654
6698
  inlineToolUiBlocks.length > 0 ? /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-2 px-4 pb-3", children: inlineToolUiBlocks.map((block) => /* @__PURE__ */ jsx29(ResourceIframe, { ui: block.ui, sessionId }, block.key)) }) : null
6655
6699
  ] }) : null
6656
6700
  ] }),
6657
- expanded && /* @__PURE__ */ jsx29("div", { className: "border-t border-[hsl(var(--border))] py-2", children: childMessages.length > 0 ? /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-3 px-3", children: childMessages.map(
6658
- (message, index) => message.role === "assistant" ? /* @__PURE__ */ jsx29(
6659
- ExpandedChildAssistantMessage,
6660
- {
6661
- message,
6662
- sessionId,
6663
- isStreaming: message.status === "streaming"
6664
- },
6665
- message.entry_id ?? `${message.timestamp ?? "child"}-${index}`
6666
- ) : message.role === "user" ? /* @__PURE__ */ jsx29(
6667
- ExpandedChildUserMessage,
6668
- {
6669
- message
6670
- },
6671
- message.entry_id ?? `${message.timestamp ?? "user"}-${index}`
6672
- ) : message.role === "error" ? /* @__PURE__ */ jsx29(
6673
- "div",
6674
- {
6675
- className: "border-l-[2px] border-l-red-500 px-3 py-2 text-[11px] text-red-200",
6676
- children: typeof message.content === "string" ? message.content : "\u5B50\u667A\u80FD\u4F53\u6267\u884C\u51FA\u9519"
6677
- },
6678
- message.entry_id ?? `${message.timestamp ?? "error"}-${index}`
6679
- ) : null
6680
- ) }) : status === "running" ? /* @__PURE__ */ jsxs24("div", { className: "px-3 py-2 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
6681
- /* @__PURE__ */ jsx29(Loader24, { size: 12, className: "mr-1.5 inline animate-spin" }),
6682
- "\u6B63\u5728\u542F\u52A8..."
6683
- ] }) : toolCall.result ? /* @__PURE__ */ jsx29("pre", { className: "max-h-[400px] overflow-auto px-3 py-2 whitespace-pre-wrap text-[11px] text-[hsl(var(--muted-foreground))]", children: typeof toolCall.result === "string" ? toolCall.result : JSON.stringify(toolCall.result, null, 2) }) : null })
6701
+ expanded && /* @__PURE__ */ jsxs24("div", { className: "border-t border-[hsl(var(--border))] py-2", children: [
6702
+ (() => {
6703
+ const taskPrompt = safeParsePrompt(toolCall.arguments);
6704
+ return taskPrompt ? /* @__PURE__ */ jsxs24("details", { className: "mx-3 mb-2 rounded-md bg-[hsl(var(--muted))]/40", children: [
6705
+ /* @__PURE__ */ jsx29("summary", { className: "cursor-pointer px-3 py-1.5 text-[11px] font-medium text-[hsl(var(--muted-foreground))]", children: "\u4EFB\u52A1" }),
6706
+ /* @__PURE__ */ jsx29("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsx29("pre", { className: "whitespace-pre-wrap text-[11px] leading-relaxed text-[hsl(var(--foreground))]", children: taskPrompt }) })
6707
+ ] }) : null;
6708
+ })(),
6709
+ childMessages.length > 0 ? /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-3 px-3", children: childMessages.map(
6710
+ (message, index) => message.role === "assistant" ? /* @__PURE__ */ jsx29(
6711
+ ExpandedChildAssistantMessage,
6712
+ {
6713
+ message,
6714
+ sessionId,
6715
+ isStreaming: message.status === "streaming"
6716
+ },
6717
+ message.entry_id ?? `${message.timestamp ?? "child"}-${index}`
6718
+ ) : message.role === "user" ? /* @__PURE__ */ jsx29(
6719
+ ExpandedChildUserMessage,
6720
+ {
6721
+ message
6722
+ },
6723
+ message.entry_id ?? `${message.timestamp ?? "user"}-${index}`
6724
+ ) : message.role === "error" ? /* @__PURE__ */ jsx29(
6725
+ "div",
6726
+ {
6727
+ className: "border-l-[2px] border-l-red-500 px-3 py-2 text-[11px] text-red-200",
6728
+ children: typeof message.content === "string" ? message.content : "\u5B50\u667A\u80FD\u4F53\u6267\u884C\u51FA\u9519"
6729
+ },
6730
+ message.entry_id ?? `${message.timestamp ?? "error"}-${index}`
6731
+ ) : null
6732
+ ) }) : status === "running" ? /* @__PURE__ */ jsxs24("div", { className: "px-3 py-2 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
6733
+ /* @__PURE__ */ jsx29(Loader24, { size: 12, className: "mr-1.5 inline animate-spin" }),
6734
+ "\u6B63\u5728\u542F\u52A8..."
6735
+ ] }) : toolCall.result ? /* @__PURE__ */ jsx29("pre", { className: "max-h-[400px] overflow-auto px-3 py-2 whitespace-pre-wrap text-[11px] text-[hsl(var(--muted-foreground))]", children: typeof toolCall.result === "string" ? toolCall.result : JSON.stringify(toolCall.result, null, 2) }) : null
6736
+ ] })
6684
6737
  ]
6685
6738
  }
6686
6739
  );
@@ -6796,6 +6849,8 @@ function getLoopCardStyles(status) {
6796
6849
 
6797
6850
  // src/react/components/chat/AssistantTurnBlock.tsx
6798
6851
  import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
6852
+ var EMPTY_MESSAGES3 = [];
6853
+ var EMPTY_AGENT_LOOPS2 = {};
6799
6854
  function defaultTurnDisplayMode({
6800
6855
  forceExpanded,
6801
6856
  hasActionableToolCall
@@ -6930,7 +6985,7 @@ function collectTurnMessages({
6930
6985
  });
6931
6986
  return merged;
6932
6987
  }
6933
- function AssistantTurnBlock({
6988
+ function AssistantTurnBlockBase({
6934
6989
  sessionId,
6935
6990
  messages: rawMessages,
6936
6991
  isStreaming = false,
@@ -6943,6 +6998,10 @@ function AssistantTurnBlock({
6943
6998
  }) {
6944
6999
  const messages = Array.isArray(rawMessages) ? rawMessages : [];
6945
7000
  const allToolCalls = useMemo15(() => messages.flatMap((m) => m.tool_calls ?? []), [messages]);
7001
+ const hasAgentToolCall = useMemo15(
7002
+ () => allToolCalls.some((toolCall) => formatToolName(toolCall.name) === "Agent"),
7003
+ [allToolCalls]
7004
+ );
6946
7005
  const hasActionableToolCall = useMemo15(
6947
7006
  () => allToolCalls.some(
6948
7007
  (tc) => tc.status === "error" || tc.status === "cancelled" || formatToolName(tc.name) === "AskUserQuestion" && tc.status === "awaiting_answer"
@@ -6959,11 +7018,15 @@ function AssistantTurnBlock({
6959
7018
  }
6960
7019
  wasStreamingRef.current = isStreaming;
6961
7020
  }, [forceExpanded, hasActionableToolCall, isStreaming]);
6962
- const sessionMessages = useChatStore((state) => state.messages[sessionId] ?? []);
6963
- const agentLoops = useChatStore((state) => state.agentLoops[sessionId] ?? {});
7021
+ const sessionMessages = useChatStore(
7022
+ (state) => hasAgentToolCall ? state.messages[sessionId] ?? EMPTY_MESSAGES3 : EMPTY_MESSAGES3
7023
+ );
7024
+ const agentLoops = useChatStore(
7025
+ (state) => hasAgentToolCall ? state.agentLoops[sessionId] ?? EMPTY_AGENT_LOOPS2 : EMPTY_AGENT_LOOPS2
7026
+ );
6964
7027
  const turnMessages = useMemo15(
6965
- () => collectTurnMessages({ rootMessages: messages, sessionMessages, agentLoops }),
6966
- [agentLoops, messages, sessionMessages]
7028
+ () => hasAgentToolCall ? collectTurnMessages({ rootMessages: messages, sessionMessages, agentLoops }) : messages,
7029
+ [agentLoops, hasAgentToolCall, messages, sessionMessages]
6967
7030
  );
6968
7031
  const resourceBlocks = useMemo15(() => getInlineResourceBlocks(turnMessages), [turnMessages]);
6969
7032
  const finalMessage = useMemo15(() => getLastTextMessage(turnMessages), [turnMessages]);
@@ -7037,6 +7100,10 @@ function AssistantTurnBlock({
7037
7100
  }
7038
7101
  );
7039
7102
  }
7103
+ function areAssistantTurnBlockPropsEqual(prev, next) {
7104
+ return prev.turnKey === next.turnKey && prev.sessionId === next.sessionId && prev.messages === next.messages && prev.isStreaming === next.isStreaming && prev.isLastTurn === next.isLastTurn && prev.askAnswers === next.askAnswers && prev.onAnswer === next.onAnswer && prev.sessionStatus === next.sessionStatus && prev.level === next.level && prev.forceExpanded === next.forceExpanded && prev.customization === next.customization;
7105
+ }
7106
+ var AssistantTurnBlock = memo3(AssistantTurnBlockBase, areAssistantTurnBlockPropsEqual);
7040
7107
  function AssistantMessages({
7041
7108
  messages,
7042
7109
  isStreaming,
@@ -7822,45 +7889,34 @@ function getAssistantTurnPreview(messages) {
7822
7889
  return "\u672C\u8F6E\u56DE\u590D";
7823
7890
  }
7824
7891
  function getMessageMeasureSignature(message) {
7825
- const contentSize = typeof message.content === "string" ? message.content.length : message.content.reduce((size, part) => size + JSON.stringify(part).length, 0);
7892
+ const contentSize = typeof message.content === "string" ? message.content.length : message.content.length;
7826
7893
  return [
7827
7894
  message.entry_id ?? message.timestamp ?? message.role,
7828
7895
  message.status ?? "",
7829
7896
  message.reasoning?.length ?? 0,
7830
7897
  message.tool_calls?.length ?? 0,
7898
+ message.blocks?.length ?? 0,
7831
7899
  contentSize
7832
7900
  ].join(":");
7833
7901
  }
7834
7902
  function getMessagesMeasureSignature(messages) {
7835
7903
  return messages.map((message) => getMessageMeasureSignature(message)).join("|");
7836
7904
  }
7837
- function safeJson(value) {
7838
- try {
7839
- return JSON.stringify(value);
7840
- } catch {
7841
- return String(value);
7842
- }
7843
- }
7844
- function hashString(value) {
7845
- let hash = 0;
7846
- for (let index = 0; index < value.length; index += 1) {
7847
- hash = hash * 31 + value.charCodeAt(index) | 0;
7848
- }
7849
- return hash.toString(36);
7850
- }
7851
- function getMessageRenderSignature(message) {
7905
+ function getMessageResetSignature(message) {
7906
+ const contentSize = typeof message.content === "string" ? message.content.length : message.content.length;
7907
+ const toolSignature = message.tool_calls?.map((toolCall) => `${toolCall.id}:${toolCall.status}:${toolCall.result == null ? 0 : 1}`).join(",") ?? "";
7852
7908
  return [
7853
7909
  message.entry_id ?? message.timestamp ?? message.role,
7854
7910
  message.status ?? "",
7855
- hashString(safeJson(message.content)),
7856
- hashString(message.reasoning ?? ""),
7857
- hashString(safeJson(message.tool_calls ?? [])),
7858
- hashString(safeJson(message.blocks ?? [])),
7859
- hashString(safeJson(message.memory_refs ?? []))
7911
+ contentSize,
7912
+ message.reasoning?.length ?? 0,
7913
+ message.blocks?.length ?? 0,
7914
+ message.memory_refs?.length ?? 0,
7915
+ toolSignature
7860
7916
  ].join(":");
7861
7917
  }
7862
- function getMessagesRenderSignature(messages) {
7863
- return messages.map((message) => getMessageRenderSignature(message)).join("|");
7918
+ function getMessagesResetSignature(messages) {
7919
+ return messages.map((message) => getMessageResetSignature(message)).join("|");
7864
7920
  }
7865
7921
  function findScrollContainer(start) {
7866
7922
  let current = start;
@@ -7885,7 +7941,17 @@ function MessageList({
7885
7941
  const agentLoops = useChatStore((state) => state.agentLoops[sessionId]);
7886
7942
  const askAnswers = useChatStore((state) => state.askAnswers[sessionId]);
7887
7943
  const agentLoopNameSet = useMemo17(() => new Set(Object.keys(agentLoops ?? {})), [agentLoops]);
7944
+ const assistantMessagesCacheRef = useRef12(/* @__PURE__ */ new Map());
7888
7945
  const renderBlocks = useMemo17(() => {
7946
+ const stableAssistantMessages = (key, buffer) => {
7947
+ const previous = assistantMessagesCacheRef.current.get(key);
7948
+ if (previous && previous.length === buffer.length && previous.every((message, index) => message === buffer[index])) {
7949
+ return previous;
7950
+ }
7951
+ const next = [...buffer];
7952
+ assistantMessagesCacheRef.current.set(key, next);
7953
+ return next;
7954
+ };
7889
7955
  const visible = messages.filter((message) => {
7890
7956
  const loopName = message.loop_name ?? "root";
7891
7957
  if (loopName !== "root") return false;
@@ -7903,13 +7969,15 @@ function MessageList({
7903
7969
  assistantTurnCount += 1;
7904
7970
  const last = assistantBuffer[assistantBuffer.length - 1];
7905
7971
  const isStreaming = assistantBuffer.some((message) => message.status === "streaming");
7972
+ const key = last.entry_id ?? `assistant-${blocks.length}`;
7973
+ const stableMessages = stableAssistantMessages(key, assistantBuffer);
7906
7974
  blocks.push({
7907
7975
  type: "assistant_turn",
7908
- messages: assistantBuffer,
7909
- key: last.entry_id ?? `assistant-${blocks.length}`,
7976
+ messages: stableMessages,
7977
+ key,
7910
7978
  anchorId: `chat-turn-${assistantTurnCount}`,
7911
7979
  isStreaming,
7912
- railTitle: lastUserPreview || getAssistantTurnPreview(assistantBuffer)
7980
+ railTitle: lastUserPreview || getAssistantTurnPreview(stableMessages)
7913
7981
  });
7914
7982
  assistantBuffer = [];
7915
7983
  };
@@ -7948,6 +8016,14 @@ function MessageList({
7948
8016
  });
7949
8017
  }
7950
8018
  flushAssistant();
8019
+ const activeAssistantKeys = new Set(
8020
+ blocks.filter((block) => block.type === "assistant_turn").map((block) => block.key)
8021
+ );
8022
+ for (const key of assistantMessagesCacheRef.current.keys()) {
8023
+ if (!activeAssistantKeys.has(key)) {
8024
+ assistantMessagesCacheRef.current.delete(key);
8025
+ }
8026
+ }
7951
8027
  return blocks;
7952
8028
  }, [agentLoopNameSet, messages]);
7953
8029
  const hasInterruptedTurn = useMemo17(
@@ -8167,7 +8243,7 @@ function MessageListContent({
8167
8243
  {
8168
8244
  label: "\u52A9\u624B\u6D88\u606F",
8169
8245
  details: block.key,
8170
- resetKey: getMessagesRenderSignature(block.messages),
8246
+ resetKey: getMessagesResetSignature(block.messages),
8171
8247
  children: customization?.components?.AssistantTurn ? /* @__PURE__ */ jsx35(
8172
8248
  customization.components.AssistantTurn,
8173
8249
  {
@@ -8300,7 +8376,10 @@ function ChatView({
8300
8376
  },
8301
8377
  [send]
8302
8378
  );
8303
- const customization = { classNames, components, renderers };
8379
+ const customization = useMemo18(
8380
+ () => ({ classNames, components, renderers }),
8381
+ [classNames, components, renderers]
8382
+ );
8304
8383
  const SkillStatus = components?.SkillStatusBar;
8305
8384
  return /* @__PURE__ */ jsxs31("div", { className: `flex min-h-0 flex-1 flex-col overflow-hidden ${classNames?.root ?? ""}`, children: [
8306
8385
  isViewer && /* @__PURE__ */ jsxs31("div", { className: `flex items-center justify-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--card))] py-2 text-xs text-[hsl(var(--muted-foreground))] ${classNames?.viewerBanner ?? ""}`, children: [
@@ -8396,4 +8475,4 @@ use-stick-to-bottom/dist/StickToBottom.js:
8396
8475
  * Licensed under the MIT License. See License.txt in the project root for license information.
8397
8476
  *--------------------------------------------------------------------------------------------*)
8398
8477
  */
8399
- //# sourceMappingURL=chunk-S6EPGDAL.js.map
8478
+ //# sourceMappingURL=chunk-K5EE7X2D.js.map