@blade-hq/agent-react 2610.0.0-beta.0 → 2610.0.0-beta.2

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.js CHANGED
@@ -545,7 +545,7 @@ function toToolCallInfo(call, results, failedToolIds) {
545
545
  }
546
546
 
547
547
  // src/components/AgentChat.tsx
548
- import { BladeApiError } from "@blade-hq/agent-client";
548
+ import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
549
549
 
550
550
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/createLucideIcon.js
551
551
  import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
@@ -618,11 +618,16 @@ var createLucideIcon = (iconName, iconNode) => {
618
618
  return Component2;
619
619
  };
620
620
 
621
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/archive.js
622
- var Archive = createLucideIcon("Archive", [
623
- ["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
624
- ["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
625
- ["path", { d: "M10 12h4", key: "a56b0p" }]
621
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-right.js
622
+ var ArrowRight = createLucideIcon("ArrowRight", [
623
+ ["path", { d: "M5 12h14", key: "1ays0h" }],
624
+ ["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
625
+ ]);
626
+
627
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up-right.js
628
+ var ArrowUpRight = createLucideIcon("ArrowUpRight", [
629
+ ["path", { d: "M7 7h10v10", key: "1tivn9" }],
630
+ ["path", { d: "M7 17 17 7", key: "1vkiza" }]
626
631
  ]);
627
632
 
628
633
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
@@ -699,24 +704,6 @@ var Download = createLucideIcon("Download", [
699
704
  ["line", { x1: "12", x2: "12", y1: "15", y2: "3", key: "1vk2je" }]
700
705
  ]);
701
706
 
702
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-code-2.js
703
- var FileCode2 = createLucideIcon("FileCode2", [
704
- ["path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4", key: "1pf5j1" }],
705
- ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
706
- ["path", { d: "m5 12-3 3 3 3", key: "oke12k" }],
707
- ["path", { d: "m9 18 3-3-3-3", key: "112psh" }]
708
- ]);
709
-
710
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js
711
- var FileSpreadsheet = createLucideIcon("FileSpreadsheet", [
712
- ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
713
- ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
714
- ["path", { d: "M8 13h2", key: "yr2amv" }],
715
- ["path", { d: "M14 13h2", key: "un5t4a" }],
716
- ["path", { d: "M8 17h2", key: "2yhykz" }],
717
- ["path", { d: "M14 17h2", key: "10kma7" }]
718
- ]);
719
-
720
707
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
721
708
  var FileText = createLucideIcon("FileText", [
722
709
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
@@ -744,11 +731,11 @@ var Film = createLucideIcon("Film", [
744
731
  ["path", { d: "M17 16.5h4", key: "go4c1d" }]
745
732
  ]);
746
733
 
747
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/image.js
748
- var Image = createLucideIcon("Image", [
749
- ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
750
- ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
751
- ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
734
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
735
+ var Globe = createLucideIcon("Globe", [
736
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
737
+ ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
738
+ ["path", { d: "M2 12h20", key: "9i4pu4" }]
752
739
  ]);
753
740
 
754
741
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
@@ -814,13 +801,6 @@ var MessageSquare = createLucideIcon("MessageSquare", [
814
801
  ["path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", key: "1lielz" }]
815
802
  ]);
816
803
 
817
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/music.js
818
- var Music = createLucideIcon("Music", [
819
- ["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
820
- ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
821
- ["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
822
- ]);
823
-
824
804
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
825
805
  var Play = createLucideIcon("Play", [
826
806
  ["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
@@ -834,6 +814,21 @@ var Settings2 = createLucideIcon("Settings2", [
834
814
  ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
835
815
  ]);
836
816
 
817
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/sparkles.js
818
+ var Sparkles = createLucideIcon("Sparkles", [
819
+ [
820
+ "path",
821
+ {
822
+ d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
823
+ key: "4pj2yx"
824
+ }
825
+ ],
826
+ ["path", { d: "M20 3v4", key: "1olli1" }],
827
+ ["path", { d: "M22 5h-4", key: "1gvqau" }],
828
+ ["path", { d: "M4 17v2", key: "vumght" }],
829
+ ["path", { d: "M5 18H3", key: "zchphs" }]
830
+ ]);
831
+
837
832
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
838
833
  var Square = createLucideIcon("Square", [
839
834
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
@@ -859,7 +854,7 @@ var X = createLucideIcon("X", [
859
854
  ]);
860
855
 
861
856
  // src/components/AgentChat.tsx
862
- import { useEffect as useEffect7, useState as useState12 } from "react";
857
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo8, useState as useState12 } from "react";
863
858
 
864
859
  // src/lib/utils.ts
865
860
  function cn(...inputs) {
@@ -1083,7 +1078,7 @@ function ConnectionBanner({ connection, className }) {
1083
1078
 
1084
1079
  // src/components/MessageList.tsx
1085
1080
  import { isHiddenInternalMessage } from "@blade-hq/agent-client";
1086
- import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo7, useRef as useRef6, useState as useState11 } from "react";
1081
+ import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useRef as useRef7, useState as useState11 } from "react";
1087
1082
 
1088
1083
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
1089
1084
  import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
@@ -1511,7 +1506,7 @@ function useStickToBottomContext() {
1511
1506
 
1512
1507
  // src/components/AssistantTurnBlock.tsx
1513
1508
  import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
1514
- import { useState as useState10 } from "react";
1509
+ import { useState as useState9 } from "react";
1515
1510
 
1516
1511
  // src/components/AgentLoopBlock.tsx
1517
1512
  import { useState as useState5 } from "react";
@@ -1714,142 +1709,14 @@ import {
1714
1709
  useEffect as useEffect4,
1715
1710
  useMemo as useMemo5,
1716
1711
  useRef as useRef5,
1717
- useState as useState7
1712
+ useState as useState6
1718
1713
  } from "react";
1719
-
1720
- // src/lib/media-tags.ts
1721
- var FILE_CARD_TAG = "blade-file-card";
1722
- var MEDIA_LINE_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?(?<wrap>[`"'*_]*)MEDIA:[ \t]*(?<path>.+?)\k<wrap>[ \t]*[。,、;;,]?[ \t]*\r?$/gm;
1723
- var MEDIA_LINE_START_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?[`"'*_]*MEDIA:/;
1724
- function basename(path) {
1725
- return path.split(/[/\\]/).filter(Boolean).pop() || path;
1726
- }
1727
- function unwrapMediaPath(path) {
1728
- if (!path.startsWith("<")) return path;
1729
- const closingBracket = path.indexOf(">");
1730
- if (closingBracket < 0) return null;
1731
- return `${path.slice(1, closingBracket)}${path.slice(closingBracket + 1)}`;
1732
- }
1733
- function isSafeMediaPath(path) {
1734
- if (!path) return false;
1735
- return !path.split(/[/\\]/).includes("..");
1736
- }
1737
- function escapeAttribute(value) {
1738
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1739
- }
1740
- function foldMediaLines(text) {
1741
- return text.replace(MEDIA_LINE_RE, (match, _wrap, rawPath) => {
1742
- const path = unwrapMediaPath(rawPath);
1743
- if (path === null) return match;
1744
- const name = basename(path);
1745
- if (!isSafeMediaPath(path)) {
1746
- return `\`${name}\``;
1747
- }
1748
- return `<${FILE_CARD_TAG} data-path="${escapeAttribute(path)}" data-name="${escapeAttribute(name)}">${escapeAttribute(name)}</${FILE_CARD_TAG}>`;
1749
- });
1750
- }
1751
- function collapseMediaTags(text, options) {
1752
- if (!text.includes("MEDIA:")) return text;
1753
- if (!options?.streaming) return foldMediaLines(text);
1754
- const newlineIndex = text.lastIndexOf("\n");
1755
- const head = newlineIndex < 0 ? "" : text.slice(0, newlineIndex + 1);
1756
- const lastLine = newlineIndex < 0 ? text : text.slice(newlineIndex + 1);
1757
- if (MEDIA_LINE_START_RE.test(lastLine)) {
1758
- return foldMediaLines(head);
1759
- }
1760
- return foldMediaLines(head) + lastLine;
1761
- }
1762
-
1763
- // src/components/FileCard.tsx
1764
- import { useState as useState6 } from "react";
1765
1714
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1766
- var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
1767
- var SHEET_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "csv"]);
1768
- var TEXT_EXTS = /* @__PURE__ */ new Set(["txt", "md", "markdown", "pdf", "doc", "docx", "rtf"]);
1769
- var CODE_EXTS = /* @__PURE__ */ new Set(["js", "jsx", "ts", "tsx", "py", "go", "json", "yaml", "yml", "html", "css"]);
1770
- var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "mkv", "avi"]);
1771
- var AUDIO_EXTS = /* @__PURE__ */ new Set(["mp3", "wav", "ogg", "m4a", "flac"]);
1772
- var ARCHIVE_EXTS = /* @__PURE__ */ new Set(["zip", "tar", "gz", "tgz", "rar", "7z"]);
1773
- function getFileIcon(name) {
1774
- const ext = name.split(".").pop()?.toLowerCase() ?? "";
1775
- if (IMAGE_EXTS.has(ext)) return Image;
1776
- if (SHEET_EXTS.has(ext)) return FileSpreadsheet;
1777
- if (TEXT_EXTS.has(ext)) return FileText;
1778
- if (CODE_EXTS.has(ext)) return FileCode2;
1779
- if (VIDEO_EXTS.has(ext)) return Film;
1780
- if (AUDIO_EXTS.has(ext)) return Music;
1781
- if (ARCHIVE_EXTS.has(ext)) return Archive;
1782
- return File;
1783
- }
1784
- function stringValue(value) {
1785
- return typeof value === "string" ? value : void 0;
1786
- }
1787
- function extractChildrenText(children) {
1788
- if (typeof children === "string") return children;
1789
- if (Array.isArray(children)) return children.map(extractChildrenText).join("");
1790
- if (children && typeof children === "object" && "props" in children) {
1791
- return extractChildrenText(children.props?.children);
1792
- }
1793
- return "";
1794
- }
1795
- function FileCard({
1796
- node,
1797
- "data-path": pathAttribute,
1798
- "data-name": nameAttribute,
1799
- dataPath,
1800
- dataName,
1801
- sessionId,
1802
- children,
1803
- className,
1804
- ...props
1805
- }) {
1806
- const client = useBladeClient();
1807
- const [downloading, setDownloading] = useState6(false);
1808
- const [failed, setFailed] = useState6(false);
1809
- const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
1810
- const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
1811
- const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
1812
- const disabled = !sessionId || !isSafeMediaPath(path);
1813
- const Icon2 = getFileIcon(name);
1814
- const handleDownload = async () => {
1815
- if (disabled || downloading || !sessionId) return;
1816
- setDownloading(true);
1817
- setFailed(false);
1818
- try {
1819
- await client.sessions.downloadFile(sessionId, path, name);
1820
- } catch {
1821
- setFailed(true);
1822
- } finally {
1823
- setDownloading(false);
1824
- }
1825
- };
1826
- return /* @__PURE__ */ jsxs6("span", { ...props, className: cn("blade-chat-file-card", className), children: [
1827
- /* @__PURE__ */ jsxs6(
1828
- "button",
1829
- {
1830
- type: "button",
1831
- onClick: handleDownload,
1832
- disabled: disabled || downloading,
1833
- "aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
1834
- title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : `\u4E0B\u8F7D ${name}`,
1835
- children: [
1836
- /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-icon", children: downloading ? /* @__PURE__ */ jsx7(LoaderCircle, { size: 15, className: "animate-spin" }) : /* @__PURE__ */ jsx7(Icon2, { size: 15 }) }),
1837
- /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-name", children: name }),
1838
- /* @__PURE__ */ jsx7(Download, { size: 13, className: "blade-chat-file-card-action" })
1839
- ]
1840
- }
1841
- ),
1842
- failed && /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
1843
- ] });
1844
- }
1845
-
1846
- // src/components/MarkdownContent.tsx
1847
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1848
1715
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
1849
1716
  function CodeBlockPre({ children, node: _node, ...props }) {
1850
1717
  const preRef = useRef5(null);
1851
- const [copied, setCopied] = useState7(false);
1852
- const [language, setLanguage] = useState7("");
1718
+ const [copied, setCopied] = useState6(false);
1719
+ const [language, setLanguage] = useState6("");
1853
1720
  useEffect4(() => {
1854
1721
  const codeEl = preRef.current?.querySelector("code");
1855
1722
  setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
@@ -1861,10 +1728,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1861
1728
  setTimeout(() => setCopied(false), 2e3);
1862
1729
  }
1863
1730
  };
1864
- return /* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
1865
- /* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
1866
- /* @__PURE__ */ jsx8("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
1867
- /* @__PURE__ */ jsxs7(
1731
+ return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
1732
+ /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
1733
+ /* @__PURE__ */ jsx7("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
1734
+ /* @__PURE__ */ jsxs6(
1868
1735
  "button",
1869
1736
  {
1870
1737
  type: "button",
@@ -1874,13 +1741,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1874
1741
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
1875
1742
  ),
1876
1743
  children: [
1877
- copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
1878
- /* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1744
+ copied ? /* @__PURE__ */ jsx7(Check, { size: 12 }) : /* @__PURE__ */ jsx7(Copy, { size: 12 }),
1745
+ /* @__PURE__ */ jsx7("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1879
1746
  ]
1880
1747
  }
1881
1748
  )
1882
1749
  ] }),
1883
- /* @__PURE__ */ jsx8(
1750
+ /* @__PURE__ */ jsx7(
1884
1751
  "pre",
1885
1752
  {
1886
1753
  ref: preRef,
@@ -1892,36 +1759,22 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1892
1759
  ] });
1893
1760
  }
1894
1761
  function ExternalAnchor({ node: _node, children, ...props }) {
1895
- return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1762
+ return /* @__PURE__ */ jsx7("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1896
1763
  }
1897
1764
  var MARKDOWN_COMPONENTS = {
1898
1765
  pre: CodeBlockPre,
1899
1766
  a: ExternalAnchor
1900
1767
  };
1901
- var CUSTOM_ALLOWED_TAGS = {
1902
- [FILE_CARD_TAG]: ["dataPath", "dataName", "data-path", "data-name"]
1903
- };
1904
1768
  function MarkdownContent({ children, className, mode, sessionId }) {
1905
- const streaming = mode === "streaming";
1906
1769
  const resolvedChildren = useMemo5(() => {
1907
- const withoutReminders = children.replace(SYSTEM_REMINDER_RE, "");
1908
- if (!sessionId) return withoutReminders;
1909
- return collapseMediaTags(withoutReminders, { streaming });
1910
- }, [children, sessionId, streaming]);
1911
- const components = useMemo5(
1912
- () => ({
1913
- ...MARKDOWN_COMPONENTS,
1914
- [FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx8(FileCard, { ...fileCardProps, sessionId })
1915
- }),
1916
- [sessionId]
1917
- );
1918
- return /* @__PURE__ */ jsx8(
1770
+ return children.replace(SYSTEM_REMINDER_RE, "");
1771
+ }, [children]);
1772
+ return /* @__PURE__ */ jsx7(
1919
1773
  _r,
1920
1774
  {
1921
1775
  className: cn("blade-chat-markdown break-words", className),
1922
1776
  mode: mode ?? "static",
1923
- components,
1924
- allowedTags: CUSTOM_ALLOWED_TAGS,
1777
+ components: MARKDOWN_COMPONENTS,
1925
1778
  children: resolvedChildren
1926
1779
  },
1927
1780
  sessionId ?? "no-session"
@@ -1929,17 +1782,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
1929
1782
  }
1930
1783
 
1931
1784
  // src/components/Shimmer.tsx
1932
- import { jsx as jsx9 } from "react/jsx-runtime";
1785
+ import { jsx as jsx8 } from "react/jsx-runtime";
1933
1786
  function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
1934
- return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1787
+ return /* @__PURE__ */ jsx8("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1935
1788
  }
1936
1789
 
1937
1790
  // src/components/ToolCallBlock.tsx
1938
- import { useState as useState9 } from "react";
1791
+ import { useState as useState8 } from "react";
1939
1792
 
1940
1793
  // src/components/AskUserQuestionBlock.tsx
1941
- import { useEffect as useEffect5, useMemo as useMemo6, useState as useState8 } from "react";
1942
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1794
+ import { useEffect as useEffect5, useMemo as useMemo6, useState as useState7 } from "react";
1795
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1943
1796
  function AskUserQuestionBlock({
1944
1797
  data,
1945
1798
  answered,
@@ -1948,10 +1801,10 @@ function AskUserQuestionBlock({
1948
1801
  answerData,
1949
1802
  onAnswer
1950
1803
  }) {
1951
- const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
1952
- const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
1953
- const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
1954
- const [submitted, setSubmitted] = useState8(false);
1804
+ const [selections, setSelections] = useState7(/* @__PURE__ */ new Map());
1805
+ const [customTexts, setCustomTexts] = useState7(/* @__PURE__ */ new Map());
1806
+ const [usingCustom, setUsingCustom] = useState7(/* @__PURE__ */ new Set());
1807
+ const [submitted, setSubmitted] = useState7(false);
1955
1808
  useEffect5(() => {
1956
1809
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
1957
1810
  setSubmitted(false);
@@ -2042,7 +1895,7 @@ ${parts.join("\n")}`;
2042
1895
  setSubmitted(true);
2043
1896
  onAnswer(text, toolCallId, nextAnswerData);
2044
1897
  };
2045
- return /* @__PURE__ */ jsxs8(
1898
+ return /* @__PURE__ */ jsxs7(
2046
1899
  "div",
2047
1900
  {
2048
1901
  className: cn(
@@ -2050,12 +1903,12 @@ ${parts.join("\n")}`;
2050
1903
  answered ? "max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80" : "max-w-lg space-y-5 p-4 text-sm"
2051
1904
  ),
2052
1905
  children: [
2053
- data.source_loop?.description && /* @__PURE__ */ jsxs8("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1906
+ data.source_loop?.description && /* @__PURE__ */ jsxs7("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
2054
1907
  "\u5B50\u667A\u80FD\u4F53\u300C",
2055
1908
  data.source_loop.description,
2056
1909
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
2057
1910
  ] }),
2058
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
1911
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx9(
2059
1912
  QuestionCard,
2060
1913
  {
2061
1914
  question: q,
@@ -2070,7 +1923,7 @@ ${parts.join("\n")}`;
2070
1923
  },
2071
1924
  q.question
2072
1925
  )),
2073
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
1926
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
2074
1927
  "button",
2075
1928
  {
2076
1929
  type: "button",
@@ -2080,14 +1933,14 @@ ${parts.join("\n")}`;
2080
1933
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
2081
1934
  }
2082
1935
  ),
2083
- submitted && !answered && /* @__PURE__ */ jsxs8(
1936
+ submitted && !answered && /* @__PURE__ */ jsxs7(
2084
1937
  "button",
2085
1938
  {
2086
1939
  type: "button",
2087
1940
  disabled: true,
2088
1941
  className: "flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80",
2089
1942
  children: [
2090
- /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
1943
+ /* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
2091
1944
  "\u786E\u8BA4\u4E2D"
2092
1945
  ]
2093
1946
  }
@@ -2108,30 +1961,30 @@ function QuestionCard({
2108
1961
  onCustomChange
2109
1962
  }) {
2110
1963
  const multi = question.multiSelect ?? false;
2111
- return /* @__PURE__ */ jsxs8("div", { children: [
2112
- /* @__PURE__ */ jsxs8("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
2113
- /* @__PURE__ */ jsx10(
1964
+ return /* @__PURE__ */ jsxs7("div", { children: [
1965
+ /* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
1966
+ /* @__PURE__ */ jsx9(
2114
1967
  MessageSquareMore,
2115
1968
  {
2116
1969
  size: answered ? 12 : 13,
2117
1970
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
2118
1971
  }
2119
1972
  ),
2120
- /* @__PURE__ */ jsx10(
1973
+ /* @__PURE__ */ jsx9(
2121
1974
  "div",
2122
1975
  {
2123
1976
  className: cn(
2124
1977
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
2125
1978
  answered ? "text-xs" : "text-sm"
2126
1979
  ),
2127
- children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
1980
+ children: /* @__PURE__ */ jsx9(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2128
1981
  }
2129
1982
  )
2130
1983
  ] }),
2131
- /* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
1984
+ /* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2132
1985
  question.options.map((opt, optIdx) => {
2133
1986
  const isSel = selected.has(optIdx);
2134
- return /* @__PURE__ */ jsxs8(
1987
+ return /* @__PURE__ */ jsxs7(
2135
1988
  "button",
2136
1989
  {
2137
1990
  type: "button",
@@ -2145,14 +1998,14 @@ function QuestionCard({
2145
1998
  answered && "cursor-default opacity-70"
2146
1999
  ),
2147
2000
  children: [
2148
- multi && /* @__PURE__ */ jsx10(
2001
+ multi && /* @__PURE__ */ jsx9(
2149
2002
  "div",
2150
2003
  {
2151
2004
  className: cn(
2152
2005
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
2153
2006
  isSel && !answered ? "border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]" : isSel ? "border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]" : "border-[hsl(var(--border))]"
2154
2007
  ),
2155
- children: isSel && /* @__PURE__ */ jsx10(
2008
+ children: isSel && /* @__PURE__ */ jsx9(
2156
2009
  Check,
2157
2010
  {
2158
2011
  size: 9,
@@ -2161,9 +2014,9 @@ function QuestionCard({
2161
2014
  )
2162
2015
  }
2163
2016
  ),
2164
- /* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
2165
- /* @__PURE__ */ jsx10("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2166
- opt.description && /* @__PURE__ */ jsx10(
2017
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
2018
+ /* @__PURE__ */ jsx9("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2019
+ opt.description && /* @__PURE__ */ jsx9(
2167
2020
  "div",
2168
2021
  {
2169
2022
  className: cn(
@@ -2180,7 +2033,7 @@ function QuestionCard({
2180
2033
  opt.label
2181
2034
  );
2182
2035
  }),
2183
- answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
2036
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs7(
2184
2037
  "div",
2185
2038
  {
2186
2039
  className: cn(
@@ -2190,8 +2043,8 @@ function QuestionCard({
2190
2043
  answered && "cursor-default opacity-70"
2191
2044
  ),
2192
2045
  children: [
2193
- /* @__PURE__ */ jsx10("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2194
- /* @__PURE__ */ jsx10(
2046
+ /* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2047
+ /* @__PURE__ */ jsx9(
2195
2048
  "input",
2196
2049
  {
2197
2050
  type: "text",
@@ -2259,7 +2112,7 @@ function normalizeOptionItem(value) {
2259
2112
  }
2260
2113
 
2261
2114
  // src/components/ToolCallBlock.tsx
2262
- import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2115
+ import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2263
2116
  function resolveAskQuestionState({
2264
2117
  toolStatus,
2265
2118
  hasAnswerData,
@@ -2279,12 +2132,12 @@ function ToolCallBlock({
2279
2132
  sessionStatus,
2280
2133
  renderer
2281
2134
  }) {
2282
- const [expanded, setExpanded] = useState9(false);
2135
+ const [expanded, setExpanded] = useState8(false);
2283
2136
  const normalizedName = formatToolName(toolCall.name);
2284
2137
  if (renderer) {
2285
2138
  const custom = renderer(toolCall);
2286
2139
  if (custom !== null && custom !== void 0) {
2287
- return /* @__PURE__ */ jsx11(Fragment, { children: custom });
2140
+ return /* @__PURE__ */ jsx10(Fragment, { children: custom });
2288
2141
  }
2289
2142
  }
2290
2143
  if (normalizedName === "AskUserQuestion") {
@@ -2296,7 +2149,7 @@ function ToolCallBlock({
2296
2149
  });
2297
2150
  const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
2298
2151
  if (askData) {
2299
- return /* @__PURE__ */ jsx11(
2152
+ return /* @__PURE__ */ jsx10(
2300
2153
  AskUserQuestionBlock,
2301
2154
  {
2302
2155
  data: askData,
@@ -2309,24 +2162,24 @@ function ToolCallBlock({
2309
2162
  );
2310
2163
  }
2311
2164
  if (toolCall.status === "pending") {
2312
- return /* @__PURE__ */ jsxs9("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
2313
- /* @__PURE__ */ jsx11(LoaderCircle, { size: 14, className: "animate-spin" }),
2314
- /* @__PURE__ */ jsx11("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2165
+ return /* @__PURE__ */ jsxs8("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
2166
+ /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
2167
+ /* @__PURE__ */ jsx10("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2315
2168
  ] });
2316
2169
  }
2317
- return /* @__PURE__ */ jsxs9("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
2318
- /* @__PURE__ */ jsx11("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2319
- /* @__PURE__ */ jsx11("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
2170
+ return /* @__PURE__ */ jsxs8("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
2171
+ /* @__PURE__ */ jsx10("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2172
+ /* @__PURE__ */ jsx10("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
2320
2173
  ] });
2321
2174
  }
2322
2175
  const tone = getToolTone(toolCall.status);
2323
2176
  const displayName = getToolDisplayLabel(toolCall);
2324
2177
  const toneClass = tone === "red" ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : tone === "amber" ? "border-l-amber-400" : tone === "blue" ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]";
2325
- const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx11(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx11(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx11(X, { size: 11 }) : /* @__PURE__ */ jsx11(Check, { size: 11 });
2178
+ const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx10(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx10(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx10(X, { size: 11 }) : /* @__PURE__ */ jsx10(Check, { size: 11 });
2326
2179
  const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
2327
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2328
- /* @__PURE__ */ jsxs9("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2329
- /* @__PURE__ */ jsxs9(
2180
+ return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2181
+ /* @__PURE__ */ jsxs8("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2182
+ /* @__PURE__ */ jsxs8(
2330
2183
  "button",
2331
2184
  {
2332
2185
  type: "button",
@@ -2334,7 +2187,7 @@ function ToolCallBlock({
2334
2187
  className: "flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
2335
2188
  "aria-expanded": expanded,
2336
2189
  children: [
2337
- /* @__PURE__ */ jsx11(
2190
+ /* @__PURE__ */ jsx10(
2338
2191
  ChevronRight,
2339
2192
  {
2340
2193
  size: 11,
@@ -2344,24 +2197,24 @@ function ToolCallBlock({
2344
2197
  )
2345
2198
  }
2346
2199
  ),
2347
- /* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2200
+ /* @__PURE__ */ jsxs8("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2348
2201
  statusIcon,
2349
- /* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
2202
+ /* @__PURE__ */ jsx10("span", { children: getToolStatusLabel(toolCall.status) })
2350
2203
  ] }),
2351
- /* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2204
+ /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2352
2205
  ]
2353
2206
  }
2354
2207
  ),
2355
- typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx11("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
2208
+ typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx10("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
2356
2209
  ] }),
2357
- expanded && /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2358
- /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2359
- /* @__PURE__ */ jsx11("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2360
- /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2361
- /* @__PURE__ */ jsx11("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: formatToolArgs(toolCall.arguments) }),
2362
- toolCall.result != null && /* @__PURE__ */ jsxs9(Fragment, { children: [
2363
- /* @__PURE__ */ jsx11("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2364
- /* @__PURE__ */ jsx11("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
2210
+ expanded && /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2211
+ /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2212
+ /* @__PURE__ */ jsx10("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2213
+ /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2214
+ /* @__PURE__ */ jsx10("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: formatToolArgs(toolCall.arguments) }),
2215
+ toolCall.result != null && /* @__PURE__ */ jsxs8(Fragment, { children: [
2216
+ /* @__PURE__ */ jsx10("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2217
+ /* @__PURE__ */ jsx10("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
2365
2218
  ] })
2366
2219
  ] })
2367
2220
  ] });
@@ -2379,11 +2232,11 @@ function buildAskUserPayload(argumentsJson) {
2379
2232
  }
2380
2233
 
2381
2234
  // src/components/AssistantTurnBlock.tsx
2382
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2235
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2383
2236
  function ThinkingBlock({ reasoning, isStreaming }) {
2384
- const [open, setOpen] = useState10(false);
2385
- return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
2386
- /* @__PURE__ */ jsxs10(
2237
+ const [open, setOpen] = useState9(false);
2238
+ return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
2239
+ /* @__PURE__ */ jsxs9(
2387
2240
  "button",
2388
2241
  {
2389
2242
  type: "button",
@@ -2391,14 +2244,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2391
2244
  "aria-expanded": open,
2392
2245
  className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
2393
2246
  children: [
2394
- /* @__PURE__ */ jsx12(Brain, { size: 12, className: "shrink-0" }),
2395
- isStreaming ? /* @__PURE__ */ jsx12(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx12("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
2396
- /* @__PURE__ */ jsxs10("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
2247
+ /* @__PURE__ */ jsx11(Brain, { size: 12, className: "shrink-0" }),
2248
+ isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
2249
+ /* @__PURE__ */ jsxs9("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
2397
2250
  "\xB7 ",
2398
2251
  new Intl.NumberFormat("zh-CN").format(reasoning.length),
2399
2252
  " \u5B57"
2400
2253
  ] }),
2401
- /* @__PURE__ */ jsx12(
2254
+ /* @__PURE__ */ jsx11(
2402
2255
  ChevronDown,
2403
2256
  {
2404
2257
  size: 12,
@@ -2408,7 +2261,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2408
2261
  ]
2409
2262
  }
2410
2263
  ),
2411
- open && /* @__PURE__ */ jsx12("div", { className: "mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]", children: reasoning })
2264
+ open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]", children: reasoning })
2412
2265
  ] });
2413
2266
  }
2414
2267
  function getMessageText(message) {
@@ -2434,21 +2287,21 @@ function AssistantTurnBlock({
2434
2287
  (message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
2435
2288
  );
2436
2289
  const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
2437
- return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
2438
- hasInterrupted && /* @__PURE__ */ jsx12("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
2290
+ return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
2291
+ hasInterrupted && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
2439
2292
  messages.map((message, index) => {
2440
2293
  const isLast = index === messages.length - 1;
2441
2294
  const streamingThis = isStreaming && isLast;
2442
2295
  const text = getMessageText(message);
2443
2296
  const toolCalls = message.tool_calls ?? [];
2444
2297
  const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
2445
- return /* @__PURE__ */ jsxs10(
2298
+ return /* @__PURE__ */ jsxs9(
2446
2299
  "div",
2447
2300
  {
2448
2301
  className: "flex flex-col gap-3",
2449
2302
  children: [
2450
- showReasoning && message.reasoning && /* @__PURE__ */ jsx12(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
2451
- text && /* @__PURE__ */ jsx12("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx12(
2303
+ showReasoning && message.reasoning && /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
2304
+ text && /* @__PURE__ */ jsx11("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx11(
2452
2305
  MarkdownContent,
2453
2306
  {
2454
2307
  mode: streamingThis ? "streaming" : "static",
@@ -2457,8 +2310,8 @@ function AssistantTurnBlock({
2457
2310
  children: text
2458
2311
  }
2459
2312
  ) }),
2460
- toolCalls.length > 0 && /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: toolCalls.map(
2461
- (toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx12(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx12(
2313
+ toolCalls.length > 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-2", children: toolCalls.map(
2314
+ (toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(
2462
2315
  ToolCallBlock,
2463
2316
  {
2464
2317
  toolCall,
@@ -2476,13 +2329,13 @@ function AssistantTurnBlock({
2476
2329
  message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
2477
2330
  );
2478
2331
  }),
2479
- isStreaming && !hasAnyContent && /* @__PURE__ */ jsx12(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
2332
+ isStreaming && !hasAnyContent && /* @__PURE__ */ jsx11(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
2480
2333
  ] });
2481
2334
  }
2482
2335
 
2483
2336
  // src/components/RenderErrorBoundary.tsx
2484
2337
  import { Component } from "react";
2485
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2338
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2486
2339
  function getFirstComponentName(componentStack) {
2487
2340
  const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
2488
2341
  return match?.[1] ?? null;
@@ -2515,23 +2368,408 @@ var RenderErrorBoundary = class extends Component {
2515
2368
  return children;
2516
2369
  }
2517
2370
  const componentName = getFirstComponentName(componentStack);
2518
- return /* @__PURE__ */ jsx13("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-start gap-2", children: [
2519
- /* @__PURE__ */ jsx13(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
2520
- /* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
2521
- /* @__PURE__ */ jsxs11("div", { className: "font-medium", children: [
2371
+ return /* @__PURE__ */ jsx12("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2", children: [
2372
+ /* @__PURE__ */ jsx12(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
2373
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
2374
+ /* @__PURE__ */ jsxs10("div", { className: "font-medium", children: [
2522
2375
  label,
2523
2376
  "\u6E32\u67D3\u5931\u8D25"
2524
2377
  ] }),
2525
- /* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
2378
+ /* @__PURE__ */ jsxs10("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
2526
2379
  componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
2527
2380
  error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
2528
2381
  ] }),
2529
- details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
2382
+ details ? /* @__PURE__ */ jsx12("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
2530
2383
  ] })
2531
2384
  ] }) });
2532
2385
  }
2533
2386
  };
2534
2387
 
2388
+ // src/components/PostChatFollowupBlock.tsx
2389
+ import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef6, useState as useState10 } from "react";
2390
+ import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2391
+ function emitInteraction(callback, event) {
2392
+ try {
2393
+ callback?.(event);
2394
+ } catch {
2395
+ }
2396
+ }
2397
+ function basename(path) {
2398
+ return path.split(/[\\/]/).filter(Boolean).pop() || path;
2399
+ }
2400
+ function isVideo(path) {
2401
+ return /\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path);
2402
+ }
2403
+ function ArtifactCard({
2404
+ artifact,
2405
+ sessionId,
2406
+ assistantEntryId,
2407
+ artifactIndex,
2408
+ onInteraction,
2409
+ onArtifactOpened
2410
+ }) {
2411
+ const client = useBladeClient();
2412
+ const [downloading, setDownloading] = useState10(false);
2413
+ const name = artifact.label || basename(artifact.target);
2414
+ if (artifact.kind === "link") {
2415
+ return /* @__PURE__ */ jsxs11(
2416
+ "a",
2417
+ {
2418
+ href: artifact.target,
2419
+ target: "_blank",
2420
+ rel: "noopener noreferrer",
2421
+ onClick: () => onArtifactOpened(artifactIndex, "link"),
2422
+ title: `${name}
2423
+ ${artifact.target}`,
2424
+ className: "group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]",
2425
+ children: [
2426
+ /* @__PURE__ */ jsx13(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
2427
+ /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
2428
+ /* @__PURE__ */ jsx13(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
2429
+ ]
2430
+ }
2431
+ );
2432
+ }
2433
+ const Icon2 = isVideo(artifact.target) ? Film : File;
2434
+ return /* @__PURE__ */ jsxs11(
2435
+ "button",
2436
+ {
2437
+ type: "button",
2438
+ disabled: !sessionId || downloading,
2439
+ onClick: async () => {
2440
+ if (!sessionId || downloading) return;
2441
+ setDownloading(true);
2442
+ emitInteraction(onInteraction, {
2443
+ type: "artifact_download_started",
2444
+ sessionId,
2445
+ assistantEntryId,
2446
+ artifactIndex,
2447
+ artifactKind: "file"
2448
+ });
2449
+ try {
2450
+ await client.sessions.downloadFile(sessionId, artifact.target, basename(artifact.target));
2451
+ emitInteraction(onInteraction, {
2452
+ type: "artifact_download_succeeded",
2453
+ sessionId,
2454
+ assistantEntryId,
2455
+ artifactIndex,
2456
+ artifactKind: "file"
2457
+ });
2458
+ } catch {
2459
+ } finally {
2460
+ setDownloading(false);
2461
+ }
2462
+ },
2463
+ title: name,
2464
+ "aria-label": `\u4E0B\u8F7D ${name}`,
2465
+ className: "group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-left text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))] disabled:opacity-60",
2466
+ children: [
2467
+ /* @__PURE__ */ jsx13(Icon2, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
2468
+ /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
2469
+ /* @__PURE__ */ jsx13(Download, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
2470
+ ]
2471
+ }
2472
+ );
2473
+ }
2474
+ var FEEDBACK_REASONS = [
2475
+ { value: "not_solved", label: "\u6CA1\u89E3\u51B3\u95EE\u9898" },
2476
+ { value: "inaccurate", label: "\u5185\u5BB9\u4E0D\u51C6\u786E" },
2477
+ { value: "incomplete", label: "\u5185\u5BB9\u4E0D\u5B8C\u6574" },
2478
+ { value: "needs_major_changes", label: "\u9700\u8981\u5927\u91CF\u4FEE\u6539" },
2479
+ { value: "too_slow", label: "\u592A\u6162" },
2480
+ { value: "other", label: "\u5176\u4ED6" }
2481
+ ];
2482
+ function feedbackReasonLabel(reason) {
2483
+ return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null;
2484
+ }
2485
+ function HistoricalResultFeedback({ feedback }) {
2486
+ const label = feedbackReasonLabel(feedback.reason);
2487
+ return /* @__PURE__ */ jsxs11(
2488
+ "section",
2489
+ {
2490
+ "aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
2491
+ className: "mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]",
2492
+ children: [
2493
+ /* @__PURE__ */ jsxs11("span", { children: [
2494
+ "\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
2495
+ feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
2496
+ ] }),
2497
+ label ? /* @__PURE__ */ jsxs11("span", { children: [
2498
+ " \xB7 ",
2499
+ label
2500
+ ] }) : null
2501
+ ]
2502
+ }
2503
+ );
2504
+ }
2505
+ function ResultFeedback({
2506
+ followup,
2507
+ sessionId,
2508
+ isViewer,
2509
+ onInteraction,
2510
+ savedFeedback,
2511
+ onFeedbackSaved
2512
+ }) {
2513
+ const client = useBladeClient();
2514
+ const [saved, setSaved] = useState10(savedFeedback ?? null);
2515
+ const [helpful, setHelpful] = useState10(savedFeedback?.helpful ?? null);
2516
+ const [reason, setReason] = useState10(savedFeedback?.reason ?? null);
2517
+ const [saving, setSaving] = useState10(false);
2518
+ const [saveError, setSaveError] = useState10(false);
2519
+ const reportedShown = useRef6(false);
2520
+ const latestChoice = useRef6(null);
2521
+ const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
2522
+ useEffect6(() => {
2523
+ if (!eligible || reportedShown.current) return;
2524
+ reportedShown.current = true;
2525
+ emitInteraction(onInteraction, {
2526
+ type: "result_feedback_shown",
2527
+ sessionId,
2528
+ assistantEntryId: followup.assistant_entry_id
2529
+ });
2530
+ }, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
2531
+ useEffect6(() => {
2532
+ if (!savedFeedback || latestChoice.current) return;
2533
+ setSaved(savedFeedback);
2534
+ setHelpful(savedFeedback.helpful);
2535
+ setReason(savedFeedback.reason);
2536
+ }, [savedFeedback]);
2537
+ const submit = useCallback5(
2538
+ async (nextHelpful, nextReason) => {
2539
+ if (!sessionId) return;
2540
+ const choice = { helpful: nextHelpful, reason: nextReason };
2541
+ latestChoice.current = choice;
2542
+ setHelpful(nextHelpful);
2543
+ setReason(nextReason);
2544
+ setSaving(true);
2545
+ setSaveError(false);
2546
+ try {
2547
+ const result = await client.sessions.putResultFeedback(
2548
+ sessionId,
2549
+ followup.assistant_entry_id,
2550
+ choice
2551
+ );
2552
+ if (latestChoice.current !== choice) return;
2553
+ setSaved(result);
2554
+ onFeedbackSaved?.(result);
2555
+ emitInteraction(onInteraction, {
2556
+ type: "result_feedback_submitted",
2557
+ sessionId,
2558
+ assistantEntryId: followup.assistant_entry_id,
2559
+ helpful: result.helpful,
2560
+ reason: result.reason,
2561
+ updatedAt: result.updated_at
2562
+ });
2563
+ } catch {
2564
+ if (latestChoice.current === choice) setSaveError(true);
2565
+ } finally {
2566
+ if (latestChoice.current === choice) setSaving(false);
2567
+ }
2568
+ },
2569
+ [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
2570
+ );
2571
+ if (!eligible) return null;
2572
+ return /* @__PURE__ */ jsxs11(
2573
+ "section",
2574
+ {
2575
+ "aria-label": "\u7ED3\u679C\u53CD\u9988",
2576
+ className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
2577
+ children: [
2578
+ /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
2579
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-1.5", children: [
2580
+ /* @__PURE__ */ jsx13(
2581
+ "button",
2582
+ {
2583
+ type: "button",
2584
+ "aria-pressed": helpful === true,
2585
+ disabled: saving,
2586
+ onClick: () => void submit(true, null),
2587
+ className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
2588
+ children: "\u6709\u5E2E\u52A9"
2589
+ }
2590
+ ),
2591
+ /* @__PURE__ */ jsx13(
2592
+ "button",
2593
+ {
2594
+ type: "button",
2595
+ "aria-pressed": helpful === false,
2596
+ disabled: saving,
2597
+ onClick: () => void submit(false, reason),
2598
+ className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
2599
+ children: "\u6CA1\u5E2E\u52A9"
2600
+ }
2601
+ )
2602
+ ] }),
2603
+ helpful === false ? /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx13(
2604
+ "button",
2605
+ {
2606
+ type: "button",
2607
+ "aria-pressed": reason === item.value,
2608
+ disabled: saving,
2609
+ onClick: () => void submit(false, item.value),
2610
+ className: "rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60",
2611
+ children: item.label
2612
+ },
2613
+ item.value
2614
+ )) }) : null,
2615
+ saveError ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
2616
+ /* @__PURE__ */ jsx13("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
2617
+ /* @__PURE__ */ jsx13(
2618
+ "button",
2619
+ {
2620
+ type: "button",
2621
+ className: "underline",
2622
+ onClick: () => {
2623
+ const choice = latestChoice.current;
2624
+ if (choice) void submit(choice.helpful, choice.reason);
2625
+ },
2626
+ children: "\u91CD\u8BD5"
2627
+ }
2628
+ )
2629
+ ] }) : saved ? /* @__PURE__ */ jsx13("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
2630
+ ]
2631
+ }
2632
+ );
2633
+ }
2634
+ function PostChatFollowupBlock({
2635
+ followup,
2636
+ sessionId,
2637
+ onSuggestion,
2638
+ isViewer = false,
2639
+ onInteraction,
2640
+ savedFeedback,
2641
+ onFeedbackSaved
2642
+ }) {
2643
+ const [expanded, setExpanded] = useState10(false);
2644
+ const adopted = useRef6(/* @__PURE__ */ new Set());
2645
+ const reportedSuggestions = useRef6(false);
2646
+ const reportedArtifacts = useRef6(/* @__PURE__ */ new Set());
2647
+ const openedArtifacts = useRef6(/* @__PURE__ */ new Set());
2648
+ const artifacts = followup.final_artifacts ?? [];
2649
+ const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
2650
+ useEffect6(() => {
2651
+ if (!reportedSuggestions.current && followup.suggestions.length > 0) {
2652
+ reportedSuggestions.current = true;
2653
+ emitInteraction(onInteraction, {
2654
+ type: "suggestions_shown",
2655
+ sessionId,
2656
+ assistantEntryId: followup.assistant_entry_id,
2657
+ count: followup.suggestions.length
2658
+ });
2659
+ }
2660
+ for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {
2661
+ if (reportedArtifacts.current.has(artifactIndex)) continue;
2662
+ const artifact = visibleArtifacts[artifactIndex];
2663
+ if (!artifact) continue;
2664
+ reportedArtifacts.current.add(artifactIndex);
2665
+ emitInteraction(onInteraction, {
2666
+ type: "artifact_shown",
2667
+ sessionId,
2668
+ assistantEntryId: followup.assistant_entry_id,
2669
+ artifactIndex,
2670
+ artifactKind: artifact.kind
2671
+ });
2672
+ }
2673
+ }, [
2674
+ followup.assistant_entry_id,
2675
+ followup.suggestions.length,
2676
+ onInteraction,
2677
+ sessionId,
2678
+ visibleArtifacts
2679
+ ]);
2680
+ const reportArtifactOpened = useCallback5(
2681
+ (artifactIndex, artifactKind) => {
2682
+ if (openedArtifacts.current.has(artifactIndex)) return;
2683
+ openedArtifacts.current.add(artifactIndex);
2684
+ emitInteraction(onInteraction, {
2685
+ type: "artifact_opened",
2686
+ sessionId,
2687
+ assistantEntryId: followup.assistant_entry_id,
2688
+ artifactIndex,
2689
+ artifactKind
2690
+ });
2691
+ },
2692
+ [followup.assistant_entry_id, onInteraction, sessionId]
2693
+ );
2694
+ if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
2695
+ return null;
2696
+ return /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]", children: [
2697
+ followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
2698
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
2699
+ /* @__PURE__ */ jsx13(Sparkles, { size: 14 }),
2700
+ "\u672C\u8F6E\u5C0F\u7ED3"
2701
+ ] }),
2702
+ followup.recaption ? /* @__PURE__ */ jsx13("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
2703
+ artifacts.length > 0 ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
2704
+ /* @__PURE__ */ jsx13("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx13(
2705
+ ArtifactCard,
2706
+ {
2707
+ artifact,
2708
+ sessionId,
2709
+ assistantEntryId: followup.assistant_entry_id,
2710
+ artifactIndex,
2711
+ onInteraction,
2712
+ onArtifactOpened: reportArtifactOpened
2713
+ },
2714
+ `${artifact.kind}:${artifactIndex}`
2715
+ )) }),
2716
+ artifacts.length > 3 ? /* @__PURE__ */ jsxs11(
2717
+ "button",
2718
+ {
2719
+ type: "button",
2720
+ onClick: () => setExpanded((value) => !value),
2721
+ "aria-expanded": expanded,
2722
+ className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
2723
+ children: [
2724
+ expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
2725
+ /* @__PURE__ */ jsx13(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
2726
+ ]
2727
+ }
2728
+ ) : null
2729
+ ] }) : null
2730
+ ] }) : null,
2731
+ followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
2732
+ /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
2733
+ followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs11(
2734
+ "button",
2735
+ {
2736
+ type: "button",
2737
+ disabled: isViewer,
2738
+ onClick: () => {
2739
+ if (!adopted.current.has(suggestionIndex)) {
2740
+ adopted.current.add(suggestionIndex);
2741
+ emitInteraction(onInteraction, {
2742
+ type: "suggestion_adopted",
2743
+ sessionId,
2744
+ assistantEntryId: followup.assistant_entry_id,
2745
+ suggestionIndex
2746
+ });
2747
+ }
2748
+ onSuggestion?.(suggestion);
2749
+ },
2750
+ className: "group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60",
2751
+ children: [
2752
+ /* @__PURE__ */ jsx13("span", { children: suggestion }),
2753
+ /* @__PURE__ */ jsx13(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
2754
+ ]
2755
+ },
2756
+ suggestion
2757
+ ))
2758
+ ] }) : null,
2759
+ /* @__PURE__ */ jsx13(
2760
+ ResultFeedback,
2761
+ {
2762
+ followup,
2763
+ sessionId,
2764
+ isViewer,
2765
+ onInteraction,
2766
+ savedFeedback,
2767
+ onFeedbackSaved
2768
+ }
2769
+ )
2770
+ ] });
2771
+ }
2772
+
2535
2773
  // src/components/UserMessageBubble.tsx
2536
2774
  import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
2537
2775
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
@@ -2617,6 +2855,8 @@ function getMessageResetSignature(messages) {
2617
2855
  }
2618
2856
  function MessageList({
2619
2857
  messages,
2858
+ postChatFollowup,
2859
+ onSuggestion,
2620
2860
  isStreaming,
2621
2861
  sessionStatus,
2622
2862
  askAnswers,
@@ -2624,7 +2864,11 @@ function MessageList({
2624
2864
  toolCallRenderer,
2625
2865
  emptyState,
2626
2866
  className,
2627
- sessionId
2867
+ sessionId,
2868
+ isViewer = false,
2869
+ onFollowupInteraction,
2870
+ resultFeedbackByEntry = /* @__PURE__ */ new Map(),
2871
+ onResultFeedbackSaved
2628
2872
  }) {
2629
2873
  const renderBlocks = useMemo7(() => {
2630
2874
  const visible = messages.filter((message) => {
@@ -2710,24 +2954,47 @@ function MessageList({
2710
2954
  return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx15(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx15(ErrorMessageBlock, { message: block.message }) : null }, block.key);
2711
2955
  }
2712
2956
  if (block.type === "assistant_turn") {
2713
- return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx15(
2957
+ const blockFeedback = block.messages.map(
2958
+ (message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
2959
+ ).find((feedback) => feedback != null);
2960
+ const hasActiveFollowup = Boolean(
2961
+ postChatFollowup && block.messages.some(
2962
+ (message) => message.entry_id === postChatFollowup.assistant_entry_id
2963
+ )
2964
+ );
2965
+ return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
2714
2966
  RenderErrorBoundary,
2715
2967
  {
2716
2968
  label: "\u52A9\u624B\u6D88\u606F",
2717
2969
  details: block.key,
2718
2970
  resetKey: getMessageResetSignature(block.messages),
2719
- children: /* @__PURE__ */ jsx15(
2720
- AssistantTurnBlock,
2721
- {
2722
- messages: block.messages,
2723
- isStreaming: block.isStreaming,
2724
- askAnswers,
2725
- onAnswer,
2726
- sessionStatus,
2727
- toolCallRenderer,
2728
- sessionId
2729
- }
2730
- )
2971
+ children: [
2972
+ /* @__PURE__ */ jsx15(
2973
+ AssistantTurnBlock,
2974
+ {
2975
+ messages: block.messages,
2976
+ isStreaming: block.isStreaming,
2977
+ askAnswers,
2978
+ onAnswer,
2979
+ sessionStatus,
2980
+ toolCallRenderer,
2981
+ sessionId
2982
+ }
2983
+ ),
2984
+ blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
2985
+ hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
2986
+ PostChatFollowupBlock,
2987
+ {
2988
+ followup: postChatFollowup,
2989
+ sessionId,
2990
+ onSuggestion,
2991
+ isViewer,
2992
+ onInteraction: onFollowupInteraction,
2993
+ savedFeedback: blockFeedback,
2994
+ onFeedbackSaved: onResultFeedbackSaved
2995
+ }
2996
+ ) : null
2997
+ ]
2731
2998
  }
2732
2999
  ) }, block.key);
2733
3000
  }
@@ -2754,8 +3021,8 @@ function MessageList({
2754
3021
  }
2755
3022
  function AutoScrollOnUserSend({ userMessageCount }) {
2756
3023
  const { scrollToBottom } = useStickToBottomContext();
2757
- const previousCountRef = useRef6(userMessageCount);
2758
- useEffect6(() => {
3024
+ const previousCountRef = useRef7(userMessageCount);
3025
+ useEffect7(() => {
2759
3026
  if (userMessageCount > previousCountRef.current) {
2760
3027
  scrollToBottom("instant");
2761
3028
  }
@@ -2766,8 +3033,8 @@ function AutoScrollOnUserSend({ userMessageCount }) {
2766
3033
  function ScrollToBottomButton() {
2767
3034
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
2768
3035
  const [visible, setVisible] = useState11(false);
2769
- const hideTimerRef = useRef6(null);
2770
- useEffect6(() => {
3036
+ const hideTimerRef = useRef7(null);
3037
+ useEffect7(() => {
2771
3038
  if (isAtBottom) {
2772
3039
  if (!hideTimerRef.current) {
2773
3040
  hideTimerRef.current = setTimeout(() => {
@@ -2789,7 +3056,7 @@ function ScrollToBottomButton() {
2789
3056
  }
2790
3057
  };
2791
3058
  }, [isAtBottom]);
2792
- const handleClick = useCallback5(() => {
3059
+ const handleClick = useCallback6(() => {
2793
3060
  if (hideTimerRef.current) {
2794
3061
  clearTimeout(hideTimerRef.current);
2795
3062
  hideTimerRef.current = null;
@@ -2837,16 +3104,22 @@ function ChatSurface({
2837
3104
  connection,
2838
3105
  errorMessage,
2839
3106
  messages,
3107
+ postChatFollowup,
2840
3108
  isStreaming,
2841
3109
  isStopping,
2842
3110
  inputText,
2843
3111
  onInputChange,
3112
+ onSuggestion,
2844
3113
  onSend,
2845
3114
  onStop,
2846
3115
  sessionStatus,
2847
3116
  askAnswers,
2848
3117
  onAnswer,
2849
3118
  sessionId,
3119
+ isViewer,
3120
+ resultFeedbackByEntry,
3121
+ onResultFeedbackSaved,
3122
+ onFollowupInteraction,
2850
3123
  beforeInput,
2851
3124
  banner
2852
3125
  }) {
@@ -2870,6 +3143,8 @@ function ChatSurface({
2870
3143
  MessageList,
2871
3144
  {
2872
3145
  messages,
3146
+ postChatFollowup,
3147
+ onSuggestion,
2873
3148
  isStreaming,
2874
3149
  sessionStatus,
2875
3150
  askAnswers,
@@ -2877,7 +3152,11 @@ function ChatSurface({
2877
3152
  toolCallRenderer: renderers?.toolCall,
2878
3153
  emptyState: slots?.emptyState,
2879
3154
  className: classNames?.messageList,
2880
- sessionId
3155
+ sessionId,
3156
+ isViewer,
3157
+ resultFeedbackByEntry,
3158
+ onResultFeedbackSaved,
3159
+ onFollowupInteraction
2881
3160
  }
2882
3161
  ),
2883
3162
  beforeInput,
@@ -2901,7 +3180,7 @@ function ChatSurface({
2901
3180
  }
2902
3181
 
2903
3182
  // src/components/AgentChat.tsx
2904
- import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
3183
+ import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2905
3184
  function isUnauthorizedError(error) {
2906
3185
  return error instanceof BladeApiError && error.status === 401;
2907
3186
  }
@@ -2973,8 +3252,10 @@ function ChatSessionView({
2973
3252
  slots,
2974
3253
  placeholder,
2975
3254
  theme,
3255
+ onFollowupInteraction,
2976
3256
  onUnauthorized
2977
3257
  }) {
3258
+ const client = useBladeClient();
2978
3259
  const { session, state, error } = useAgentSession(sessionId, {
2979
3260
  createOptions,
2980
3261
  onSessionCreated
@@ -2982,12 +3263,50 @@ function ChatSessionView({
2982
3263
  const replay = useReplay(session);
2983
3264
  const [stopRequested, setStopRequested] = useState12(false);
2984
3265
  const [inputText, setInputText] = useState12("");
2985
- useEffect7(() => {
3266
+ const [resultFeedback, setResultFeedback] = useState12([]);
3267
+ const resolvedSessionId = session?.sessionId;
3268
+ const isViewer = state?.viewerRole === "viewer";
3269
+ useEffect8(() => {
3270
+ setResultFeedback([]);
3271
+ if (!resolvedSessionId || isViewer) return;
3272
+ let cancelled = false;
3273
+ void client.sessions.listResultFeedback(resolvedSessionId).then(
3274
+ (items) => {
3275
+ if (cancelled) return;
3276
+ setResultFeedback((current) => {
3277
+ const merged = new Map(items.map((item) => [item.assistant_entry_id, item]));
3278
+ for (const item of current) {
3279
+ const fetched = merged.get(item.assistant_entry_id);
3280
+ if (!fetched || fetched.updated_at < item.updated_at) {
3281
+ merged.set(item.assistant_entry_id, item);
3282
+ }
3283
+ }
3284
+ return [...merged.values()];
3285
+ });
3286
+ },
3287
+ () => {
3288
+ }
3289
+ );
3290
+ return () => {
3291
+ cancelled = true;
3292
+ };
3293
+ }, [client, isViewer, resolvedSessionId]);
3294
+ const resultFeedbackByEntry = useMemo8(
3295
+ () => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
3296
+ [resultFeedback]
3297
+ );
3298
+ const handleResultFeedbackSaved = useCallback7((saved) => {
3299
+ setResultFeedback((current) => [
3300
+ ...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
3301
+ saved
3302
+ ]);
3303
+ }, []);
3304
+ useEffect8(() => {
2986
3305
  if (session) {
2987
3306
  onSessionReady?.(session);
2988
3307
  }
2989
3308
  }, [session, onSessionReady]);
2990
- useEffect7(() => {
3309
+ useEffect8(() => {
2991
3310
  if (!session) return;
2992
3311
  const offAttach = session.on("attachRequested", ({ label, content }) => {
2993
3312
  setInputText((prev) => `${prev ? `${prev}
@@ -3003,12 +3322,12 @@ ${content}`);
3003
3322
  offInsert();
3004
3323
  };
3005
3324
  }, [session]);
3006
- useEffect7(() => {
3325
+ useEffect8(() => {
3007
3326
  if (isUnauthorizedError(error)) {
3008
3327
  onUnauthorized();
3009
3328
  }
3010
3329
  }, [error, onUnauthorized]);
3011
- useEffect7(() => {
3330
+ useEffect8(() => {
3012
3331
  if (!session || !commands) return;
3013
3332
  const unsubscribes = Object.entries(commands).map(
3014
3333
  ([action, handler]) => session.onCommand(action, (payload) => handler(payload))
@@ -3033,12 +3352,13 @@ ${content}`);
3033
3352
  ChatSurface,
3034
3353
  {
3035
3354
  theme,
3355
+ onFollowupInteraction,
3036
3356
  classNames,
3037
3357
  renderers,
3038
3358
  slots,
3039
3359
  placeholder,
3040
3360
  connection: state?.connection ?? "connecting",
3041
- banner: /* @__PURE__ */ jsxs15(Fragment2, { children: [
3361
+ banner: /* @__PURE__ */ jsxs15(Fragment3, { children: [
3042
3362
  /* @__PURE__ */ jsx17(
3043
3363
  ReplayBar,
3044
3364
  {
@@ -3053,10 +3373,14 @@ ${content}`);
3053
3373
  ] }),
3054
3374
  errorMessage,
3055
3375
  messages: state?.messages ?? [],
3376
+ postChatFollowup: latestPostChatFollowup(state?.turns ?? []),
3377
+ resultFeedbackByEntry,
3378
+ onResultFeedbackSaved: handleResultFeedbackSaved,
3056
3379
  isStreaming,
3057
3380
  isStopping,
3058
3381
  inputText,
3059
3382
  onInputChange: setInputText,
3383
+ onSuggestion: setInputText,
3060
3384
  onSend: handleSend,
3061
3385
  onStop: handleStop,
3062
3386
  sessionStatus: state?.status ?? void 0,
@@ -3067,13 +3391,14 @@ ${content}`);
3067
3391
  askUserAnswer: { tool_call_id: toolCallId, ...answerData }
3068
3392
  });
3069
3393
  },
3070
- sessionId: session?.sessionId
3394
+ sessionId: resolvedSessionId,
3395
+ isViewer
3071
3396
  }
3072
3397
  );
3073
3398
  }
3074
3399
 
3075
3400
  // src/components/LlmChat.tsx
3076
- import { useEffect as useEffect8, useMemo as useMemo8, useState as useState14 } from "react";
3401
+ import { useEffect as useEffect9, useMemo as useMemo9, useState as useState14 } from "react";
3077
3402
 
3078
3403
  // src/components/LlmAdvancedSettings.tsx
3079
3404
  import { useState as useState13 } from "react";
@@ -3212,7 +3537,7 @@ function LlmChat({
3212
3537
  const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
3213
3538
  const [inputText, setInputText] = useState14("");
3214
3539
  const [stopRequested, setStopRequested] = useState14(false);
3215
- const handle = useMemo8(
3540
+ const handle = useMemo9(
3216
3541
  () => ({
3217
3542
  insertText: (text) => setInputText((prev) => prev ? `${prev}
3218
3543
  ${text}` : text),
@@ -3221,7 +3546,7 @@ ${text}` : text),
3221
3546
  }),
3222
3547
  [send, reset]
3223
3548
  );
3224
- useEffect8(() => {
3549
+ useEffect9(() => {
3225
3550
  onReady?.(handle);
3226
3551
  }, [handle, onReady]);
3227
3552
  return /* @__PURE__ */ jsx19(
@@ -3311,7 +3636,8 @@ lucide-react/dist/esm/shared/src/utils.js:
3311
3636
  lucide-react/dist/esm/defaultAttributes.js:
3312
3637
  lucide-react/dist/esm/Icon.js:
3313
3638
  lucide-react/dist/esm/createLucideIcon.js:
3314
- lucide-react/dist/esm/icons/archive.js:
3639
+ lucide-react/dist/esm/icons/arrow-right.js:
3640
+ lucide-react/dist/esm/icons/arrow-up-right.js:
3315
3641
  lucide-react/dist/esm/icons/arrow-up.js:
3316
3642
  lucide-react/dist/esm/icons/bot.js:
3317
3643
  lucide-react/dist/esm/icons/brain.js:
@@ -3321,21 +3647,19 @@ lucide-react/dist/esm/icons/chevron-right.js:
3321
3647
  lucide-react/dist/esm/icons/circle-alert.js:
3322
3648
  lucide-react/dist/esm/icons/copy.js:
3323
3649
  lucide-react/dist/esm/icons/download.js:
3324
- lucide-react/dist/esm/icons/file-code-2.js:
3325
- lucide-react/dist/esm/icons/file-spreadsheet.js:
3326
3650
  lucide-react/dist/esm/icons/file-text.js:
3327
3651
  lucide-react/dist/esm/icons/file.js:
3328
3652
  lucide-react/dist/esm/icons/film.js:
3329
- lucide-react/dist/esm/icons/image.js:
3653
+ lucide-react/dist/esm/icons/globe.js:
3330
3654
  lucide-react/dist/esm/icons/layers.js:
3331
3655
  lucide-react/dist/esm/icons/lightbulb.js:
3332
3656
  lucide-react/dist/esm/icons/loader-circle.js:
3333
3657
  lucide-react/dist/esm/icons/lock-keyhole.js:
3334
3658
  lucide-react/dist/esm/icons/message-square-more.js:
3335
3659
  lucide-react/dist/esm/icons/message-square.js:
3336
- lucide-react/dist/esm/icons/music.js:
3337
3660
  lucide-react/dist/esm/icons/play.js:
3338
3661
  lucide-react/dist/esm/icons/settings-2.js:
3662
+ lucide-react/dist/esm/icons/sparkles.js:
3339
3663
  lucide-react/dist/esm/icons/square.js:
3340
3664
  lucide-react/dist/esm/icons/triangle-alert.js:
3341
3665
  lucide-react/dist/esm/icons/x.js: