@blade-hq/agent-kit 1.0.29 → 1.0.31

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 (44) hide show
  1. package/dist/{chunk-TVWC3PED.js → chunk-3EVO45IN.js} +2 -2
  2. package/dist/{chunk-UWNQ4PXK.js → chunk-3PX2W7II.js} +51 -12
  3. package/dist/chunk-3PX2W7II.js.map +1 -0
  4. package/dist/{chunk-YJ6BX3FU.js → chunk-DSC6KQOI.js} +45 -2
  5. package/dist/chunk-DSC6KQOI.js.map +1 -0
  6. package/dist/{chunk-PYYDXV2A.js → chunk-O32JOWYM.js} +2 -2
  7. package/dist/{chunk-N2OE5MVV.js → chunk-RAWQ7LWR.js} +973 -712
  8. package/dist/chunk-RAWQ7LWR.js.map +1 -0
  9. package/dist/{chunk-YXHPJGXR.js → chunk-XUWAM6MF.js} +173 -59
  10. package/dist/chunk-XUWAM6MF.js.map +1 -0
  11. package/dist/{chunk-HCQDDDOX.js → chunk-ZPZMN7KQ.js} +2 -2
  12. package/dist/client/index.js +1 -1
  13. package/dist/client/resources/sessions.d.ts +2 -0
  14. package/dist/client/types/rest.d.ts +639 -92
  15. package/dist/react/api/model-config.d.ts +8 -11
  16. package/dist/react/api/published-apps.js +3 -3
  17. package/dist/react/api/sessions.js +2 -2
  18. package/dist/react/components/chat/FileDeliveryCards.d.ts +11 -0
  19. package/dist/react/components/chat/PlanningToggle.d.ts +7 -0
  20. package/dist/react/components/chat/UserMessageBubble.d.ts +1 -1
  21. package/dist/react/components/chat/index.d.ts +1 -0
  22. package/dist/react/components/chat/index.js +7 -5
  23. package/dist/react/components/markdown/ExternalLinkDialog.d.ts +3 -1
  24. package/dist/react/components/plan/index.js +4 -4
  25. package/dist/react/components/session/index.js +3 -3
  26. package/dist/react/components/workspace/index.js +61 -42
  27. package/dist/react/components/workspace/index.js.map +1 -1
  28. package/dist/react/hooks/use-model-preferences.d.ts +2 -0
  29. package/dist/react/index.d.ts +2 -2
  30. package/dist/react/index.js +20 -8
  31. package/dist/react/index.js.map +1 -1
  32. package/dist/react/schemas/session.d.ts +4 -0
  33. package/dist/react/sockets/event-bridge.d.ts +6 -0
  34. package/dist/react/stores/ui-store.d.ts +6 -1
  35. package/dist/react/types/solution.d.ts +1 -0
  36. package/dist/style.css +1 -1
  37. package/package.json +1 -1
  38. package/dist/chunk-N2OE5MVV.js.map +0 -1
  39. package/dist/chunk-UWNQ4PXK.js.map +0 -1
  40. package/dist/chunk-YJ6BX3FU.js.map +0 -1
  41. package/dist/chunk-YXHPJGXR.js.map +0 -1
  42. /package/dist/{chunk-TVWC3PED.js.map → chunk-3EVO45IN.js.map} +0 -0
  43. /package/dist/{chunk-PYYDXV2A.js.map → chunk-O32JOWYM.js.map} +0 -0
  44. /package/dist/{chunk-HCQDDDOX.js.map → chunk-ZPZMN7KQ.js.map} +0 -0
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  getSessionFilePath,
3
3
  resolveSessionFilePreviewTarget
4
- } from "./chunk-PYYDXV2A.js";
4
+ } from "./chunk-O32JOWYM.js";
5
5
  import {
6
6
  CardJSON,
7
7
  cardRegistry
8
8
  } from "./chunk-2UP7MG3J.js";
