@blade-hq/agent-react 2610.0.0-beta.1 → 2610.0.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -545,7 +545,7 @@ function toToolCallInfo(call, results, failedToolIds) {
545
545
  }
546
546
 
547
547
  // src/components/AgentChat.tsx
548
- import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
548
+ import { BladeApiError } 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,16 +618,11 @@ 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/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" }]
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" }]
631
626
  ]);
632
627
 
633
628
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
@@ -704,6 +699,24 @@ var Download = createLucideIcon("Download", [
704
699
  ["line", { x1: "12", x2: "12", y1: "15", y2: "3", key: "1vk2je" }]
705
700
  ]);
706
701
 
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
+
707
720
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
708
721
  var FileText = createLucideIcon("FileText", [
709
722
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
@@ -731,11 +744,11 @@ var Film = createLucideIcon("Film", [
731
744
  ["path", { d: "M17 16.5h4", key: "go4c1d" }]
732
745
  ]);
733
746
 
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" }]
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" }]
739
752
  ]);
740
753
 
741
754
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
@@ -801,6 +814,13 @@ var MessageSquare = createLucideIcon("MessageSquare", [
801
814
  ["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" }]
802
815
  ]);
803
816
 
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
+
804
824
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
805
825
  var Play = createLucideIcon("Play", [
806
826
  ["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
@@ -814,21 +834,6 @@ var Settings2 = createLucideIcon("Settings2", [
814
834
  ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
815
835
  ]);
816
836
 
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
-
832
837
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
833
838
  var Square = createLucideIcon("Square", [
834
839
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
@@ -854,7 +859,7 @@ var X = createLucideIcon("X", [
854
859
  ]);
855
860
 
856
861
  // src/components/AgentChat.tsx
857
- import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo8, useState as useState12 } from "react";
862
+ import { useEffect as useEffect7, useState as useState12 } from "react";
858
863
 
859
864
  // src/lib/utils.ts
860
865
  function cn(...inputs) {
@@ -1078,7 +1083,7 @@ function ConnectionBanner({ connection, className }) {
1078
1083
 
1079
1084
  // src/components/MessageList.tsx
1080
1085
  import { isHiddenInternalMessage } from "@blade-hq/agent-client";
1081
- import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useRef as useRef7, useState as useState11 } from "react";
1086
+ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo7, useRef as useRef6, useState as useState11 } from "react";
1082
1087
 
1083
1088
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
1084
1089
  import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
@@ -1506,7 +1511,7 @@ function useStickToBottomContext() {
1506
1511
 
1507
1512
  // src/components/AssistantTurnBlock.tsx
1508
1513
  import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
1509
- import { useState as useState9 } from "react";
1514
+ import { useState as useState10 } from "react";
1510
1515
 
1511
1516
  // src/components/AgentLoopBlock.tsx
1512
1517
  import { useState as useState5 } from "react";
@@ -1709,14 +1714,142 @@ import {
1709
1714
  useEffect as useEffect4,
1710
1715
  useMemo as useMemo5,
1711
1716
  useRef as useRef5,
1712
- useState as useState6
1717
+ useState as useState7
1713
1718
  } 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";
1714
1765
  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";
1715
1848
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
1716
1849
  function CodeBlockPre({ children, node: _node, ...props }) {
1717
1850
  const preRef = useRef5(null);
1718
- const [copied, setCopied] = useState6(false);
1719
- const [language, setLanguage] = useState6("");
1851
+ const [copied, setCopied] = useState7(false);
1852
+ const [language, setLanguage] = useState7("");
1720
1853
  useEffect4(() => {
1721
1854
  const codeEl = preRef.current?.querySelector("code");
1722
1855
  setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
@@ -1728,10 +1861,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1728
1861
  setTimeout(() => setCopied(false), 2e3);
1729
1862
  }
1730
1863
  };
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(
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(
1735
1868
  "button",
1736
1869
  {
1737
1870
  type: "button",
@@ -1741,13 +1874,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1741
1874
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
1742
1875
  ),
1743
1876
  children: [
1744
- copied ? /* @__PURE__ */ jsx7(Check, { size: 12 }) : /* @__PURE__ */ jsx7(Copy, { size: 12 }),
1745
- /* @__PURE__ */ jsx7("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1877
+ copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
1878
+ /* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1746
1879
  ]
1747
1880
  }
1748
1881
  )
1749
1882
  ] }),
1750
- /* @__PURE__ */ jsx7(
1883
+ /* @__PURE__ */ jsx8(
1751
1884
  "pre",
1752
1885
  {
1753
1886
  ref: preRef,
@@ -1759,22 +1892,36 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1759
1892
  ] });
1760
1893
  }
1761
1894
  function ExternalAnchor({ node: _node, children, ...props }) {
1762
- return /* @__PURE__ */ jsx7("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1895
+ return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1763
1896
  }
1764
1897
  var MARKDOWN_COMPONENTS = {
1765
1898
  pre: CodeBlockPre,
1766
1899
  a: ExternalAnchor
1767
1900
  };
1901
+ var CUSTOM_ALLOWED_TAGS = {
1902
+ [FILE_CARD_TAG]: ["dataPath", "dataName", "data-path", "data-name"]
1903
+ };
1768
1904
  function MarkdownContent({ children, className, mode, sessionId }) {
1905
+ const streaming = mode === "streaming";
1769
1906
  const resolvedChildren = useMemo5(() => {
1770
- return children.replace(SYSTEM_REMINDER_RE, "");
1771
- }, [children]);
1772
- return /* @__PURE__ */ jsx7(
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(
1773
1919
  _r,
1774
1920
  {
1775
1921
  className: cn("blade-chat-markdown break-words", className),
1776
1922
  mode: mode ?? "static",
1777
- components: MARKDOWN_COMPONENTS,
1923
+ components,
1924
+ allowedTags: CUSTOM_ALLOWED_TAGS,
1778
1925
  children: resolvedChildren
1779
1926
  },
1780
1927
  sessionId ?? "no-session"
@@ -1782,17 +1929,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
1782
1929
  }
1783
1930
 
1784
1931
  // src/components/Shimmer.tsx
1785
- import { jsx as jsx8 } from "react/jsx-runtime";
1932
+ import { jsx as jsx9 } from "react/jsx-runtime";
1786
1933
  function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
1787
- return /* @__PURE__ */ jsx8("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1934
+ return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1788
1935
  }
1789
1936
 
1790
1937
  // src/components/ToolCallBlock.tsx
1791
- import { useState as useState8 } from "react";
1938
+ import { useState as useState9 } from "react";
1792
1939
 
1793
1940
  // src/components/AskUserQuestionBlock.tsx
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";
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";
1796
1943
  function AskUserQuestionBlock({
1797
1944
  data,
1798
1945
  answered,
@@ -1801,10 +1948,10 @@ function AskUserQuestionBlock({
1801
1948
  answerData,
1802
1949
  onAnswer
1803
1950
  }) {
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);
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);
1808
1955
  useEffect5(() => {
1809
1956
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
1810
1957
  setSubmitted(false);
@@ -1895,7 +2042,7 @@ ${parts.join("\n")}`;
1895
2042
  setSubmitted(true);
1896
2043
  onAnswer(text, toolCallId, nextAnswerData);
1897
2044
  };
1898
- return /* @__PURE__ */ jsxs7(
2045
+ return /* @__PURE__ */ jsxs8(
1899
2046
  "div",
1900
2047
  {
1901
2048
  className: cn(
@@ -1903,12 +2050,12 @@ ${parts.join("\n")}`;
1903
2050
  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"
1904
2051
  ),
1905
2052
  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: [
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: [
1907
2054
  "\u5B50\u667A\u80FD\u4F53\u300C",
1908
2055
  data.source_loop.description,
1909
2056
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
1910
2057
  ] }),
1911
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx9(
2058
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
1912
2059
  QuestionCard,
1913
2060
  {
1914
2061
  question: q,
@@ -1923,7 +2070,7 @@ ${parts.join("\n")}`;
1923
2070
  },
1924
2071
  q.question
1925
2072
  )),
1926
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
2073
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
1927
2074
  "button",
1928
2075
  {
1929
2076
  type: "button",
@@ -1933,14 +2080,14 @@ ${parts.join("\n")}`;
1933
2080
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
1934
2081
  }
1935
2082
  ),
1936
- submitted && !answered && /* @__PURE__ */ jsxs7(
2083
+ submitted && !answered && /* @__PURE__ */ jsxs8(
1937
2084
  "button",
1938
2085
  {
1939
2086
  type: "button",
1940
2087
  disabled: true,
1941
2088
  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",
1942
2089
  children: [
1943
- /* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
2090
+ /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
1944
2091
  "\u786E\u8BA4\u4E2D"
1945
2092
  ]
1946
2093
  }
@@ -1961,30 +2108,30 @@ function QuestionCard({
1961
2108
  onCustomChange
1962
2109
  }) {
1963
2110
  const multi = question.multiSelect ?? false;
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(
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(
1967
2114
  MessageSquareMore,
1968
2115
  {
1969
2116
  size: answered ? 12 : 13,
1970
2117
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
1971
2118
  }
1972
2119
  ),
1973
- /* @__PURE__ */ jsx9(
2120
+ /* @__PURE__ */ jsx10(
1974
2121
  "div",
1975
2122
  {
1976
2123
  className: cn(
1977
2124
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
1978
2125
  answered ? "text-xs" : "text-sm"
1979
2126
  ),
1980
- children: /* @__PURE__ */ jsx9(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2127
+ children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
1981
2128
  }
1982
2129
  )
1983
2130
  ] }),
1984
- /* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2131
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
1985
2132
  question.options.map((opt, optIdx) => {
1986
2133
  const isSel = selected.has(optIdx);
1987
- return /* @__PURE__ */ jsxs7(
2134
+ return /* @__PURE__ */ jsxs8(
1988
2135
  "button",
1989
2136
  {
1990
2137
  type: "button",
@@ -1998,14 +2145,14 @@ function QuestionCard({
1998
2145
  answered && "cursor-default opacity-70"
1999
2146
  ),
2000
2147
  children: [
2001
- multi && /* @__PURE__ */ jsx9(
2148
+ multi && /* @__PURE__ */ jsx10(
2002
2149
  "div",
2003
2150
  {
2004
2151
  className: cn(
2005
2152
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
2006
2153
  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))]"
2007
2154
  ),
2008
- children: isSel && /* @__PURE__ */ jsx9(
2155
+ children: isSel && /* @__PURE__ */ jsx10(
2009
2156
  Check,
2010
2157
  {
2011
2158
  size: 9,
@@ -2014,9 +2161,9 @@ function QuestionCard({
2014
2161
  )
2015
2162
  }
2016
2163
  ),
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(
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(
2020
2167
  "div",
2021
2168
  {
2022
2169
  className: cn(
@@ -2033,7 +2180,7 @@ function QuestionCard({
2033
2180
  opt.label
2034
2181
  );
2035
2182
  }),
2036
- answered && !isCustom ? null : /* @__PURE__ */ jsxs7(
2183
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
2037
2184
  "div",
2038
2185
  {
2039
2186
  className: cn(
@@ -2043,8 +2190,8 @@ function QuestionCard({
2043
2190
  answered && "cursor-default opacity-70"
2044
2191
  ),
2045
2192
  children: [
2046
- /* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2047
- /* @__PURE__ */ jsx9(
2193
+ /* @__PURE__ */ jsx10("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2194
+ /* @__PURE__ */ jsx10(
2048
2195
  "input",
2049
2196
  {
2050
2197
  type: "text",
@@ -2112,7 +2259,7 @@ function normalizeOptionItem(value) {
2112
2259
  }
2113
2260
 
2114
2261
  // src/components/ToolCallBlock.tsx
2115
- import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2262
+ import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2116
2263
  function resolveAskQuestionState({
2117
2264
  toolStatus,
2118
2265
  hasAnswerData,
@@ -2132,12 +2279,12 @@ function ToolCallBlock({
2132
2279
  sessionStatus,
2133
2280
  renderer
2134
2281
  }) {
2135
- const [expanded, setExpanded] = useState8(false);
2282
+ const [expanded, setExpanded] = useState9(false);
2136
2283
  const normalizedName = formatToolName(toolCall.name);
2137
2284
  if (renderer) {
2138
2285
  const custom = renderer(toolCall);
2139
2286
  if (custom !== null && custom !== void 0) {
2140
- return /* @__PURE__ */ jsx10(Fragment, { children: custom });
2287
+ return /* @__PURE__ */ jsx11(Fragment, { children: custom });
2141
2288
  }
2142
2289
  }
2143
2290
  if (normalizedName === "AskUserQuestion") {
@@ -2149,7 +2296,7 @@ function ToolCallBlock({
2149
2296
  });
2150
2297
  const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
2151
2298
  if (askData) {
2152
- return /* @__PURE__ */ jsx10(
2299
+ return /* @__PURE__ */ jsx11(
2153
2300
  AskUserQuestionBlock,
2154
2301
  {
2155
2302
  data: askData,
@@ -2162,24 +2309,24 @@ function ToolCallBlock({
2162
2309
  );
2163
2310
  }
2164
2311
  if (toolCall.status === "pending") {
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" })
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" })
2168
2315
  ] });
2169
2316
  }
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" })
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" })
2173
2320
  ] });
2174
2321
  }
2175
2322
  const tone = getToolTone(toolCall.status);
2176
2323
  const displayName = getToolDisplayLabel(toolCall);
2177
2324
  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))]";
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 });
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 });
2179
2326
  const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
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(
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(
2183
2330
  "button",
2184
2331
  {
2185
2332
  type: "button",
@@ -2187,7 +2334,7 @@ function ToolCallBlock({
2187
2334
  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",
2188
2335
  "aria-expanded": expanded,
2189
2336
  children: [
2190
- /* @__PURE__ */ jsx10(
2337
+ /* @__PURE__ */ jsx11(
2191
2338
  ChevronRight,
2192
2339
  {
2193
2340
  size: 11,
@@ -2197,24 +2344,24 @@ function ToolCallBlock({
2197
2344
  )
2198
2345
  }
2199
2346
  ),
2200
- /* @__PURE__ */ jsxs8("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2347
+ /* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2201
2348
  statusIcon,
2202
- /* @__PURE__ */ jsx10("span", { children: getToolStatusLabel(toolCall.status) })
2349
+ /* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
2203
2350
  ] }),
2204
- /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2351
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2205
2352
  ]
2206
2353
  }
2207
2354
  ),
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) })
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) })
2209
2356
  ] }),
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) })
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) })
2218
2365
  ] })
2219
2366
  ] })
2220
2367
  ] });
@@ -2232,11 +2379,11 @@ function buildAskUserPayload(argumentsJson) {
2232
2379
  }
2233
2380
 
2234
2381
  // src/components/AssistantTurnBlock.tsx
2235
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2382
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2236
2383
  function ThinkingBlock({ reasoning, isStreaming }) {
2237
- const [open, setOpen] = useState9(false);
2238
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
2239
- /* @__PURE__ */ jsxs9(
2384
+ const [open, setOpen] = useState10(false);
2385
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
2386
+ /* @__PURE__ */ jsxs10(
2240
2387
  "button",
2241
2388
  {
2242
2389
  type: "button",
@@ -2244,14 +2391,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2244
2391
  "aria-expanded": open,
2245
2392
  className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
2246
2393
  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: [
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: [
2250
2397
  "\xB7 ",
2251
2398
  new Intl.NumberFormat("zh-CN").format(reasoning.length),
2252
2399
  " \u5B57"
2253
2400
  ] }),
2254
- /* @__PURE__ */ jsx11(
2401
+ /* @__PURE__ */ jsx12(
2255
2402
  ChevronDown,
2256
2403
  {
2257
2404
  size: 12,
@@ -2261,7 +2408,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2261
2408
  ]
2262
2409
  }
2263
2410
  ),
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 })
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 })
2265
2412
  ] });
2266
2413
  }
2267
2414
  function getMessageText(message) {
@@ -2287,21 +2434,21 @@ function AssistantTurnBlock({
2287
2434
  (message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
2288
2435
  );
2289
2436
  const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
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" }),
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" }),
2292
2439
  messages.map((message, index) => {
2293
2440
  const isLast = index === messages.length - 1;
2294
2441
  const streamingThis = isStreaming && isLast;
2295
2442
  const text = getMessageText(message);
2296
2443
  const toolCalls = message.tool_calls ?? [];
2297
2444
  const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
2298
- return /* @__PURE__ */ jsxs9(
2445
+ return /* @__PURE__ */ jsxs10(
2299
2446
  "div",
2300
2447
  {
2301
2448
  className: "flex flex-col gap-3",
2302
2449
  children: [
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(
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(
2305
2452
  MarkdownContent,
2306
2453
  {
2307
2454
  mode: streamingThis ? "streaming" : "static",
@@ -2310,8 +2457,8 @@ function AssistantTurnBlock({
2310
2457
  children: text
2311
2458
  }
2312
2459
  ) }),
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(
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(
2315
2462
  ToolCallBlock,
2316
2463
  {
2317
2464
  toolCall,
@@ -2329,13 +2476,13 @@ function AssistantTurnBlock({
2329
2476
  message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
2330
2477
  );
2331
2478
  }),
2332
- isStreaming && !hasAnyContent && /* @__PURE__ */ jsx11(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
2479
+ isStreaming && !hasAnyContent && /* @__PURE__ */ jsx12(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
2333
2480
  ] });
2334
2481
  }
2335
2482
 
2336
2483
  // src/components/RenderErrorBoundary.tsx
2337
2484
  import { Component } from "react";
2338
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2485
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2339
2486
  function getFirstComponentName(componentStack) {
2340
2487
  const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
2341
2488
  return match?.[1] ?? null;
@@ -2368,408 +2515,23 @@ var RenderErrorBoundary = class extends Component {
2368
2515
  return children;
2369
2516
  }
2370
2517
  const componentName = getFirstComponentName(componentStack);
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: [
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: [
2375
2522
  label,
2376
2523
  "\u6E32\u67D3\u5931\u8D25"
2377
2524
  ] }),
2378
- /* @__PURE__ */ jsxs10("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
2525
+ /* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
2379
2526
  componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
2380
2527
  error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
2381
2528
  ] }),
2382
- details ? /* @__PURE__ */ jsx12("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
2529
+ details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
2383
2530
  ] })
2384
2531
  ] }) });
2385
2532
  }
2386
2533
  };
2387
2534
 
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
-
2773
2535
  // src/components/UserMessageBubble.tsx
2774
2536
  import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
2775
2537
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
@@ -2855,8 +2617,6 @@ function getMessageResetSignature(messages) {
2855
2617
  }
2856
2618
  function MessageList({
2857
2619
  messages,
2858
- postChatFollowup,
2859
- onSuggestion,
2860
2620
  isStreaming,
2861
2621
  sessionStatus,
2862
2622
  askAnswers,
@@ -2864,11 +2624,7 @@ function MessageList({
2864
2624
  toolCallRenderer,
2865
2625
  emptyState,
2866
2626
  className,
2867
- sessionId,
2868
- isViewer = false,
2869
- onFollowupInteraction,
2870
- resultFeedbackByEntry = /* @__PURE__ */ new Map(),
2871
- onResultFeedbackSaved
2627
+ sessionId
2872
2628
  }) {
2873
2629
  const renderBlocks = useMemo7(() => {
2874
2630
  const visible = messages.filter((message) => {
@@ -2954,47 +2710,24 @@ function MessageList({
2954
2710
  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);
2955
2711
  }
2956
2712
  if (block.type === "assistant_turn") {
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(
2713
+ return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx15(
2966
2714
  RenderErrorBoundary,
2967
2715
  {
2968
2716
  label: "\u52A9\u624B\u6D88\u606F",
2969
2717
  details: block.key,
2970
2718
  resetKey: getMessageResetSignature(block.messages),
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
- ]
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
+ )
2998
2731
  }
2999
2732
  ) }, block.key);
3000
2733
  }
@@ -3021,8 +2754,8 @@ function MessageList({
3021
2754
  }
3022
2755
  function AutoScrollOnUserSend({ userMessageCount }) {
3023
2756
  const { scrollToBottom } = useStickToBottomContext();
3024
- const previousCountRef = useRef7(userMessageCount);
3025
- useEffect7(() => {
2757
+ const previousCountRef = useRef6(userMessageCount);
2758
+ useEffect6(() => {
3026
2759
  if (userMessageCount > previousCountRef.current) {
3027
2760
  scrollToBottom("instant");
3028
2761
  }
@@ -3033,8 +2766,8 @@ function AutoScrollOnUserSend({ userMessageCount }) {
3033
2766
  function ScrollToBottomButton() {
3034
2767
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
3035
2768
  const [visible, setVisible] = useState11(false);
3036
- const hideTimerRef = useRef7(null);
3037
- useEffect7(() => {
2769
+ const hideTimerRef = useRef6(null);
2770
+ useEffect6(() => {
3038
2771
  if (isAtBottom) {
3039
2772
  if (!hideTimerRef.current) {
3040
2773
  hideTimerRef.current = setTimeout(() => {
@@ -3056,7 +2789,7 @@ function ScrollToBottomButton() {
3056
2789
  }
3057
2790
  };
3058
2791
  }, [isAtBottom]);
3059
- const handleClick = useCallback6(() => {
2792
+ const handleClick = useCallback5(() => {
3060
2793
  if (hideTimerRef.current) {
3061
2794
  clearTimeout(hideTimerRef.current);
3062
2795
  hideTimerRef.current = null;
@@ -3104,22 +2837,16 @@ function ChatSurface({
3104
2837
  connection,
3105
2838
  errorMessage,
3106
2839
  messages,
3107
- postChatFollowup,
3108
2840
  isStreaming,
3109
2841
  isStopping,
3110
2842
  inputText,
3111
2843
  onInputChange,
3112
- onSuggestion,
3113
2844
  onSend,
3114
2845
  onStop,
3115
2846
  sessionStatus,
3116
2847
  askAnswers,
3117
2848
  onAnswer,
3118
2849
  sessionId,
3119
- isViewer,
3120
- resultFeedbackByEntry,
3121
- onResultFeedbackSaved,
3122
- onFollowupInteraction,
3123
2850
  beforeInput,
3124
2851
  banner
3125
2852
  }) {
@@ -3143,8 +2870,6 @@ function ChatSurface({
3143
2870
  MessageList,
3144
2871
  {
3145
2872
  messages,
3146
- postChatFollowup,
3147
- onSuggestion,
3148
2873
  isStreaming,
3149
2874
  sessionStatus,
3150
2875
  askAnswers,
@@ -3152,11 +2877,7 @@ function ChatSurface({
3152
2877
  toolCallRenderer: renderers?.toolCall,
3153
2878
  emptyState: slots?.emptyState,
3154
2879
  className: classNames?.messageList,
3155
- sessionId,
3156
- isViewer,
3157
- resultFeedbackByEntry,
3158
- onResultFeedbackSaved,
3159
- onFollowupInteraction
2880
+ sessionId
3160
2881
  }
3161
2882
  ),
3162
2883
  beforeInput,
@@ -3180,7 +2901,7 @@ function ChatSurface({
3180
2901
  }
3181
2902
 
3182
2903
  // src/components/AgentChat.tsx
3183
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2904
+ import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
3184
2905
  function isUnauthorizedError(error) {
3185
2906
  return error instanceof BladeApiError && error.status === 401;
3186
2907
  }
@@ -3252,10 +2973,8 @@ function ChatSessionView({
3252
2973
  slots,
3253
2974
  placeholder,
3254
2975
  theme,
3255
- onFollowupInteraction,
3256
2976
  onUnauthorized
3257
2977
  }) {
3258
- const client = useBladeClient();
3259
2978
  const { session, state, error } = useAgentSession(sessionId, {
3260
2979
  createOptions,
3261
2980
  onSessionCreated
@@ -3263,50 +2982,12 @@ function ChatSessionView({
3263
2982
  const replay = useReplay(session);
3264
2983
  const [stopRequested, setStopRequested] = useState12(false);
3265
2984
  const [inputText, setInputText] = useState12("");
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(() => {
2985
+ useEffect7(() => {
3305
2986
  if (session) {
3306
2987
  onSessionReady?.(session);
3307
2988
  }
3308
2989
  }, [session, onSessionReady]);
3309
- useEffect8(() => {
2990
+ useEffect7(() => {
3310
2991
  if (!session) return;
3311
2992
  const offAttach = session.on("attachRequested", ({ label, content }) => {
3312
2993
  setInputText((prev) => `${prev ? `${prev}
@@ -3322,12 +3003,12 @@ ${content}`);
3322
3003
  offInsert();
3323
3004
  };
3324
3005
  }, [session]);
3325
- useEffect8(() => {
3006
+ useEffect7(() => {
3326
3007
  if (isUnauthorizedError(error)) {
3327
3008
  onUnauthorized();
3328
3009
  }
3329
3010
  }, [error, onUnauthorized]);
3330
- useEffect8(() => {
3011
+ useEffect7(() => {
3331
3012
  if (!session || !commands) return;
3332
3013
  const unsubscribes = Object.entries(commands).map(
3333
3014
  ([action, handler]) => session.onCommand(action, (payload) => handler(payload))
@@ -3352,13 +3033,12 @@ ${content}`);
3352
3033
  ChatSurface,
3353
3034
  {
3354
3035
  theme,
3355
- onFollowupInteraction,
3356
3036
  classNames,
3357
3037
  renderers,
3358
3038
  slots,
3359
3039
  placeholder,
3360
3040
  connection: state?.connection ?? "connecting",
3361
- banner: /* @__PURE__ */ jsxs15(Fragment3, { children: [
3041
+ banner: /* @__PURE__ */ jsxs15(Fragment2, { children: [
3362
3042
  /* @__PURE__ */ jsx17(
3363
3043
  ReplayBar,
3364
3044
  {
@@ -3373,14 +3053,10 @@ ${content}`);
3373
3053
  ] }),
3374
3054
  errorMessage,
3375
3055
  messages: state?.messages ?? [],
3376
- postChatFollowup: latestPostChatFollowup(state?.turns ?? []),
3377
- resultFeedbackByEntry,
3378
- onResultFeedbackSaved: handleResultFeedbackSaved,
3379
3056
  isStreaming,
3380
3057
  isStopping,
3381
3058
  inputText,
3382
3059
  onInputChange: setInputText,
3383
- onSuggestion: setInputText,
3384
3060
  onSend: handleSend,
3385
3061
  onStop: handleStop,
3386
3062
  sessionStatus: state?.status ?? void 0,
@@ -3391,14 +3067,13 @@ ${content}`);
3391
3067
  askUserAnswer: { tool_call_id: toolCallId, ...answerData }
3392
3068
  });
3393
3069
  },
3394
- sessionId: resolvedSessionId,
3395
- isViewer
3070
+ sessionId: session?.sessionId
3396
3071
  }
3397
3072
  );
3398
3073
  }
3399
3074
 
3400
3075
  // src/components/LlmChat.tsx
3401
- import { useEffect as useEffect9, useMemo as useMemo9, useState as useState14 } from "react";
3076
+ import { useEffect as useEffect8, useMemo as useMemo8, useState as useState14 } from "react";
3402
3077
 
3403
3078
  // src/components/LlmAdvancedSettings.tsx
3404
3079
  import { useState as useState13 } from "react";
@@ -3537,7 +3212,7 @@ function LlmChat({
3537
3212
  const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
3538
3213
  const [inputText, setInputText] = useState14("");
3539
3214
  const [stopRequested, setStopRequested] = useState14(false);
3540
- const handle = useMemo9(
3215
+ const handle = useMemo8(
3541
3216
  () => ({
3542
3217
  insertText: (text) => setInputText((prev) => prev ? `${prev}
3543
3218
  ${text}` : text),
@@ -3546,7 +3221,7 @@ ${text}` : text),
3546
3221
  }),
3547
3222
  [send, reset]
3548
3223
  );
3549
- useEffect9(() => {
3224
+ useEffect8(() => {
3550
3225
  onReady?.(handle);
3551
3226
  }, [handle, onReady]);
3552
3227
  return /* @__PURE__ */ jsx19(
@@ -3636,8 +3311,7 @@ lucide-react/dist/esm/shared/src/utils.js:
3636
3311
  lucide-react/dist/esm/defaultAttributes.js:
3637
3312
  lucide-react/dist/esm/Icon.js:
3638
3313
  lucide-react/dist/esm/createLucideIcon.js:
3639
- lucide-react/dist/esm/icons/arrow-right.js:
3640
- lucide-react/dist/esm/icons/arrow-up-right.js:
3314
+ lucide-react/dist/esm/icons/archive.js:
3641
3315
  lucide-react/dist/esm/icons/arrow-up.js:
3642
3316
  lucide-react/dist/esm/icons/bot.js:
3643
3317
  lucide-react/dist/esm/icons/brain.js:
@@ -3647,19 +3321,21 @@ lucide-react/dist/esm/icons/chevron-right.js:
3647
3321
  lucide-react/dist/esm/icons/circle-alert.js:
3648
3322
  lucide-react/dist/esm/icons/copy.js:
3649
3323
  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:
3650
3326
  lucide-react/dist/esm/icons/file-text.js:
3651
3327
  lucide-react/dist/esm/icons/file.js:
3652
3328
  lucide-react/dist/esm/icons/film.js:
3653
- lucide-react/dist/esm/icons/globe.js:
3329
+ lucide-react/dist/esm/icons/image.js:
3654
3330
  lucide-react/dist/esm/icons/layers.js:
3655
3331
  lucide-react/dist/esm/icons/lightbulb.js:
3656
3332
  lucide-react/dist/esm/icons/loader-circle.js:
3657
3333
  lucide-react/dist/esm/icons/lock-keyhole.js:
3658
3334
  lucide-react/dist/esm/icons/message-square-more.js:
3659
3335
  lucide-react/dist/esm/icons/message-square.js:
3336
+ lucide-react/dist/esm/icons/music.js:
3660
3337
  lucide-react/dist/esm/icons/play.js:
3661
3338
  lucide-react/dist/esm/icons/settings-2.js:
3662
- lucide-react/dist/esm/icons/sparkles.js:
3663
3339
  lucide-react/dist/esm/icons/square.js:
3664
3340
  lucide-react/dist/esm/icons/triangle-alert.js:
3665
3341
  lucide-react/dist/esm/icons/x.js: