@blade-hq/agent-kit 0.5.22 → 0.5.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/{SkillStatusBar-Dn5im-__.d.ts → SkillStatusBar-DO-ltefK.d.ts} +3 -0
  2. package/dist/{chunk-QF2LUFUM.js → chunk-63IWZ3AO.js} +2 -2
  3. package/dist/{chunk-IE6AP6CS.js → chunk-7FGJNW56.js} +13 -5
  4. package/dist/chunk-7FGJNW56.js.map +1 -0
  5. package/dist/{chunk-CCMNSYPI.js → chunk-DTDZFQRY.js} +24 -2
  6. package/dist/chunk-DTDZFQRY.js.map +1 -0
  7. package/dist/chunk-H62LH2AG.js +37 -0
  8. package/dist/chunk-H62LH2AG.js.map +1 -0
  9. package/dist/{chunk-ER4RLUJW.js → chunk-KW66QITV.js} +285 -96
  10. package/dist/chunk-KW66QITV.js.map +1 -0
  11. package/dist/{chunk-3YAZYX2V.js → chunk-QKSZPJQX.js} +4 -37
  12. package/dist/chunk-QKSZPJQX.js.map +1 -0
  13. package/dist/client/index.d.ts +57 -0
  14. package/dist/react/api/vibe-coding.js +1 -1
  15. package/dist/react/components/chat/index.d.ts +2 -2
  16. package/dist/react/components/chat/index.js +5 -4
  17. package/dist/react/components/plan/index.js +3 -2
  18. package/dist/react/components/plan/index.js.map +1 -1
  19. package/dist/react/components/session/index.js +2 -2
  20. package/dist/react/components/workspace/index.js +5 -3
  21. package/dist/react/components/workspace/index.js.map +1 -1
  22. package/dist/react/index.d.ts +2 -2
  23. package/dist/react/index.js +6 -5
  24. package/dist/react/index.js.map +1 -1
  25. package/dist/style.css +1 -1
  26. package/package.json +1 -1
  27. package/dist/chunk-3YAZYX2V.js.map +0 -1
  28. package/dist/chunk-CCMNSYPI.js.map +0 -1
  29. package/dist/chunk-ER4RLUJW.js.map +0 -1
  30. package/dist/chunk-IE6AP6CS.js.map +0 -1
  31. /package/dist/{chunk-QF2LUFUM.js.map → chunk-63IWZ3AO.js.map} +0 -0
@@ -2,11 +2,15 @@ import {
2
2
  CardJSON,
3
3
  cardRegistry
4
4
  } from "./chunk-2UP7MG3J.js";
