@blade-hq/agent-kit 0.5.1 → 0.5.3

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 (35) hide show
  1. package/dist/{AskUserQuestionBlock-CjvG_pUY.d.ts → AskUserQuestionBlock---kOTouk.d.ts} +2 -2
  2. package/dist/{SkillStatusBar-B7-EU8A4.d.ts → SkillStatusBar-BKAGU9tr.d.ts} +7 -5
  3. package/dist/{blade-client-BKiP6U73.d.ts → blade-client-R3cLVOYs.d.ts} +31 -5
  4. package/dist/{chunk-L2KVYOQP.js → chunk-BFF6D2XV.js} +2 -2
  5. package/dist/{chunk-UPHYN7CQ.js → chunk-IRCXJHXT.js} +2 -2
  6. package/dist/{chunk-ROGNJYST.js → chunk-Q6CSM5DE.js} +63 -5
  7. package/dist/chunk-Q6CSM5DE.js.map +1 -0
  8. package/dist/{chunk-75BPCDBW.js → chunk-RTBAPZIO.js} +277 -216
  9. package/dist/chunk-RTBAPZIO.js.map +1 -0
  10. package/dist/{chunk-DSBIKYKQ.js → chunk-UQEXX57F.js} +16 -10
  11. package/dist/chunk-UQEXX57F.js.map +1 -0
  12. package/dist/{chunk-DAFIIANJ.js → chunk-ZQQNSKQS.js} +2 -11
  13. package/dist/chunk-ZQQNSKQS.js.map +1 -0
  14. package/dist/client/index.d.ts +11 -7
  15. package/dist/client/index.js +1 -1
  16. package/dist/{projection-DIfyh6RK.d.ts → projection-BWYEFYNn.d.ts} +1 -1
  17. package/dist/react/api/vibe-coding.d.ts +3 -3
  18. package/dist/react/api/vibe-coding.js +2 -2
  19. package/dist/react/components/chat/index.d.ts +6 -6
  20. package/dist/react/components/chat/index.js +5 -5
  21. package/dist/react/components/plan/index.d.ts +3 -3
  22. package/dist/react/components/plan/index.js +3 -3
  23. package/dist/react/components/session/index.js +3 -3
  24. package/dist/react/components/workspace/index.js +32 -4
  25. package/dist/react/components/workspace/index.js.map +1 -1
  26. package/dist/react/index.d.ts +15 -11
  27. package/dist/react/index.js +6 -6
  28. package/dist/style.css +1 -1
  29. package/package.json +1 -1
  30. package/dist/chunk-75BPCDBW.js.map +0 -1
  31. package/dist/chunk-DAFIIANJ.js.map +0 -1
  32. package/dist/chunk-DSBIKYKQ.js.map +0 -1
  33. package/dist/chunk-ROGNJYST.js.map +0 -1
  34. /package/dist/{chunk-L2KVYOQP.js.map → chunk-BFF6D2XV.js.map} +0 -0
  35. /package/dist/{chunk-UPHYN7CQ.js.map → chunk-IRCXJHXT.js.map} +0 -0