9
9
  import {
10
+ apiFetch,
10
11
  formatToolName,
11
12
  getAuthedUrl,
12
13
  useUiStore
13
- } from "./chunk-UWNQ4PXK.js";
14
+ } from "./chunk-3PX2W7II.js";
14
15
  import {
15
16
  cn,
16
17
  copyToClipboard
@@ -376,6 +377,7 @@ function CardCodeBlock({ className, children, node, ...props }) {
376
377
 
377
378
  // src/react/components/markdown/MarkdownContent.tsx
378
379
  import { mermaid } from "@streamdown/mermaid";
380
+ import { useQuery } from "@tanstack/react-query";
379
381
  import { Check as Check2, Copy as Copy2, Download as Download2, X as X2 } from "lucide-react";
380
382
  import {
381
383
  useEffect as useEffect3,
@@ -387,11 +389,11 @@ import { createPortal as createPortal2 } from "react-dom";
387
389
  import { Streamdown, defaultRehypePlugins } from "streamdown";
388
390
 
389
391
  // src/react/components/markdown/ExternalLinkDialog.tsx
390
- import { Check, Copy, ExternalLink, X } from "lucide-react";
392
+ import { Check, Copy, ExternalLink, Monitor, X } from "lucide-react";
391
393
  import { useEffect as useEffect2, useState as useState2 } from "react";
392
394
  import { createPortal } from "react-dom";
393
395
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
394
- function ExternalLinkDialog({ isOpen, onClose, onConfirm, url }) {
396
+ function ExternalLinkDialog({ isOpen, onClose, onConfirm, url, onOpenInternal }) {
395
397
  const [copied, setCopied] = useState2(false);
396
398
  useEffect2(() => {
397
399
  if (!isOpen) return;
@@ -471,6 +473,21 @@ function ExternalLinkDialog({ isOpen, onClose, onConfirm, url }) {
471
473
  ]
472
474
  }
473
475
  ),
476
+ onOpenInternal && /* @__PURE__ */ jsxs3(
477
+ "button",
478
+ {
479
+ type: "button",
480
+ onClick: () => {
481
+ void onOpenInternal();
482
+ onClose();
483
+ },
484
+ className: "inline-flex items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--accent))]",
485
+ children: [
486
+ /* @__PURE__ */ jsx4(Monitor, { size: 14 }),
487
+ /* @__PURE__ */ jsx4("span", { children: "\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00" })
488
+ ]
489
+ }
490
+ ),
474
491
  /* @__PURE__ */ jsxs3(
475
492
  "button",
476
493
  {
@@ -647,33 +664,80 @@ var LINK_SAFETY_CONFIG = {
647
664
  enabled: true,
648
665
  renderModal: (props) => /* @__PURE__ */ jsx6(ExternalLinkDialog, { ...props })
649
666
  };
650
- var hardenFn = Array.isArray(defaultRehypePlugins.harden) ? defaultRehypePlugins.harden[0] : defaultRehypePlugins.harden;
651
- var REHYPE_PLUGINS = Object.values({
652
- ...defaultRehypePlugins,
653
- harden: [
654
- hardenFn,
655
- {
656
- allowedImagePrefixes: ["*"],
657
- allowedLinkPrefixes: ["*"],
658
- allowedProtocols: ["*"],
659
- defaultOrigin: "http://localhost",
660
- allowDataImages: true
661
- }
662
- ]
663
- });
667
+ async function openInSandboxBrowser(sessionId, url) {
668
+ try {
669
+ await apiFetch(`/api/sessions/${encodeURIComponent(sessionId)}/browser/tabs`, {
670
+ method: "POST",
671
+ headers: { "Content-Type": "application/json" },
672
+ body: JSON.stringify({ url })
673
+ });
674
+ const uiStore = useUiStore.getState();
675
+ uiStore.clearRightPanelTabUnread("browser");
676
+ uiStore.setActiveRightTab("browser");
677
+ } catch (error) {
678
+ console.warn("open in sandbox browser failed", error);
679
+ }
680
+ }
664
681
  var STREAMDOWN_PLUGINS = { mermaid };
665
682
  var SYSTEM_REMINDER_TAG = "system-reminder";
666
683
  var TOOL_CALL_TAG = "blade-malformed-tool-call";
667
684
  var FILE_CARD_TAG = "blade-file-card";
668
685
  var AGENT_WORKSPACE_DIR_NAME = "\u667A\u80FD\u52A9\u624B\u5DE5\u4F5C\u7A7A\u95F4";
669
- var HIDDEN_SYSTEM_REMINDER_TAGS = {
670
- [SYSTEM_REMINDER_TAG]: []
686
+ var CUSTOM_ALLOWED_TAGS = {
687
+ [SYSTEM_REMINDER_TAG]: [],
688
+ [TOOL_CALL_TAG]: ["data-key", "data-raw"],
689
+ [FILE_CARD_TAG]: ["data-rel-path", "data-name"]
671
690
  };
672
691
  var HIDDEN_SYSTEM_REMINDER_COMPONENTS = {
673
692
  [SYSTEM_REMINDER_TAG]: () => null
674
693
  };
675
694
  var TOOL_CALL_BLOCK_RE = /<tool_call>\s*[\s\S]*?\s*<\/tool_call>/gi;
676
695
  var malformedToolCallContent = /* @__PURE__ */ new Map();
696
+ var sanitizePlugin = defaultRehypePlugins.sanitize;
697
+ var sanitizeFn = Array.isArray(sanitizePlugin) ? sanitizePlugin[0] : sanitizePlugin;
698
+ var sanitizeSchema = Array.isArray(sanitizePlugin) ? sanitizePlugin[1] : {};
699
+ var hardenFn = Array.isArray(defaultRehypePlugins.harden) ? defaultRehypePlugins.harden[0] : defaultRehypePlugins.harden;
700
+ function buildSanitizeAttributes(allowedTags) {
701
+ return Object.fromEntries(
702
+ Object.entries(allowedTags).map(([tag, attributes]) => [
703
+ tag,
704
+ [
705
+ ...new Set(
706
+ attributes.flatMap((attribute) => [
707
+ attribute,
708
+ attribute.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
709
+ ])
710
+ )
711
+ ]
712
+ ])
713
+ );
714
+ }
715
+ function buildRehypePlugins(allowedTags) {
716
+ return Object.values({
717
+ ...defaultRehypePlugins,
718
+ sanitize: [
719
+ sanitizeFn,
720
+ {
721
+ ...sanitizeSchema,
722
+ tagNames: [...sanitizeSchema.tagNames ?? [], ...Object.keys(allowedTags)],
723
+ attributes: {
724
+ ...sanitizeSchema.attributes ?? {},
725
+ ...buildSanitizeAttributes(allowedTags)
726
+ }
727
+ }
728
+ ],
729
+ harden: [
730
+ hardenFn,
731
+ {
732
+ allowedImagePrefixes: ["*"],
733
+ allowedLinkPrefixes: ["*"],
734
+ allowedProtocols: ["*"],
735
+ defaultOrigin: "http://localhost",
736
+ allowDataImages: true
737
+ }
738
+ ]
739
+ });
740
+ }
677
741
  function InlineToolCallBlock({
678
742
  children,
679
743
  node,
@@ -788,8 +852,11 @@ function CodeBlockPre({ children, node: _node, ...props }) {
788
852
  const preRef = useRef(null);
789
853
  const [copied, setCopied] = useState4(false);
790
854
  const [hasCodeNode, setHasCodeNode] = useState4(false);
855
+ const [language, setLanguage] = useState4("");
791
856
  useEffect3(() => {
792
- setHasCodeNode(!!preRef.current?.querySelector("code"));
857
+ const codeEl = preRef.current?.querySelector("code");
858
+ setHasCodeNode(!!codeEl);
859
+ setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
793
860
  }, []);
794
861
  const getRawCode = () => preRef.current?.querySelector("code")?.textContent ?? "";
795
862
  const handleCopy = async () => {
@@ -811,37 +878,55 @@ function CodeBlockPre({ children, node: _node, ...props }) {
811
878
  a.click();
812
879
  URL.revokeObjectURL(url);
813
880
  };
814
- return /* @__PURE__ */ jsxs5("div", { className: "relative group", children: [
815
- /* @__PURE__ */ jsx6("pre", { ref: preRef, ...props, children }),
816
- hasCodeNode && /* @__PURE__ */ jsxs5("div", { className: "absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
817
- /* @__PURE__ */ jsxs5(
818
- "button",
819
- {
820
- type: "button",
821
- onClick: handleCopy,
822
- className: cn(
823
- "flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] transition-colors",
824
- copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] hover:bg-[hsl(var(--accent))]"
825
- ),
826
- children: [
827
- copied ? /* @__PURE__ */ jsx6(Check2, { size: 12 }) : /* @__PURE__ */ jsx6(Copy2, { size: 12 }),
828
- /* @__PURE__ */ jsx6("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
829
- ]
830
- }
831
- ),
832
- /* @__PURE__ */ jsxs5(
833
- "button",
834
- {
835
- type: "button",
836
- onClick: handleDownload,
837
- 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))]",
838
- children: [
839
- /* @__PURE__ */ jsx6(Download2, { size: 12 }),
840
- /* @__PURE__ */ jsx6("span", { children: "\u4E0B\u8F7D" })
841
- ]
842
- }
843
- )
844
- ] })
881
+ if (!hasCodeNode) {
882
+ return /* @__PURE__ */ jsx6("div", { className: "relative", children: /* @__PURE__ */ jsx6("pre", { ref: preRef, ...props, children }) });
883
+ }
884
+ return /* @__PURE__ */ jsxs5("div", { className: "not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
885
+ /* @__PURE__ */ jsxs5("div", { className: "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: [
886
+ /* @__PURE__ */ jsx6("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
887
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-0.5", children: [
888
+ /* @__PURE__ */ jsxs5(
889
+ "button",
890
+ {
891
+ type: "button",
892
+ onClick: handleCopy,
893
+ className: cn(
894
+ "flex h-6 items-center gap-1 rounded-md px-1.5 text-[11px] transition-colors",
895
+ copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
896
+ ),
897
+ children: [
898
+ copied ? /* @__PURE__ */ jsx6(Check2, { size: 12 }) : /* @__PURE__ */ jsx6(Copy2, { size: 12 }),
899
+ /* @__PURE__ */ jsx6("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
900
+ ]
901
+ }
902
+ ),
903
+ /* @__PURE__ */ jsxs5(
904
+ "button",
905
+ {
906
+ type: "button",
907
+ onClick: handleDownload,
908
+ className: "flex h-6 items-center gap-1 rounded-md px-1.5 text-[11px] text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
909
+ children: [
910
+ /* @__PURE__ */ jsx6(Download2, { size: 12 }),
911
+ /* @__PURE__ */ jsx6("span", { children: "\u4E0B\u8F7D" })
912
+ ]
913
+ }
914
+ )
915
+ ] })
916
+ ] }),
917
+ /* @__PURE__ */ jsx6(
918
+ "pre",
919
+ {
920
+ ref: preRef,
921
+ ...props,
922
+ className: cn(
923
+ props.className,
924
+ // 原型:13.5px/1.7 等宽、浅底;覆盖 prose 的继承字号与主题里手写的 pre 样式。
925
+ "m-0! overflow-x-auto! rounded-none! border-0! bg-[hsl(var(--muted))/0.25]! px-4! py-3.5! font-mono text-[13.5px]! leading-[1.7]! [&_code]:bg-transparent! [&_code]:p-0! [&_code]:font-mono [&_code]:text-[13.5px]! [&_code]:text-[hsl(var(--foreground))]"
926
+ ),
927
+ children
928
+ }
929
+ )
845
930
  ] });
846
931
  }
847
932
  function isExternalImageSrc(src) {
@@ -906,7 +991,7 @@ function collapseMediaTags(children, sessionId) {
906
991
  return match;
907
992
  }
908
993
  const name = basename2(relPath);
909
- return `<${FILE_CARD_TAG} data-rel-path="${encodeURIComponent(relPath)}" data-name="${escapeAttribute(name)}">${encodeURIComponent(relPath)}</${FILE_CARD_TAG}>`;
994
+ return `<${FILE_CARD_TAG} data-rel-path="${encodeURIComponent(relPath)}" data-name="${escapeAttribute(name)}">${escapeAttribute(name)}</${FILE_CARD_TAG}>`;
910
995
  });
911
996
  }
912
997
  function collapseInlineToolCalls(children) {
@@ -955,9 +1040,7 @@ function MarkdownContent({
955
1040
  const resolvedAllowedTags = useMemo2(
956
1041
  () => ({
957
1042
  ...allowedTags ?? {},
958
- ...HIDDEN_SYSTEM_REMINDER_TAGS,
959
- [TOOL_CALL_TAG]: ["data-key", "data-raw"],
960
- [FILE_CARD_TAG]: ["data-rel-path", "data-name"]
1043
+ ...CUSTOM_ALLOWED_TAGS
961
1044
  }),
962
1045
  [allowedTags]
963
1046
  );
@@ -972,6 +1055,10 @@ function MarkdownContent({
972
1055
  }),
973
1056
  [components]
974
1057
  );
1058
+ const resolvedRehypePlugins = useMemo2(
1059
+ () => buildRehypePlugins(resolvedAllowedTags),
1060
+ [resolvedAllowedTags]
1061
+ );
975
1062
  const resolvedPlugins = useMemo2(
976
1063
  () => ({
977
1064
  ...STREAMDOWN_PLUGINS,
@@ -979,14 +1066,41 @@ function MarkdownContent({
979
1066
  }),
980
1067
  [plugins]
981
1068
  );
1069
+ const { data: hasBrowserMirror = false } = useQuery({
1070
+ queryKey: ["browser-mirror-ready", sessionId],
1071
+ queryFn: async () => {
1072
+ const resp = await apiFetch(`/api/sessions/${sessionId}/browser/health`, {
1073
+ method: "GET"
1074
+ }).then(
1075
+ () => true,
1076
+ () => false
1077
+ );
1078
+ return resp;
1079
+ },
1080
+ enabled: Boolean(sessionId),
1081
+ staleTime: 6e4
1082
+ });
1083
+ const linkSafety = useMemo2(
1084
+ () => sessionId && hasBrowserMirror ? {
1085
+ enabled: true,
1086
+ renderModal: (modalProps) => /* @__PURE__ */ jsx6(
1087
+ ExternalLinkDialog,
1088
+ {
1089
+ ...modalProps,
1090
+ onOpenInternal: () => openInSandboxBrowser(sessionId, modalProps.url)
1091
+ }
1092
+ )
1093
+ } : LINK_SAFETY_CONFIG,
1094
+ [sessionId, hasBrowserMirror]
1095
+ );
982
1096
  return /* @__PURE__ */ jsx6(
983
1097
  Streamdown,
984
1098
  {
985
1099
  ...props,
986
1100
  className: cn("break-words [&_button[data-streamdown=link]]:max-w-full", props.className),
987
- linkSafety: LINK_SAFETY_CONFIG,
1101
+ linkSafety,
988
1102
  mode: mode ?? "static",
989
- rehypePlugins: REHYPE_PLUGINS,
1103
+ rehypePlugins: resolvedRehypePlugins,
990
1104
  allowedTags: resolvedAllowedTags,
991
1105
  components: resolvedComponents,
992
1106
  plugins: resolvedPlugins,
@@ -1951,4 +2065,4 @@ export {
1951
2065
  PlanSummaryCard,
1952
2066
  extractLatestPlanMessages
1953
2067
  };
1954
- //# sourceMappingURL=chunk-YXHPJGXR.js.map
2068
+ //# sourceMappingURL=chunk-XUWAM6MF.js.map