5
+ import {
6
+ getSessionFilePath,
7
+ resolveSessionFilePreviewTarget
8
+ } from "./chunk-QKSZPJQX.js";
5
9
  import {
6
10
  formatToolName,
7
11
  getAuthedUrl,
8
12
  useUiStore
9
- } from "./chunk-CCMNSYPI.js";
13
+ } from "./chunk-DTDZFQRY.js";
10
14
  import {
11
15
  cn,
12
16
  copyToClipboard
@@ -155,7 +159,7 @@ async function loadCodeHighlighter() {
155
159
  html,
156
160
  javascript,
157
161
  json,
158
- jsx8,
162
+ jsx9,
159
163
  markdown,
160
164
  python,
161
165
  rust,
@@ -173,7 +177,7 @@ async function loadCodeHighlighter() {
173
177
  html.default,
174
178
  javascript.default,
175
179
  json.default,
176
- jsx8.default,
180
+ jsx9.default,
177
181
  markdown.default,
178
182
  python.default,
179
183
  rust.default,
@@ -372,12 +376,12 @@ function CardCodeBlock({ className, children, node, ...props }) {
372
376
 
373
377
  // src/react/components/markdown/MarkdownContent.tsx
374
378
  import { mermaid } from "@streamdown/mermaid";
375
- import { Check as Check2, Copy as Copy2, Download, X as X2 } from "lucide-react";
379
+ import { Check as Check2, Copy as Copy2, Download as Download2, X as X2 } from "lucide-react";
376
380
  import {
377
381
  useEffect as useEffect3,
378
382
  useMemo as useMemo2,
379
383
  useRef,
380
- useState as useState3
384
+ useState as useState4
381
385
  } from "react";
382
386
  import { createPortal as createPortal2 } from "react-dom";
383
387
  import { Streamdown } from "streamdown";
@@ -492,15 +496,162 @@ function ExternalLinkDialog({ isOpen, onClose, onConfirm, url }) {
492
496
  );
493
497
  }
494
498
 
495
- // src/react/components/markdown/MarkdownContent.tsx
499
+ // src/react/components/markdown/FileCardComponent.tsx
500
+ import {
501
+ Archive,
502
+ Download,
503
+ File,
504
+ FileCode2,
505
+ FileSpreadsheet,
506
+ FileText,
507
+ Film,
508
+ Image,
509
+ Loader2,
510
+ Music
511
+ } from "lucide-react";
512
+ import { useState as useState3 } from "react";
496
513
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
514
+ var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
515
+ var SHEET_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "csv"]);
516
+ var TEXT_EXTS = /* @__PURE__ */ new Set(["txt", "md", "markdown", "pdf", "doc", "docx", "rtf"]);
517
+ var CODE_EXTS = /* @__PURE__ */ new Set(["js", "jsx", "ts", "tsx", "py", "go", "json", "yaml", "yml", "html", "css"]);
518
+ var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "mkv", "avi"]);
519
+ var AUDIO_EXTS = /* @__PURE__ */ new Set(["mp3", "wav", "ogg", "m4a", "flac"]);
520
+ var ARCHIVE_EXTS = /* @__PURE__ */ new Set(["zip", "tar", "gz", "tgz", "rar", "7z"]);
521
+ function stringValue(value) {
522
+ return typeof value === "string" ? value : void 0;
523
+ }
524
+ function decodeAttribute(value) {
525
+ if (!value) return "";
526
+ try {
527
+ return decodeURIComponent(value);
528
+ } catch {
529
+ return value;
530
+ }
531
+ }
532
+ function isSafeRelPath(p) {
533
+ if (!p || p.startsWith("/")) return false;
534
+ const segments = p.split("/");
535
+ return segments.every((s) => s !== ".." && s !== ".");
536
+ }
537
+ function basename(path) {
538
+ return path.split("/").filter(Boolean).pop() || path;
539
+ }
540
+ function getExt(name) {
541
+ return name.split(".").pop()?.toLowerCase() ?? "";
542
+ }
543
+ function getFileIcon(name) {
544
+ const ext = getExt(name);
545
+ if (IMAGE_EXTS.has(ext)) return Image;
546
+ if (SHEET_EXTS.has(ext)) return FileSpreadsheet;
547
+ if (TEXT_EXTS.has(ext)) return FileText;
548
+ if (CODE_EXTS.has(ext)) return FileCode2;
549
+ if (VIDEO_EXTS.has(ext)) return Film;
550
+ if (AUDIO_EXTS.has(ext)) return Music;
551
+ if (ARCHIVE_EXTS.has(ext)) return Archive;
552
+ return File;
553
+ }
554
+ function extractChildrenText(children) {
555
+ if (typeof children === "string") return children;
556
+ if (Array.isArray(children)) return children.map(extractChildrenText).join("");
557
+ if (children && typeof children === "object" && "props" in children) {
558
+ return extractChildrenText(children.props?.children);
559
+ }
560
+ return "";
561
+ }
562
+ function FileCardComponent({
563
+ node,
564
+ "data-rel-path": dataRelPathAttribute,
565
+ "data-name": dataNameAttribute,
566
+ dataRelPath,
567
+ dataName,
568
+ children,
569
+ className,
570
+ ...props
571
+ }) {
572
+ const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
573
+ const childrenText = extractChildrenText(children);
574
+ const relPath = decodeAttribute(
575
+ dataRelPathAttribute ?? dataRelPath ?? stringValue(nodeProperties?.["data-rel-path"]) ?? stringValue(nodeProperties?.dataRelPath)
576
+ ) || decodeAttribute(childrenText || void 0);
577
+ const name = decodeAttribute(
578
+ dataNameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName)
579
+ ) || basename(relPath);
580
+ const { sessionId } = useCardContext();
581
+ const [opening, setOpening] = useState3(false);
582
+ const Icon = getFileIcon(name);
583
+ const disabled = !sessionId || !relPath || !isSafeRelPath(relPath);
584
+ const handleOpen = async () => {
585
+ if (disabled || opening) return;
586
+ setOpening(true);
587
+ try {
588
+ const target = await resolveSessionFilePreviewTarget(sessionId, relPath, name);
589
+ const uiStore = useUiStore.getState();
590
+ uiStore.pushArtifact(target);
591
+ uiStore.setActiveRightTab("preview");
592
+ } finally {
593
+ setOpening(false);
594
+ }
595
+ };
596
+ const handleDownload = (event) => {
597
+ event.stopPropagation();
598
+ if (disabled) return;
599
+ const a = document.createElement("a");
600
+ a.href = getAuthedUrl(getSessionFilePath(sessionId, relPath));
601
+ a.download = name;
602
+ a.click();
603
+ };
604
+ return /* @__PURE__ */ jsxs4(
605
+ "span",
606
+ {
607
+ ...props,
608
+ className: cn("not-prose my-2 flex max-w-full items-stretch", className),
609
+ children: [
610
+ /* @__PURE__ */ jsxs4(
611
+ "button",
612
+ {
613
+ type: "button",
614
+ onClick: handleOpen,
615
+ disabled: disabled || opening,
616
+ className: "flex min-w-0 flex-1 items-center gap-3 rounded-l-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2.5 text-left text-[hsl(var(--card-foreground))] shadow-sm transition-colors hover:border-[hsl(var(--primary))]/45 hover:bg-[hsl(var(--accent))]/45 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none disabled:cursor-not-allowed disabled:opacity-60",
617
+ "aria-label": `\u6253\u5F00\u6587\u4EF6\uFF1A${name}`,
618
+ children: [
619
+ /* @__PURE__ */ jsx5("span", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-[hsl(var(--muted))] text-[hsl(var(--primary))]", children: opening ? /* @__PURE__ */ jsx5(Loader2, { size: 18, className: "animate-spin" }) : /* @__PURE__ */ jsx5(Icon, { size: 18 }) }),
620
+ /* @__PURE__ */ jsxs4("span", { className: "min-w-0 flex-1", children: [
621
+ /* @__PURE__ */ jsx5("span", { className: "block truncate text-sm font-medium leading-5", children: name }),
622
+ /* @__PURE__ */ jsx5("span", { className: "block truncate text-xs leading-4 text-[hsl(var(--muted-foreground))]", children: "\u70B9\u51FB\u9884\u89C8\u6587\u4EF6" })
623
+ ] })
624
+ ]
625
+ }
626
+ ),
627
+ /* @__PURE__ */ jsx5(
628
+ "button",
629
+ {
630
+ type: "button",
631
+ onClick: handleDownload,
632
+ disabled,
633
+ className: "flex w-11 shrink-0 items-center justify-center rounded-r-lg border border-l-0 border-[hsl(var(--border))] bg-[hsl(var(--card))] text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))] focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none disabled:cursor-not-allowed disabled:opacity-60",
634
+ "aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
635
+ title: "\u4E0B\u8F7D",
636
+ children: /* @__PURE__ */ jsx5(Download, { size: 16 })
637
+ }
638
+ )
639
+ ]
640
+ }
641
+ );
642
+ }
643
+
644
+ // src/react/components/markdown/MarkdownContent.tsx
645
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
497
646
  var LINK_SAFETY_CONFIG = {
498
647
  enabled: true,
499
- renderModal: (props) => /* @__PURE__ */ jsx5(ExternalLinkDialog, { ...props })
648
+ renderModal: (props) => /* @__PURE__ */ jsx6(ExternalLinkDialog, { ...props })
500
649
  };