@@ -8,13 +8,13 @@ import {
8
8
  getCodeLanguageFromFilename,
9
9
  parseAskUserQuestion,
10
10
  useHighlightedCodeHtml
11
- } from "./chunk-DAFIIANJ.js";
11
+ } from "./chunk-ZQQNSKQS.js";
12
12
  import {
13
13
  Collapsible,
14
14
  CollapsibleContent,
15
15
  CollapsibleTrigger,
16
16
  resolveSessionFilePreviewTarget
17
- } from "./chunk-UPHYN7CQ.js";
17
+ } from "./chunk-IRCXJHXT.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-DSBIKYKQ.js";
60
+ } from "./chunk-UQEXX57F.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-ROGNJYST.js";
69
+ } from "./chunk-Q6CSM5DE.js";
70
70
  import {
71
71
  cn,
72
72
  copyToClipboard
@@ -80,13 +80,15 @@ import { Eye } from "lucide-react";
80
80
  import { useCallback as useCallback13 } from "react";
81
81
 
82
82
  // src/react/hooks/use-chat.ts
83
- import { useCallback, useEffect } from "react";
83
+ import { useCallback, useEffect, useRef, useState } from "react";
84
84
  var EMPTY_MESSAGES = [];
85
85
  function useChat(sessionId) {
86
86
  const messages = useChatStore((s) => s.messages[sessionId] ?? EMPTY_MESSAGES);
87
87
  const isStreaming = useChatStore((s) => s.isStreaming[sessionId] ?? false);
88
88
  const mode = useSessionStore((s) => s.modes[sessionId] ?? "executing");
89
89
  const setAnswerCallback = useAnswerCallbackStore((s) => s.setAnswerCallback);
90
+ const [isStopping, setIsStopping] = useState(false);
91
+ const previousSessionIdRef = useRef(sessionId);
90
92
  const send = useCallback(
91
93
  (msg, mode2, askuserAnswer, options) => {
92
94
  const synthesizedAnswer = askuserAnswer ?? buildPendingAskUserAnswer(sessionId, msg);
@@ -94,9 +96,27 @@ function useChat(sessionId) {
94
96
  },
95
97
  [sessionId]
96
98
  );
97
- const stop = useCallback(() => {
98
- getSocket().stop(sessionId);
99
- }, [sessionId]);
99
+ const stop = useCallback(async () => {
100
+ if (isStopping) return;
101
+ setIsStopping(true);
102
+ try {
103
+ await getSocket().stop(sessionId);
104
+ } catch (error) {
105
+ console.error("[chat] stop request failed", error);
106
+ } finally {
107
+ setIsStopping(false);
108
+ }
109
+ }, [isStopping, sessionId]);
110
+ useEffect(() => {
111
+ if (previousSessionIdRef.current !== sessionId) {
112
+ previousSessionIdRef.current = sessionId;
113
+ setIsStopping(false);
114
+ return;
115
+ }
116
+ if (!isStreaming) {
117
+ setIsStopping(false);
118
+ }
119
+ }, [isStreaming, sessionId]);
100
120
  const answer = useCallback(
101
121
  (msg, toolCallId, answerData) => {
102
122
  send(msg, mode, { tool_call_id: toolCallId, ...answerData });
@@ -107,7 +127,7 @@ function useChat(sessionId) {
107
127
  setAnswerCallback(sessionId, answer);
108
128
  return () => setAnswerCallback(sessionId, void 0);
109
129
  }, [answer, sessionId, setAnswerCallback]);
110
- return { messages, isStreaming, send, stop };
130
+ return { messages, isStreaming, isStopping, send, stop };
111
131
  }
112
132
  function buildPendingAskUserAnswer(sessionId, message) {
113
133
  const session = useSessionStore.getState().sessions.find((item) => item.id === sessionId);
@@ -180,17 +200,17 @@ import {
180
200
  useEffect as useEffect7,
181
201
  useEffectEvent as useEffectEvent2,
182
202
  useMemo as useMemo7,
183
- useRef as useRef4,
184
- useState as useState7
203
+ useRef as useRef5,
204
+ useState as useState8
185
205
  } from "react";
186
206
  import { createRoot } from "react-dom/client";
187
207
  import { toast } from "sonner";
188
208
 
189
209
  // src/react/asr/use-tiptap-voice-input.ts
190
- import { useCallback as useCallback3, useEffect as useEffect3, useEffectEvent, useRef as useRef2 } from "react";
210
+ import { useCallback as useCallback3, useEffect as useEffect3, useEffectEvent, useRef as useRef3 } from "react";
191
211
 
192
212
  // src/react/asr/use-voice-input.ts
193
- import { useCallback as useCallback2, useEffect as useEffect2, useRef, useState } from "react";
213
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
194
214
 
195
215
  // src/react/asr/voice-input-support.ts
196
216
  function isIpHost(hostname) {
@@ -237,14 +257,14 @@ function matchesRequest(data, requestId) {
237
257
  return !rid || rid === requestId;
238
258
  }
239
259
  function useVoiceInput(options) {
240
- const [status, setStatus] = useState("idle");
241
- const [error, setError] = useState(null);
242
- const [level, setLevel] = useState(0);
243
- const sessionRef = useRef(null);
244
- const startingRef = useRef(false);
245
- const stoppingRef = useRef(false);
246
- const unmountedRef = useRef(false);
247
- const optionsRef = useRef(options);
260
+ const [status, setStatus] = useState2("idle");
261
+ const [error, setError] = useState2(null);
262
+ const [level, setLevel] = useState2(0);
263
+ const sessionRef = useRef2(null);
264
+ const startingRef = useRef2(false);
265
+ const stoppingRef = useRef2(false);
266
+ const unmountedRef = useRef2(false);
267
+ const optionsRef = useRef2(options);
248
268
  optionsRef.current = options;
249
269
  const reportError = useCallback2((err) => {
250
270
  const payload = typeof err === "string" ? { title: err } : err;
@@ -499,8 +519,8 @@ function useVoiceInput(options) {
499
519
  // src/react/asr/use-tiptap-voice-input.ts
500
520
  function useTiptapVoiceInput(options) {
501
521
  const { editorRef, workletUrl, onError } = options;
502
- const partialRangeRef = useRef2(null);
503
- const lastPartialLenRef = useRef2(0);
522
+ const partialRangeRef = useRef3(null);
523
+ const lastPartialLenRef = useRef3(0);
504
524
  const handlePartial = useEffectEvent((text) => {
505
525
  const ed = editorRef.current;
506
526
  if (!ed) return;
@@ -619,7 +639,7 @@ function VoiceWaveform({ level, color = "currentColor", size = 16 }) {
619
639
 
620
640
  // src/react/components/model/ModelSelector.tsx
621
641
  import { Loader2, Sparkles } from "lucide-react";
622
- import { useEffect as useEffect4, useMemo, useRef as useRef3, useState as useState2 } from "react";
642
+ import { useEffect as useEffect4, useMemo, useRef as useRef4, useState as useState3 } from "react";
623
643
 
624
644
  // src/react/hooks/use-model-preferences.ts
625
645
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -704,6 +724,21 @@ function usePreferredModel() {
704
724
 
705
725
  // src/react/components/model/ModelSelector.tsx
706
726
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
727
+ var inputModalityLabels = {
728
+ text: "\u6587\u672C",
729
+ image: "\u56FE\u7247",
730
+ file: "\u6587\u4EF6",
731
+ audio: "\u97F3\u9891",
732
+ video: "\u89C6\u9891"
733
+ };
734
+ function getInputModalities(model) {
735
+ const values = model.inputModalities && model.inputModalities.length > 0 ? model.inputModalities : model.supportsImage ? ["text", "image"] : ["text"];
736
+ const normalized = values.map((value) => value.trim().toLowerCase()).filter((value) => value.length > 0);
737
+ return normalized.length > 0 ? Array.from(new Set(normalized)) : ["text"];
738
+ }
739
+ function formatInputModalities(model) {
740
+ return getInputModalities(model).map((value) => inputModalityLabels[value] ?? value).join("\u3001");
741
+ }
707
742
  function ModelSelector({
708
743
  value,
709
744
  onValueChange,
@@ -712,9 +747,9 @@ function ModelSelector({
712
747
  placement = "bottom"
713
748
  }) {
714
749
  const { models, defaultModel, isLoading } = useModelConfig();
715
- const [isOpen, setIsOpen] = useState2(false);
716
- const [query, setQuery] = useState2("");
717
- const rootRef = useRef3(null);
750
+ const [isOpen, setIsOpen] = useState3(false);
751
+ const [query, setQuery] = useState3("");
752
+ const rootRef = useRef4(null);
718
753
  useEffect4(() => {
719
754
  if (!isOpen) return;
720
755
  const closeOnPointerDown = (event) => {
@@ -729,13 +764,21 @@ function ModelSelector({
729
764
  const normalized = query.trim().toLowerCase();
730
765
  if (!normalized) return models;
731
766
  return models.filter(
732
- (model) => model.id.toLowerCase().includes(normalized) || model.label.toLowerCase().includes(normalized)
767
+ (model) => {
768
+ const inputLabel = formatInputModalities(model).toLowerCase();
769
+ return model.id.toLowerCase().includes(normalized) || model.label.toLowerCase().includes(normalized) || inputLabel.includes(normalized);
770
+ }
733
771
  );
734
772
  }, [models, query]);
735
773
  const canSelect = models.length > 0;
736
- const selectedLabel = models.find((model) => model.id === value)?.label;
737
- const defaultLabel = models.find((model) => model.id === defaultModel)?.label;
774
+ const selectedModel = models.find((model) => model.id === value);
775
+ const defaultModelOption = models.find((model) => model.id === defaultModel);
776
+ const selectedLabel = selectedModel?.label;
777
+ const defaultLabel = defaultModelOption?.label;
738
778
  const label = isLoading ? "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B..." : models.length === 0 ? "\u65E0\u53EF\u7528\u6A21\u578B" : selectedLabel || defaultLabel || value.trim() || "\u672A\u8FDE\u63A5\u5230\u6A21\u578B";
779
+ const activeModel = selectedModel ?? defaultModelOption;
780
+ const activeInputLabel = activeModel ? formatInputModalities(activeModel) : "";
781
+ const selectorTitle = activeInputLabel ? `${label}\uFF0C\u652F\u6301\u8F93\u5165\uFF1A${activeInputLabel}` : label;
739
782
  const dropdownPosition = placement === "top" ? "bottom-[calc(100%+8px)]" : "top-[calc(100%+8px)]";
740
783
  if (!canSelect) {
741
784
  return /* @__PURE__ */ jsxs(
@@ -758,17 +801,24 @@ function ModelSelector({
758
801
  disabled,
759
802
  onClick: () => setIsOpen((current) => !current),
760
803
  className: "flex min-w-0 items-center gap-1 rounded-lg px-2.5 py-[5px] text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50",
761
- title: label,
804
+ title: selectorTitle,
762
805
  children: [
763
806
  /* @__PURE__ */ jsx2(Sparkles, { size: 14, "aria-hidden": "true" }),
764
- /* @__PURE__ */ jsx2("span", { className: `whitespace-nowrap text-xs ${compact ? "" : "max-w-[180px]"}`, children: label })
807
+ /* @__PURE__ */ jsx2(
808
+ "span",
809
+ {
810
+ className: `min-w-0 truncate whitespace-nowrap text-xs ${compact ? "max-w-[120px]" : "max-w-[180px]"}`,
811
+ children: label
812
+ }
813
+ ),
814
+ activeInputLabel ? /* @__PURE__ */ jsx2("span", { className: "shrink-0 rounded border border-[hsl(var(--border))] px-1 py-0 text-[10px] leading-4 text-[hsl(var(--muted-foreground))]", children: activeInputLabel }) : null
765
815
  ]
766
816
  }
767
817
  ),
768
818
  isOpen && /* @__PURE__ */ jsxs(
769
819
  "div",
770
820
  {
771
- className: `absolute left-0 z-50 w-[260px] overflow-hidden rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--popover))] shadow-xl ${dropdownPosition}`,
821
+ className: `absolute left-0 z-50 w-[320px] overflow-hidden rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--popover))] shadow-xl ${dropdownPosition}`,
772
822
  children: [
773
823
  /* @__PURE__ */ jsx2("div", { className: "border-b border-[hsl(var(--border))] p-2", children: /* @__PURE__ */ jsx2(
774
824
  "input",
@@ -779,21 +829,28 @@ function ModelSelector({
779
829
  className: "h-8 w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2.5 text-xs text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground))] focus:border-[hsl(var(--ring))]"
780
830
  }
781
831
  ) }),
782
- /* @__PURE__ */ jsx2("div", { className: "max-h-[220px] overflow-y-auto p-1", children: filteredModels.length === 0 ? /* @__PURE__ */ jsx2("div", { className: "px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B" }) : filteredModels.map((model) => /* @__PURE__ */ jsx2(
783
- "button",
784
- {
785
- type: "button",
786
- onClick: () => {
787
- onValueChange(model.id);
788
- setIsOpen(false);
789
- setQuery("");
832
+ /* @__PURE__ */ jsx2("div", { className: "max-h-[220px] overflow-y-auto p-1", children: filteredModels.length === 0 ? /* @__PURE__ */ jsx2("div", { className: "px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B" }) : filteredModels.map((model) => {
833
+ const inputLabel = formatInputModalities(model);
834
+ const title = model.id === model.label ? `${model.id}\uFF0C\u652F\u6301\u8F93\u5165\uFF1A${inputLabel}` : `${model.label} (${model.id})\uFF0C\u652F\u6301\u8F93\u5165\uFF1A${inputLabel}`;
835
+ return /* @__PURE__ */ jsxs(
836
+ "button",
837
+ {
838
+ type: "button",
839
+ onClick: () => {
840
+ onValueChange(model.id);
841
+ setIsOpen(false);
842
+ setQuery("");
843
+ },
844
+ className: `flex w-full items-center justify-between gap-3 rounded-lg px-3 py-2 text-left text-xs transition-colors ${model.id === value ? "bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"}`,
845
+ title,
846
+ children: [
847
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: model.label }),
848
+ /* @__PURE__ */ jsx2("span", { className: "shrink-0 text-[11px] text-[hsl(var(--muted-foreground))]", children: inputLabel })
849
+ ]
790
850
  },
791
- className: `block w-full truncate rounded-lg px-3 py-2 text-left text-xs transition-colors ${model.id === value ? "bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"}`,
792
- title: model.id === model.label ? model.id : `${model.label} (${model.id})`,
793
- children: model.label
794
- },
795
- model.id
796
- )) })
851
+ model.id
852
+ );
853
+ }) })
797
854
  ]
798
855
  }
799
856
  )
@@ -801,7 +858,7 @@ function ModelSelector({
801
858
  }
802
859
 
803
860
  // src/react/hooks/use-input-history.ts
804
- import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo2, useState as useState3 } from "react";
861
+ import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo2, useState as useState4 } from "react";
805
862
  var MAX_HISTORY_ENTRIES = 50;
806
863
  function getStorageKey(sessionId) {
807
864
  return sessionId ? `input-history:${sessionId}` : null;
@@ -826,9 +883,9 @@ function readEntries(storageKey) {
826
883
  }
827
884
  function useInputHistory(sessionId) {
828
885
  const storageKey = useMemo2(() => getStorageKey(sessionId), [sessionId]);
829
- const [entries, setEntries] = useState3([]);
830
- const [cursor, setCursor] = useState3(null);
831
- const [draft, setDraft] = useState3("");
886
+ const [entries, setEntries] = useState4([]);
887
+ const [cursor, setCursor] = useState4(null);
888
+ const [draft, setDraft] = useState4("");
832
889
  useEffect5(() => {
833
890
  setEntries(readEntries(storageKey));
834
891
  setCursor(null);
@@ -967,7 +1024,7 @@ function skillDisplayName(skill) {
967
1024
 
968
1025
  // src/react/components/chat/FileCompletionMenu.tsx
969
1026
  import { ChevronRight, File, Folder } from "lucide-react";
970
- import { forwardRef, useCallback as useCallback5, useImperativeHandle, useMemo as useMemo4, useState as useState4 } from "react";
1027
+ import { forwardRef, useCallback as useCallback5, useImperativeHandle, useMemo as useMemo4, useState as useState5 } from "react";
971
1028
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
972
1029
  var ROOT_DIR = ".";
973
1030
  function isVisibleEntry(entry) {
@@ -1071,7 +1128,7 @@ var FileCompletionMenu = forwardRef(
1071
1128
  }
1072
1129
  );
1073
1130
  var FileCompletionMenuContent = forwardRef(function FileCompletionMenuContent2({ command, editor, filterQuery, items, onExit, range, sessionId }, ref) {
1074
- const [rawSelectedIndex, setRawSelectedIndex] = useState4(0);
1131
+ const [rawSelectedIndex, setRawSelectedIndex] = useState5(0);
1075
1132
  const filteredEntries = useMemo4(() => filterEntries(items, filterQuery), [filterQuery, items]);
1076
1133
  const selectedIndex = filteredEntries.length === 0 ? 0 : Math.min(rawSelectedIndex, filteredEntries.length - 1);
1077
1134
  const handleSelect = useCallback5(
@@ -1152,11 +1209,11 @@ var FileCompletionMenuContent = forwardRef(function FileCompletionMenuContent2({
1152
1209
  });
1153
1210
 
1154
1211
  // src/react/components/chat/SkillCompletionMenu.tsx
1155
- import { forwardRef as forwardRef2, useCallback as useCallback6, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
1212
+ import { forwardRef as forwardRef2, useCallback as useCallback6, useImperativeHandle as useImperativeHandle2, useState as useState6 } from "react";
1156
1213
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1157
1214
  var SkillCompletionMenu = forwardRef2(
1158
1215
  function SkillCompletionMenu2({ command, items }, ref) {
1159
- const [rawSelectedIndex, setRawSelectedIndex] = useState5(0);
1216
+ const [rawSelectedIndex, setRawSelectedIndex] = useState6(0);
1160
1217
  const selectedIndex = items.length === 0 ? 0 : Math.min(rawSelectedIndex, items.length - 1);
1161
1218
  const handleSelect = useCallback6(
1162
1219
  (item) => {
@@ -1261,7 +1318,7 @@ var SkillCompletionMenu = forwardRef2(
1261
1318
 
1262
1319
  // src/react/components/chat/BackgroundTasksPill.tsx
1263
1320
  import { FileText, Square, Terminal } from "lucide-react";
1264
- import { useState as useState6 } from "react";
1321
+ import { useState as useState7 } from "react";
1265
1322
 
1266
1323
  // src/react/hooks/use-background-tasks.ts
1267
1324
  import { useQuery as useQuery2 } from "@tanstack/react-query";
@@ -1310,8 +1367,8 @@ function statusClass(status) {
1310
1367
  }
1311
1368
  function BackgroundTasksPill({ sessionId }) {
1312
1369
  const { data: tasks } = useBackgroundTasks(sessionId);
1313
- const [open, setOpen] = useState6(false);
1314
- const [logTask, setLogTask] = useState6(null);
1370
+ const [open, setOpen] = useState7(false);
1371
+ const [logTask, setLogTask] = useState7(null);
1315
1372
  if (tasks.length === 0) return null;
1316
1373
  const runningCount = tasks.filter((task) => task.status === "running").length;
1317
1374
  const openTaskLog = async (task) => {
@@ -2094,20 +2151,38 @@ function ComposerFilePill({
2094
2151
  const isFile = attachment.kind === "file";
2095
2152
  const isFailed = isFile && attachment.status === "failed";
2096
2153
  const isUploading = isFile && attachment.status === "uploading";
2154
+ const uploadPercent = isUploading && typeof attachment.uploadProgress === "number" ? Math.max(0, Math.min(100, Math.round(attachment.uploadProgress * 100))) : null;
2097
2155
  return /* @__PURE__ */ jsxs8(
2098
2156
  "div",
2099
2157
  {
2100
- className: `flex shrink-0 items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] ${isFailed ? "border-red-500/30 bg-red-500/5 text-red-400" : "border-[hsl(var(--border))] bg-[hsl(var(--card))] text-[hsl(var(--foreground))]"}`,
2158
+ className: `relative flex shrink-0 items-center gap-1.5 overflow-hidden rounded-full border px-2.5 py-1 text-[11px] ${isFailed ? "border-red-500/30 bg-red-500/5 text-red-400" : "border-[hsl(var(--border))] bg-[hsl(var(--card))] text-[hsl(var(--foreground))]"}`,
2101
2159
  children: [
2102
- isUploading ? /* @__PURE__ */ jsx9(Loader22, { size: 12, className: "shrink-0 animate-spin text-[hsl(var(--muted-foreground))]" }) : /* @__PURE__ */ jsx9(Icon, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
2103
- /* @__PURE__ */ jsx9("span", { className: "max-w-32 truncate", children: attachment.name }),
2160
+ uploadPercent !== null ? /* @__PURE__ */ jsx9(
2161
+ "span",
2162
+ {
2163
+ className: "absolute inset-y-0 left-0 bg-[hsl(var(--primary))]/10 transition-[width]",
2164
+ style: { width: `${uploadPercent}%` }
2165
+ }
2166
+ ) : null,
2167
+ isUploading ? /* @__PURE__ */ jsx9(
2168
+ Loader22,
2169
+ {
2170
+ size: 12,
2171
+ className: "relative z-10 shrink-0 animate-spin text-[hsl(var(--muted-foreground))]"
2172
+ }
2173
+ ) : /* @__PURE__ */ jsx9(Icon, { size: 12, className: "relative z-10 shrink-0 text-[hsl(var(--muted-foreground))]" }),
2174
+ /* @__PURE__ */ jsx9("span", { className: "relative z-10 max-w-32 truncate", children: attachment.name }),
2175
+ uploadPercent !== null ? /* @__PURE__ */ jsxs8("span", { className: "relative z-10 font-mono text-[10px] tabular-nums text-[hsl(var(--muted-foreground))]", children: [
2176
+ uploadPercent,
2177
+ "%"
2178
+ ] }) : null,
2104
2179
  /* @__PURE__ */ jsx9(
2105
2180
  "button",
2106
2181
  {
2107
2182
  type: "button",
2108
2183
  onClick: () => onRemove(attachment.id),
2109
2184
  "aria-label": `\u79FB\u9664 ${attachment.name}`,
2110
- className: "ml-0.5 inline-flex shrink-0 items-center justify-center rounded-full text-[hsl(var(--muted-foreground))] transition hover:text-rose-400",
2185
+ className: "relative z-10 ml-0.5 inline-flex shrink-0 items-center justify-center rounded-full text-[hsl(var(--muted-foreground))] transition hover:text-rose-400",
2111
2186
  children: /* @__PURE__ */ jsx9(X2, { size: 10 })
2112
2187
  }
2113
2188
  )
@@ -2121,13 +2196,13 @@ function AddContextDialog({
2121
2196
  onAdd
2122
2197
  }) {
2123
2198
  const CONTEXT_INLINE_THRESHOLD = 200;
2124
- const [label, setLabel] = useState7("");
2125
- const [content, setContent] = useState7("");
2126
- const [showSessionPicker, setShowSessionPicker] = useState7(false);
2127
- const [sessions, setSessions] = useState7([]);
2128
- const [loadingSessions, setLoadingSessions] = useState7(false);
2129
- const [importingId, setImportingId] = useState7(null);
2130
- const [isImportProcessing, setIsImportProcessing] = useState7(false);
2199
+ const [label, setLabel] = useState8("");
2200
+ const [content, setContent] = useState8("");
2201
+ const [showSessionPicker, setShowSessionPicker] = useState8(false);
2202
+ const [sessions, setSessions] = useState8([]);
2203
+ const [loadingSessions, setLoadingSessions] = useState8(false);
2204
+ const [importingId, setImportingId] = useState8(null);
2205
+ const [isImportProcessing, setIsImportProcessing] = useState8(false);
2131
2206
  const sanitizeContextFolderName = (raw) => {
2132
2207
  const normalized = raw.trim().toLowerCase();
2133
2208
  const safe = normalized.replace(/[^\w\-.\u4e00-\u9fa5]+/g, "_").replace(/^_+|_+$/g, "");
@@ -2357,7 +2432,7 @@ function ComposerContextPill({
2357
2432
  content,
2358
2433
  onRemove
2359
2434
  }) {
2360
- const [showDetail, setShowDetail] = useState7(false);
2435
+ const [showDetail, setShowDetail] = useState8(false);
2361
2436
  const tokenK = formatTokenK(content);
2362
2437
  return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2363
2438
  /* @__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: [
@@ -2552,6 +2627,7 @@ function ChatInput({
2552
2627
  onSend,
2553
2628
  onStop,
2554
2629
  isStreaming,
2630
+ isStopping = false,
2555
2631
  mode = "executing",
2556
2632
  onToggleMode,
2557
2633
  renderAttachments,
@@ -2571,13 +2647,13 @@ function ChatInput({
2571
2647
  onResyncSkills,
2572
2648
  isResyncingSkills = false
2573
2649
  }) {
2574
- const [input, setInputInternal] = useState7(externalDraft?.value ?? "");
2650
+ const [input, setInputInternal] = useState8(externalDraft?.value ?? "");
2575
2651
  const setInput = useEffectEvent2((value) => {
2576
2652
  setInputInternal(value);
2577
2653
  externalDraft?.setValue(value);
2578
2654
  });
2579
2655
  const externalDraftValue = externalDraft?.value;
2580
- const inputRef = useRef4(input);
2656
+ const inputRef = useRef5(input);
2581
2657
  inputRef.current = input;
2582
2658
  useEffect7(() => {
2583
2659
  if (externalDraftValue == null) return;
@@ -2587,7 +2663,7 @@ function ChatInput({
2587
2663
  }
2588
2664
  setInputInternal(externalDraftValue);
2589
2665
  }, [externalDraftValue]);
2590
- const [composerAttachments, setComposerAttachmentsInternal] = useState7(
2666
+ const [composerAttachments, setComposerAttachmentsInternal] = useState8(
2591
2667
  externalAttachments?.value ?? []
2592
2668
  );
2593
2669
  const setComposerAttachments = useEffectEvent2(
@@ -2600,24 +2676,24 @@ function ChatInput({
2600
2676
  }
2601
2677
  );
2602
2678
  const externalAttachmentsValue = externalAttachments?.value;
2603
- const composerAttachmentsRef = useRef4(composerAttachments);
2679
+ const composerAttachmentsRef = useRef5(composerAttachments);
2604
2680
  composerAttachmentsRef.current = composerAttachments;
2605
2681
  useEffect7(() => {
2606
2682
  if (externalAttachmentsValue == null) return;
2607
2683
  if (composerAttachmentsRef.current === externalAttachmentsValue) return;
2608
2684
  setComposerAttachmentsInternal(externalAttachmentsValue);
2609
2685
  }, [externalAttachmentsValue]);
2610
- const [dragging, setDragging] = useState7(false);
2611
- const [isEditorFocused, setIsEditorFocused] = useState7(false);
2612
- const [oversizedFiles, setOversizedFiles] = useState7([]);
2613
- const [selectedModel, setSelectedModel] = useState7("");
2686
+ const [dragging, setDragging] = useState8(false);
2687
+ const [isEditorFocused, setIsEditorFocused] = useState8(false);
2688
+ const [oversizedFiles, setOversizedFiles] = useState8([]);
2689
+ const [selectedModel, setSelectedModel] = useState8("");
2614
2690
  const { setPreferredModel } = usePreferredModel();
2615
2691
  const queryClient = useQueryClient2();
2616
- const actionMenuRef = useRef4(null);
2617
- const fileInputRef = useRef4(null);
2618
- const folderInputRef = useRef4(null);
2619
- const editorRef = useRef4(null);
2620
- const localImageUrlsRef = useRef4(/* @__PURE__ */ new Map());
2692
+ const actionMenuRef = useRef5(null);
2693
+ const fileInputRef = useRef5(null);
2694
+ const folderInputRef = useRef5(null);
2695
+ const editorRef = useRef5(null);
2696
+ const localImageUrlsRef = useRef5(/* @__PURE__ */ new Map());
2621
2697
  const activeSessionId = useSessionStore((state) => state.activeSessionId);
2622
2698
  const sessions = useSessionStore((state) => state.sessions);
2623
2699
  const activeSessionModel = useMemo7(
@@ -2650,7 +2726,7 @@ function ChatInput({
2650
2726
  const inputHistory = useInputHistory(activeSessionId);
2651
2727
  const getSessionId = useEffectEvent2(() => activeSessionId);
2652
2728
  const handleSlashCommand = useEffectEvent2((commandId) => onCommand?.(commandId));
2653
- const [localImageUrls, setLocalImageUrls] = useState7({});
2729
+ const [localImageUrls, setLocalImageUrls] = useState8({});
2654
2730
  useEffect7(() => {
2655
2731
  const closeActionMenu = (event) => {
2656
2732
  const menu = actionMenuRef.current;
@@ -2979,7 +3055,7 @@ function ChatInput({
2979
3055
  editor.commands.focus("end");
2980
3056
  setRewindDraft(activeSessionId, null);
2981
3057
  }, [activeSessionId, editor, rewindDraft, setRewindDraft]);
2982
- const submittingRef = useRef4(false);
3058
+ const submittingRef = useRef5(false);
2983
3059
  useEffect7(() => {
2984
3060
  let changed = false;
2985
3061
  const nextLocalIds = /* @__PURE__ */ new Set();
@@ -3058,14 +3134,14 @@ function ChatInput({
3058
3134
  });
3059
3135
  const { isRecording, level: voiceLevel } = voice;
3060
3136
  const handleMicDisabledClick = () => toast.info("\u8BED\u97F3\u8F93\u5165\u529F\u80FD\u672A\u5F00\u542F\uFF0C\u8BF7\u5728\u540E\u7AEF .env \u914D\u7F6E ASR_API_KEY");
3061
- const voiceStopRef = useRef4(voice.stop);
3137
+ const voiceStopRef = useRef5(voice.stop);
3062
3138
  voiceStopRef.current = voice.stop;
3063
3139
  useEffect7(() => {
3064
3140
  if (isStreaming && isRecording) {
3065
3141
  void voiceStopRef.current();
3066
3142
  }
3067
3143
  }, [isStreaming, isRecording]);
3068
- const prevSessionIdRef = useRef4(activeSessionId);
3144
+ const prevSessionIdRef = useRef5(activeSessionId);
3069
3145
  useEffect7(() => {
3070
3146
  if (prevSessionIdRef.current !== activeSessionId) {
3071
3147
  prevSessionIdRef.current = activeSessionId;
@@ -3116,14 +3192,24 @@ function ChatInput({
3116
3192
  if (pendingFiles.length > 0) {
3117
3193
  setComposerAttachments(
3118
3194
  (prev) => prev.map(
3119
- (a) => a.kind === "file" && a.status === "pending" ? { ...a, status: "uploading" } : a
3195
+ (a) => a.kind === "file" && a.status === "pending" ? { ...a, status: "uploading", uploadProgress: 0 } : a
3120
3196
  )
3121
3197
  );
3122
3198
  try {
3123
3199
  const result = await uploadFiles(
3124
3200
  uploadSessionId,
3125
3201
  ".",
3126
- pendingFiles.map((a) => ({ file: a.file, name: a.name }))
3202
+ pendingFiles.map((a) => ({ file: a.file, name: a.name })),
3203
+ {
3204
+ onProgress: (progress) => {
3205
+ if (typeof progress.percent !== "number") return;
3206
+ setComposerAttachments(
3207
+ (prev) => prev.map(
3208
+ (a) => pendingFiles.some((pending) => pending.id === a.id) && a.kind === "file" && a.status === "uploading" ? { ...a, uploadProgress: progress.percent } : a
3209
+ )
3210
+ );
3211
+ }
3212
+ }
3127
3213
  );
3128
3214
  const uploadResultById = /* @__PURE__ */ new Map();
3129
3215
  const failedSet = new Set(result.failed);
@@ -3139,9 +3225,15 @@ function ChatInput({
3139
3225
  updatedAttachments = composerAttachments.map((a) => {
3140
3226
  if (!uploadResultById.has(a.id)) return a;
3141
3227
  const uploadedPath = uploadResultById.get(a.id);
3142
- if (!uploadedPath) return { ...a, status: "failed" };
3228
+ if (!uploadedPath) return { ...a, status: "failed", uploadProgress: null };
3143
3229
  const actualName = uploadedPath.split("/").pop() || a.name;
3144
- return { ...a, name: actualName, status: "uploaded", uploadedPath };
3230
+ return {
3231
+ ...a,
3232
+ name: actualName,
3233
+ status: "uploaded",
3234
+ uploadedPath,
3235
+ uploadProgress: null
3236
+ };
3145
3237
  });
3146
3238
  setComposerAttachments(updatedAttachments);
3147
3239
  if (result.uploaded.length > 0) {
@@ -3183,7 +3275,7 @@ function ChatInput({
3183
3275
  const removeAttachment = (id) => {
3184
3276
  setComposerAttachments((prev) => prev.filter((attachment) => attachment.id !== id));
3185
3277
  };
3186
- const [showAddContext, setShowAddContext] = useState7(false);
3278
+ const [showAddContext, setShowAddContext] = useState8(false);
3187
3279
  const isPlanning = mode === "planning";
3188
3280
  const placeholder = isPlanning ? "\u89C4\u5212\u8FDB\u884C\u4E2D\u2026 \u53EF\u8F93\u5165\u8865\u5145\u9700\u6C42\u6216\u7B49\u5F85\u5B8C\u6210" : "\u8F93\u5165\u6D88\u606F\u2026";
3189
3281
  const attachments = renderAttachments?.() ?? null;
@@ -3559,10 +3651,11 @@ function ChatInput({
3559
3651
  "button",
3560
3652
  {
3561
3653
  type: "button",
3562
- onClick: onStop,
3563
- "aria-label": "\u505C\u6B62\u751F\u6210",
3564
- className: "flex h-7 w-7 items-center justify-center rounded-lg bg-[hsl(var(--destructive))] text-[hsl(var(--destructive-foreground))] transition-opacity hover:opacity-80",
3565
- children: /* @__PURE__ */ jsx9(Square2, { size: 13 })
3654
+ onClick: () => void onStop(),
3655
+ disabled: isStopping,
3656
+ "aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u751F\u6210",
3657
+ className: "flex h-7 w-7 items-center justify-center rounded-lg bg-[hsl(var(--destructive))] text-[hsl(var(--destructive-foreground))] transition-opacity hover:opacity-80 disabled:cursor-wait disabled:opacity-70",
3658
+ children: isStopping ? /* @__PURE__ */ jsx9(Loader22, { size: 13, className: "animate-spin" }) : /* @__PURE__ */ jsx9(Square2, { size: 13 })
3566
3659
  }
3567
3660
  ) : /* @__PURE__ */ jsx9(
3568
3661
  "button",
@@ -3652,12 +3745,12 @@ import {
3652
3745
  useEffect as useEffect16,
3653
3746
  useEffectEvent as useEffectEvent4,
3654
3747
  useMemo as useMemo17,
3655
- useRef as useRef11,
3656
- useState as useState20
3748
+ useRef as useRef12,
3749
+ useState as useState21
3657
3750
  } from "react";
3658
3751
 
3659
3752
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
3660
- import { useCallback as useCallback7, useMemo as useMemo8, useRef as useRef5, useState as useState8 } from "react";
3753
+ import { useCallback as useCallback7, useMemo as useMemo8, useRef as useRef6, useState as useState9 } from "react";
3661
3754
  var DEFAULT_SPRING_ANIMATION = {
3662
3755
  /**
3663
3756
  * A value from 0 to 1, on how much to damp the animation.
@@ -3694,10 +3787,10 @@ globalThis.document?.addEventListener("click", () => {
3694
3787
  mouseDown = false;
3695
3788
  });
3696
3789
  var useStickToBottom = (options = {}) => {
3697
- const [escapedFromLock, updateEscapedFromLock] = useState8(false);
3698
- const [isAtBottom, updateIsAtBottom] = useState8(options.initial !== false);
3699
- const [isNearBottom, setIsNearBottom] = useState8(false);
3700
- const optionsRef = useRef5(null);
3790
+ const [escapedFromLock, updateEscapedFromLock] = useState9(false);
3791
+ const [isAtBottom, updateIsAtBottom] = useState9(options.initial !== false);
3792
+ const [isNearBottom, setIsNearBottom] = useState9(false);
3793
+ const optionsRef = useRef6(null);
3701
3794
  optionsRef.current = options;
3702
3795
  const isSelecting = useCallback7(() => {
3703
3796
  if (!mouseDown) {
@@ -4001,11 +4094,11 @@ function mergeAnimations(...animations) {
4001
4094
 
4002
4095
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
4003
4096
  import * as React from "react";
4004
- import { createContext, useContext, useEffect as useEffect8, useImperativeHandle as useImperativeHandle3, useLayoutEffect, useMemo as useMemo9, useRef as useRef6 } from "react";
4097
+ import { createContext, useContext, useEffect as useEffect8, useImperativeHandle as useImperativeHandle3, useLayoutEffect, useMemo as useMemo9, useRef as useRef7 } from "react";
4005
4098
  var StickToBottomContext = createContext(null);
4006
4099
  var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect8;
4007
4100
  function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
4008
- const customTargetScrollTop = useRef6(null);
4101
+ const customTargetScrollTop = useRef7(null);
4009
4102
  const targetScrollTop = React.useCallback((target, elements) => {
4010
4103
  const get = context?.targetScrollTop ?? currentTargetScrollTop;
4011
4104
  return get?.(target, elements) ?? target;
@@ -4082,7 +4175,7 @@ function useStickToBottomContext() {
4082
4175
 
4083
4176
  // src/react/components/chat/AssistantTurnBlock.tsx
4084
4177
  import { AlertCircle, BookOpen, Check as Check4, ChevronRight as ChevronRight5 } from "lucide-react";
4085
- import { useCallback as useCallback11, useEffect as useEffect14, useMemo as useMemo15, useRef as useRef10, useState as useState17 } from "react";
4178
+ import { useCallback as useCallback11, useEffect as useEffect14, useMemo as useMemo15, useRef as useRef11, useState as useState18 } from "react";
4086
4179
 
4087
4180
  // src/react/routes.ts
4088
4181
  var MEMORIES_ROUTE = "/memories";
@@ -4100,8 +4193,8 @@ import {
4100
4193
  useContext as useContext2,
4101
4194
  useEffect as useEffect9,
4102
4195
  useMemo as useMemo11,
4103
- useRef as useRef7,
4104
- useState as useState9
4196
+ useRef as useRef8,
4197
+ useState as useState10
4105
4198
  } from "react";
4106
4199
  import { createPortal as createPortal2 } from "react-dom";
4107
4200
  import { Streamdown } from "streamdown";
@@ -4168,7 +4261,6 @@ var useReasoning = () => {
4168
4261
  }
4169
4262
  return context;
4170
4263
  };
4171
- var AUTO_CLOSE_DELAY = 3e3;
4172
4264
  var MS_IN_S = 1e3;
4173
4265
  var Reasoning = memo2(
4174
4266
  ({
@@ -4181,10 +4273,8 @@ var Reasoning = memo2(
4181
4273
  children,
4182
4274
  ...props
4183
4275
  }) => {
4184
- const resolvedDefaultOpen = defaultOpen ?? isStreaming;
4185
- const isExplicitlyClosed = defaultOpen === false;
4186
4276
  const [isOpen, setIsOpen] = useControllableState({
4187
- defaultProp: resolvedDefaultOpen,
4277
+ defaultProp: defaultOpen ?? false,
4188
4278
  onChange: onOpenChange,
4189
4279
  prop: open
4190
4280
  });
@@ -4192,12 +4282,9 @@ var Reasoning = memo2(
4192
4282
  defaultProp: void 0,
4193
4283
  prop: durationProp
4194
4284
  });
4195
- const hasEverStreamedRef = useRef7(isStreaming);
4196
- const [hasAutoClosed, setHasAutoClosed] = useState9(false);
4197
- const startTimeRef = useRef7(null);
4285
+ const startTimeRef = useRef8(null);
4198
4286
  useEffect9(() => {
4199
4287
  if (isStreaming) {
4200
- hasEverStreamedRef.current = true;
4201
4288
  if (startTimeRef.current === null) {
4202
4289
  startTimeRef.current = Date.now();
4203
4290
  }
@@ -4206,20 +4293,6 @@ var Reasoning = memo2(
4206
4293
  startTimeRef.current = null;
4207
4294
  }
4208
4295
  }, [isStreaming, setDuration]);
4209
- useEffect9(() => {
4210
- if (isStreaming && !isOpen && !isExplicitlyClosed) {
4211
- setIsOpen(true);
4212
- }
4213
- }, [isStreaming, isOpen, setIsOpen, isExplicitlyClosed]);
4214
- useEffect9(() => {
4215
- if (hasEverStreamedRef.current && !isStreaming && isOpen && !hasAutoClosed) {
4216
- const timer = setTimeout(() => {
4217
- setIsOpen(false);
4218
- setHasAutoClosed(true);
4219
- }, AUTO_CLOSE_DELAY);
4220
- return () => clearTimeout(timer);
4221
- }
4222
- }, [isStreaming, isOpen, setIsOpen, hasAutoClosed]);
4223
4296
  const handleOpenChange = useCallback9(
4224
4297
  (newOpen) => {
4225
4298
  setIsOpen(newOpen);
@@ -4233,7 +4306,7 @@ var Reasoning = memo2(
4233
4306
  return /* @__PURE__ */ jsx12(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx12(
4234
4307
  Collapsible,
4235
4308
  {
4236
- className: cn("not-prose mb-4", className),
4309
+ className: cn("not-prose", className),
4237
4310
  onOpenChange: handleOpenChange,
4238
4311
  open: isOpen,
4239
4312
  ...props,
@@ -4242,38 +4315,36 @@ var Reasoning = memo2(
4242
4315
  ) });
4243
4316
  }
4244
4317
  );
4245
- var defaultGetThinkingMessage = (isStreaming, duration) => {
4246
- if (isStreaming || duration === 0) {
4247
- return /* @__PURE__ */ jsx12(Shimmer, { duration: 1, children: "\u6DF1\u5EA6\u601D\u8003\u4E2D..." });
4248
- }
4249
- if (duration === void 0) {
4250
- return /* @__PURE__ */ jsx12("p", { children: "\u5DF2\u6DF1\u5EA6\u601D\u8003" });
4251
- }
4252
- return /* @__PURE__ */ jsxs10("p", { children: [
4253
- "\u5DF2\u6DF1\u5EA6\u601D\u8003 ",
4254
- duration,
4255
- " \u79D2"
4256
- ] });
4257
- };
4318
+ function formatWordCount(value) {
4319
+ return new Intl.NumberFormat("zh-CN").format(value);
4320
+ }
4258
4321
  var ReasoningTrigger = memo2(
4259
4322
  ({
4260
4323
  className,
4261
4324
  children,
4262
- getThinkingMessage = defaultGetThinkingMessage,
4325
+ wordCount,
4263
4326
  ...props
4264
4327
  }) => {
4265
4328
  const { isStreaming, isOpen, duration } = useReasoning();
4329
+ const status = isStreaming || duration === 0 ? "\u6B63\u5728\u601D\u8003" : "\u5DF2\u601D\u8003";
4330
+ const detail = `${formatWordCount(wordCount)} \u5B57`;
4266
4331
  return /* @__PURE__ */ jsx12(
4267
4332
  CollapsibleTrigger,
4268
4333
  {
4269
4334
  className: cn(
4270
- "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",
4335
+ "flex w-fit items-center gap-2 rounded-full px-1 py-0.5 text-muted-foreground text-sm transition-colors hover:text-foreground",
4271
4336
  className
4272
4337
  ),
4273
4338
  ...props,
4274
4339
  children: children ?? /* @__PURE__ */ jsxs10(Fragment3, { children: [
4275
4340
  /* @__PURE__ */ jsx12(BrainIcon, { className: "size-4" }),
4276
- getThinkingMessage(isStreaming, duration),
4341
+ /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
4342
+ isStreaming || duration === 0 ? /* @__PURE__ */ jsx12(Shimmer, { duration: 1, children: status }) : /* @__PURE__ */ jsx12("span", { children: status }),
4343
+ /* @__PURE__ */ jsxs10("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
4344
+ "\xB7 ",
4345
+ detail
4346
+ ] })
4347
+ ] }),
4277
4348
  /* @__PURE__ */ jsx12(
4278
4349
  ChevronDownIcon,
4279
4350
  {
@@ -4295,7 +4366,7 @@ var ReasoningContent = memo2(
4295
4366
  CollapsibleContent,
4296
4367
  {
4297
4368
  className: cn(
4298
- "mt-4 text-sm",
4369
+ "mt-2 max-h-60 overflow-auto rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted))]/20 px-3 py-2 text-sm",
4299
4370
  "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
4300
4371
  className
4301
4372
  ),
@@ -4309,7 +4380,7 @@ function compactReasoningText(text) {
4309
4380
  }
4310
4381
  function ThinkingBadge({ reasoning, variant = "inline", onClick }) {
4311
4382
  const compactReasoning = compactReasoningText(reasoning);
4312
- const [open, setOpen] = useState9(false);
4383
+ const [open, setOpen] = useState10(false);
4313
4384
  return /* @__PURE__ */ jsxs10(
4314
4385
  "span",
4315
4386
  {
@@ -4386,10 +4457,10 @@ ReasoningContent.displayName = "ReasoningContent";
4386
4457
 
4387
4458
  // src/react/components/chat/AgentLoopBlock.tsx
4388
4459
  import { Bot, Check as Check3, ChevronRight as ChevronRight4, FileText as FileText6, Loader2 as Loader24, MessageSquareMore as MessageSquareMore2 } from "lucide-react";
4389
- import { useEffect as useEffect13, useMemo as useMemo14, useState as useState16 } from "react";
4460
+ import { useEffect as useEffect13, useMemo as useMemo14, useState as useState17 } from "react";
4390
4461
 
4391
4462
  // src/react/components/chat/ResourceIframe.tsx
4392
- import { useEffect as useEffect10, useEffectEvent as useEffectEvent3, useRef as useRef8, useState as useState10 } from "react";
4463
+ import { useEffect as useEffect10, useEffectEvent as useEffectEvent3, useRef as useRef9, useState as useState11 } from "react";
4393
4464
  import { jsx as jsx13 } from "react/jsx-runtime";
4394
4465
  function isResourceBridgeMessage(value) {
4395
4466
  return typeof value === "object" && value !== null && value.__resourceBridge === true && typeof value.action === "string";
@@ -4398,8 +4469,8 @@ var INLINE_HEIGHT_MIN = 80;
4398
4469
  var INLINE_HEIGHT_MAX = 6e3;
4399
4470
  var INLINE_HEIGHT_PADDING = 8;
4400
4471
  function ResourceIframe({ ui, sessionId }) {
4401
- const iframeRef = useRef8(null);
4402
- const iframeKeyRef = useRef8(
4472
+ const iframeRef = useRef9(null);
4473
+ const iframeKeyRef = useRef9(
4403
4474
  `iframe-${Math.random().toString(36).slice(2, 10)}`
4404
4475
  );
4405
4476
  const activeSessionId = useSessionStore((state) => state.activeSessionId);
@@ -4407,7 +4478,7 @@ function ResourceIframe({ ui, sessionId }) {
4407
4478
  const theme = useUiStore((state) => state.theme);
4408
4479
  const resourceUri = ui.resourceUri ?? ui.resourceURI;
4409
4480
  const iframeLabel = ui.title ?? resourceUri ?? "\u5DE5\u5177\u754C\u9762";
4410
- const [autoHeight, setAutoHeight] = useState10(null);
4481
+ const [autoHeight, setAutoHeight] = useState11(null);
4411
4482
  useEffect10(() => {
4412
4483
  setAutoHeight(null);
4413
4484
  }, [ui.resourceHTML, resourceUri]);
@@ -4518,7 +4589,7 @@ function ResourceIframe({ ui, sessionId }) {
4518
4589
 
4519
4590
  // src/react/components/chat/ToolCallBlock.tsx
4520
4591
  import { Check, ChevronRight as ChevronRight3, Loader2 as Loader23, MessageSquareMore, PanelRightOpen, X as X4 } from "lucide-react";
4521
- import { useMemo as useMemo12, useState as useState13 } from "react";
4592
+ import { useMemo as useMemo12, useState as useState14 } from "react";
4522
4593
 
4523
4594
  // src/react/components/chat/tool-renderers/shared.tsx
4524
4595
  import { jsx as jsx14 } from "react/jsx-runtime";
@@ -4845,21 +4916,21 @@ function FileEditRenderer({ toolCall }) {
4845
4916
  }
4846
4917
 
4847
4918
  // src/react/components/chat/tool-renderers/FileReadRenderer.tsx
4848
- import { useState as useState12 } from "react";
4919
+ import { useState as useState13 } from "react";
4849
4920
 
4850
4921
  // src/react/components/chat/ImageLightbox.tsx
4851
4922
  import { ChevronLeft, ChevronRight as ChevronRight2, Download as Download2, ExternalLink, Minus, Plus as Plus2, RotateCcw, X as X3 } from "lucide-react";
4852
- import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef9, useState as useState11 } from "react";
4923
+ import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef10, useState as useState12 } from "react";
4853
4924
  import { createPortal as createPortal3 } from "react-dom";
4854
4925
  import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
4855
4926
  function ImageLightbox({ open, onOpenChange, images, initialIndex = 0 }) {
4856
- const [currentIndex, setCurrentIndex] = useState11(initialIndex);
4857
- const [scale, setScale] = useState11(1);
4858
- const [translate, setTranslate] = useState11({ x: 0, y: 0 });
4859
- const dragging = useRef9(false);
4860
- const dragStart = useRef9({ x: 0, y: 0 });
4861
- const translateStart = useRef9({ x: 0, y: 0 });
4862
- const imgRef = useRef9(null);
4927
+ const [currentIndex, setCurrentIndex] = useState12(initialIndex);
4928
+ const [scale, setScale] = useState12(1);
4929
+ const [translate, setTranslate] = useState12({ x: 0, y: 0 });
4930
+ const dragging = useRef10(false);
4931
+ const dragStart = useRef10({ x: 0, y: 0 });
4932
+ const translateStart = useRef10({ x: 0, y: 0 });
4933
+ const imgRef = useRef10(null);
4863
4934
  const reset = useCallback10(() => {
4864
4935
  setScale(1);
4865
4936
  setTranslate({ x: 0, y: 0 });
@@ -4878,7 +4949,7 @@ function ImageLightbox({ open, onOpenChange, images, initialIndex = 0 }) {
4878
4949
  setCurrentIndex((i) => Math.min(images.length - 1, i + 1));
4879
4950
  reset();
4880
4951
  }, [images.length, reset]);
4881
- const prevOpenRef = useRef9(false);
4952
+ const prevOpenRef = useRef10(false);
4882
4953
  useEffect11(() => {
4883
4954
  if (open && !prevOpenRef.current) {
4884
4955
  setCurrentIndex(initialIndex);
@@ -5168,7 +5239,7 @@ function formatLineRange(startLine, endLine) {
5168
5239
  return startLine === endLine ? `\u7B2C ${startLine} \u884C` : `\u7B2C ${startLine}-${endLine} \u884C`;
5169
5240
  }
5170
5241
  function FileReadRenderer({ toolCall }) {
5171
- const [lightboxIndex, setLightboxIndex] = useState12(null);
5242
+ const [lightboxIndex, setLightboxIndex] = useState13(null);
5172
5243
  const argsValue = parseJsonValue(toolCall.arguments);
5173
5244
  const args = isPlainObject(argsValue) ? argsValue : null;
5174
5245
  const filePath = extractToolFilePath(toolCall);
@@ -5466,7 +5537,7 @@ function ToolCallBlock({
5466
5537
  reasoning,
5467
5538
  customization
5468
5539
  }) {
5469
- const [expanded, setExpanded] = useState13(false);
5540
+ const [expanded, setExpanded] = useState14(false);
5470
5541
  const activeSessionId = useSessionStore((s) => s.activeSessionId);
5471
5542
  const sessions = useSessionStore((s) => s.sessions);
5472
5543
  const pushArtifact = useUiStore((s) => s.pushArtifact);
@@ -5670,7 +5741,7 @@ function buildAskUserPayload(argumentsJson) {
5670
5741
  }
5671
5742
 
5672
5743
  // src/react/components/chat/UserMessageBubble.tsx
5673
- import { useState as useState15 } from "react";
5744
+ import { useState as useState16 } from "react";
5674
5745
 
5675
5746
  // src/react/lib/preview-dispatch.ts
5676
5747
  var IMAGE_EXTS = [
@@ -6003,10 +6074,10 @@ function MessageFileAttachmentList({
6003
6074
 
6004
6075
  // src/react/components/chat/MessageActions.tsx
6005
6076
  import { Check as Check2, Copy } from "lucide-react";
6006
- import { useState as useState14 } from "react";
6077
+ import { useState as useState15 } from "react";
6007
6078
  import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
6008
6079
  function MessageActions({ content, className }) {
6009
- const [copied, setCopied] = useState14(false);
6080
+ const [copied, setCopied] = useState15(false);
6010
6081
  const handleCopy = async () => {
6011
6082
  const ok = await copyToClipboard(content);
6012
6083
  if (ok) {
@@ -6196,8 +6267,8 @@ function UserMessageBubble({ message, className }) {
6196
6267
  alt: attachment.name || "\u7528\u6237\u4E0A\u4F20\u7684\u56FE\u7247"
6197
6268
  }))
6198
6269
  ];
6199
- const [lightboxIndex, setLightboxIndex] = useState15(null);
6200
- const [preview, setPreview] = useState15(null);
6270
+ const [lightboxIndex, setLightboxIndex] = useState16(null);
6271
+ const [preview, setPreview] = useState16(null);
6201
6272
  const handleTextAttachmentPreview = (attachment) => {
6202
6273
  if (!activeSessionId) return;
6203
6274
  const pathForUrl = attachment.uploadedPath ?? attachment.name;
@@ -6364,7 +6435,7 @@ function AgentLoopBlock({ toolCall, sessionId, reasoning }) {
6364
6435
  () => visibleLoopToolCalls.some((childToolCall) => childToolCall.status === "awaiting_answer"),
6365
6436
  [visibleLoopToolCalls]
6366
6437
  );
6367
- const [expanded, setExpanded] = useState16(hasAwaitingAnswer);
6438
+ const [expanded, setExpanded] = useState17(hasAwaitingAnswer);
6368
6439
  const completedToolLabels = useMemo14(
6369
6440
  () => visibleLoopToolCalls.flatMap((childToolCall) => {
6370
6441
  const isCompleted = childToolCall.status === "done" || childToolCall.status !== "pending" && childToolCall.status !== "awaiting_answer" && childToolCall.status !== "error" && childToolCall.status !== "cancelled";
@@ -6616,12 +6687,9 @@ function ExpandedChildAssistantMessage({
6616
6687
  const text = typeof message.content === "string" ? message.content.trim() : message.content.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
6617
6688
  const toolCalls = message.tool_calls ?? [];
6618
6689
  const hasToolCalls = toolCalls.length > 0;
6619
- const firstToolCallId = toolCalls[0]?.id;
6620
- const toolReasoning = !isStreaming && !text && hasToolCalls ? message.reasoning : void 0;
6621
- const standaloneReasoning = !isStreaming && !text && !hasToolCalls ? message.reasoning : void 0;
6622
6690
  return /* @__PURE__ */ jsxs24("div", { className: "flex flex-col gap-2", children: [
6623
- message.reasoning && isStreaming && /* @__PURE__ */ jsxs24(Reasoning, { isStreaming, children: [
6624
- /* @__PURE__ */ jsx29(ReasoningTrigger, {}),
6691
+ message.reasoning && /* @__PURE__ */ jsxs24(Reasoning, { isStreaming, children: [
6692
+ /* @__PURE__ */ jsx29(ReasoningTrigger, { wordCount: message.reasoning.length }),
6625
6693
  /* @__PURE__ */ jsx29(ReasoningContent, { children: message.reasoning })
6626
6694
  ] }),
6627
6695
  text ? /* @__PURE__ */ jsx29(
@@ -6630,18 +6698,17 @@ function ExpandedChildAssistantMessage({
6630
6698
  text,
6631
6699
  isStreaming,
6632
6700
  sessionId,
6633
- reasoning: !isStreaming ? message.reasoning : void 0
6701
+ reasoning: void 0
6634
6702
  }
6635
6703
  ) : null,
6636
6704
  !text && isStreaming && /* @__PURE__ */ jsx29("span", { className: "pl-8 text-[12px] text-[hsl(var(--muted-foreground))]", children: "\u6B63\u5728\u751F\u6210..." }),
6637
- standaloneReasoning ? /* @__PURE__ */ jsx29("div", { className: "pl-8", children: /* @__PURE__ */ jsx29(ThinkingBadge, { reasoning: standaloneReasoning, variant: "block" }) }) : null,
6638
6705
  hasToolCalls && /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-2", children: toolCalls.map(
6639
- (toolCall, index) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx29(
6706
+ (toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx29(
6640
6707
  AgentLoopBlock,
6641
6708
  {
6642
6709
  toolCall,
6643
6710
  sessionId,
6644
- reasoning: toolCall.id === firstToolCallId ? toolReasoning : void 0
6711
+ reasoning: void 0
6645
6712
  },
6646
6713
  toolCall.id
6647
6714
  ) : /* @__PURE__ */ jsx29(
@@ -6651,7 +6718,7 @@ function ExpandedChildAssistantMessage({
6651
6718
  sessionId,
6652
6719
  level: 2,
6653
6720
  turnBlocks: message.blocks,
6654
- reasoning: index === 0 ? toolReasoning : void 0
6721
+ reasoning: void 0
6655
6722
  },
6656
6723
  toolCall.id
6657
6724
  )
@@ -6873,10 +6940,10 @@ function AssistantTurnBlock({
6873
6940
  ),
6874
6941
  [allToolCalls]
6875
6942
  );
6876
- const [displayMode, setDisplayMode] = useState17(
6943
+ const [displayMode, setDisplayMode] = useState18(
6877
6944
  defaultTurnDisplayMode({ forceExpanded, hasActionableToolCall })
6878
6945
  );
6879
- const wasStreamingRef = useRef10(isStreaming);
6946
+ const wasStreamingRef = useRef11(isStreaming);
6880
6947
  useEffect14(() => {
6881
6948
  if (wasStreamingRef.current && !isStreaming && !forceExpanded) {
6882
6949
  setDisplayMode(defaultTurnDisplayMode({ forceExpanded, hasActionableToolCall }));
@@ -6975,23 +7042,18 @@ function AssistantMessages({
6975
7042
  return messages.map(
6976
7043
  (message, index) => isRenderableAssistantMessage(message, isStreaming && index === messages.length - 1) ? (() => {
6977
7044
  const isStreamingLastMessage = isStreaming && index === messages.length - 1;
6978
- const hasText = !!getMessageText(message);
6979
7045
  const reasoning = message.reasoning;
6980
7046
  const hasReasoning = !!reasoning;
6981
- const hasAttachments = getImageParts(message.content).length > 0 || getFileParts(message.content).length > 0;
6982
7047
  const toolCalls = message.tool_calls ?? [];
6983
7048
  const hasToolCalls = toolCalls.length > 0;
6984
- const firstToolCallId = toolCalls[0]?.id;
6985
7049
  const toolRenderItems = groupDetailedToolCalls(toolCalls);
6986
- const toolReasoning = hasReasoning && !hasText && !hasAttachments && hasToolCalls && !isStreamingLastMessage ? reasoning : void 0;
6987
- const contentReasoning = hasReasoning && !isStreamingLastMessage && (hasText || hasAttachments || !hasToolCalls) ? reasoning : void 0;
6988
7050
  return /* @__PURE__ */ jsxs25(
6989
7051
  "div",
6990
7052
  {
6991
7053
  className: "flex flex-col gap-3",
6992
7054
  children: [
6993
- hasReasoning && isStreamingLastMessage && /* @__PURE__ */ jsxs25(Reasoning, { isStreaming: isStreamingLastMessage, children: [
6994
- /* @__PURE__ */ jsx30(ReasoningTrigger, {}),
7055
+ hasReasoning && /* @__PURE__ */ jsxs25(Reasoning, { isStreaming: isStreamingLastMessage, children: [
7056
+ /* @__PURE__ */ jsx30(ReasoningTrigger, { wordCount: reasoning.length }),
6995
7057
  /* @__PURE__ */ jsx30(ReasoningContent, { children: reasoning })
6996
7058
  ] }),
6997
7059
  /* @__PURE__ */ jsx30(
@@ -7000,7 +7062,7 @@ function AssistantMessages({
7000
7062
  message,
7001
7063
  isStreaming: isStreamingLastMessage,
7002
7064
  sessionId,
7003
- reasoning: contentReasoning,
7065
+ reasoning: void 0,
7004
7066
  className: customization?.classNames?.assistantText
7005
7067
  }
7006
7068
  ),
@@ -7011,7 +7073,7 @@ function AssistantMessages({
7011
7073
  {
7012
7074
  toolCalls: item.toolCalls,
7013
7075
  kind: item.kind,
7014
- reasoning: item.toolCalls[0]?.id === firstToolCallId ? toolReasoning : void 0,
7076
+ reasoning: void 0,
7015
7077
  sessionId,
7016
7078
  sessionStatus,
7017
7079
  askAnswers,
@@ -7029,7 +7091,7 @@ function AssistantMessages({
7029
7091
  {
7030
7092
  toolCall,
7031
7093
  sessionId,
7032
- reasoning: toolCall.id === firstToolCallId ? toolReasoning : void 0
7094
+ reasoning: void 0
7033
7095
  },
7034
7096
  toolCall.id
7035
7097
  ) : customization?.components?.ToolCall ? /* @__PURE__ */ jsx30(
@@ -7043,7 +7105,7 @@ function AssistantMessages({
7043
7105
  sessionStatus,
7044
7106
  level,
7045
7107
  turnBlocks: message.blocks,
7046
- reasoning: toolCall.id === firstToolCallId ? toolReasoning : void 0,
7108
+ reasoning: void 0,
7047
7109
  customization
7048
7110
  },
7049
7111
  toolCall.id
@@ -7058,7 +7120,7 @@ function AssistantMessages({
7058
7120
  sessionStatus,
7059
7121
  level,
7060
7122
  turnBlocks: message.blocks,
7061
- reasoning: toolCall.id === firstToolCallId ? toolReasoning : void 0,
7123
+ reasoning: void 0,
7062
7124
  customization
7063
7125
  },
7064
7126
  toolCall.id
@@ -7100,7 +7162,7 @@ function CompactToolGroupBlock({
7100
7162
  level,
7101
7163
  customization
7102
7164
  }) {
7103
- const [expanded, setExpanded] = useState17(false);
7165
+ const [expanded, setExpanded] = useState18(false);
7104
7166
  const indentClass = level === 2 ? "ml-3" : "ml-4";
7105
7167
  const hasError = toolCalls.some(
7106
7168
  (tc) => tc.status === "error" || tc.status === "cancelled"
@@ -7266,7 +7328,7 @@ function AssistantText({
7266
7328
  }
7267
7329
  function MemoryRefsHint({ refs: rawRefs }) {
7268
7330
  const refs = Array.isArray(rawRefs) ? rawRefs : [];
7269
- const [expanded, setExpanded] = useState17(false);
7331
+ const [expanded, setExpanded] = useState18(false);
7270
7332
  const hasSkill = refs.some((r3) => r3.skill_name);
7271
7333
  const label = hasSkill ? "\u53C2\u8003\u4E86\u8BE5\u6280\u80FD\u7684\u5386\u53F2\u7ECF\u9A8C" : "\u53C2\u8003\u4E86\u5386\u53F2\u7ECF\u9A8C";
7272
7334
  return /* @__PURE__ */ jsxs25("div", { className: "ml-4", children: [
@@ -7314,7 +7376,7 @@ function MemoryRefsHint({ refs: rawRefs }) {
7314
7376
 
7315
7377
  // src/react/components/chat/CompactionCard.tsx
7316
7378
  import { ChevronDown as ChevronDown2, ChevronRight as ChevronRight6, Loader2 as Loader25, Square as Square3, XCircle } from "lucide-react";
7317
- import { useState as useState18 } from "react";
7379
+ import { useState as useState19 } from "react";
7318
7380
  import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
7319
7381
  var PERCENT_FORMATTER = new Intl.NumberFormat("zh-CN", {
7320
7382
  style: "percent",
@@ -7374,7 +7436,7 @@ function CompactionCard({
7374
7436
  ...compaction,
7375
7437
  status: status ?? "completed"
7376
7438
  } : activeCompaction;
7377
- const [expanded, setExpanded] = useState18(false);
7439
+ const [expanded, setExpanded] = useState19(false);
7378
7440
  if (!source || !source.compaction_id) {
7379
7441
  return null;
7380
7442
  }
@@ -7416,7 +7478,7 @@ function CompactionCard({
7416
7478
  "button",
7417
7479
  {
7418
7480
  type: "button",
7419
- onClick: () => getSocket().stop(sessionId),
7481
+ onClick: () => void getSocket().stop(sessionId),
7420
7482
  className: "shrink-0 rounded-md border border-current/15 px-2 py-0.5 text-[11px] transition-opacity hover:opacity-80",
7421
7483
  children: "\u53D6\u6D88\u6574\u7406"
7422
7484
  }
@@ -7535,7 +7597,7 @@ import {
7535
7597
  TerminalSquare,
7536
7598
  WandSparkles
7537
7599
  } from "lucide-react";
7538
- import { useEffect as useEffect15, useMemo as useMemo16, useState as useState19 } from "react";
7600
+ import { useEffect as useEffect15, useMemo as useMemo16, useState as useState20 } from "react";
7539
7601
  import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
7540
7602
  var EMPTY_EVENTS = [];
7541
7603
  function formatElapsedDuration(durationMs) {
@@ -7614,7 +7676,7 @@ function getTurnStartedAt(events) {
7614
7676
  return null;
7615
7677
  }
7616
7678
  function useElapsedDuration(startedAt, active) {
7617
- const [now, setNow] = useState19(() => Date.now());
7679
+ const [now, setNow] = useState20(() => Date.now());
7618
7680
  useEffect15(() => {
7619
7681
  if (!active || startedAt == null) {
7620
7682
  return;
@@ -7729,15 +7791,12 @@ function parseModeChange(message) {
7729
7791
  return null;
7730
7792
  }
7731
7793
  function getPlanningDividerKind(message) {
7732
- if (message.kind === "planning_enter" || message.kind === "planning_exit") {
7733
- return message.kind;
7734
- }
7735
7794
  const modeChange = parseModeChange(message);
7736
7795
  if (modeChange?.to === "planning") {
7737
- return "planning_enter";
7796
+ return "enter";
7738
7797
  }
7739
7798
  if (modeChange?.from === "planning") {
7740
- return "planning_exit";
7799
+ return "exit";
7741
7800
  }
7742
7801
  return null;
7743
7802
  }
@@ -7909,10 +7968,10 @@ function MessageList({
7909
7968
  ) ?? null,
7910
7969
  [lastTurnId, renderBlocks]
7911
7970
  );
7912
- const containerRef = useRef11(null);
7913
- const scrollContainerRef = useRef11(null);
7914
- const frameRef = useRef11(null);
7915
- const [activeTurnId, setActiveTurnId] = useState20(lastTurnId);
7971
+ const containerRef = useRef12(null);
7972
+ const scrollContainerRef = useRef12(null);
7973
+ const frameRef = useRef12(null);
7974
+ const [activeTurnId, setActiveTurnId] = useState21(lastTurnId);
7916
7975
  const layoutSignature = useMemo17(
7917
7976
  () => getMessagesMeasureSignature(messages),
7918
7977
  [messages]
@@ -8085,7 +8144,7 @@ function MessageListContent({
8085
8144
  (toolCall) => formatToolName(toolCall.name) === "ExitPlanMode" && toolCall.status !== "error" && toolCall.status !== "cancelled"
8086
8145
  )
8087
8146
  );
8088
- const isFollowedByPlanningExit = nextBlock?.type === "planning_divider" && nextBlock.kind === "planning_exit";
8147
+ const isFollowedByPlanningExit = nextBlock?.type === "planning_divider" && nextBlock.kind === "exit";
8089
8148
  const showPlanCard = (hasExitPlan || isFollowedByPlanningExit) && onConfirmPlan && sessionStatus === "waiting_for_input";
8090
8149
  return /* @__PURE__ */ jsxs30(
8091
8150
  "div",
@@ -8182,7 +8241,7 @@ function ScrollToBottomButton() {
8182
8241
  );
8183
8242
  }
8184
8243
  function PlanningDivider({ kind }) {
8185
- const isEnter = kind === "planning_enter";
8244
+ const isEnter = kind === "enter";
8186
8245
  return /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-3 py-1", children: [
8187
8246
  /* @__PURE__ */ jsx35("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
8188
8247
  /* @__PURE__ */ jsxs30("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300", children: [
@@ -8210,6 +8269,7 @@ function ChatView({
8210
8269
  const {
8211
8270
  messages,
8212
8271
  isStreaming,
8272
+ isStopping,
8213
8273
  send,
8214
8274
  stop
8215
8275
  } = useChat(sessionId);
@@ -8257,6 +8317,7 @@ function ChatView({
8257
8317
  onSend: (msg, _targetSessionId, model) => send(msg, mode, void 0, { model: model || void 0 }),
8258
8318
  onStop: stop,
8259
8319
  isStreaming,
8320
+ isStopping,
8260
8321
  mode,
8261
8322
  onToggleMode: toggleMode,
8262
8323
  renderAttachments,
@@ -8326,4 +8387,4 @@ use-stick-to-bottom/dist/StickToBottom.js:
8326
8387
  * Licensed under the MIT License. See License.txt in the project root for license information.
8327
8388
  *--------------------------------------------------------------------------------------------*)
8328
8389
  */
8329
- //# sourceMappingURL=chunk-75BPCDBW.js.map
8390
+ //# sourceMappingURL=chunk-RTBAPZIO.js.map