501
650
  var STREAMDOWN_PLUGINS = { mermaid };
502
651
  var SYSTEM_REMINDER_TAG = "system-reminder";
503
652
  var TOOL_CALL_TAG = "blade-malformed-tool-call";
653
+ var FILE_CARD_TAG = "blade-file-card";
654
+ var AGENT_WORKSPACE_DIR_NAME = "\u667A\u80FD\u52A9\u624B\u5DE5\u4F5C\u7A7A\u95F4";
504
655
  var HIDDEN_SYSTEM_REMINDER_TAGS = {
505
656
  [SYSTEM_REMINDER_TAG]: []
506
657
  };
@@ -526,14 +677,14 @@ function InlineToolCallBlock({
526
677
  node,
527
678
  children
528
679
  }) || extractText(children).trim();
529
- const [open, setOpen] = useState3(false);
530
- return /* @__PURE__ */ jsxs4(
680
+ const [open, setOpen] = useState4(false);
681
+ return /* @__PURE__ */ jsxs5(
531
682
  "span",
532
683
  {
533
684
  ...props,
534
685
  className: "not-prose relative inline-flex align-baseline",
535
686
  children: [
536
- /* @__PURE__ */ jsx5(
687
+ /* @__PURE__ */ jsx6(
537
688
  "button",
538
689
  {
539
690
  type: "button",
@@ -545,7 +696,7 @@ function InlineToolCallBlock({
545
696
  }
546
697
  ),
547
698
  open ? createPortal2(
548
- /* @__PURE__ */ jsx5(
699
+ /* @__PURE__ */ jsx6(
549
700
  "div",
550
701
  {
551
702
  className: "fixed inset-0 z-[70] flex items-center justify-center bg-black/10 p-6",
@@ -561,26 +712,26 @@ function InlineToolCallBlock({
561
712
  setOpen(false);
562
713
  }
563
714
  },
564
- children: /* @__PURE__ */ jsxs4(
715
+ children: /* @__PURE__ */ jsxs5(
565
716
  "div",
566
717
  {
567
718
  className: "m-0 flex max-h-[min(520px,calc(100vh-48px))] w-[min(760px,calc(100vw-48px))] flex-col overflow-hidden rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--popover))] text-left text-[13px] leading-6 text-[hsl(var(--popover-foreground))] shadow-xl",
568
719
  "aria-label": "\u6298\u53E0\u7684\u5185\u5BB9",
569
720
  children: [
570
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-3 border-b border-[hsl(var(--border))] px-4 py-2", children: [
571
- /* @__PURE__ */ jsx5("span", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u6298\u53E0\u7684\u5185\u5BB9" }),
572
- /* @__PURE__ */ jsx5(
721
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between gap-3 border-b border-[hsl(var(--border))] px-4 py-2", children: [
722
+ /* @__PURE__ */ jsx6("span", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u6298\u53E0\u7684\u5185\u5BB9" }),
723
+ /* @__PURE__ */ jsx6(
573
724
  "button",
574
725
  {
575
726
  type: "button",
576
727
  onClick: () => setOpen(false),
577
728
  className: "inline-flex h-6 w-6 items-center justify-center rounded-md text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))] focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
578
729
  "aria-label": "\u5173\u95ED\u6298\u53E0\u7684\u5185\u5BB9",
579
- children: /* @__PURE__ */ jsx5(X2, { size: 14, "aria-hidden": "true" })
730
+ children: /* @__PURE__ */ jsx6(X2, { size: 14, "aria-hidden": "true" })
580
731
  }
581
732
  )
582
733
  ] }),
583
- /* @__PURE__ */ jsx5("pre", { className: "m-0 overflow-auto px-4 py-3 font-mono text-[12px] leading-5 whitespace-pre-wrap text-[hsl(var(--popover-foreground))]", children: rawText || children })
734
+ /* @__PURE__ */ jsx6("pre", { className: "m-0 overflow-auto px-4 py-3 font-mono text-[12px] leading-5 whitespace-pre-wrap text-[hsl(var(--popover-foreground))]", children: rawText || children })
584
735
  ]
585
736
  }
586
737
  )
@@ -610,19 +761,19 @@ function resolveCollapsedContent({
610
761
  children
611
762
  }) {
612
763
  const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
613
- const key = dataKeyAttribute ?? dataKey ?? stringValue(nodeProperties?.["data-key"]) ?? stringValue(nodeProperties?.dataKey) ?? extractText(children).trim();
764
+ const key = dataKeyAttribute ?? dataKey ?? stringValue2(nodeProperties?.["data-key"]) ?? stringValue2(nodeProperties?.dataKey) ?? extractText(children).trim();
614
765
  const cached = key ? malformedToolCallContent.get(key) : void 0;
615
766
  if (cached) return cached;
616
- const raw = dataRawAttribute ?? dataRaw ?? stringValue(nodeProperties?.["data-raw"]) ?? stringValue(nodeProperties?.dataRaw) ?? "";
767
+ const raw = dataRawAttribute ?? dataRaw ?? stringValue2(nodeProperties?.["data-raw"]) ?? stringValue2(nodeProperties?.dataRaw) ?? "";
617
768
  return decodeCollapsedContent(raw) || extractText(children).trim();
618
769
  }
619
- function stringValue(value) {
770
+ function stringValue2(value) {
620
771
  return typeof value === "string" ? value : void 0;
621
772
  }
622
773
  function CodeBlockPre({ children, node: _node, ...props }) {
623
774
  const preRef = useRef(null);
624
- const [copied, setCopied] = useState3(false);
625
- const [hasCodeNode, setHasCodeNode] = useState3(false);
775
+ const [copied, setCopied] = useState4(false);
776
+ const [hasCodeNode, setHasCodeNode] = useState4(false);
626
777
  useEffect3(() => {
627
778
  setHasCodeNode(!!preRef.current?.querySelector("code"));
628
779
  }, []);
@@ -646,10 +797,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
646
797
  a.click();
647
798
  URL.revokeObjectURL(url);
648
799
  };
649
- return /* @__PURE__ */ jsxs4("div", { className: "relative group", children: [
650
- /* @__PURE__ */ jsx5("pre", { ref: preRef, ...props, children }),
651
- hasCodeNode && /* @__PURE__ */ jsxs4("div", { className: "absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
652
- /* @__PURE__ */ jsxs4(
800
+ return /* @__PURE__ */ jsxs5("div", { className: "relative group", children: [
801
+ /* @__PURE__ */ jsx6("pre", { ref: preRef, ...props, children }),
802
+ hasCodeNode && /* @__PURE__ */ jsxs5("div", { className: "absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
803
+ /* @__PURE__ */ jsxs5(
653
804
  "button",
654
805
  {
655
806
  type: "button",
@@ -659,20 +810,20 @@ function CodeBlockPre({ children, node: _node, ...props }) {
659
810
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] hover:bg-[hsl(var(--accent))]"
660
811
  ),
661
812
  children: [
662
- copied ? /* @__PURE__ */ jsx5(Check2, { size: 12 }) : /* @__PURE__ */ jsx5(Copy2, { size: 12 }),
663
- /* @__PURE__ */ jsx5("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
813
+ copied ? /* @__PURE__ */ jsx6(Check2, { size: 12 }) : /* @__PURE__ */ jsx6(Copy2, { size: 12 }),
814
+ /* @__PURE__ */ jsx6("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
664
815
  ]
665
816
  }
666
817
  ),
667
- /* @__PURE__ */ jsxs4(
818
+ /* @__PURE__ */ jsxs5(
668
819
  "button",
669
820
  {
670
821
  type: "button",
671
822
  onClick: handleDownload,
672
823
  className: "flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] transition-colors text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] hover:bg-[hsl(var(--accent))]",
673
824
  children: [
674
- /* @__PURE__ */ jsx5(Download, { size: 12 }),
675
- /* @__PURE__ */ jsx5("span", { children: "\u4E0B\u8F7D" })
825
+ /* @__PURE__ */ jsx6(Download2, { size: 12 }),
826
+ /* @__PURE__ */ jsx6("span", { children: "\u4E0B\u8F7D" })
676
827
  ]
677
828
  }
678
829
  )
@@ -694,6 +845,7 @@ function decodeMarkdownPath(src) {
694
845
  }
695
846
  }
696
847
  var MARKDOWN_IMAGE_PATTERN = /!\[((?:\\.|[^\]\\])*)\]\(\s*(?:<([^>\n]+)>|([^\s)]+))(\s+(?:"[^"]*"|'[^']*'|\([^)]*\)))?\s*\)/g;
848
+ var MEDIA_LINE_RE = /^[ \t]*[`"']?MEDIA:[ \t]*(\S+?)[`"']?[ \t]*$/gm;
697
849
  function resolveSessionImageMarkdown(children, sessionId) {
698
850
  return children.replace(
699
851
  MARKDOWN_IMAGE_PATTERN,
@@ -709,6 +861,40 @@ function resolveSessionImageMarkdown(children, sessionId) {
709
861
  }
710
862
  );
711
863
  }
864
+ function stripWorkspacePrefix(path, sessionId) {
865
+ const sessionRoot = `/root/${AGENT_WORKSPACE_DIR_NAME}/${sessionId}`;
866
+ if (path === sessionRoot || path === `${sessionRoot}/`) {
867
+ return null;
868
+ }
869
+ let relPath;
870
+ if (path.startsWith(`${sessionRoot}/`)) {
871
+ relPath = path.slice(sessionRoot.length + 1);
872
+ } else if (!path.startsWith("/")) {
873
+ relPath = path.replace(/^\.\//, "");
874
+ } else {
875
+ return null;
876
+ }
877
+ if (!relPath || relPath.startsWith("../") || relPath.includes("/../")) {
878
+ return null;
879
+ }
880
+ return relPath;
881
+ }
882
+ function basename2(path) {
883
+ return path.split("/").filter(Boolean).pop() || path;
884
+ }
885
+ function escapeAttribute(value) {
886
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
887
+ }
888
+ function collapseMediaTags(children, sessionId) {
889
+ return children.replace(MEDIA_LINE_RE, (match, absPath) => {
890
+ const relPath = stripWorkspacePrefix(absPath, sessionId);
891
+ if (!relPath) {
892
+ return match;
893
+ }
894
+ const name = basename2(relPath);
895
+ return `<${FILE_CARD_TAG} data-rel-path="${encodeURIComponent(relPath)}" data-name="${escapeAttribute(name)}">${encodeURIComponent(relPath)}</${FILE_CARD_TAG}>`;
896
+ });
897
+ }
712
898
  function collapseInlineToolCalls(children) {
713
899
  return children.replace(
714
900
  TOOL_CALL_BLOCK_RE,
@@ -747,7 +933,8 @@ function MarkdownContent({
747
933
  () => {
748
934
  if (typeof children !== "string") return children;
749
935
  const withResolvedImages = sessionId ? resolveSessionImageMarkdown(children, sessionId) : children;
750
- return collapseInlineToolCalls(withResolvedImages);
936
+ const withMediaCards = sessionId ? collapseMediaTags(withResolvedImages, sessionId) : withResolvedImages;
937
+ return collapseInlineToolCalls(withMediaCards);
751
938
  },
752
939
  [children, sessionId]
753
940
  );
@@ -755,7 +942,8 @@ function MarkdownContent({
755
942
  () => ({
756
943
  ...allowedTags ?? {},
757
944
  ...HIDDEN_SYSTEM_REMINDER_TAGS,
758
- [TOOL_CALL_TAG]: ["data-key", "data-raw"]
945
+ [TOOL_CALL_TAG]: ["data-key", "data-raw"],
946
+ [FILE_CARD_TAG]: ["data-rel-path", "data-name"]
759
947
  }),
760
948
  [allowedTags]
761
949
  );
@@ -765,7 +953,8 @@ function MarkdownContent({
765
953
  pre: CodeBlockPre,
766
954
  ...components ?? {},
767
955
  ...HIDDEN_SYSTEM_REMINDER_COMPONENTS,
768
- [TOOL_CALL_TAG]: InlineToolCallBlock
956
+ [TOOL_CALL_TAG]: InlineToolCallBlock,
957
+ [FILE_CARD_TAG]: FileCardComponent
769
958
  }),
770
959
  [components]
771
960
  );
@@ -776,7 +965,7 @@ function MarkdownContent({
776
965
  }),
777
966
  [plugins]
778
967
  );
779
- return /* @__PURE__ */ jsx5(
968
+ return /* @__PURE__ */ jsx6(
780
969
  Streamdown,
781
970
  {
782
971
  ...props,
@@ -791,9 +980,9 @@ function MarkdownContent({
791
980
  }
792
981
 
793
982
  // src/react/components/chat/AskUserQuestionBlock.tsx
794
- import { Check as Check3, Loader2, MessageSquareMore } from "lucide-react";
795
- import { useEffect as useEffect4, useMemo as useMemo3, useState as useState4 } from "react";
796
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
983
+ import { Check as Check3, Loader2 as Loader22, MessageSquareMore } from "lucide-react";
984
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState5 } from "react";
985
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
797
986
  function AskUserQuestionBlock({
798
987
  data,
799
988
  answered,
@@ -802,10 +991,10 @@ function AskUserQuestionBlock({
802
991
  answerData,
803
992
  onAnswer
804
993
  }) {
805
- const [selections, setSelections] = useState4(/* @__PURE__ */ new Map());
806
- const [customTexts, setCustomTexts] = useState4(/* @__PURE__ */ new Map());
807
- const [usingCustom, setUsingCustom] = useState4(/* @__PURE__ */ new Set());
808
- const [submitted, setSubmitted] = useState4(false);
994
+ const [selections, setSelections] = useState5(/* @__PURE__ */ new Map());
995
+ const [customTexts, setCustomTexts] = useState5(/* @__PURE__ */ new Map());
996
+ const [usingCustom, setUsingCustom] = useState5(/* @__PURE__ */ new Set());
997
+ const [submitted, setSubmitted] = useState5(false);
809
998
  useEffect4(() => {
810
999
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
811
1000
  setSubmitted(false);
@@ -903,7 +1092,7 @@ ${parts.join("\n")}`;
903
1092
  setSubmitted(true);
904
1093
  onAnswer(text, toolCallId, nextAnswerData);
905
1094
  };
906
- return /* @__PURE__ */ jsxs5(
1095
+ return /* @__PURE__ */ jsxs6(
907
1096
  "div",
908
1097
  {
909
1098
  className: cn(
@@ -911,12 +1100,12 @@ ${parts.join("\n")}`;
911
1100
  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"
912
1101
  ),
913
1102
  children: [
914
- data.source_loop?.description && /* @__PURE__ */ jsxs5("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1103
+ data.source_loop?.description && /* @__PURE__ */ jsxs6("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
915
1104
  "\u5B50\u667A\u80FD\u4F53\u300C",
916
1105
  data.source_loop.description,
917
1106
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
918
1107
  ] }),
919
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx6(
1108
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx7(
920
1109
  QuestionCard,
921
1110
  {
922
1111
  question: q,
@@ -931,7 +1120,7 @@ ${parts.join("\n")}`;
931
1120
  },
932
1121
  q.question
933
1122
  )),
934
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx6(
1123
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx7(
935
1124
  "button",
936
1125
  {
937
1126
  type: "button",
@@ -941,14 +1130,14 @@ ${parts.join("\n")}`;
941
1130
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
942
1131
  }
943
1132
  ),
944
- submitted && !answered && /* @__PURE__ */ jsxs5(
1133
+ submitted && !answered && /* @__PURE__ */ jsxs6(
945
1134
  "button",
946
1135
  {
947
1136
  type: "button",
948
1137
  disabled: true,
949
1138
  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",
950
1139
  children: [
951
- /* @__PURE__ */ jsx6(Loader2, { size: 14, className: "animate-spin" }),
1140
+ /* @__PURE__ */ jsx7(Loader22, { size: 14, className: "animate-spin" }),
952
1141
  "\u786E\u8BA4\u4E2D"
953
1142
  ]
954
1143
  }
@@ -969,23 +1158,23 @@ function QuestionCard({
969
1158
  onCustomChange
970
1159
  }) {
971
1160
  const multi = question.multiSelect ?? false;
972
- return /* @__PURE__ */ jsxs5("div", { children: [
973
- /* @__PURE__ */ jsxs5("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
974
- /* @__PURE__ */ jsx6(
1161
+ return /* @__PURE__ */ jsxs6("div", { children: [
1162
+ /* @__PURE__ */ jsxs6("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
1163
+ /* @__PURE__ */ jsx7(
975
1164
  MessageSquareMore,
976
1165
  {
977
1166
  size: answered ? 12 : 13,
978
1167
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
979
1168
  }
980
1169
  ),
981
- /* @__PURE__ */ jsx6(
1170
+ /* @__PURE__ */ jsx7(
982
1171
  "div",
983
1172
  {
984
1173
  className: cn(
985
1174
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
986
1175
  answered ? "text-xs" : "text-sm"
987
1176
  ),
988
- children: /* @__PURE__ */ jsx6(
1177
+ children: /* @__PURE__ */ jsx7(
989
1178
  MarkdownContent,
990
1179
  {
991
1180
  className: cn(
@@ -999,10 +1188,10 @@ function QuestionCard({
999
1188
  }
1000
1189
  )
1001
1190
  ] }),
1002
- /* @__PURE__ */ jsxs5("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
1191
+ /* @__PURE__ */ jsxs6("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
1003
1192
  question.options.map((opt, optIdx) => {
1004
1193
  const isSel = selected.has(optIdx);
1005
- return /* @__PURE__ */ jsxs5(
1194
+ return /* @__PURE__ */ jsxs6(
1006
1195
  "button",
1007
1196
  {
1008
1197
  type: "button",
@@ -1016,14 +1205,14 @@ function QuestionCard({
1016
1205
  answered && "cursor-default opacity-70"
1017
1206
  ),
1018
1207
  children: [
1019
- multi && /* @__PURE__ */ jsx6(
1208
+ multi && /* @__PURE__ */ jsx7(
1020
1209
  "div",
1021
1210
  {
1022
1211
  className: cn(
1023
1212
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
1024
1213
  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))]"
1025
1214
  ),
1026
- children: isSel && /* @__PURE__ */ jsx6(
1215
+ children: isSel && /* @__PURE__ */ jsx7(
1027
1216
  Check3,
1028
1217
  {
1029
1218
  size: 9,
@@ -1032,9 +1221,9 @@ function QuestionCard({
1032
1221
  )
1033
1222
  }
1034
1223
  ),
1035
- /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1036
- /* @__PURE__ */ jsx6("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
1037
- opt.description && /* @__PURE__ */ jsx6(
1224
+ /* @__PURE__ */ jsxs6("div", { className: "min-w-0", children: [
1225
+ /* @__PURE__ */ jsx7("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
1226
+ opt.description && /* @__PURE__ */ jsx7(
1038
1227
  "div",
1039
1228
  {
1040
1229
  className: cn(
@@ -1051,7 +1240,7 @@ function QuestionCard({
1051
1240
  opt.label
1052
1241
  );
1053
1242
  }),
1054
- answered && !isCustom ? null : /* @__PURE__ */ jsxs5(
1243
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs6(
1055
1244
  "div",
1056
1245
  {
1057
1246
  className: cn(
@@ -1061,8 +1250,8 @@ function QuestionCard({
1061
1250
  answered && "cursor-default opacity-70"
1062
1251
  ),
1063
1252
  children: [
1064
- /* @__PURE__ */ jsx6("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
1065
- /* @__PURE__ */ jsx6(
1253
+ /* @__PURE__ */ jsx7("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
1254
+ /* @__PURE__ */ jsx7(
1066
1255
  "input",
1067
1256
  {
1068
1257
  type: "text",
@@ -1485,8 +1674,8 @@ function extractRefs(content) {
1485
1674
 
1486
1675
  // src/react/components/plan/PlanSummaryCard.tsx
1487
1676
  import { CheckCircle2, ChevronRight, PencilLine, Play, Sparkles } from "lucide-react";
1488
- import { useMemo as useMemo4, useState as useState5 } from "react";
1489
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1677
+ import { useMemo as useMemo4, useState as useState6 } from "react";
1678
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1490
1679
  function PlanSummaryCard({
1491
1680
  messages,
1492
1681
  sessionStatus,
@@ -1506,25 +1695,25 @@ function PlanSummaryCard({
1506
1695
  }
1507
1696
  setActiveRightTab("situation");
1508
1697
  };
1509
- return /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.55)] p-4", children: [
1510
- /* @__PURE__ */ jsxs6(
1698
+ return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.55)] p-4", children: [
1699
+ /* @__PURE__ */ jsxs7(
1511
1700
  "button",
1512
1701
  {
1513
1702
  type: "button",
1514
1703
  onClick: openPlanPanel,
1515
1704
  className: "flex w-full flex-col gap-4 text-left transition-colors hover:text-[hsl(var(--foreground))]",
1516
1705
  children: [
1517
- /* @__PURE__ */ jsxs6("div", { className: "flex items-start justify-between gap-3", children: [
1518
- /* @__PURE__ */ jsxs6("div", { className: "min-w-0 space-y-2", children: [
1519
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1520
- /* @__PURE__ */ jsx7(Sparkles, { size: 12 }),
1521
- /* @__PURE__ */ jsx7("span", { children: "\u89C4\u5212\u6458\u8981" })
1706
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start justify-between gap-3", children: [
1707
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0 space-y-2", children: [
1708
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1709
+ /* @__PURE__ */ jsx8(Sparkles, { size: 12 }),
1710
+ /* @__PURE__ */ jsx8("span", { children: "\u89C4\u5212\u6458\u8981" })
1522
1711
  ] }),
1523
- /* @__PURE__ */ jsx7("div", { className: "truncate text-base font-medium text-[hsl(var(--foreground))]", children: data.intent || "\u5F53\u524D\u89C4\u5212" }),
1524
- /* @__PURE__ */ jsx7("div", { className: "flex flex-wrap items-center gap-2 text-xs", children: summaryState.kind === "progress" ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
1525
- /* @__PURE__ */ jsx7("span", { className: "rounded-full bg-[hsl(var(--accent))] px-2.5 py-1 text-[hsl(var(--foreground))]", children: summaryState.label }),
1526
- /* @__PURE__ */ jsx7("span", { className: "font-mono tracking-[0.25em] text-[hsl(var(--muted-foreground))]", children: summaryState.dots })
1527
- ] }) : /* @__PURE__ */ jsx7(
1712
+ /* @__PURE__ */ jsx8("div", { className: "truncate text-base font-medium text-[hsl(var(--foreground))]", children: data.intent || "\u5F53\u524D\u89C4\u5212" }),
1713
+ /* @__PURE__ */ jsx8("div", { className: "flex flex-wrap items-center gap-2 text-xs", children: summaryState.kind === "progress" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1714
+ /* @__PURE__ */ jsx8("span", { className: "rounded-full bg-[hsl(var(--accent))] px-2.5 py-1 text-[hsl(var(--foreground))]", children: summaryState.label }),
1715
+ /* @__PURE__ */ jsx8("span", { className: "font-mono tracking-[0.25em] text-[hsl(var(--muted-foreground))]", children: summaryState.dots })
1716
+ ] }) : /* @__PURE__ */ jsx8(
1528
1717
  "span",
1529
1718
  {
1530
1719
  className: cn(
@@ -1537,7 +1726,7 @@ function PlanSummaryCard({
1537
1726
  }
1538
1727
  ) })
1539
1728
  ] }),
1540
- /* @__PURE__ */ jsx7(
1729
+ /* @__PURE__ */ jsx8(
1541
1730
  ChevronRight,
1542
1731
  {
1543
1732
  size: 16,
@@ -1546,31 +1735,31 @@ function PlanSummaryCard({
1546
1735
  }
1547
1736
  )
1548
1737
  ] }),
1549
- summaryState.kind === "complete" && /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1550
- /* @__PURE__ */ jsxs6("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
1738
+ summaryState.kind === "complete" && /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1739
+ /* @__PURE__ */ jsxs7("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
1551
1740
  data.selectedSkills.length,
1552
1741
  " \u4E2A\u6280\u80FD"
1553
1742
  ] }),
1554
- /* @__PURE__ */ jsxs6("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
1743
+ /* @__PURE__ */ jsxs7("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
1555
1744
  stepCount,
1556
1745
  " \u4E2A\u6B65\u9AA4"
1557
1746
  ] }),
1558
- /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))]", children: [
1559
- /* @__PURE__ */ jsx7(CheckCircle2, { size: 12 }),
1747
+ /* @__PURE__ */ jsxs7("span", { className: "inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))]", children: [
1748
+ /* @__PURE__ */ jsx8(CheckCircle2, { size: 12 }),
1560
1749
  "\u53F3\u4FA7\u67E5\u770B\u8BE6\u60C5"
1561
1750
  ] })
1562
1751
  ] })
1563
1752
  ]
1564
1753
  }
1565
1754
  ),
1566
- canConfirm && /* @__PURE__ */ jsx7(PlanConfirmationPanel, { onConfirmPlan })
1755
+ canConfirm && /* @__PURE__ */ jsx8(PlanConfirmationPanel, { onConfirmPlan })
1567
1756
  ] });
1568
1757
  }
1569
1758
  function PlanConfirmationPanel({
1570
1759
  onConfirmPlan
1571
1760
  }) {
1572
- const [isEditing, setIsEditing] = useState5(false);
1573
- const [revisionText, setRevisionText] = useState5("");
1761
+ const [isEditing, setIsEditing] = useState6(false);
1762
+ const [revisionText, setRevisionText] = useState6("");
1574
1763
  const submitRevision = () => {
1575
1764
  const trimmed = revisionText.trim();
1576
1765
  if (!trimmed) return;
@@ -1578,35 +1767,35 @@ function PlanConfirmationPanel({
1578
1767
  setRevisionText("");
1579
1768
  setIsEditing(false);
1580
1769
  };
1581
- return /* @__PURE__ */ jsxs6("div", { className: "space-y-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background)/0.45)] p-3", children: [
1582
- /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap gap-2", children: [
1583
- /* @__PURE__ */ jsxs6(
1770
+ return /* @__PURE__ */ jsxs7("div", { className: "space-y-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background)/0.45)] p-3", children: [
1771
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap gap-2", children: [
1772
+ /* @__PURE__ */ jsxs7(
1584
1773
  "button",
1585
1774
  {
1586
1775
  type: "button",
1587
1776
  onClick: () => onConfirmPlan?.("execute"),
1588
1777
  className: "inline-flex items-center gap-1.5 rounded-lg bg-[hsl(var(--primary))] px-3 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90",
1589
1778
  children: [
1590
- /* @__PURE__ */ jsx7(Play, { size: 14 }),
1779
+ /* @__PURE__ */ jsx8(Play, { size: 14 }),
1591
1780
  "\u5F00\u59CB\u6267\u884C"
1592
1781
  ]
1593
1782
  }
1594
1783
  ),
1595
- /* @__PURE__ */ jsxs6(
1784
+ /* @__PURE__ */ jsxs7(
1596
1785
  "button",
1597
1786
  {
1598
1787
  type: "button",
1599
1788
  onClick: () => setIsEditing((value) => !value),
1600
1789
  className: "inline-flex items-center gap-1.5 rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--accent))]",
1601
1790
  children: [
1602
- /* @__PURE__ */ jsx7(PencilLine, { size: 14 }),
1791
+ /* @__PURE__ */ jsx8(PencilLine, { size: 14 }),
1603
1792
  "\u4FEE\u6539"
1604
1793
  ]
1605
1794
  }
1606
1795
  )
1607
1796
  ] }),
1608
- isEditing && /* @__PURE__ */ jsxs6("div", { className: "space-y-2", children: [
1609
- /* @__PURE__ */ jsx7(
1797
+ isEditing && /* @__PURE__ */ jsxs7("div", { className: "space-y-2", children: [
1798
+ /* @__PURE__ */ jsx8(
1610
1799
  "textarea",
1611
1800
  {
1612
1801
  value: revisionText,
@@ -1615,7 +1804,7 @@ function PlanConfirmationPanel({
1615
1804
  className: "min-h-24 w-full resize-y rounded-lg border border-[hsl(var(--border))] bg-transparent px-3 py-2 text-sm text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground))]"
1616
1805
  }
1617
1806
  ),
1618
- /* @__PURE__ */ jsx7("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx7(
1807
+ /* @__PURE__ */ jsx8("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx8(
1619
1808
  "button",
1620
1809
  {
1621
1810
  type: "button",
@@ -1728,4 +1917,4 @@ export {
1728
1917
  PlanSummaryCard,
1729
1918
  extractLatestPlanMessages
1730
1919
  };
1731
- //# sourceMappingURL=chunk-ER4RLUJW.js.map
1920
+ //# sourceMappingURL=chunk-KW66QITV.js.map