@workerdeck/ui 2.15.0 → 3.0.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.
Files changed (45) hide show
  1. package/README.md +6 -5
  2. package/build/{SessionPanel-BVKqPs_3.d.mts → SessionPanel-C1bU4yTM.d.mts} +11 -11
  3. package/build/index.d.mts +46 -9
  4. package/build/index.mjs +70 -43
  5. package/build/index.mjs.map +1 -1
  6. package/build/scoped.css +156 -16
  7. package/build/{use-path-links-BgWXmfAS.mjs → use-path-links-BEWxt0ZM.mjs} +1566 -852
  8. package/build/use-path-links-BEWxt0ZM.mjs.map +1 -0
  9. package/build/workspace.d.mts +3 -3
  10. package/build/workspace.mjs +3 -3
  11. package/build/workspace.mjs.map +1 -1
  12. package/package.json +4 -4
  13. package/src/components/agent/PermissionPrompt.tsx +16 -2
  14. package/src/components/agent/Response.tsx +8 -1
  15. package/src/components/agent/SessionBrowser.tsx +6 -0
  16. package/src/components/agent/SessionItem.tsx +3 -0
  17. package/src/components/agent/SessionPanel.tsx +28 -33
  18. package/src/components/agent/SessionSteps.tsx +46 -5
  19. package/src/components/agent/SessionWorkspace.tsx +3 -3
  20. package/src/components/agent/ShellStrip.tsx +27 -4
  21. package/src/components/agent/ShellTerminal.tsx +1 -1
  22. package/src/components/agent/ToolCallCard.tsx +18 -41
  23. package/src/components/agent/Transcript.tsx +98 -93
  24. package/src/components/agent/TranscriptItemView.tsx +102 -59
  25. package/src/components/agent/TranscriptRows.tsx +4 -2
  26. package/src/components/agent/image-viewer.tsx +326 -0
  27. package/src/components/agent/markdown-image.tsx +91 -0
  28. package/src/components/agent/shell-actions.tsx +2 -0
  29. package/src/components/agent/tool-result-image.tsx +40 -0
  30. package/src/components/agent/transcript-variant.tsx +3 -21
  31. package/src/components/prompt-area/use-prompt-area-keydown.ts +4 -2
  32. package/src/components/terminal/PermissionPrompt.tsx +20 -4
  33. package/src/components/terminal/TerminalTranscript.tsx +38 -21
  34. package/src/components/terminal/affordances.tsx +100 -74
  35. package/src/components/terminal/height.ts +48 -32
  36. package/src/components/terminal/image-box.ts +31 -0
  37. package/src/components/terminal/items.tsx +92 -14
  38. package/src/components/terminal/markdown.tsx +4 -1
  39. package/src/components/terminal/shell-row.ts +17 -3
  40. package/src/index.ts +19 -4
  41. package/src/lib/shell-request.ts +115 -0
  42. package/src/lib/tool-icon.ts +13 -1
  43. package/src/styles/terminal.css +129 -22
  44. package/src/styles/theme.css +4 -0
  45. package/build/use-path-links-BgWXmfAS.mjs.map +0 -1
@@ -5,13 +5,13 @@ import { clsx } from "clsx";
5
5
  import { extendTailwindMerge } from "tailwind-merge";
6
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Select } from "@base-ui/react/select";
8
- import { ArrowDown, ArrowDownCircle, ArrowLeft, ArrowRight, ArrowUp, AtSign, Brain, ChartPie, Check, CheckSquare, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, Circle, CircleAlert, ClipboardList, Clock, Code, Copy, Download, File, FileDiff, FileText, Folder, FolderSearch, FolderTree, Gauge, Globe, Hand, Image, Info, Link2, LoaderCircle, MessageCircleQuestion, MessageSquareText, MoreHorizontal, Paperclip, PencilLine, Plug, Power, PowerOff, Puzzle, RefreshCw, RotateCcw, RotateCw, Search, ShieldCheck, SlashSquare, SlidersHorizontal, Sparkles, Square, SquarePen, Terminal, TriangleAlert, Users, UsersRound, WifiOff, Wrench, X, Zap } from "lucide-react";
8
+ import { ArrowDown, ArrowDownCircle, ArrowLeft, ArrowRight, ArrowUp, AtSign, Bot, BotOff, Brain, ChartPie, Check, CheckSquare, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, Circle, CircleAlert, ClipboardList, Clock, Code, Copy, Download, File, FileDiff, FileText, Folder, FolderSearch, FolderTree, Gauge, Globe, Hand, Image, Info, Link2, LoaderCircle, Maximize, Maximize2, MessageCircleQuestion, MessageSquareText, Minus, MoreHorizontal, Paperclip, PencilLine, Plug, Plus, Power, PowerOff, Puzzle, RefreshCw, RotateCcw, RotateCw, Search, ShieldCheck, SlashSquare, SlidersHorizontal, Sparkles, Square, SquarePen, SquareTerminal, Star, Terminal, TriangleAlert, Users, UsersRound, WifiOff, Wrench, X, Zap } from "lucide-react";
9
9
  import { Menu } from "@base-ui/react/menu";
10
10
  import { Dialog } from "@base-ui/react/dialog";
11
11
  import { Tooltip } from "@base-ui/react/tooltip";
12
- import { PRICING_NOTE, PROTOCOL_VERSION, UNPRICED_WARN_SHARE, canonicalModel, costOf, costOfByModel, isPeerSendTool, mergeUsage, orderUsageWindows, parseTodoWriteInput, parseUserQuestions, peerDeliveredTo, pricingAgeNote, rateFor, sessionTasks, subagentLabel, taskCountLabel, taskSummary, toolTitle, totalTokens, unknownBasisModels, usageInfos, usageIsStale, visibleTasks } from "@workerdeck/protocol";
12
+ import { PRICING_NOTE, PROTOCOL_VERSION, SHELL_INTERACTIVE_NOTE, UNPRICED_WARN_SHARE, canonicalModel, costOf, costOfByModel, isPeerSendTool, mergeUsage, orderUsageWindows, parseTodoWriteInput, parseUserQuestions, peerDeliveredTo, pricingAgeNote, rateFor, sessionTasks, subagentLabel, taskCountLabel, taskSummary, toolTitle, totalTokens, unknownBasisModels, usageInfos, usageIsStale, visibleTasks } from "@workerdeck/protocol";
13
13
  import { recapLine, scanPromptTokens, summarizeSince, useAttachments, useClaudeSession, useDraft, useHostFileSearch, usePeerSessions, useProfileUsage, useShellTerminal, useToolCallHost } from "@workerdeck/react";
14
- import { Streamdown } from "streamdown";
14
+ import { Streamdown, defaultRehypePlugins } from "streamdown";
15
15
  import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
16
16
  import { createPortal } from "react-dom";
17
17
  import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
@@ -2775,10 +2775,11 @@ function usePromptAreaKeydown({ editorRef, triggers, markdownEnabled, submitOnEn
2775
2775
  const launcher = triggers.find((t) => t.mode === "launch" && t.char === e.key);
2776
2776
  const editor = editorRef.current;
2777
2777
  if (launcher?.onActivate && editor) {
2778
- const cursorPos = getCursorOffset(editor);
2779
- if (cursorPos !== null) {
2778
+ const caret = getCursorOffset(editor);
2779
+ if (caret !== null) {
2780
2780
  const segments = readSegmentsFromDOM();
2781
2781
  const plainText = segmentsToPlainText(segments);
2782
+ const cursorPos = Math.min(caret, plainText.length);
2782
2783
  if (isValidTriggerPosition(plainText, cursorPos, launcher.position)) {
2783
2784
  e.preventDefault();
2784
2785
  launcher.onActivate({
@@ -4575,36 +4576,14 @@ function TranscriptVariantProvider({ value, children }) {
4575
4576
  function useTranscriptVariant() {
4576
4577
  return useContext(VariantContext);
4577
4578
  }
4578
- const DensityContext = createContext("comfortable");
4579
- function TranscriptDensityProvider({ value, children }) {
4580
- return /* @__PURE__ */ jsx(DensityContext.Provider, {
4581
- value,
4582
- children
4583
- });
4584
- }
4585
- function useTranscriptDensity() {
4586
- return useContext(DensityContext);
4587
- }
4588
4579
  const ROW_GAP = {
4589
4580
  terminal: {
4590
- comfortable: {
4591
- className: "term-row-gap",
4592
- px: 18
4593
- },
4594
- compact: {
4595
- className: "term-row-gap",
4596
- px: 18
4597
- }
4581
+ className: "term-row-gap",
4582
+ px: 18
4598
4583
  },
4599
4584
  cards: {
4600
- comfortable: {
4601
- className: "pt-4",
4602
- px: 16
4603
- },
4604
- compact: {
4605
- className: "pt-2",
4606
- px: 8
4607
- }
4585
+ className: "pt-5",
4586
+ px: 20
4608
4587
  }
4609
4588
  };
4610
4589
  //#endregion
@@ -4613,7 +4592,19 @@ function toolIcon(toolName) {
4613
4592
  switch (toolName) {
4614
4593
  case "Bash":
4615
4594
  case "BashOutput":
4616
- case "KillShell": return Terminal;
4595
+ case "KillShell":
4596
+ case "shell_list":
4597
+ case "shell_read":
4598
+ case "shell_run":
4599
+ case "shell_write":
4600
+ case "shell_kill":
4601
+ case "shell_request_write":
4602
+ case "mcp__workerdeck__shell_list":
4603
+ case "mcp__workerdeck__shell_read":
4604
+ case "mcp__workerdeck__shell_run":
4605
+ case "mcp__workerdeck__shell_write":
4606
+ case "mcp__workerdeck__shell_kill":
4607
+ case "mcp__workerdeck__shell_request_write": return Terminal;
4617
4608
  case "Read": return FileText;
4618
4609
  case "Write": return SquarePen;
4619
4610
  case "Edit":
@@ -4765,69 +4756,11 @@ function PromptTokenText({ text, className }) {
4765
4756
  });
4766
4757
  }
4767
4758
  //#endregion
4768
- //#region src/components/terminal/shell-row.ts
4769
- const SHELL_GLYPH = "$";
4770
- const SHELL_KILL_GLYPH = "✕";
4771
- const SHELL_MISSING = "output expired or not tracked by this gateway";
4772
- function shellInfoLabel(shell) {
4773
- return shell.label || shell.command.split("\n")[0] || "";
4774
- }
4775
- function shellTitle(shell) {
4776
- return `#${shell.ordinal} ${shellInfoLabel(shell)}`;
4777
- }
4778
- function shellLabel(item) {
4779
- return shellInfoLabel(item.shell);
4780
- }
4781
- function shellStatusText(item) {
4782
- return shellInfoStatusText(item.shell);
4783
- }
4784
- function shellInfoFailed(shell) {
4785
- return shell.status === "exited" && shell.exitCode !== 0;
4786
- }
4787
- function shellInfoStatusText(shell) {
4788
- if (shell.status === "running") return "running";
4789
- if (typeof shell.exitCode === "number") return shell.exitCode === 0 ? "exit 0" : `exit ${shell.exitCode}`;
4790
- switch (shell.endReason) {
4791
- case "killed": return "killed";
4792
- case "timeout": return "timed out";
4793
- case "server_stopped": return "killed: the gateway stopped";
4794
- case "server_restarted": return "ended: the gateway restarted, the process may still be running";
4795
- case "spawn_failed": return "failed to start";
4796
- default: return "ended";
4797
- }
4798
- }
4799
- function shellFailed(item) {
4800
- return shellInfoFailed(item.shell);
4801
- }
4802
- function shellHeaderText(item) {
4803
- return `${shellLabel(item)} · ${shellStatusText(item)}`;
4804
- }
4805
- function shellBodyLines(item, open) {
4806
- const source = open && item.expanded !== void 0 ? item.expanded : item.text;
4807
- if (source === "") return [];
4808
- const lines = source.split("\n");
4809
- if (!open) return lines;
4810
- const kept = [];
4811
- let chars = 0;
4812
- for (const line of lines) {
4813
- if (kept.length > 0 && chars + line.length > 1e5) break;
4814
- kept.push(line);
4815
- chars += line.length + 1;
4816
- }
4817
- return kept;
4818
- }
4819
- function shellFooterText(item, open, shown) {
4820
- if (item.missing) return SHELL_MISSING;
4821
- if (!open) return item.truncated ? "… more output - expand to fetch it" : void 0;
4822
- const total = item.expanded === void 0 ? void 0 : item.expanded.split("\n").length;
4823
- if (total !== void 0 && total > shown) return `… +${(total - shown).toLocaleString()} lines not shown`;
4824
- if (item.expanded === void 0 && item.truncated) return "… fetching the full output";
4825
- }
4826
- //#endregion
4827
4759
  //#region src/components/terminal/affordances.tsx
4828
4760
  const DEFAULTS = {
4829
4761
  hover: true,
4830
- actions: true
4762
+ actions: true,
4763
+ labels: false
4831
4764
  };
4832
4765
  const AffordanceContext = createContext(DEFAULTS);
4833
4766
  function useAffordances() {
@@ -4836,7 +4769,8 @@ function useAffordances() {
4836
4769
  function resolveAffordances(value) {
4837
4770
  if (value === false) return {
4838
4771
  hover: false,
4839
- actions: false
4772
+ actions: false,
4773
+ labels: false
4840
4774
  };
4841
4775
  if (value === true || value === void 0) return DEFAULTS;
4842
4776
  return {
@@ -4850,17 +4784,52 @@ function AffordanceProvider({ value, children }) {
4850
4784
  children
4851
4785
  });
4852
4786
  }
4853
- function WithActions({ actions, children, className }) {
4787
+ const PlacementContext = createContext("below");
4788
+ function ActionPlacementProvider({ value, children }) {
4789
+ return /* @__PURE__ */ jsx(PlacementContext.Provider, {
4790
+ value,
4791
+ children
4792
+ });
4793
+ }
4794
+ function WithActions({ actions, children, className, placement }) {
4854
4795
  const { actions: enabled } = useAffordances();
4796
+ const inherited = useContext(PlacementContext);
4855
4797
  if (!enabled) return /* @__PURE__ */ jsx(Fragment$1, { children });
4856
4798
  return /* @__PURE__ */ jsxs("div", {
4857
4799
  className: cn("term-hoverable", className),
4858
- children: [children, /* @__PURE__ */ jsx("div", {
4800
+ "data-placement": placement ?? inherited,
4801
+ children: [/* @__PURE__ */ jsx(PlacementContext.Provider, {
4802
+ value: "below",
4803
+ children
4804
+ }), /* @__PURE__ */ jsx("div", {
4859
4805
  className: "term-actions",
4860
4806
  children: actions
4861
4807
  })]
4862
4808
  });
4863
4809
  }
4810
+ function ActionButton({ icon: Icon, word, label, tone, pressed, filled, onPress }) {
4811
+ const { labels } = useAffordances();
4812
+ return /* @__PURE__ */ jsxs("button", {
4813
+ type: "button",
4814
+ className: "term-action",
4815
+ title: label,
4816
+ "aria-label": label,
4817
+ "aria-pressed": pressed,
4818
+ "data-tone": tone,
4819
+ onClick: (event) => {
4820
+ event.stopPropagation();
4821
+ onPress();
4822
+ },
4823
+ children: [/* @__PURE__ */ jsx(Icon, {
4824
+ "aria-hidden": true,
4825
+ className: "term-action-icon",
4826
+ fill: filled ? "currentColor" : "none"
4827
+ }), labels ? /* @__PURE__ */ jsx("span", {
4828
+ className: "term-action-word",
4829
+ children: word
4830
+ }) : null]
4831
+ });
4832
+ }
4864
4833
  const BookmarkContext = createContext(void 0);
4865
4834
  function BookmarkProvider({ value, children }) {
4866
4835
  return /* @__PURE__ */ jsx(BookmarkContext.Provider, {
@@ -4872,77 +4841,71 @@ function BookmarkAction({ id }) {
4872
4841
  const handle = useContext(BookmarkContext);
4873
4842
  if (!handle) return null;
4874
4843
  const active = handle.has(id);
4875
- const label = active ? "Remove bookmark" : "Bookmark";
4876
- return /* @__PURE__ */ jsx("button", {
4877
- type: "button",
4878
- className: "term-action",
4879
- title: label,
4880
- "aria-label": label,
4881
- "aria-pressed": active,
4882
- onClick: (event) => {
4883
- event.stopPropagation();
4884
- handle.toggle(id);
4885
- },
4886
- children: active ? "★" : "☆"
4844
+ return /* @__PURE__ */ jsx(ActionButton, {
4845
+ icon: Star,
4846
+ filled: true,
4847
+ word: active ? "bookmarked" : "bookmark",
4848
+ label: active ? "Remove bookmark" : "Bookmark",
4849
+ tone: active ? "yellow" : void 0,
4850
+ pressed: active,
4851
+ onPress: () => handle.toggle(id)
4887
4852
  });
4888
4853
  }
4889
4854
  function OpenSubagentAction({ onOpen, label = "Open sub-agent" }) {
4890
- return /* @__PURE__ */ jsx("button", {
4891
- type: "button",
4892
- className: "term-action",
4893
- title: label,
4894
- "aria-label": label,
4895
- onClick: (event) => {
4896
- event.stopPropagation();
4897
- onOpen();
4898
- },
4899
- children: "⤢"
4855
+ return /* @__PURE__ */ jsx(ActionButton, {
4856
+ icon: Maximize2,
4857
+ word: "open",
4858
+ label,
4859
+ onPress: onOpen
4900
4860
  });
4901
4861
  }
4902
4862
  function OpenShellAction({ onOpen, label = "Open terminal" }) {
4903
- return /* @__PURE__ */ jsx("button", {
4904
- type: "button",
4905
- className: "term-action",
4906
- title: label,
4907
- "aria-label": label,
4908
- "data-tone": "magenta",
4909
- onClick: (event) => {
4910
- event.stopPropagation();
4911
- onOpen();
4912
- },
4913
- children: "⤢"
4863
+ return /* @__PURE__ */ jsx(ActionButton, {
4864
+ icon: SquareTerminal,
4865
+ word: "enter shell",
4866
+ label,
4867
+ tone: "magenta",
4868
+ onPress: onOpen
4914
4869
  });
4915
4870
  }
4916
4871
  function KillShellAction({ onKill, label = "Kill this shell" }) {
4917
- return /* @__PURE__ */ jsx("button", {
4918
- type: "button",
4919
- className: "term-action",
4920
- title: label,
4921
- "aria-label": label,
4922
- "data-tone": "red",
4923
- onClick: (event) => {
4924
- event.stopPropagation();
4925
- onKill();
4926
- },
4927
- children: "✕"
4872
+ return /* @__PURE__ */ jsx(ActionButton, {
4873
+ icon: X,
4874
+ word: "kill",
4875
+ label,
4876
+ tone: "danger",
4877
+ onPress: onKill
4878
+ });
4879
+ }
4880
+ function AgentWriteAction({ granted, label, onToggle }) {
4881
+ return /* @__PURE__ */ jsx(ActionButton, {
4882
+ icon: granted ? Bot : BotOff,
4883
+ word: granted ? "agent can type" : "agent read-only",
4884
+ label,
4885
+ tone: granted ? "yellow" : void 0,
4886
+ pressed: granted,
4887
+ onPress: onToggle
4928
4888
  });
4929
4889
  }
4930
4890
  function CopyAction({ text, label = "Copy" }) {
4931
4891
  const [copied, setCopied] = useState(false);
4932
- return /* @__PURE__ */ jsx("button", {
4933
- type: "button",
4934
- className: "term-action",
4935
- title: label,
4936
- "aria-label": label,
4937
- onClick: (event) => {
4938
- event.stopPropagation();
4892
+ return /* @__PURE__ */ jsx(ActionButton, {
4893
+ icon: copied ? Check : Copy,
4894
+ word: copied ? "copied" : "copy",
4895
+ label,
4896
+ onPress: () => {
4939
4897
  copyText(text).then((ok) => {
4940
4898
  if (!ok) return;
4941
4899
  setCopied(true);
4942
4900
  setTimeout(() => setCopied(false), 1200);
4943
4901
  });
4944
- },
4945
- children: copied ? "✓" : "⧉"
4902
+ }
4903
+ });
4904
+ }
4905
+ function AgentWriteIcon({ granted, className }) {
4906
+ return /* @__PURE__ */ jsx(granted ? Bot : BotOff, {
4907
+ "aria-hidden": true,
4908
+ className
4946
4909
  });
4947
4910
  }
4948
4911
  //#endregion
@@ -5133,119 +5096,650 @@ function useFileLinks() {
5133
5096
  return useContext(FileLinkContext);
5134
5097
  }
5135
5098
  //#endregion
5136
- //#region src/components/terminal/markdown.tsx
5137
- function codeText(node) {
5138
- if (node === null || node === void 0 || typeof node === "boolean") return "";
5139
- if (typeof node === "string" || typeof node === "number") return String(node);
5140
- if (Array.isArray(node)) return node.map(codeText).join("");
5141
- const element = node;
5142
- return element.props ? codeText(element.props.children) : "";
5099
+ //#region src/components/terminal/image-box.ts
5100
+ const HOST_IMAGE_TOOLS = /* @__PURE__ */ new Set(["CodexImageGeneration", "CodexImageView"]);
5101
+ const MEDIA_EXTENSIONS = {
5102
+ "image/png": "png",
5103
+ "image/jpeg": "jpg",
5104
+ "image/gif": "gif",
5105
+ "image/webp": "webp",
5106
+ "image/svg+xml": "svg"
5107
+ };
5108
+ function imagePlaceholder(image) {
5109
+ return `image · ${formatBytes(image.bytes)}`;
5143
5110
  }
5144
- function fenceLanguage(node) {
5145
- const className = (Array.isArray(node) ? node.find(Boolean) : node)?.props?.className;
5146
- return /language-([\w-]+)/.exec(className ?? "")?.[1];
5111
+ const IMAGE_UNAVAILABLE = "image unavailable";
5112
+ function hostImagePathOf(item) {
5113
+ if (!HOST_IMAGE_TOOLS.has(item.name)) return;
5114
+ const input = item.input;
5115
+ const path = input?.savedPath ?? input?.path;
5116
+ return typeof path === "string" ? path : void 0;
5147
5117
  }
5148
- function CodeBand({ code, language }) {
5149
- return /* @__PURE__ */ jsx(WithActions, {
5150
- className: "term-block",
5151
- actions: /* @__PURE__ */ jsx(CopyAction, {
5152
- text: code,
5153
- label: "Copy code"
5154
- }),
5155
- children: /* @__PURE__ */ jsx(Band, {
5156
- className: "term-code",
5157
- "data-language": language,
5158
- children: /* @__PURE__ */ jsx("pre", {
5159
- className: "term-pre",
5160
- children: code
5161
- })
5162
- })
5163
- });
5118
+ function baseName(path) {
5119
+ return path.split(/[\\/]/).pop() || path;
5164
5120
  }
5165
- function heading(tone) {
5166
- return function Heading({ children }) {
5167
- return /* @__PURE__ */ jsx("div", {
5168
- className: "term-block",
5169
- "data-tone": tone,
5170
- "data-weight": "bold",
5171
- children
5172
- });
5173
- };
5121
+ function resultImageName(toolName, image) {
5122
+ const extension = MEDIA_EXTENSIONS[image.mediaType] ?? image.mediaType.split("/").pop() ?? "img";
5123
+ return `${toolName.toLowerCase()}-${image.partIndex + 1}.${extension}`;
5174
5124
  }
5175
- function Link({ href, children }) {
5176
- const links = useFileLinks();
5177
- const file = links ? parseFileLink(href, links.cwd) : void 0;
5178
- if (file) return /* @__PURE__ */ jsx("button", {
5125
+ //#endregion
5126
+ //#region src/components/agent/image-viewer.tsx
5127
+ const ZOOM_STEPS = [
5128
+ .1,
5129
+ .25,
5130
+ .33,
5131
+ .5,
5132
+ .67,
5133
+ .75,
5134
+ 1,
5135
+ 1.25,
5136
+ 1.5,
5137
+ 2,
5138
+ 3,
5139
+ 4,
5140
+ 6,
5141
+ 8
5142
+ ];
5143
+ const STAGE_PADDING = 16;
5144
+ const DRAG_SLOP$1 = 4;
5145
+ const WHEEL_ZOOM_RATE = .01;
5146
+ const WHEEL_DELTA_CAP = 25;
5147
+ const ViewerContext = createContext(void 0);
5148
+ function useOpenImage() {
5149
+ return useContext(ViewerContext);
5150
+ }
5151
+ function ImageViewerProvider({ children }) {
5152
+ const [image, setImage] = useState();
5153
+ const close = useCallback(() => setImage(void 0), []);
5154
+ return /* @__PURE__ */ jsxs(ViewerContext.Provider, {
5155
+ value: setImage,
5156
+ children: [children, image ? /* @__PURE__ */ jsx(ImageViewer, {
5157
+ image,
5158
+ onClose: close
5159
+ }, image.src) : null]
5160
+ });
5161
+ }
5162
+ function ViewableImage({ image, className, children }) {
5163
+ const open = useOpenImage();
5164
+ if (!open) return /* @__PURE__ */ jsx(Fragment$1, { children });
5165
+ return /* @__PURE__ */ jsx("button", {
5179
5166
  type: "button",
5180
- className: "term-link",
5181
- "data-tone": "blue",
5182
- title: file.line === void 0 ? file.path : `${file.path}:${file.line}`,
5167
+ "aria-label": `View ${image.name}`,
5168
+ title: image.name,
5169
+ "data-slot": "viewable-image",
5170
+ className: cn("cursor-zoom-in", className),
5183
5171
  onClick: (event) => {
5184
5172
  event.stopPropagation();
5185
- links?.open(file.path, file.line);
5173
+ open(image);
5186
5174
  },
5187
5175
  children
5188
5176
  });
5189
- return /* @__PURE__ */ jsx("a", {
5190
- className: "term-link",
5191
- "data-tone": "blue",
5192
- href,
5193
- target: "_blank",
5194
- rel: "noreferrer",
5195
- children
5196
- });
5197
5177
  }
5198
- const TERMINAL_COMPONENTS = {
5199
- p: ({ children }) => /* @__PURE__ */ jsx("div", {
5200
- className: "term-block",
5201
- children
5202
- }),
5203
- h1: heading("bright"),
5204
- h2: heading("bright"),
5205
- h3: heading("bright"),
5206
- h4: heading("fg"),
5207
- h5: heading("fg"),
5208
- h6: heading("fg"),
5209
- ul: ({ children }) => /* @__PURE__ */ jsx("ul", {
5210
- className: "term-block term-list",
5211
- children
5212
- }),
5213
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", {
5214
- className: "term-block term-list term-list-ordered",
5215
- children
5216
- }),
5217
- li: ({ children }) => /* @__PURE__ */ jsxs("li", {
5218
- className: "term-row term-li",
5219
- children: [/* @__PURE__ */ jsx("span", {
5220
- className: "term-gutter",
5221
- "aria-hidden": true
5222
- }), /* @__PURE__ */ jsx("div", {
5223
- className: "term-body",
5224
- children
5225
- })]
5226
- }),
5227
- blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", {
5228
- className: "term-block term-quote",
5229
- "data-tone": "dim",
5230
- children
5231
- }),
5232
- hr: () => /* @__PURE__ */ jsx("div", {
5233
- className: "term-block term-rule",
5234
- "aria-hidden": true
5235
- }),
5236
- pre: ({ children }) => /* @__PURE__ */ jsx(CodeBand, {
5237
- code: codeText(children),
5238
- language: fenceLanguage(children)
5239
- }),
5240
- code: ({ children }) => /* @__PURE__ */ jsx("code", {
5241
- className: "term-inline-code",
5242
- "data-tone": "blue",
5243
- children
5244
- }),
5245
- strong: ({ children }) => /* @__PURE__ */ jsx("strong", {
5246
- "data-tone": "bright",
5247
- "data-weight": "bold",
5248
- children
5178
+ function ImageViewer({ image, onClose, className }) {
5179
+ const rootRef = useRef(null);
5180
+ const stageRef = useRef(null);
5181
+ const [natural, setNatural] = useState();
5182
+ const [stage, setStage] = useState();
5183
+ const [zoom, setZoom] = useState("fit");
5184
+ const [failed, setFailed] = useState(false);
5185
+ const anchor = useRef(void 0);
5186
+ const wheelScale = useRef(void 0);
5187
+ const drag = useRef(void 0);
5188
+ const dragged = useRef(false);
5189
+ const fitScale = natural && stage ? Math.min(1, (stage.width - 32) / natural.width, (stage.height - 32) / natural.height) : 1;
5190
+ const scale = zoom === "fit" ? Math.max(fitScale, .01) : zoom;
5191
+ const scrollable = natural !== void 0 && stage !== void 0 && (natural.width * scale > stage.width || natural.height * scale > stage.height);
5192
+ useEffect(() => {
5193
+ const previous = document.activeElement;
5194
+ rootRef.current?.focus();
5195
+ return () => previous?.focus?.();
5196
+ }, []);
5197
+ useEffect(() => {
5198
+ const element = stageRef.current;
5199
+ if (!element) return;
5200
+ const observer = new ResizeObserver(() => setStage({
5201
+ width: element.clientWidth,
5202
+ height: element.clientHeight
5203
+ }));
5204
+ observer.observe(element);
5205
+ return () => observer.disconnect();
5206
+ }, []);
5207
+ useLayoutEffect(() => {
5208
+ const element = stageRef.current;
5209
+ const point = anchor.current;
5210
+ if (!element || !point) return;
5211
+ anchor.current = void 0;
5212
+ wheelScale.current = void 0;
5213
+ element.scrollLeft = point.x * element.scrollWidth - point.viewX;
5214
+ element.scrollTop = point.y * element.scrollHeight - point.viewY;
5215
+ }, [scale]);
5216
+ const zoomTo = useCallback((next, focus) => {
5217
+ const element = stageRef.current;
5218
+ if (element && element.scrollWidth > 0 && element.scrollHeight > 0) {
5219
+ const rect = element.getBoundingClientRect();
5220
+ const viewX = focus ? focus.clientX - rect.left : element.clientWidth / 2;
5221
+ const viewY = focus ? focus.clientY - rect.top : element.clientHeight / 2;
5222
+ anchor.current = {
5223
+ x: (element.scrollLeft + viewX) / element.scrollWidth,
5224
+ y: (element.scrollTop + viewY) / element.scrollHeight,
5225
+ viewX,
5226
+ viewY
5227
+ };
5228
+ }
5229
+ setZoom(next);
5230
+ }, []);
5231
+ const step = (direction) => {
5232
+ const next = direction > 0 ? ZOOM_STEPS.find((value) => value > scale + .001) : ZOOM_STEPS.filter((value) => value < scale - .001).at(-1);
5233
+ zoomTo(next ?? (direction > 0 ? ZOOM_STEPS[ZOOM_STEPS.length - 1] : ZOOM_STEPS[0]));
5234
+ };
5235
+ const onKeyDown = (event) => {
5236
+ event.stopPropagation();
5237
+ if (event.key === "Escape") {
5238
+ event.preventDefault();
5239
+ onClose();
5240
+ } else if (event.key === "+" || event.key === "=") {
5241
+ event.preventDefault();
5242
+ step(1);
5243
+ } else if (event.key === "-" || event.key === "_") {
5244
+ event.preventDefault();
5245
+ step(-1);
5246
+ } else if (event.key === "0") {
5247
+ event.preventDefault();
5248
+ zoomTo("fit");
5249
+ } else if (event.key === "1") {
5250
+ event.preventDefault();
5251
+ zoomTo(1);
5252
+ }
5253
+ };
5254
+ const scaleRef = useRef(scale);
5255
+ scaleRef.current = scale;
5256
+ useEffect(() => {
5257
+ const element = stageRef.current;
5258
+ if (!element) return;
5259
+ const onWheel = (event) => {
5260
+ if (!event.ctrlKey && !event.metaKey) return;
5261
+ event.preventDefault();
5262
+ const delta = Math.max(-25, Math.min(WHEEL_DELTA_CAP, event.deltaY));
5263
+ const base = wheelScale.current ?? scaleRef.current;
5264
+ const next = Math.min(ZOOM_STEPS[ZOOM_STEPS.length - 1], Math.max(ZOOM_STEPS[0], base * Math.exp(-delta * WHEEL_ZOOM_RATE)));
5265
+ wheelScale.current = next;
5266
+ zoomTo(next, event);
5267
+ };
5268
+ element.addEventListener("wheel", onWheel, { passive: false });
5269
+ return () => element.removeEventListener("wheel", onWheel);
5270
+ }, [zoomTo]);
5271
+ const onPointerDown = (event) => {
5272
+ const element = stageRef.current;
5273
+ if (!scrollable || !element || event.button !== 0) return;
5274
+ dragged.current = false;
5275
+ drag.current = {
5276
+ x: event.clientX,
5277
+ y: event.clientY,
5278
+ left: element.scrollLeft,
5279
+ top: element.scrollTop
5280
+ };
5281
+ element.setPointerCapture(event.pointerId);
5282
+ };
5283
+ const onPointerMove = (event) => {
5284
+ const element = stageRef.current;
5285
+ const start = drag.current;
5286
+ if (!element || !start) return;
5287
+ if (Math.abs(event.clientX - start.x) + Math.abs(event.clientY - start.y) > DRAG_SLOP$1) dragged.current = true;
5288
+ element.scrollLeft = start.left - (event.clientX - start.x);
5289
+ element.scrollTop = start.top - (event.clientY - start.y);
5290
+ };
5291
+ const endDrag = (event) => {
5292
+ drag.current = void 0;
5293
+ stageRef.current?.releasePointerCapture?.(event.pointerId);
5294
+ };
5295
+ return /* @__PURE__ */ jsxs("div", {
5296
+ ref: rootRef,
5297
+ role: "dialog",
5298
+ "aria-modal": "true",
5299
+ "aria-label": image.name,
5300
+ tabIndex: -1,
5301
+ "data-slot": "image-viewer",
5302
+ onKeyDown,
5303
+ onClick: (event) => event.stopPropagation(),
5304
+ className: cn("absolute inset-0 z-50 flex flex-col bg-bg outline-none", className),
5305
+ children: [/* @__PURE__ */ jsxs("div", {
5306
+ className: "flex shrink-0 items-center gap-1 border-b border-border px-2 py-1.5",
5307
+ children: [
5308
+ /* @__PURE__ */ jsxs("div", {
5309
+ className: "min-w-0 flex-1 px-1",
5310
+ children: [/* @__PURE__ */ jsx("div", {
5311
+ className: "truncate font-mono text-body-sm text-fg-1",
5312
+ title: image.name,
5313
+ children: image.name
5314
+ }), natural ? /* @__PURE__ */ jsxs("div", {
5315
+ className: "text-label text-fg-4",
5316
+ children: [
5317
+ natural.width,
5318
+ " × ",
5319
+ natural.height
5320
+ ]
5321
+ }) : null]
5322
+ }),
5323
+ /* @__PURE__ */ jsx(Button, {
5324
+ variant: "ghost",
5325
+ size: "icon-sm",
5326
+ "aria-label": "Zoom out",
5327
+ title: "Zoom out (-)",
5328
+ onClick: () => step(-1),
5329
+ disabled: failed,
5330
+ children: /* @__PURE__ */ jsx(Minus, {})
5331
+ }),
5332
+ /* @__PURE__ */ jsxs("span", {
5333
+ className: "w-12 text-center font-mono text-label text-fg-3 tabular-nums",
5334
+ children: [Math.round(scale * 100), "%"]
5335
+ }),
5336
+ /* @__PURE__ */ jsx(Button, {
5337
+ variant: "ghost",
5338
+ size: "icon-sm",
5339
+ "aria-label": "Zoom in",
5340
+ title: "Zoom in (+)",
5341
+ onClick: () => step(1),
5342
+ disabled: failed,
5343
+ children: /* @__PURE__ */ jsx(Plus, {})
5344
+ }),
5345
+ /* @__PURE__ */ jsx(Button, {
5346
+ variant: zoom === "fit" ? "secondary" : "ghost",
5347
+ size: "icon-sm",
5348
+ "aria-label": "Fit to view",
5349
+ "aria-pressed": zoom === "fit",
5350
+ title: "Fit to view (0)",
5351
+ onClick: () => zoomTo("fit"),
5352
+ disabled: failed,
5353
+ children: /* @__PURE__ */ jsx(Maximize, {})
5354
+ }),
5355
+ /* @__PURE__ */ jsx(Button, {
5356
+ variant: zoom === 1 ? "secondary" : "ghost",
5357
+ size: "sm",
5358
+ "aria-label": "Actual size",
5359
+ "aria-pressed": zoom === 1,
5360
+ title: "Actual size (1)",
5361
+ onClick: () => zoomTo(1),
5362
+ disabled: failed,
5363
+ className: "font-mono",
5364
+ children: "1:1"
5365
+ }),
5366
+ /* @__PURE__ */ jsx("a", {
5367
+ href: image.src,
5368
+ download: image.name,
5369
+ "aria-label": "Download",
5370
+ title: "Download",
5371
+ className: "inline-flex size-7 items-center justify-center rounded-md text-fg-2 transition-colors hover:bg-surface-hover hover:text-foreground",
5372
+ children: /* @__PURE__ */ jsx(Download, { className: "size-4" })
5373
+ }),
5374
+ /* @__PURE__ */ jsx(Button, {
5375
+ variant: "ghost",
5376
+ size: "icon-sm",
5377
+ "aria-label": "Close",
5378
+ title: "Close (Esc)",
5379
+ onClick: onClose,
5380
+ children: /* @__PURE__ */ jsx(X, {})
5381
+ })
5382
+ ]
5383
+ }), /* @__PURE__ */ jsx("div", {
5384
+ ref: stageRef,
5385
+ "data-slot": "image-viewer-stage",
5386
+ onPointerDown,
5387
+ onPointerMove,
5388
+ onPointerUp: endDrag,
5389
+ onPointerCancel: endDrag,
5390
+ onClick: (event) => {
5391
+ if (dragged.current) {
5392
+ dragged.current = false;
5393
+ return;
5394
+ }
5395
+ if (event.target === event.currentTarget || event.target === event.currentTarget.firstElementChild) onClose();
5396
+ },
5397
+ className: cn("min-h-0 flex-1 overflow-auto bg-surface-hover/40", scrollable && "cursor-grab active:cursor-grabbing"),
5398
+ children: /* @__PURE__ */ jsx("div", {
5399
+ className: "flex min-h-full w-max min-w-full items-center justify-center",
5400
+ style: { padding: STAGE_PADDING },
5401
+ children: failed ? /* @__PURE__ */ jsx("span", {
5402
+ className: "text-body-sm text-fg-4",
5403
+ children: "Image could not be displayed"
5404
+ }) : /* @__PURE__ */ jsx("img", {
5405
+ src: image.src,
5406
+ alt: image.name,
5407
+ draggable: false,
5408
+ onLoad: (event) => setNatural({
5409
+ width: event.currentTarget.naturalWidth,
5410
+ height: event.currentTarget.naturalHeight
5411
+ }),
5412
+ onError: () => setFailed(true),
5413
+ onDoubleClick: () => zoomTo(zoom === "fit" ? 1 : "fit"),
5414
+ className: "block max-w-none select-none",
5415
+ style: natural ? {
5416
+ width: natural.width * scale,
5417
+ height: natural.height * scale,
5418
+ imageRendering: scale > 2 ? "pixelated" : void 0
5419
+ } : {
5420
+ maxWidth: "100%",
5421
+ maxHeight: "100%",
5422
+ visibility: "hidden"
5423
+ }
5424
+ })
5425
+ })
5426
+ })]
5427
+ });
5428
+ }
5429
+ //#endregion
5430
+ //#region src/components/agent/tool-result-image.tsx
5431
+ const noop$1 = async () => void 0;
5432
+ const ImageContext = createContext(noop$1);
5433
+ function ToolResultImageProvider({ value, children }) {
5434
+ return /* @__PURE__ */ jsx(ImageContext.Provider, {
5435
+ value: value ?? noop$1,
5436
+ children
5437
+ });
5438
+ }
5439
+ function useToolResultImageLoader() {
5440
+ return useContext(ImageContext);
5441
+ }
5442
+ const MOUNT_SETTLE_MS = 150;
5443
+ function useToolResultImageSrc(ref) {
5444
+ const load = useToolResultImageLoader();
5445
+ const [state, setState] = useState({ failed: false });
5446
+ const { toolUseId, sourceSeq, partIndex, mediaType, bytes } = ref;
5447
+ useEffect(() => {
5448
+ let live = true;
5449
+ setState({ failed: false });
5450
+ const timer = setTimeout(() => {
5451
+ load({
5452
+ toolUseId,
5453
+ sourceSeq,
5454
+ partIndex,
5455
+ mediaType,
5456
+ bytes
5457
+ }).then((src) => {
5458
+ if (live) setState({
5459
+ src,
5460
+ failed: src === void 0
5461
+ });
5462
+ }).catch(() => {
5463
+ if (live) setState({ failed: true });
5464
+ });
5465
+ }, MOUNT_SETTLE_MS);
5466
+ return () => {
5467
+ live = false;
5468
+ clearTimeout(timer);
5469
+ };
5470
+ }, [
5471
+ load,
5472
+ toolUseId,
5473
+ sourceSeq,
5474
+ partIndex,
5475
+ mediaType,
5476
+ bytes
5477
+ ]);
5478
+ return state;
5479
+ }
5480
+ const HostImageContext = createContext(void 0);
5481
+ function HostImageProvider({ value, children }) {
5482
+ return /* @__PURE__ */ jsx(HostImageContext.Provider, {
5483
+ value,
5484
+ children
5485
+ });
5486
+ }
5487
+ function useHostImageSrc(path, override) {
5488
+ const provided = useContext(HostImageContext);
5489
+ const load = override ?? provided;
5490
+ const [state, setState] = useState({ failed: false });
5491
+ useEffect(() => {
5492
+ if (path === void 0) return;
5493
+ if (!load) {
5494
+ setState({ failed: true });
5495
+ return;
5496
+ }
5497
+ let live = true;
5498
+ setState({ failed: false });
5499
+ load(path).then((src) => {
5500
+ if (live) setState({
5501
+ src,
5502
+ failed: src === void 0
5503
+ });
5504
+ }).catch(() => {
5505
+ if (live) setState({ failed: true });
5506
+ });
5507
+ return () => {
5508
+ live = false;
5509
+ };
5510
+ }, [load, path]);
5511
+ return state;
5512
+ }
5513
+ const CACHE_BUDGET_BYTES = 67108864;
5514
+ function useToolResultImages(client, sessionId) {
5515
+ const cache = useRef(/* @__PURE__ */ new Map());
5516
+ useEffect(() => () => {
5517
+ for (const entry of cache.current.values()) if (entry.url) URL.revokeObjectURL(entry.url);
5518
+ cache.current.clear();
5519
+ }, []);
5520
+ return useCallback((ref) => {
5521
+ if (!sessionId) return Promise.resolve(void 0);
5522
+ const key = `${sessionId}:${ref.sourceSeq}:${ref.toolUseId}:${ref.partIndex}`;
5523
+ const hit = cache.current.get(key);
5524
+ if (hit) {
5525
+ cache.current.delete(key);
5526
+ cache.current.set(key, hit);
5527
+ return hit.pending;
5528
+ }
5529
+ const pending = client.toolResultImage(sessionId, ref.sourceSeq, ref.toolUseId, ref.partIndex).then((blob) => {
5530
+ if (blob.size === 0) return;
5531
+ const url = URL.createObjectURL(blob);
5532
+ const entry = cache.current.get(key);
5533
+ if (entry) {
5534
+ entry.url = url;
5535
+ entry.bytes = blob.size;
5536
+ evict(cache.current, key);
5537
+ } else URL.revokeObjectURL(url);
5538
+ return url;
5539
+ }).catch(() => void 0);
5540
+ cache.current.set(key, {
5541
+ pending,
5542
+ bytes: ref.bytes
5543
+ });
5544
+ return pending;
5545
+ }, [client, sessionId]);
5546
+ }
5547
+ function evict(cache, keep) {
5548
+ let held = 0;
5549
+ for (const entry of cache.values()) held += entry.bytes;
5550
+ for (const [key, entry] of cache) {
5551
+ if (held <= CACHE_BUDGET_BYTES) return;
5552
+ if (key === keep) continue;
5553
+ if (entry.url) URL.revokeObjectURL(entry.url);
5554
+ cache.delete(key);
5555
+ held -= entry.bytes;
5556
+ }
5557
+ }
5558
+ //#endregion
5559
+ //#region src/components/agent/markdown-image.tsx
5560
+ const LOCAL_PREFIX = "https://local-image.invalid/";
5561
+ const WEB_SOURCE = /^(?:https?:|data:image\/|blob:)/i;
5562
+ function shieldLocalImages() {
5563
+ return (tree) => {
5564
+ walk(tree);
5565
+ };
5566
+ }
5567
+ function walk(node) {
5568
+ if (node.type === "element" && node.tagName === "img" && node.properties) {
5569
+ const src = node.properties.src;
5570
+ if (typeof src === "string" && src.trim() && !WEB_SOURCE.test(src.trim())) node.properties.src = LOCAL_PREFIX + encodeURIComponent(src.trim());
5571
+ }
5572
+ for (const child of node.children ?? []) walk(child);
5573
+ }
5574
+ const MARKDOWN_REHYPE_PLUGINS = [shieldLocalImages, ...Object.values(defaultRehypePlugins)];
5575
+ function localImagePath(src) {
5576
+ if (!src?.startsWith(LOCAL_PREFIX)) return;
5577
+ try {
5578
+ return decodeURIComponent(src.slice(28));
5579
+ } catch {
5580
+ return;
5581
+ }
5582
+ }
5583
+ function MarkdownImage({ src, alt, terminal }) {
5584
+ const links = useFileLinks();
5585
+ const raw = typeof src === "string" ? src : void 0;
5586
+ const local = localImagePath(raw);
5587
+ const path = local === void 0 ? void 0 : parseFileLink(local, links?.cwd)?.path;
5588
+ const host = useHostImageSrc(path);
5589
+ const resolved = local === void 0 ? raw : host.src;
5590
+ const label = typeof alt === "string" && alt ? alt : void 0;
5591
+ const name = path ? baseName(path) : label ?? "image";
5592
+ const failed = local !== void 0 && (path === void 0 || host.failed);
5593
+ if (terminal) return /* @__PURE__ */ jsx("span", {
5594
+ className: "term-image",
5595
+ "data-state": resolved ? "loaded" : failed ? "failed" : "pending",
5596
+ style: { height: `calc(var(--term-line) * 12)` },
5597
+ children: resolved ? /* @__PURE__ */ jsx(ViewableImage, {
5598
+ image: {
5599
+ src: resolved,
5600
+ name
5601
+ },
5602
+ children: /* @__PURE__ */ jsx("img", {
5603
+ src: resolved,
5604
+ alt: label ?? name
5605
+ })
5606
+ }) : /* @__PURE__ */ jsx("span", {
5607
+ "data-tone": "faint",
5608
+ children: failed ? `${IMAGE_UNAVAILABLE} · ${local ?? name}` : name
5609
+ })
5610
+ });
5611
+ if (!resolved) return /* @__PURE__ */ jsx("span", {
5612
+ className: "my-1 inline-block rounded-md border border-border bg-surface-hover px-2 py-1 text-label text-fg-4",
5613
+ children: failed ? `${IMAGE_UNAVAILABLE} · ${local ?? name}` : name
5614
+ });
5615
+ return /* @__PURE__ */ jsx(ViewableImage, {
5616
+ image: {
5617
+ src: resolved,
5618
+ name
5619
+ },
5620
+ className: "my-1 block max-w-full",
5621
+ children: /* @__PURE__ */ jsx("img", {
5622
+ src: resolved,
5623
+ alt: label ?? name,
5624
+ className: "max-h-96 w-auto max-w-full rounded-md border border-border"
5625
+ })
5626
+ });
5627
+ }
5628
+ //#endregion
5629
+ //#region src/components/terminal/markdown.tsx
5630
+ function codeText(node) {
5631
+ if (node === null || node === void 0 || typeof node === "boolean") return "";
5632
+ if (typeof node === "string" || typeof node === "number") return String(node);
5633
+ if (Array.isArray(node)) return node.map(codeText).join("");
5634
+ const element = node;
5635
+ return element.props ? codeText(element.props.children) : "";
5636
+ }
5637
+ function fenceLanguage(node) {
5638
+ const className = (Array.isArray(node) ? node.find(Boolean) : node)?.props?.className;
5639
+ return /language-([\w-]+)/.exec(className ?? "")?.[1];
5640
+ }
5641
+ function CodeBand({ code, language }) {
5642
+ return /* @__PURE__ */ jsx(WithActions, {
5643
+ className: "term-block",
5644
+ placement: "inline",
5645
+ actions: /* @__PURE__ */ jsx(CopyAction, {
5646
+ text: code,
5647
+ label: "Copy code"
5648
+ }),
5649
+ children: /* @__PURE__ */ jsx(Band, {
5650
+ className: "term-code",
5651
+ "data-language": language,
5652
+ children: /* @__PURE__ */ jsx("pre", {
5653
+ className: "term-pre",
5654
+ children: code
5655
+ })
5656
+ })
5657
+ });
5658
+ }
5659
+ function heading(tone) {
5660
+ return function Heading({ children }) {
5661
+ return /* @__PURE__ */ jsx("div", {
5662
+ className: "term-block",
5663
+ "data-tone": tone,
5664
+ "data-weight": "bold",
5665
+ children
5666
+ });
5667
+ };
5668
+ }
5669
+ function Link({ href, children }) {
5670
+ const links = useFileLinks();
5671
+ const file = links ? parseFileLink(href, links.cwd) : void 0;
5672
+ if (file) return /* @__PURE__ */ jsx("button", {
5673
+ type: "button",
5674
+ className: "term-link",
5675
+ "data-tone": "blue",
5676
+ title: file.line === void 0 ? file.path : `${file.path}:${file.line}`,
5677
+ onClick: (event) => {
5678
+ event.stopPropagation();
5679
+ links?.open(file.path, file.line);
5680
+ },
5681
+ children
5682
+ });
5683
+ return /* @__PURE__ */ jsx("a", {
5684
+ className: "term-link",
5685
+ "data-tone": "blue",
5686
+ href,
5687
+ target: "_blank",
5688
+ rel: "noreferrer",
5689
+ children
5690
+ });
5691
+ }
5692
+ const TERMINAL_COMPONENTS = {
5693
+ p: ({ children }) => /* @__PURE__ */ jsx("div", {
5694
+ className: "term-block",
5695
+ children
5696
+ }),
5697
+ h1: heading("bright"),
5698
+ h2: heading("bright"),
5699
+ h3: heading("bright"),
5700
+ h4: heading("fg"),
5701
+ h5: heading("fg"),
5702
+ h6: heading("fg"),
5703
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", {
5704
+ className: "term-block term-list",
5705
+ children
5706
+ }),
5707
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", {
5708
+ className: "term-block term-list term-list-ordered",
5709
+ children
5710
+ }),
5711
+ li: ({ children }) => /* @__PURE__ */ jsxs("li", {
5712
+ className: "term-row term-li",
5713
+ children: [/* @__PURE__ */ jsx("span", {
5714
+ className: "term-gutter",
5715
+ "aria-hidden": true
5716
+ }), /* @__PURE__ */ jsx("div", {
5717
+ className: "term-body",
5718
+ children
5719
+ })]
5720
+ }),
5721
+ blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", {
5722
+ className: "term-block term-quote",
5723
+ "data-tone": "dim",
5724
+ children
5725
+ }),
5726
+ hr: () => /* @__PURE__ */ jsx("div", {
5727
+ className: "term-block term-rule",
5728
+ "aria-hidden": true
5729
+ }),
5730
+ pre: ({ children }) => /* @__PURE__ */ jsx(CodeBand, {
5731
+ code: codeText(children),
5732
+ language: fenceLanguage(children)
5733
+ }),
5734
+ code: ({ children }) => /* @__PURE__ */ jsx("code", {
5735
+ className: "term-inline-code",
5736
+ "data-tone": "blue",
5737
+ children
5738
+ }),
5739
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", {
5740
+ "data-tone": "bright",
5741
+ "data-weight": "bold",
5742
+ children
5249
5743
  }),
5250
5744
  em: ({ children }) => /* @__PURE__ */ jsx("em", {
5251
5745
  className: "term-em",
@@ -5255,6 +5749,11 @@ const TERMINAL_COMPONENTS = {
5255
5749
  href,
5256
5750
  children
5257
5751
  }),
5752
+ img: ({ src, alt }) => /* @__PURE__ */ jsx(MarkdownImage, {
5753
+ src,
5754
+ alt,
5755
+ terminal: true
5756
+ }),
5258
5757
  table: ({ children }) => /* @__PURE__ */ jsx("div", {
5259
5758
  className: "term-block term-table-wrap",
5260
5759
  children: /* @__PURE__ */ jsx("table", {
@@ -5281,6 +5780,7 @@ const TerminalMarkdown = memo(function TerminalMarkdown({ children, streaming, c
5281
5780
  parseIncompleteMarkdown: streaming,
5282
5781
  controls: false,
5283
5782
  components: TERMINAL_COMPONENTS,
5783
+ rehypePlugins: MARKDOWN_REHYPE_PLUGINS,
5284
5784
  className: cn("term-md", className),
5285
5785
  children
5286
5786
  });
@@ -5479,12 +5979,6 @@ function scrollParent(from) {
5479
5979
  return null;
5480
5980
  }
5481
5981
  //#endregion
5482
- //#region src/components/terminal/image-box.ts
5483
- function imagePlaceholder(image) {
5484
- return `image · ${formatBytes(image.bytes)}`;
5485
- }
5486
- const IMAGE_UNAVAILABLE = "image unavailable";
5487
- //#endregion
5488
5982
  //#region src/components/terminal/result-preview.ts
5489
5983
  const PREVIEW_LINES = 4;
5490
5984
  const PREVIEW_CHARS = 400;
@@ -5521,11 +6015,11 @@ function collapsedResult(lines, totalChars) {
5521
6015
  }
5522
6016
  //#endregion
5523
6017
  //#region src/components/agent/tool-result-fetch.tsx
5524
- const noop$1 = async () => false;
5525
- const FetchContext = createContext(noop$1);
6018
+ const noop = async () => false;
6019
+ const FetchContext = createContext(noop);
5526
6020
  function ToolResultFetchProvider({ value, children }) {
5527
6021
  return /* @__PURE__ */ jsx(FetchContext.Provider, {
5528
- value: value ?? noop$1,
6022
+ value: value ?? noop,
5529
6023
  children
5530
6024
  });
5531
6025
  }
@@ -5545,102 +6039,6 @@ function useToolTitle(name) {
5545
6039
  return toolTitle(name, useContext(TitlesContext));
5546
6040
  }
5547
6041
  //#endregion
5548
- //#region src/components/agent/tool-result-image.tsx
5549
- const noop = async () => void 0;
5550
- const ImageContext = createContext(noop);
5551
- function ToolResultImageProvider({ value, children }) {
5552
- return /* @__PURE__ */ jsx(ImageContext.Provider, {
5553
- value: value ?? noop,
5554
- children
5555
- });
5556
- }
5557
- function useToolResultImageLoader() {
5558
- return useContext(ImageContext);
5559
- }
5560
- const MOUNT_SETTLE_MS = 150;
5561
- function useToolResultImageSrc(ref) {
5562
- const load = useToolResultImageLoader();
5563
- const [state, setState] = useState({ failed: false });
5564
- const { toolUseId, sourceSeq, partIndex, mediaType, bytes } = ref;
5565
- useEffect(() => {
5566
- let live = true;
5567
- setState({ failed: false });
5568
- const timer = setTimeout(() => {
5569
- load({
5570
- toolUseId,
5571
- sourceSeq,
5572
- partIndex,
5573
- mediaType,
5574
- bytes
5575
- }).then((src) => {
5576
- if (live) setState({
5577
- src,
5578
- failed: src === void 0
5579
- });
5580
- }).catch(() => {
5581
- if (live) setState({ failed: true });
5582
- });
5583
- }, MOUNT_SETTLE_MS);
5584
- return () => {
5585
- live = false;
5586
- clearTimeout(timer);
5587
- };
5588
- }, [
5589
- load,
5590
- toolUseId,
5591
- sourceSeq,
5592
- partIndex,
5593
- mediaType,
5594
- bytes
5595
- ]);
5596
- return state;
5597
- }
5598
- const CACHE_BUDGET_BYTES = 67108864;
5599
- function useToolResultImages(client, sessionId) {
5600
- const cache = useRef(/* @__PURE__ */ new Map());
5601
- useEffect(() => () => {
5602
- for (const entry of cache.current.values()) if (entry.url) URL.revokeObjectURL(entry.url);
5603
- cache.current.clear();
5604
- }, []);
5605
- return useCallback((ref) => {
5606
- if (!sessionId) return Promise.resolve(void 0);
5607
- const key = `${sessionId}:${ref.sourceSeq}:${ref.toolUseId}:${ref.partIndex}`;
5608
- const hit = cache.current.get(key);
5609
- if (hit) {
5610
- cache.current.delete(key);
5611
- cache.current.set(key, hit);
5612
- return hit.pending;
5613
- }
5614
- const pending = client.toolResultImage(sessionId, ref.sourceSeq, ref.toolUseId, ref.partIndex).then((blob) => {
5615
- if (blob.size === 0) return;
5616
- const url = URL.createObjectURL(blob);
5617
- const entry = cache.current.get(key);
5618
- if (entry) {
5619
- entry.url = url;
5620
- entry.bytes = blob.size;
5621
- evict(cache.current, key);
5622
- } else URL.revokeObjectURL(url);
5623
- return url;
5624
- }).catch(() => void 0);
5625
- cache.current.set(key, {
5626
- pending,
5627
- bytes: ref.bytes
5628
- });
5629
- return pending;
5630
- }, [client, sessionId]);
5631
- }
5632
- function evict(cache, keep) {
5633
- let held = 0;
5634
- for (const entry of cache.values()) held += entry.bytes;
5635
- for (const [key, entry] of cache) {
5636
- if (held <= CACHE_BUDGET_BYTES) return;
5637
- if (key === keep) continue;
5638
- if (entry.url) URL.revokeObjectURL(entry.url);
5639
- cache.delete(key);
5640
- held -= entry.bytes;
5641
- }
5642
- }
5643
- //#endregion
5644
6042
  //#region src/components/terminal/todos.ts
5645
6043
  const PREVIEW_TODOS = 8;
5646
6044
  const GLYPH = {
@@ -5678,8 +6076,75 @@ function ShellActionsProvider({ value, children }) {
5678
6076
  children
5679
6077
  });
5680
6078
  }
5681
- function useShellActions() {
5682
- return useContext(ShellActionsContext);
6079
+ function useShellActions() {
6080
+ return useContext(ShellActionsContext);
6081
+ }
6082
+ //#endregion
6083
+ //#region src/components/terminal/shell-row.ts
6084
+ const SHELL_GLYPH = "$";
6085
+ const SHELL_KILL_GLYPH = "✕";
6086
+ const SHELL_MISSING = "output expired or not tracked by this gateway";
6087
+ const SHELL_AGENT_WRITE_NOTE = "agent may type";
6088
+ function shellGrantable(shell) {
6089
+ return shell.status === "running" && shell.owner === "user";
6090
+ }
6091
+ function shellAgentWriteLabel(shell) {
6092
+ return shell.agentWrite === true ? "Revoke the agent’s access to this shell" : "Let the agent type into this shell";
6093
+ }
6094
+ function shellInfoLabel(shell) {
6095
+ return shell.label || shell.command.split("\n")[0] || "";
6096
+ }
6097
+ function shellTitle(shell) {
6098
+ return `#${shell.ordinal} ${shellInfoLabel(shell)}`;
6099
+ }
6100
+ function shellLabel(item) {
6101
+ return shellInfoLabel(item.shell);
6102
+ }
6103
+ function shellStatusText(item) {
6104
+ return shellInfoStatusText(item.shell);
6105
+ }
6106
+ function shellInfoFailed(shell) {
6107
+ return shell.status === "exited" && shell.exitCode !== 0;
6108
+ }
6109
+ function shellInfoStatusText(shell) {
6110
+ if (shell.status === "running") return shell.agentWrite === true ? `running · ${SHELL_AGENT_WRITE_NOTE}` : "running";
6111
+ if (typeof shell.exitCode === "number") return shell.exitCode === 0 ? "exit 0" : `exit ${shell.exitCode}`;
6112
+ switch (shell.endReason) {
6113
+ case "killed": return "killed";
6114
+ case "timeout": return "timed out";
6115
+ case "server_stopped": return "killed: the gateway stopped";
6116
+ case "server_restarted": return "ended: the gateway restarted, the process may still be running";
6117
+ case "spawn_failed": return "failed to start";
6118
+ default: return "ended";
6119
+ }
6120
+ }
6121
+ function shellFailed(item) {
6122
+ return shellInfoFailed(item.shell);
6123
+ }
6124
+ function shellHeaderText(item) {
6125
+ return `${shellLabel(item)} · ${shellStatusText(item)}`;
6126
+ }
6127
+ function shellBodyLines(item, open) {
6128
+ const source = open && item.expanded !== void 0 ? item.expanded : item.text;
6129
+ if (source === "" || item.shell.interactive) return [];
6130
+ const lines = source.split("\n");
6131
+ if (!open) return lines;
6132
+ const kept = [];
6133
+ let chars = 0;
6134
+ for (const line of lines) {
6135
+ if (kept.length > 0 && chars + line.length > 1e5) break;
6136
+ kept.push(line);
6137
+ chars += line.length + 1;
6138
+ }
6139
+ return kept;
6140
+ }
6141
+ function shellFooterText(item, open, shown) {
6142
+ if (item.shell.interactive) return SHELL_INTERACTIVE_NOTE;
6143
+ if (item.missing) return SHELL_MISSING;
6144
+ if (!open) return item.truncated ? "… more output - expand to fetch it" : void 0;
6145
+ const total = item.expanded === void 0 ? void 0 : item.expanded.split("\n").length;
6146
+ if (total !== void 0 && total > shown) return `… +${(total - shown).toLocaleString()} lines not shown`;
6147
+ if (item.expanded === void 0 && item.truncated) return "… fetching the full output";
5683
6148
  }
5684
6149
  const RESULT_PREVIEW_CHARS$1 = 2e3;
5685
6150
  function clipToChars(lines, maxChars) {
@@ -5747,7 +6212,7 @@ function PeerSendRow({ item }) {
5747
6212
  })
5748
6213
  });
5749
6214
  }
5750
- function UserRow({ item }) {
6215
+ function UserRow({ item, attachmentUrl }) {
5751
6216
  return /* @__PURE__ */ jsx(WithActions, {
5752
6217
  actions: /* @__PURE__ */ jsx(BookmarkAction, { id: item.id }),
5753
6218
  children: /* @__PURE__ */ jsxs("div", {
@@ -5757,7 +6222,10 @@ function UserRow({ item }) {
5757
6222
  glyph: "❯",
5758
6223
  glyphTone: "dim",
5759
6224
  tone: "dim",
5760
- children: item.attachments.map((attachment) => attachment.name).join(", ")
6225
+ children: item.attachments.map((attachment, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 ? ", " : null, /* @__PURE__ */ jsx(AttachmentName, {
6226
+ attachment,
6227
+ href: attachmentUrl?.(attachment.id)
6228
+ })] }, attachment.id))
5761
6229
  }) : null, item.text ? item.text.split("\n").map((line, index) => item.origin && index === 0 ? /* @__PURE__ */ jsx(PeerRow, {
5762
6230
  glyph: "↦",
5763
6231
  who: peerName(item.origin),
@@ -5829,6 +6297,7 @@ function ToolRow({ item }) {
5829
6297
  const copyable = typeof command === "string" ? command : text;
5830
6298
  const todos = todoPreview(item.name, item.input);
5831
6299
  const title = useToolTitle(item.name);
6300
+ const hostPath = hostImagePathOf(item);
5832
6301
  return /* @__PURE__ */ jsx("div", {
5833
6302
  ref: reveal,
5834
6303
  className: open ? "term-open" : void 0,
@@ -5872,8 +6341,10 @@ function ToolRow({ item }) {
5872
6341
  ]
5873
6342
  })
5874
6343
  }),
6344
+ hostPath !== void 0 ? /* @__PURE__ */ jsx(TerminalHostImage, { path: hostPath }) : null,
5875
6345
  item.result?.images?.map((image) => /* @__PURE__ */ jsx(TerminalImage, {
5876
6346
  toolUseId: item.id,
6347
+ name: resultImageName(item.name, image),
5877
6348
  image
5878
6349
  }, image.partIndex)),
5879
6350
  item.patch && !open ? /* @__PURE__ */ jsx(TerminalDiff, { patch: item.patch }) : todos && !open ? /* @__PURE__ */ jsx(TerminalTodos, { preview: todos }) : text ? /* @__PURE__ */ jsxs(Fragment$1, { children: [preview.map((line, index) => /* @__PURE__ */ jsx(Row, {
@@ -5945,11 +6416,43 @@ function TerminalTodos({ preview }) {
5945
6416
  children: preview.more
5946
6417
  }) : null] });
5947
6418
  }
5948
- function TerminalImage({ toolUseId, image }) {
5949
- const { src, failed } = useToolResultImageSrc({
6419
+ function AttachmentName({ attachment, href }) {
6420
+ const open = useOpenImage();
6421
+ if (!open || !href || !attachment.mediaType.startsWith("image/")) return /* @__PURE__ */ jsx(Fragment$1, { children: attachment.name });
6422
+ return /* @__PURE__ */ jsx("button", {
6423
+ type: "button",
6424
+ className: "term-press term-link",
6425
+ onClick: (event) => {
6426
+ event.stopPropagation();
6427
+ open({
6428
+ src: href,
6429
+ name: attachment.name
6430
+ });
6431
+ },
6432
+ children: attachment.name
6433
+ });
6434
+ }
6435
+ function TerminalImage({ toolUseId, name, image }) {
6436
+ const state = useToolResultImageSrc({
5950
6437
  toolUseId,
5951
6438
  ...image
5952
6439
  });
6440
+ return /* @__PURE__ */ jsx(TerminalImageBox, {
6441
+ state,
6442
+ name,
6443
+ placeholder: imagePlaceholder(image)
6444
+ });
6445
+ }
6446
+ function TerminalHostImage({ path }) {
6447
+ const state = useHostImageSrc(path);
6448
+ return /* @__PURE__ */ jsx(TerminalImageBox, {
6449
+ state,
6450
+ name: baseName(path),
6451
+ placeholder: baseName(path)
6452
+ });
6453
+ }
6454
+ function TerminalImageBox({ state, name, placeholder }) {
6455
+ const { src, failed } = state;
5953
6456
  return /* @__PURE__ */ jsx(Row, {
5954
6457
  indent: 1,
5955
6458
  columns: 3,
@@ -5957,12 +6460,18 @@ function TerminalImage({ toolUseId, image }) {
5957
6460
  className: "term-image",
5958
6461
  "data-state": src ? "loaded" : failed ? "failed" : "pending",
5959
6462
  style: { height: `calc(var(--term-line) * 12)` },
5960
- children: src ? /* @__PURE__ */ jsx("img", {
5961
- src,
5962
- alt: imagePlaceholder(image)
6463
+ children: src ? /* @__PURE__ */ jsx(ViewableImage, {
6464
+ image: {
6465
+ src,
6466
+ name
6467
+ },
6468
+ children: /* @__PURE__ */ jsx("img", {
6469
+ src,
6470
+ alt: name
6471
+ })
5963
6472
  }) : /* @__PURE__ */ jsx(Ink, {
5964
6473
  tone: "faint",
5965
- children: failed ? IMAGE_UNAVAILABLE : imagePlaceholder(image)
6474
+ children: failed ? IMAGE_UNAVAILABLE : placeholder
5966
6475
  })
5967
6476
  })
5968
6477
  });
@@ -5991,7 +6500,10 @@ function ToolRunRow({ items }) {
5991
6500
  children: runSummary(items, busy)
5992
6501
  }) : runSummary(items, busy)
5993
6502
  })
5994
- }), open ? /* @__PURE__ */ jsx("div", { children: items.map((item) => /* @__PURE__ */ jsx(ToolRow, { item }, item.id)) }) : null]
6503
+ }), open ? /* @__PURE__ */ jsx("div", { children: items.map((item, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 ? /* @__PURE__ */ jsx(Blank, {}) : null, index < items.length - 1 ? /* @__PURE__ */ jsx(ActionPlacementProvider, {
6504
+ value: "below",
6505
+ children: /* @__PURE__ */ jsx(ToolRow, { item })
6506
+ }) : /* @__PURE__ */ jsx(ToolRow, { item })] }, item.id)) }) : null]
5995
6507
  });
5996
6508
  }
5997
6509
  function RunRow({ items }) {
@@ -6068,6 +6580,11 @@ function ShellRow({ item }) {
6068
6580
  children: /* @__PURE__ */ jsxs(WithActions, {
6069
6581
  actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [
6070
6582
  actions.open ? /* @__PURE__ */ jsx(OpenShellAction, { onOpen: () => actions.open?.(shellId) }) : null,
6583
+ actions.agentWrite && shellGrantable(item.shell) ? /* @__PURE__ */ jsx(AgentWriteAction, {
6584
+ granted: item.shell.agentWrite === true,
6585
+ label: shellAgentWriteLabel(item.shell),
6586
+ onToggle: () => void actions.agentWrite?.(shellId, item.shell.agentWrite !== true)
6587
+ }) : null,
6071
6588
  running ? /* @__PURE__ */ jsx(KillShellAction, { onKill: () => void actions.kill(shellId) }) : null,
6072
6589
  /* @__PURE__ */ jsx(BookmarkAction, { id: item.id }),
6073
6590
  /* @__PURE__ */ jsx(CopyAction, {
@@ -7632,13 +8149,103 @@ function planFromRequest(request) {
7632
8149
  return plan;
7633
8150
  }
7634
8151
  //#endregion
8152
+ //#region src/lib/shell-request.ts
8153
+ const CONTROL_NAMES = {
8154
+ "\r": "enter",
8155
+ "\n": "newline",
8156
+ " ": "tab",
8157
+ "\x1B": "escape",
8158
+ "": "backspace"
8159
+ };
8160
+ const SHELL_WRITE_TOOLS = [
8161
+ "shell_run",
8162
+ "shell_write",
8163
+ "shell_kill",
8164
+ "shell_request_write"
8165
+ ];
8166
+ function shellToolOf(toolName) {
8167
+ if (typeof toolName !== "string") return;
8168
+ const bare = toolName.startsWith("mcp__") ? toolName.slice(toolName.indexOf("__", 5) + 2) : toolName;
8169
+ return SHELL_WRITE_TOOLS.includes(bare) ? bare : void 0;
8170
+ }
8171
+ function shellRequestPayload(request) {
8172
+ const tool = shellToolOf(request?.toolName);
8173
+ if (!tool) return;
8174
+ const input = request?.input !== null && typeof request?.input === "object" ? request.input : {};
8175
+ const shellId = typeof input.shellId === "string" ? input.shellId : "";
8176
+ switch (tool) {
8177
+ case "shell_run": return {
8178
+ kind: "run",
8179
+ command: typeof input.command === "string" ? input.command : ""
8180
+ };
8181
+ case "shell_write": {
8182
+ const keys = Array.isArray(input.keys) ? input.keys.filter((key) => typeof key === "string") : void 0;
8183
+ return {
8184
+ kind: "write",
8185
+ shellId,
8186
+ ...typeof input.data === "string" ? { data: input.data } : {},
8187
+ ...keys && keys.length > 0 ? { keys } : {}
8188
+ };
8189
+ }
8190
+ case "shell_kill": return {
8191
+ kind: "kill",
8192
+ shellId
8193
+ };
8194
+ case "shell_request_write": return {
8195
+ kind: "grant",
8196
+ shellId,
8197
+ reason: typeof input.reason === "string" ? input.reason : ""
8198
+ };
8199
+ }
8200
+ }
8201
+ function shellRequestTitle(payload) {
8202
+ switch (payload.kind) {
8203
+ case "run": return "Agent wants to start a shell";
8204
+ case "write": return `Agent wants to type into shell ${payload.shellId}`;
8205
+ case "kill": return `Agent wants to kill shell ${payload.shellId}`;
8206
+ case "grant": return `Agent asks to type into your shell ${payload.shellId}`;
8207
+ }
8208
+ }
8209
+ function shellRequestLines(payload) {
8210
+ switch (payload.kind) {
8211
+ case "run": return payload.command.split("\n").map((line, index) => index === 0 ? `$ ${line}` : ` ${line}`);
8212
+ case "write": {
8213
+ const lines = [];
8214
+ if (payload.data !== void 0) lines.push(`types: ${visibleControls(payload.data)}`);
8215
+ if (payload.keys) lines.push(`presses: ${payload.keys.map((key) => `[${key}]`).join(" ")}`);
8216
+ return lines;
8217
+ }
8218
+ case "kill": return [`kill ${payload.shellId}`];
8219
+ case "grant": return [`why: ${visibleControls(payload.reason)}`, "until the shell ends or you revoke it"];
8220
+ }
8221
+ }
8222
+ function visibleControls(text) {
8223
+ let out = "";
8224
+ for (const char of text) {
8225
+ const code = char.codePointAt(0);
8226
+ if (code >= 32 && code !== 127) {
8227
+ out += char;
8228
+ continue;
8229
+ }
8230
+ const named = CONTROL_NAMES[char];
8231
+ out += named ? `[${named}]` : `[ctrl-${String.fromCharCode(code + 64).toLowerCase()}]`;
8232
+ }
8233
+ return out;
8234
+ }
8235
+ //#endregion
7635
8236
  //#region src/components/agent/Response.tsx
8237
+ const COMPONENTS = { img: ({ src, alt }) => /* @__PURE__ */ jsx(MarkdownImage, {
8238
+ src,
8239
+ alt
8240
+ }) };
7636
8241
  const Response = memo(function Response({ children, streaming, className }) {
7637
8242
  return /* @__PURE__ */ jsx(Streamdown, {
7638
8243
  mode: streaming ? "streaming" : "static",
7639
8244
  parseIncompleteMarkdown: streaming,
7640
8245
  shikiTheme: ["github-light", "github-dark"],
7641
8246
  linkSafety: { enabled: false },
8247
+ components: COMPONENTS,
8248
+ rehypePlugins: MARKDOWN_REHYPE_PLUGINS,
7642
8249
  className: cn("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className),
7643
8250
  children
7644
8251
  });
@@ -7656,6 +8263,7 @@ function PermissionPrompt({ request, onApprove, onDeny, className }) {
7656
8263
  setDenying(false);
7657
8264
  };
7658
8265
  const plan = planFromRequest(request);
8266
+ const shell = plan ? void 0 : shellRequestPayload(request);
7659
8267
  const summary = toolInputPreview(request.input);
7660
8268
  const ToolIcon = toolIcon(request.toolName);
7661
8269
  return /* @__PURE__ */ jsxs("div", {
@@ -7670,7 +8278,7 @@ function PermissionPrompt({ request, onApprove, onDeny, className }) {
7670
8278
  children: [
7671
8279
  /* @__PURE__ */ jsx("div", {
7672
8280
  className: "text-body-sm font-medium text-fg-1",
7673
- children: plan ? "Plan ready for review" : request.title ?? request.displayName ?? "Permission needed"
8281
+ children: plan ? "Plan ready for review" : shell ? shellRequestTitle(shell) : request.title ?? request.displayName ?? "Permission needed"
7674
8282
  }),
7675
8283
  request.description ? /* @__PURE__ */ jsx("div", {
7676
8284
  className: "mt-0.5 text-label text-fg-3",
@@ -7684,6 +8292,11 @@ function PermissionPrompt({ request, onApprove, onDeny, className }) {
7684
8292
  className: "mt-1.5 max-h-72 overflow-y-auto rounded-md bg-code-bg px-2.5 py-2 text-body-sm text-fg-2",
7685
8293
  children: /* @__PURE__ */ jsx(Response, { children: plan })
7686
8294
  }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
8295
+ shell ? /* @__PURE__ */ jsx("pre", {
8296
+ "data-slot": "shell-request",
8297
+ className: "mt-1.5 max-h-48 overflow-auto rounded-md bg-code-bg px-2.5 py-1.5 font-mono text-body-sm whitespace-pre-wrap text-fg-1",
8298
+ children: shellRequestLines(shell).join("\n")
8299
+ }) : null,
7687
8300
  /* @__PURE__ */ jsxs("div", {
7688
8301
  className: "mt-1.5 flex min-w-0 items-center gap-2",
7689
8302
  children: [
@@ -7692,7 +8305,7 @@ function PermissionPrompt({ request, onApprove, onDeny, className }) {
7692
8305
  className: "shrink-0 font-mono text-label font-medium text-fg-2",
7693
8306
  children: request.toolName
7694
8307
  }),
7695
- summary ? /* @__PURE__ */ jsx("span", {
8308
+ summary && !shell ? /* @__PURE__ */ jsx("span", {
7696
8309
  className: "min-w-0 truncate font-mono text-label text-fg-4",
7697
8310
  children: summary
7698
8311
  }) : null
@@ -7988,19 +8601,26 @@ function useSubagentFrame(options) {
7988
8601
  }
7989
8602
  //#endregion
7990
8603
  //#region src/components/agent/ShellStrip.tsx
7991
- function ShellStrip({ shell, label, cols, onBack, onKill, terminal, fontSize, lineHeight }) {
8604
+ function ShellStrip({ shell, label, cols, onBack, onKill, onAgentWrite, terminal, fontSize, lineHeight }) {
7992
8605
  const running = shell?.status === "running";
7993
8606
  const failed = shell ? shellInfoFailed(shell) : false;
7994
8607
  const name = shell ? shellTitle(shell) : label;
7995
8608
  const status = shell ? shellInfoStatusText(shell) : void 0;
7996
8609
  const width = cols ?? shell?.cols;
7997
8610
  const detail = width === void 0 ? void 0 : `${width} cols`;
8611
+ const granted = shell?.agentWrite === true;
8612
+ const grant = shell && onAgentWrite && shellGrantable(shell) ? () => onAgentWrite(!granted) : void 0;
7998
8613
  if (terminal) return /* @__PURE__ */ jsx(TerminalSurface, {
7999
8614
  fontSize,
8000
8615
  lineHeight,
8001
8616
  className: "shrink-0",
8002
8617
  children: /* @__PURE__ */ jsx(WithActions, {
8003
- actions: running && onKill ? /* @__PURE__ */ jsx(KillShellAction, { onKill }) : null,
8618
+ placement: "inline",
8619
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [grant && shell ? /* @__PURE__ */ jsx(AgentWriteAction, {
8620
+ granted,
8621
+ label: shellAgentWriteLabel(shell),
8622
+ onToggle: grant
8623
+ }) : null, running && onKill ? /* @__PURE__ */ jsx(KillShellAction, { onKill }) : null] }),
8004
8624
  children: /* @__PURE__ */ jsxs(Row, {
8005
8625
  glyph: "←",
8006
8626
  glyphTone: "dim",
@@ -8048,6 +8668,18 @@ function ShellStrip({ shell, label, cols, onBack, onKill, terminal, fontSize, li
8048
8668
  className: "shrink-0 text-label text-fg-4",
8049
8669
  children: detail
8050
8670
  }) : null,
8671
+ grant && shell ? /* @__PURE__ */ jsx("button", {
8672
+ type: "button",
8673
+ "aria-label": shellAgentWriteLabel(shell),
8674
+ "aria-pressed": granted,
8675
+ title: shellAgentWriteLabel(shell),
8676
+ className: cn("shrink-0 text-label", granted ? "text-warning" : "text-fg-3 hover:text-fg-1"),
8677
+ onClick: grant,
8678
+ children: /* @__PURE__ */ jsx(AgentWriteIcon, {
8679
+ granted,
8680
+ className: "size-3.5"
8681
+ })
8682
+ }) : null,
8051
8683
  running && onKill ? /* @__PURE__ */ jsx("button", {
8052
8684
  type: "button",
8053
8685
  "aria-label": "Kill this shell",
@@ -8158,7 +8790,7 @@ function ShellTerminal({ handle, shellId, onEnded, onShell, fontSize, className
8158
8790
  return () => observer.disconnect();
8159
8791
  }, [ready]);
8160
8792
  return /* @__PURE__ */ jsxs("div", {
8161
- className: cn("relative min-h-0 flex-1 overflow-hidden px-2 py-1", className),
8793
+ className: cn("wd-shell-term relative min-h-0 flex-1 overflow-hidden px-2 py-1", className),
8162
8794
  children: [/* @__PURE__ */ jsx("div", {
8163
8795
  ref: host,
8164
8796
  className: "h-full w-full",
@@ -8592,9 +9224,10 @@ function TerminalPermissionPrompt({ request, onApprove, onDeny, className }) {
8592
9224
  setDenying(false);
8593
9225
  };
8594
9226
  const plan = planFromRequest(request);
8595
- const patch = plan ? void 0 : previewPatch(request.input);
8596
- const summary = plan ? "" : toolInputPreview(request.input);
8597
- const heading = plan ? "Plan ready for review" : request.displayName ?? request.title ?? "Permission needed";
9227
+ const shell = plan ? void 0 : shellRequestPayload(request);
9228
+ const patch = plan || shell ? void 0 : previewPatch(request.input);
9229
+ const summary = plan || shell ? "" : toolInputPreview(request.input);
9230
+ const heading = plan ? "Plan ready for review" : shell ? shellRequestTitle(shell) : request.displayName ?? request.title ?? "Permission needed";
8598
9231
  const subject = patch?.path ?? (summary || void 0);
8599
9232
  const reasonInput = denying ? /* @__PURE__ */ jsx(PromptInput, {
8600
9233
  value: reason,
@@ -8657,6 +9290,13 @@ function TerminalPermissionPrompt({ request, onApprove, onDeny, className }) {
8657
9290
  }),
8658
9291
  /* @__PURE__ */ jsx(Blank, {}),
8659
9292
  /* @__PURE__ */ jsx(Rule, { dashed: true })
9293
+ ] }) : shell ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
9294
+ shellRequestLines(shell).map((line, index) => /* @__PURE__ */ jsx(Row, {
9295
+ bold: true,
9296
+ children: line
9297
+ }, index)),
9298
+ /* @__PURE__ */ jsx(Blank, {}),
9299
+ /* @__PURE__ */ jsx(Rule, { dashed: true })
8660
9300
  ] }) : patch ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
8661
9301
  /* @__PURE__ */ jsx(TerminalDiff, { patch }),
8662
9302
  /* @__PURE__ */ jsx(Blank, {}),
@@ -8669,7 +9309,7 @@ function TerminalPermissionPrompt({ request, onApprove, onDeny, className }) {
8669
9309
  tone: "faint",
8670
9310
  children: request.decisionReason
8671
9311
  }) : null,
8672
- /* @__PURE__ */ jsx(Row, { children: plan ? "Ready to implement this plan?" : request.title ?? `Do you want to proceed?` }),
9312
+ /* @__PURE__ */ jsx(Row, { children: plan ? "Ready to implement this plan?" : shell ? "Do you want to let it?" : request.title ?? `Do you want to proceed?` }),
8673
9313
  /* @__PURE__ */ jsx(Choices, {
8674
9314
  label: heading,
8675
9315
  options,
@@ -9428,6 +10068,8 @@ function measureCh(surface) {
9428
10068
  probe.remove();
9429
10069
  return width;
9430
10070
  }
10071
+ const MARKDOWN_IMAGE = /!\[[^\]]*\]\([^)]*\)/;
10072
+ const MARKDOWN_IMAGE_LINE = /^\s*!\[[^\]]*\]\([^)\s]+\)\s*$/;
9431
10073
  const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
9432
10074
  const WIDE_RANGES = [
9433
10075
  [4352, 4447],
@@ -9651,11 +10293,39 @@ function hardLines(source) {
9651
10293
  if (current.length) out.push(current.join(" "));
9652
10294
  return out;
9653
10295
  }
10296
+ function isBlank(s) {
10297
+ return s !== void 0 && s.trim() === "";
10298
+ }
10299
+ function listMarker(s) {
10300
+ const m = /^(\s*)([-*+]|\d+\.)\s+(.*)$/.exec(s);
10301
+ if (!m) return;
10302
+ return {
10303
+ depth: Math.floor(m[1].length / 2),
10304
+ ordered: /\d/.test(m[2]),
10305
+ text: m[3]
10306
+ };
10307
+ }
10308
+ function numberHunk(hunk) {
10309
+ let newLine = hunk.newStart;
10310
+ let oldLine = hunk.oldStart;
10311
+ return hunk.lines.map((line) => {
10312
+ const kind = line.startsWith("+") ? "add" : line.startsWith("-") ? "remove" : "context";
10313
+ const text = line.slice(1);
10314
+ const number = kind === "add" ? newLine++ : kind === "remove" ? oldLine++ : newLine;
10315
+ if (kind === "context") {
10316
+ oldLine++;
10317
+ newLine++;
10318
+ }
10319
+ return {
10320
+ number,
10321
+ text
10322
+ };
10323
+ });
10324
+ }
9654
10325
  function parseBlocks(md) {
9655
10326
  const lines = md.split("\n");
9656
10327
  const blocks = [];
9657
10328
  let i = 0;
9658
- const isBlank = (s) => s !== void 0 && s.trim() === "";
9659
10329
  while (i < lines.length) {
9660
10330
  const line = lines[i];
9661
10331
  if (line.trim() === "") {
@@ -9709,15 +10379,6 @@ function parseBlocks(md) {
9709
10379
  });
9710
10380
  continue;
9711
10381
  }
9712
- const listMarker = (s) => {
9713
- const m = /^(\s*)([-*+]|\d+\.)\s+(.*)$/.exec(s);
9714
- if (!m) return;
9715
- return {
9716
- depth: Math.floor(m[1].length / 2),
9717
- ordered: /\d/.test(m[2]),
9718
- text: m[3]
9719
- };
9720
- };
9721
10382
  if (listMarker(line)) {
9722
10383
  const items = [];
9723
10384
  const gutters = [];
@@ -9773,9 +10434,11 @@ function parseBlocks(md) {
9773
10434
  para.push(lines[i]);
9774
10435
  i += 1;
9775
10436
  }
9776
- blocks.push({
10437
+ if (para.length === 1 && MARKDOWN_IMAGE_LINE.test(para[0])) blocks.push({ t: "image" });
10438
+ else blocks.push({
9777
10439
  t: "p",
9778
- lines: hardLines(para)
10440
+ lines: hardLines(para),
10441
+ images: para.some((row) => MARKDOWN_IMAGE.test(row))
9779
10442
  });
9780
10443
  }
9781
10444
  return blocks;
@@ -9799,10 +10462,16 @@ function markdownHeight(md, m, extraPx = 0) {
9799
10462
  const r = textLines(line, cols);
9800
10463
  acc = add(acc, {
9801
10464
  px: r.lines * m.line,
9802
- exact: r.exact
10465
+ exact: r.exact && !block.images
9803
10466
  });
9804
10467
  }
9805
10468
  break;
10469
+ case "image":
10470
+ acc = add(acc, {
10471
+ px: 12 * m.line,
10472
+ exact: true
10473
+ });
10474
+ break;
9806
10475
  case "h": {
9807
10476
  const r = textLines(block.text, cols);
9808
10477
  acc = add(acc, {
@@ -9874,24 +10543,7 @@ function markdownHeight(md, m, extraPx = 0) {
9874
10543
  };
9875
10544
  }
9876
10545
  function diffHeight(patch, m, extraPx = 0) {
9877
- const walk = (hunk) => {
9878
- let newLine = hunk.newStart;
9879
- let oldLine = hunk.oldStart;
9880
- return hunk.lines.map((line) => {
9881
- const kind = line.startsWith("+") ? "add" : line.startsWith("-") ? "remove" : "context";
9882
- const text = line.slice(1);
9883
- const number = kind === "add" ? newLine++ : kind === "remove" ? oldLine++ : newLine;
9884
- if (kind === "context") {
9885
- oldLine++;
9886
- newLine++;
9887
- }
9888
- return {
9889
- number,
9890
- text
9891
- };
9892
- });
9893
- };
9894
- const hunks = patch.hunks.map(walk);
10546
+ const hunks = patch.hunks.map(numberHunk);
9895
10547
  const numbered = patch.hunks.some((hunk) => hunk.newStart > 0);
9896
10548
  const width = numbered ? String(Math.max(...hunks.flat().map((row) => row.number), 1)).length : 0;
9897
10549
  const columns = numbered ? width + 3 : 2;
@@ -9924,9 +10576,9 @@ function toolRowHeight(item, m, extraPx) {
9924
10576
  gutterCells: 2,
9925
10577
  extraPx
9926
10578
  });
9927
- const images = item.result?.images;
9928
- if (images?.length) acc = add(acc, {
9929
- px: images.length * 12 * m.line,
10579
+ const boxes = (item.result?.images?.length ?? 0) + (hostImagePathOf(item) === void 0 ? 0 : 1);
10580
+ if (boxes > 0) acc = add(acc, {
10581
+ px: boxes * 12 * m.line,
9930
10582
  exact: true
9931
10583
  });
9932
10584
  if (item.patch) return add(acc, diffHeight(item.patch, m, extraPx));
@@ -10951,9 +11603,12 @@ function useTranscriptJumps(options) {
10951
11603
  }
10952
11604
  //#endregion
10953
11605
  //#region src/components/terminal/TerminalTranscript.tsx
10954
- function TerminalItemView({ item, fileUrl }) {
11606
+ function TerminalItemView({ item, fileUrl, attachmentUrl }) {
10955
11607
  switch (item.kind) {
10956
- case "user": return /* @__PURE__ */ jsx(UserRow, { item });
11608
+ case "user": return /* @__PURE__ */ jsx(UserRow, {
11609
+ item,
11610
+ attachmentUrl
11611
+ });
10957
11612
  case "assistant_text": return /* @__PURE__ */ jsx(AssistantRow, { item });
10958
11613
  case "thinking": return /* @__PURE__ */ jsx(ThinkingRow, { item });
10959
11614
  case "tool_call": return isPeerSend(item) ? /* @__PURE__ */ jsx(PeerSendRow, { item }) : /* @__PURE__ */ jsx(ToolRow, { item });
@@ -11021,10 +11676,17 @@ function TaskRow({ block, fileUrl, onOpenSubagent }) {
11021
11676
  children: [brief ? /* @__PURE__ */ jsx(BriefRow, {
11022
11677
  text: brief,
11023
11678
  terminal: true
11024
- }) : null, block.children.map((leaf, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && blockNeedsBlank(block.children[index - 1], leaf) ? /* @__PURE__ */ jsx(Blank, {}) : null, "run" in leaf ? /* @__PURE__ */ jsx(RunRow, { items: leaf.run }) : /* @__PURE__ */ jsx(TerminalItemView, {
11025
- item: leaf.item,
11026
- fileUrl
11027
- })] }, leaf.key))]
11679
+ }) : null, block.children.map((leaf, index) => {
11680
+ const next = block.children[index + 1];
11681
+ const view = "run" in leaf ? /* @__PURE__ */ jsx(RunRow, { items: leaf.run }) : /* @__PURE__ */ jsx(TerminalItemView, {
11682
+ item: leaf.item,
11683
+ fileUrl
11684
+ });
11685
+ return /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && blockNeedsBlank(block.children[index - 1], leaf) ? /* @__PURE__ */ jsx(Blank, {}) : null, next && !blockNeedsBlank(leaf, next) ? /* @__PURE__ */ jsx(ActionPlacementProvider, {
11686
+ value: "inline",
11687
+ children: view
11688
+ }) : view] }, leaf.key);
11689
+ })]
11028
11690
  }) : null]
11029
11691
  });
11030
11692
  return onOpenSubagent === void 0 ? row : /* @__PURE__ */ jsx(WithActions, {
@@ -11059,13 +11721,19 @@ function TerminalTranscript({ state, fileUrl, fontSize, lineHeight, affordances,
11059
11721
  className: cn("term-transcript", className),
11060
11722
  children: /* @__PURE__ */ jsxs(PeerNamesProvider, {
11061
11723
  items: state.items,
11062
- children: [blocks.map((block, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && blockNeedsBlank(blocks[index - 1], block) ? /* @__PURE__ */ jsx(Blank, {}) : null, "run" in block ? /* @__PURE__ */ jsx(RunRow, { items: block.run }) : "item" in block ? /* @__PURE__ */ jsx(TerminalItemView, {
11063
- item: block.item,
11064
- fileUrl
11065
- }) : /* @__PURE__ */ jsx(TaskRow, {
11066
- block,
11067
- fileUrl
11068
- })] }, block.key)), working(state) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [state.items.length > 0 ? /* @__PURE__ */ jsx(Blank, {}) : null, /* @__PURE__ */ jsx(WorkingRow, {
11724
+ children: [blocks.map((block, index) => {
11725
+ const next = blocks[index + 1];
11726
+ return /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && blockNeedsBlank(blocks[index - 1], block) ? /* @__PURE__ */ jsx(Blank, {}) : null, /* @__PURE__ */ jsx(ActionPlacementProvider, {
11727
+ value: next && !blockNeedsBlank(block, next) ? "inline" : "below",
11728
+ children: "run" in block ? /* @__PURE__ */ jsx(RunRow, { items: block.run }) : "item" in block ? /* @__PURE__ */ jsx(TerminalItemView, {
11729
+ item: block.item,
11730
+ fileUrl
11731
+ }) : /* @__PURE__ */ jsx(TaskRow, {
11732
+ block,
11733
+ fileUrl
11734
+ })
11735
+ })] }, block.key);
11736
+ }), working(state) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [state.items.length > 0 ? /* @__PURE__ */ jsx(Blank, {}) : null, /* @__PURE__ */ jsx(WorkingRow, {
11069
11737
  label: state.status === "starting" ? "Starting…" : "Working…",
11070
11738
  startedAt: runStartedAt,
11071
11739
  tokens: state.contextUsage?.totalTokens
@@ -11163,13 +11831,6 @@ function Reasoning({ children, isStreaming = false, defaultOpen, className }) {
11163
11831
  //#endregion
11164
11832
  //#region src/components/agent/ToolCallCard.tsx
11165
11833
  const RESULT_PREVIEW_CHARS = 2e3;
11166
- const IMAGE_TOOLS = /* @__PURE__ */ new Set(["CodexImageGeneration", "CodexImageView"]);
11167
- function imagePathOf(item) {
11168
- if (!IMAGE_TOOLS.has(item.name)) return;
11169
- const input = item.input;
11170
- const path = input?.savedPath ?? input?.path;
11171
- return typeof path === "string" ? path : void 0;
11172
- }
11173
11834
  const STATE_BADGE = {
11174
11835
  running: {
11175
11836
  label: "Running",
@@ -11252,7 +11913,7 @@ function ToolCallCard({ item, hostImage, className }) {
11252
11913
  const [fullResult, setFullResult] = useState(false);
11253
11914
  const [fetching, setFetching] = useState(false);
11254
11915
  const fetchResult = useToolResultFetcher();
11255
- const imagePath = imagePathOf(item);
11916
+ const imagePath = hostImagePathOf(item);
11256
11917
  const status = item.status ?? (item.result === void 0 ? "running" : "settled");
11257
11918
  const badge = STATE_BADGE[status];
11258
11919
  const isError = status === "failed" || item.result?.isError === true;
@@ -11308,7 +11969,7 @@ function ToolCallCard({ item, hostImage, className }) {
11308
11969
  }) : null] }) : null
11309
11970
  ]
11310
11971
  }) : null;
11311
- const image = imagePath && hostImage ? /* @__PURE__ */ jsx(HostImage, {
11972
+ const image = imagePath ? /* @__PURE__ */ jsx(HostImage, {
11312
11973
  path: imagePath,
11313
11974
  load: hostImage
11314
11975
  }) : null;
@@ -11316,6 +11977,7 @@ function ToolCallCard({ item, hostImage, className }) {
11316
11977
  className: "flex flex-col gap-2 border-t border-border p-2.5",
11317
11978
  children: item.result.images.map((ref) => /* @__PURE__ */ jsx(ResultImage, {
11318
11979
  toolUseId: item.id,
11980
+ name: resultImageName(item.name, ref),
11319
11981
  image: ref
11320
11982
  }, ref.partIndex))
11321
11983
  }) : null;
@@ -11371,17 +12033,24 @@ function PlainPayload({ code, label, className }) {
11371
12033
  className
11372
12034
  });
11373
12035
  }
11374
- function ResultImage({ toolUseId, image }) {
12036
+ function ResultImage({ toolUseId, name, image }) {
11375
12037
  const { src, failed } = useToolResultImageSrc({
11376
12038
  toolUseId,
11377
12039
  ...image
11378
12040
  });
11379
12041
  return /* @__PURE__ */ jsx("div", {
11380
12042
  className: "flex h-60 items-start overflow-hidden rounded-md border border-border bg-surface-hover",
11381
- children: src ? /* @__PURE__ */ jsx("img", {
11382
- src,
11383
- alt: imagePlaceholder(image),
11384
- className: "h-full max-w-full object-contain"
12043
+ children: src ? /* @__PURE__ */ jsx(ViewableImage, {
12044
+ image: {
12045
+ src,
12046
+ name
12047
+ },
12048
+ className: "h-full max-w-full",
12049
+ children: /* @__PURE__ */ jsx("img", {
12050
+ src,
12051
+ alt: name,
12052
+ className: "h-full max-w-full object-contain"
12053
+ })
11385
12054
  }) : /* @__PURE__ */ jsx("span", {
11386
12055
  className: "p-2 text-label text-fg-4",
11387
12056
  children: failed ? IMAGE_UNAVAILABLE : imagePlaceholder(image)
@@ -11389,24 +12058,22 @@ function ResultImage({ toolUseId, image }) {
11389
12058
  });
11390
12059
  }
11391
12060
  function HostImage({ path, load }) {
11392
- const [src, setSrc] = useState();
11393
- useEffect(() => {
11394
- let cancelled = false;
11395
- setSrc(void 0);
11396
- load(path).then((url) => {
11397
- if (!cancelled) setSrc(url);
11398
- }).catch(() => {});
11399
- return () => {
11400
- cancelled = true;
11401
- };
11402
- }, [path, load]);
12061
+ const { src } = useHostImageSrc(path, load);
11403
12062
  if (!src) return null;
12063
+ const name = baseName(path);
11404
12064
  return /* @__PURE__ */ jsx("div", {
11405
12065
  className: "border-t border-border p-2.5",
11406
- children: /* @__PURE__ */ jsx("img", {
11407
- src,
11408
- alt: path.split("/").pop() ?? "Generated image",
11409
- className: "max-h-96 w-auto max-w-full rounded-md border border-border"
12066
+ children: /* @__PURE__ */ jsx(ViewableImage, {
12067
+ image: {
12068
+ src,
12069
+ name
12070
+ },
12071
+ className: "block max-w-full",
12072
+ children: /* @__PURE__ */ jsx("img", {
12073
+ src,
12074
+ alt: name,
12075
+ className: "max-h-96 w-auto max-w-full rounded-md border border-border"
12076
+ })
11410
12077
  })
11411
12078
  });
11412
12079
  }
@@ -11451,35 +12118,45 @@ function NoticeRow({ item }) {
11451
12118
  function TranscriptItemView({ item, fileUrl, attachmentUrl, hostImage, terminal }) {
11452
12119
  if (terminal) return /* @__PURE__ */ jsx(TerminalItemView, {
11453
12120
  item,
11454
- fileUrl
12121
+ fileUrl,
12122
+ attachmentUrl
11455
12123
  });
11456
12124
  switch (item.kind) {
11457
- case "user": return /* @__PURE__ */ jsxs(Message, {
11458
- from: "user",
11459
- children: [
11460
- item.origin ? /* @__PURE__ */ jsx("div", {
11461
- className: "wd-peer-origin text-xs text-muted-foreground",
11462
- children: peerLabel(item.origin)
11463
- }) : null,
11464
- item.attachments?.length ? /* @__PURE__ */ jsx(SentAttachments, {
11465
- attachments: item.attachments,
11466
- attachmentUrl
11467
- }) : null,
11468
- item.text ? /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(PromptTokenText, { text: item.text }) }) : null
11469
- ]
12125
+ case "user": return /* @__PURE__ */ jsx(WithActions, {
12126
+ actions: /* @__PURE__ */ jsx(BookmarkAction, { id: item.id }),
12127
+ children: /* @__PURE__ */ jsxs(Message, {
12128
+ from: "user",
12129
+ children: [
12130
+ item.origin ? /* @__PURE__ */ jsx("div", {
12131
+ className: "wd-peer-origin text-xs text-muted-foreground",
12132
+ children: peerLabel(item.origin)
12133
+ }) : null,
12134
+ item.attachments?.length ? /* @__PURE__ */ jsx(SentAttachments, {
12135
+ attachments: item.attachments,
12136
+ attachmentUrl
12137
+ }) : null,
12138
+ item.text ? /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(PromptTokenText, { text: item.text }) }) : null
12139
+ ]
12140
+ })
11470
12141
  });
11471
- case "assistant_text": return /* @__PURE__ */ jsx(Message, {
11472
- from: "assistant",
11473
- children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Response, {
11474
- streaming: item.streaming,
11475
- children: item.text
11476
- }) })
12142
+ case "assistant_text": return /* @__PURE__ */ jsx(WithActions, {
12143
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BookmarkAction, { id: item.id }), item.streaming ? null : /* @__PURE__ */ jsx(CopyAction, {
12144
+ text: item.text,
12145
+ label: "Copy message"
12146
+ })] }),
12147
+ children: /* @__PURE__ */ jsx(Message, {
12148
+ from: "assistant",
12149
+ children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Response, {
12150
+ streaming: item.streaming,
12151
+ children: item.text
12152
+ }) })
12153
+ })
11477
12154
  });
11478
12155
  case "thinking": return /* @__PURE__ */ jsx(Reasoning, {
11479
12156
  isStreaming: item.id === "streaming-thinking",
11480
12157
  children: item.text
11481
12158
  });
11482
- case "tool_call": return /* @__PURE__ */ jsx(ToolCallCard, {
12159
+ case "tool_call": return /* @__PURE__ */ jsx(ToolCard, {
11483
12160
  item,
11484
12161
  hostImage
11485
12162
  });
@@ -11494,6 +12171,20 @@ function TranscriptItemView({ item, fileUrl, attachmentUrl, hostImage, terminal
11494
12171
  default: return null;
11495
12172
  }
11496
12173
  }
12174
+ function ToolCard({ item, hostImage }) {
12175
+ const command = item.input?.command;
12176
+ const copyable = typeof command === "string" ? command : item.result?.text ?? "";
12177
+ return /* @__PURE__ */ jsx(WithActions, {
12178
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BookmarkAction, { id: item.id }), copyable ? /* @__PURE__ */ jsx(CopyAction, {
12179
+ text: copyable,
12180
+ label: "Copy"
12181
+ }) : null] }),
12182
+ children: /* @__PURE__ */ jsx(ToolCallCard, {
12183
+ item,
12184
+ hostImage
12185
+ })
12186
+ });
12187
+ }
11497
12188
  function ShellCard({ item }) {
11498
12189
  const [open, setOpen] = useState(false);
11499
12190
  const actions = useShellActions();
@@ -11510,58 +12201,59 @@ function ShellCard({ item }) {
11510
12201
  shellId,
11511
12202
  verify
11512
12203
  ]);
11513
- return /* @__PURE__ */ jsxs("div", {
11514
- "data-slot": "shell",
11515
- className: "overflow-hidden rounded-md border border-border bg-surface",
11516
- children: [
11517
- /* @__PURE__ */ jsxs("div", {
11518
- className: "flex items-center gap-2 px-3 py-1.5",
11519
- children: [
11520
- /* @__PURE__ */ jsx("span", {
11521
- className: "font-mono text-label text-[var(--wd-shell-accent)]",
11522
- children: "$"
11523
- }),
11524
- /* @__PURE__ */ jsx("button", {
11525
- type: "button",
11526
- "aria-expanded": open,
11527
- className: "min-w-0 flex-1 truncate text-left font-mono text-label text-fg-1",
11528
- onClick: () => {
11529
- const next = !open;
11530
- setOpen(next);
11531
- if (next && item.truncated && item.expanded === void 0 && !item.missing) actions.loadOutput(shellId);
11532
- },
11533
- children: shellLabel(item)
11534
- }),
11535
- /* @__PURE__ */ jsx("span", {
11536
- className: cn("shrink-0 text-label", failed ? "text-danger" : "text-fg-4"),
11537
- children: shellStatusText(item)
11538
- }),
11539
- actions.open ? /* @__PURE__ */ jsx("button", {
11540
- type: "button",
11541
- "aria-label": "Open terminal",
11542
- title: "Open terminal",
11543
- className: "shrink-0 text-label text-fg-3 hover:text-fg-1",
11544
- onClick: () => actions.open?.(shellId),
11545
- children: "⤢"
11546
- }) : null,
11547
- running ? /* @__PURE__ */ jsx("button", {
11548
- type: "button",
11549
- "aria-label": "Kill this shell",
11550
- className: "shrink-0 text-label text-fg-3",
11551
- onClick: () => void actions.kill(shellId),
11552
- children: "✕"
11553
- }) : null
11554
- ]
11555
- }),
11556
- lines.length > 0 ? /* @__PURE__ */ jsx("pre", {
11557
- className: "overflow-x-auto border-t border-border px-3 py-2 font-mono text-label whitespace-pre-wrap text-fg-2",
11558
- children: lines.join("\n")
12204
+ return /* @__PURE__ */ jsx(WithActions, {
12205
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [
12206
+ actions.open ? /* @__PURE__ */ jsx(OpenShellAction, { onOpen: () => actions.open?.(shellId) }) : null,
12207
+ actions.agentWrite && shellGrantable(item.shell) ? /* @__PURE__ */ jsx(AgentWriteAction, {
12208
+ granted: item.shell.agentWrite === true,
12209
+ label: shellAgentWriteLabel(item.shell),
12210
+ onToggle: () => void actions.agentWrite?.(shellId, item.shell.agentWrite !== true)
11559
12211
  }) : null,
11560
- footer ? /* @__PURE__ */ jsx("div", {
11561
- className: "px-3 pb-2 text-label text-fg-4",
11562
- children: footer
11563
- }) : null
11564
- ]
12212
+ running ? /* @__PURE__ */ jsx(KillShellAction, { onKill: () => void actions.kill(shellId) }) : null,
12213
+ /* @__PURE__ */ jsx(BookmarkAction, { id: item.id }),
12214
+ /* @__PURE__ */ jsx(CopyAction, {
12215
+ text: item.shell.command,
12216
+ label: "Copy command"
12217
+ })
12218
+ ] }),
12219
+ children: /* @__PURE__ */ jsxs("div", {
12220
+ "data-slot": "shell",
12221
+ className: "overflow-hidden rounded-md border border-border bg-surface",
12222
+ children: [
12223
+ /* @__PURE__ */ jsxs("div", {
12224
+ className: "flex items-center gap-2 px-3 py-1.5",
12225
+ children: [
12226
+ /* @__PURE__ */ jsx("span", {
12227
+ className: "font-mono text-label text-[var(--wd-shell-accent)]",
12228
+ children: "$"
12229
+ }),
12230
+ /* @__PURE__ */ jsx("button", {
12231
+ type: "button",
12232
+ "aria-expanded": open,
12233
+ className: "min-w-0 flex-1 truncate text-left font-mono text-label text-fg-1",
12234
+ onClick: () => {
12235
+ const next = !open;
12236
+ setOpen(next);
12237
+ if (next && item.truncated && item.expanded === void 0 && !item.missing) actions.loadOutput(shellId);
12238
+ },
12239
+ children: shellLabel(item)
12240
+ }),
12241
+ /* @__PURE__ */ jsx("span", {
12242
+ className: cn("shrink-0 text-label", failed ? "text-danger" : "text-fg-4"),
12243
+ children: shellStatusText(item)
12244
+ })
12245
+ ]
12246
+ }),
12247
+ lines.length > 0 ? /* @__PURE__ */ jsx("pre", {
12248
+ className: "overflow-x-auto border-t border-border px-3 py-2 font-mono text-label whitespace-pre-wrap text-fg-2",
12249
+ children: lines.join("\n")
12250
+ }) : null,
12251
+ footer ? /* @__PURE__ */ jsx("div", {
12252
+ className: "px-3 pb-2 text-label text-fg-4",
12253
+ children: footer
12254
+ }) : null
12255
+ ]
12256
+ })
11565
12257
  });
11566
12258
  }
11567
12259
  function CompactionRow({ item }) {
@@ -11612,10 +12304,17 @@ function SentAttachments({ attachments, attachmentUrl }) {
11612
12304
  className: "mb-1 flex flex-wrap justify-start gap-1.5",
11613
12305
  children: attachments.map((attachment) => {
11614
12306
  const href = attachmentUrl?.(attachment.id);
11615
- return attachment.mediaType.startsWith("image/") && href ? /* @__PURE__ */ jsx("img", {
11616
- src: href,
11617
- alt: attachment.name,
11618
- className: "size-20 rounded-md border border-border object-cover"
12307
+ return attachment.mediaType.startsWith("image/") && href ? /* @__PURE__ */ jsx(ViewableImage, {
12308
+ image: {
12309
+ src: href,
12310
+ name: attachment.name
12311
+ },
12312
+ className: "rounded-md",
12313
+ children: /* @__PURE__ */ jsx("img", {
12314
+ src: href,
12315
+ alt: attachment.name,
12316
+ className: "size-20 rounded-md border border-border object-cover"
12317
+ })
11619
12318
  }, attachment.id) : /* @__PURE__ */ jsx("span", {
11620
12319
  className: "rounded-full border border-border bg-surface px-2.5 py-1 text-body-xs text-fg-3",
11621
12320
  children: attachment.name
@@ -11839,7 +12538,8 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
11839
12538
  children: virtualizer.getVirtualItems().map((virtualRow) => {
11840
12539
  const row = rows[virtualRow.index];
11841
12540
  const gapClass = virtualRow.index > 0 && (!terminal || gapBefore(rows, virtualRow.index)) ? gap.className : void 0;
11842
- const content = "run" in row ? /* @__PURE__ */ jsx("div", {
12541
+ const flushBelow = terminal && virtualRow.index < rows.length - 1 && !gapBefore(rows, virtualRow.index + 1);
12542
+ const view = "run" in row ? /* @__PURE__ */ jsx("div", {
11843
12543
  className: cn(read(boundary, row.index) && "opacity-45"),
11844
12544
  children: /* @__PURE__ */ jsx(RunRow, { items: row.run })
11845
12545
  }) : "item" in row ? /* @__PURE__ */ jsx("div", {
@@ -11866,6 +12566,10 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
11866
12566
  onOpenSubagent
11867
12567
  })
11868
12568
  });
12569
+ const content = /* @__PURE__ */ jsx(ActionPlacementProvider, {
12570
+ value: flushBelow ? "inline" : "below",
12571
+ children: view
12572
+ });
11869
12573
  if (stickyPrompt && "item" in row && row.item.kind === "user" && parentOf(row.item) === void 0) {
11870
12574
  const next = promptRows.find((index) => index > virtualRow.index);
11871
12575
  const laneEnd = next === void 0 ? virtualizer.getTotalSize() : measurements[next]?.start ?? virtualRow.start;
@@ -11937,7 +12641,14 @@ function showLoader(state) {
11937
12641
  return last.kind !== "turn_result" || state.status === "running";
11938
12642
  }
11939
12643
  function TerminalShell({ active, fontSize, lineHeight, affordances, children }) {
11940
- if (!active) return /* @__PURE__ */ jsx(Fragment$1, { children });
12644
+ if (!active) return /* @__PURE__ */ jsx("div", {
12645
+ "data-cards": "",
12646
+ className: "contents",
12647
+ children: /* @__PURE__ */ jsx(AffordanceProvider, {
12648
+ value: resolveAffordances(affordances),
12649
+ children
12650
+ })
12651
+ });
11941
12652
  return /* @__PURE__ */ jsx(TerminalSurface, {
11942
12653
  fontSize,
11943
12654
  lineHeight,
@@ -11947,11 +12658,11 @@ function TerminalShell({ active, fontSize, lineHeight, affordances, children })
11947
12658
  children
11948
12659
  });
11949
12660
  }
11950
- function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage, variant = "cards", density = "comfortable", fontSize, lineHeight, affordances, stickyPrompt = false, scrubber, bookmarks, sessionNames, replaying = false, catchUp, jumpToRecapRef, repinRef, reveal, frame, onOpenSubagent, emptyState, className }) {
12661
+ function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage, variant = "cards", fontSize, lineHeight, affordances, stickyPrompt = false, scrubber, bookmarks, sessionNames, replaying = false, catchUp, jumpToRecapRef, repinRef, reveal, frame, onOpenSubagent, emptyState, className }) {
11951
12662
  const terminal = variant === "terminal";
11952
12663
  const items = useMemo(() => frame ? subagentItems(state.items, frame.parentToolUseId) : state.items, [state.items, frame]);
11953
12664
  const frameTask = useMemo(() => frame ? state.items.find((item) => item.kind === "tool_call" && item.id === frame.parentToolUseId) : void 0, [state.items, frame]);
11954
- const gap = ROW_GAP[variant][density];
12665
+ const gap = ROW_GAP[variant];
11955
12666
  const runStartedAt = useRunStart(state.status);
11956
12667
  const boundary = !frame && catchUp && catchUp.from > 0 && catchUp.from < state.items.length ? catchUp.from : void 0;
11957
12668
  const recap = useMemo(() => boundary === void 0 ? void 0 : recapLine(summarizeSince(state, boundary)), [state, boundary]);
@@ -11985,83 +12696,86 @@ function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage,
11985
12696
  const skillNames = useMemo(() => state.skills?.map((skill) => skill.name), [state.skills]);
11986
12697
  return /* @__PURE__ */ jsx(TranscriptVariantProvider, {
11987
12698
  value: variant,
11988
- children: /* @__PURE__ */ jsx(SkillNamesProvider, {
11989
- names: skillNames,
11990
- children: /* @__PURE__ */ jsx(SessionNamesProvider, {
11991
- names: sessionNames,
11992
- children: /* @__PURE__ */ jsxs(Conversation, {
11993
- className: cn(replaying && "invisible", className),
11994
- children: [
11995
- /* @__PURE__ */ jsx(ConversationContent, {
11996
- className: cn(terminal && "gap-0 p-0"),
11997
- children: /* @__PURE__ */ jsxs(TerminalShell, {
11998
- active: terminal,
11999
- fontSize,
12000
- lineHeight,
12001
- affordances,
12002
- children: [
12003
- frame ? frameTask === void 0 && !replaying ? /* @__PURE__ */ jsx("div", {
12004
- className: cn(terminal ? "term-row text-fg-4" : "p-4 text-body-sm text-fg-4"),
12005
- children: "This sub-agent's work is not in this transcript."
12006
- }) : null : items.length === 0 && state.status !== "starting" ? emptyState !== void 0 ? emptyState : /* @__PURE__ */ jsx(SessionEmptyState, {
12007
- cwd: state.cwd,
12008
- hasCommands: !!state.commands?.length,
12009
- hasSkills: !!state.skills?.some((s) => s.enabled),
12010
- canBrowseFiles
12011
- }) : null,
12012
- frame && frameTask === void 0 && !replaying ? null : /* @__PURE__ */ jsx(TranscriptRows, {
12013
- rows,
12014
- boundary,
12015
- since: catchUp?.since,
12016
- terminal,
12017
- replaying,
12018
- stickyPrompt: !frame && stickyPrompt,
12019
- gap,
12020
- fontSize,
12021
- lineHeight,
12022
- items,
12023
- pendingApprovals: state.pendingApprovals,
12024
- scrubber,
12025
- bookmarks: frame ? void 0 : bookmarks,
12026
- affordances,
12027
- fileUrl,
12028
- attachmentUrl,
12029
- hostImage,
12030
- jumpToRecapRef,
12031
- repinRef,
12032
- reveal: frame ? void 0 : reveal,
12033
- frameParentId: frame?.parentToolUseId,
12034
- onOpenSubagent: frame ? void 0 : onOpenSubagent
12035
- }),
12036
- (frame ? frameTask !== void 0 && taskBusy(frameTask, items) : showLoader(state)) ? terminal ? /* @__PURE__ */ jsxs(Fragment$1, { children: [state.items.length > 0 ? /* @__PURE__ */ jsx("div", {
12037
- className: "term-blank",
12038
- "aria-hidden": true
12039
- }) : null, /* @__PURE__ */ jsx(WorkingRow, {
12040
- label: state.status === "starting" ? "Starting…" : "Working…",
12041
- startedAt: runStartedAt,
12042
- tokens: state.contextUsage?.totalTokens
12043
- })] }) : /* @__PURE__ */ jsx(Loader, { label: state.status === "starting" ? "Starting session…" : void 0 }) : null
12044
- ]
12045
- })
12046
- }),
12047
- /* @__PURE__ */ jsx(ConversationScrollButton, {}),
12048
- replaying ? /* @__PURE__ */ jsx("div", {
12049
- "data-slot": "transcript-hold",
12050
- "aria-hidden": true,
12051
- className: "wd-hold-appear visible pointer-events-none absolute inset-0 overflow-hidden",
12052
- children: /* @__PURE__ */ jsx("div", {
12053
- className: cn("mx-auto w-full max-w-[var(--wd-transcript-max-width)]", !terminal && "px-4 py-4"),
12054
- children: terminal ? /* @__PURE__ */ jsx(TerminalSurface, {
12699
+ children: /* @__PURE__ */ jsx(HostImageProvider, {
12700
+ value: hostImage,
12701
+ children: /* @__PURE__ */ jsx(SkillNamesProvider, {
12702
+ names: skillNames,
12703
+ children: /* @__PURE__ */ jsx(SessionNamesProvider, {
12704
+ names: sessionNames,
12705
+ children: /* @__PURE__ */ jsxs(Conversation, {
12706
+ className: cn(replaying && "invisible", className),
12707
+ children: [
12708
+ /* @__PURE__ */ jsx(ConversationContent, {
12709
+ className: cn(terminal && "gap-0 p-0"),
12710
+ children: /* @__PURE__ */ jsxs(TerminalShell, {
12711
+ active: terminal,
12055
12712
  fontSize,
12056
12713
  lineHeight,
12057
- affordances: false,
12058
- bleed: "1ch",
12059
- className: "term-transcript",
12060
- children: /* @__PURE__ */ jsx(WorkingRow, { label: "Loading…" })
12061
- }) : /* @__PURE__ */ jsx(Loader, { label: "Loading session…" })
12062
- })
12063
- }) : null
12064
- ]
12714
+ affordances,
12715
+ children: [
12716
+ frame ? frameTask === void 0 && !replaying ? /* @__PURE__ */ jsx("div", {
12717
+ className: cn(terminal ? "term-row text-fg-4" : "p-4 text-body-sm text-fg-4"),
12718
+ children: "This sub-agent's work is not in this transcript."
12719
+ }) : null : items.length === 0 && state.status !== "starting" ? emptyState !== void 0 ? emptyState : /* @__PURE__ */ jsx(SessionEmptyState, {
12720
+ cwd: state.cwd,
12721
+ hasCommands: !!state.commands?.length,
12722
+ hasSkills: !!state.skills?.some((s) => s.enabled),
12723
+ canBrowseFiles
12724
+ }) : null,
12725
+ frame && frameTask === void 0 && !replaying ? null : /* @__PURE__ */ jsx(TranscriptRows, {
12726
+ rows,
12727
+ boundary,
12728
+ since: catchUp?.since,
12729
+ terminal,
12730
+ replaying,
12731
+ stickyPrompt: !frame && stickyPrompt,
12732
+ gap,
12733
+ fontSize,
12734
+ lineHeight,
12735
+ items,
12736
+ pendingApprovals: state.pendingApprovals,
12737
+ scrubber,
12738
+ bookmarks: frame ? void 0 : bookmarks,
12739
+ affordances,
12740
+ fileUrl,
12741
+ attachmentUrl,
12742
+ hostImage,
12743
+ jumpToRecapRef,
12744
+ repinRef,
12745
+ reveal: frame ? void 0 : reveal,
12746
+ frameParentId: frame?.parentToolUseId,
12747
+ onOpenSubagent: frame ? void 0 : onOpenSubagent
12748
+ }),
12749
+ (frame ? frameTask !== void 0 && taskBusy(frameTask, items) : showLoader(state)) ? terminal ? /* @__PURE__ */ jsxs(Fragment$1, { children: [state.items.length > 0 ? /* @__PURE__ */ jsx("div", {
12750
+ className: "term-blank",
12751
+ "aria-hidden": true
12752
+ }) : null, /* @__PURE__ */ jsx(WorkingRow, {
12753
+ label: state.status === "starting" ? "Starting…" : "Working…",
12754
+ startedAt: runStartedAt,
12755
+ tokens: state.contextUsage?.totalTokens
12756
+ })] }) : /* @__PURE__ */ jsx(Loader, { label: state.status === "starting" ? "Starting session…" : void 0 }) : null
12757
+ ]
12758
+ })
12759
+ }),
12760
+ /* @__PURE__ */ jsx(ConversationScrollButton, {}),
12761
+ replaying ? /* @__PURE__ */ jsx("div", {
12762
+ "data-slot": "transcript-hold",
12763
+ "aria-hidden": true,
12764
+ className: "wd-hold-appear visible pointer-events-none absolute inset-0 overflow-hidden",
12765
+ children: /* @__PURE__ */ jsx("div", {
12766
+ className: cn("mx-auto w-full max-w-[var(--wd-transcript-max-width)]", !terminal && "px-4 py-4"),
12767
+ children: terminal ? /* @__PURE__ */ jsx(TerminalSurface, {
12768
+ fontSize,
12769
+ lineHeight,
12770
+ affordances: false,
12771
+ bleed: "1ch",
12772
+ className: "term-transcript",
12773
+ children: /* @__PURE__ */ jsx(WorkingRow, { label: "Loading…" })
12774
+ }) : /* @__PURE__ */ jsx(Loader, { label: "Loading session…" })
12775
+ })
12776
+ }) : null
12777
+ ]
12778
+ })
12065
12779
  })
12066
12780
  })
12067
12781
  })
@@ -12395,7 +13109,7 @@ const INTERACTIVE = [
12395
13109
  "[role=\"radio\"]",
12396
13110
  "[role=\"tab\"]"
12397
13111
  ].join(",");
12398
- function SessionPanel({ client, sessionId, header, panelSurface = "internal", statusSurface = "internal", statusPlacement = "top", onOpenPanel, onVitals, transcriptVariant = "cards", transcriptDensity = "comfortable", transcriptFont = "sans", affordances, terminalMetrics, scrubber = false, bookmarks, peerMentions = true, onToggleBookmark, reveal, subagents, shells, openSubagent, onSubagentChange, openShell, onShellChange, stickyPrompt = false, controlsSurface = "internal", onControls, focusComposerOnClick = false, unseen, readOnly = false, approvalPrompts, toolHost, clientTools, cacheTranscript, emptyState, onLinkClick, onOpenFile, fontSize, className }) {
13112
+ function SessionPanel({ client, sessionId, header, panelSurface = "internal", statusSurface = "internal", statusPlacement = "top", onOpenPanel, onVitals, transcriptVariant = "cards", transcriptFont = "sans", affordances, terminalMetrics, scrubber = false, bookmarks, peerMentions = true, onToggleBookmark, reveal, subagents, shells, openSubagent, onSubagentChange, openShell, onShellChange, stickyPrompt = false, controlsSurface = "internal", onControls, focusComposerOnClick = false, unseen, readOnly = false, approvalPrompts, toolHost, clientTools, cacheTranscript, emptyState, onLinkClick, onOpenFile, fontSize, className }) {
12399
13113
  const effectiveTermFontSize = terminalMetrics?.fontSize ?? fontSize;
12400
13114
  const effectiveTermLineHeight = terminalMetrics?.lineHeight ?? (fontSize !== void 0 ? Math.round(fontSize * (18 / 13)) : void 0);
12401
13115
  const external = panelSurface === "external";
@@ -12406,7 +13120,7 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
12406
13120
  const [panel, setPanel] = useState();
12407
13121
  const [showCompletedTasks, setShowCompletedTasks] = useState(false);
12408
13122
  const [taskReveal, setTaskReveal] = useState();
12409
- const { state, connection, replaying, protocolMismatch, shell, pricing, models, effectiveModel, handle, send, runShell, approve, deny, interrupt, clearContext, setModel, setPermissionMode, reconnectNow, loadFullResult, loadShellOutput, verifyShell, killShell } = useClaudeSession(client, sessionId, {
13123
+ const { state, connection, replaying, protocolMismatch, shell, pricing, models, effectiveModel, handle, send, runShell, approve, deny, interrupt, clearContext, setModel, setPermissionMode, reconnectNow, loadFullResult, loadShellOutput, verifyShell, killShell, setShellAgentWrite } = useClaudeSession(client, sessionId, {
12410
13124
  onProtocolError: setProtocolError,
12411
13125
  cacheTranscript
12412
13126
  });
@@ -12419,16 +13133,19 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
12419
13133
  openShell,
12420
13134
  onShellChange
12421
13135
  });
13136
+ const agentWrite = state.session?.shellAgentWrite !== void 0 ? setShellAgentWrite : void 0;
12422
13137
  const shellActions = useMemo(() => ({
12423
13138
  loadOutput: loadShellOutput,
12424
13139
  verify: verifyShell,
12425
13140
  kill: killShell,
12426
- open: enterShell
13141
+ open: enterShell,
13142
+ agentWrite
12427
13143
  }), [
12428
13144
  loadShellOutput,
12429
13145
  verifyShell,
12430
13146
  killShell,
12431
- enterShell
13147
+ enterShell,
13148
+ agentWrite
12432
13149
  ]);
12433
13150
  useEffect(() => setProtocolError(void 0), [sessionId]);
12434
13151
  const { subagentId, enterSubagent, leaveSubagent, returnReveal, frameItems: subagentFrameItems, task: subagentTask, fallbackLabel: subagentFallbackLabel } = useSubagentFrame({
@@ -12762,251 +13479,248 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
12762
13479
  };
12763
13480
  return /* @__PURE__ */ jsx(TranscriptVariantProvider, {
12764
13481
  value: transcriptVariant,
12765
- children: /* @__PURE__ */ jsx(TranscriptDensityProvider, {
12766
- value: transcriptDensity,
12767
- children: /* @__PURE__ */ jsx(FileLinkProvider, {
12768
- value: fileLinks,
12769
- children: /* @__PURE__ */ jsx(ToolResultFetchProvider, {
12770
- value: loadFullResult,
12771
- children: /* @__PURE__ */ jsx(ShellActionsProvider, {
12772
- value: shellActions,
12773
- children: /* @__PURE__ */ jsx(ToolTitleProvider, {
12774
- value: state.toolTitles,
12775
- children: /* @__PURE__ */ jsx(ToolResultImageProvider, {
12776
- value: resultImages,
12777
- children: /* @__PURE__ */ jsxs("div", {
12778
- ref: panelRef,
12779
- "data-slot": "session-panel",
12780
- "data-agent-font": transcriptFont,
12781
- onClick: handleClick,
12782
- className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-bg", className),
12783
- style: fontSize !== void 0 ? { "--wd-font-size": `${Math.round(fontSize)}px` } : void 0,
12784
- children: [
12785
- headerTakesActions ? header({ actions: menu }) : header,
12786
- statusPlacement === "top" ? statusBar : null,
12787
- protocolMismatch !== void 0 ? /* @__PURE__ */ jsxs(Notice, {
12788
- level: "warning",
13482
+ children: /* @__PURE__ */ jsx(FileLinkProvider, {
13483
+ value: fileLinks,
13484
+ children: /* @__PURE__ */ jsx(ToolResultFetchProvider, {
13485
+ value: loadFullResult,
13486
+ children: /* @__PURE__ */ jsx(ShellActionsProvider, {
13487
+ value: shellActions,
13488
+ children: /* @__PURE__ */ jsx(ToolTitleProvider, {
13489
+ value: state.toolTitles,
13490
+ children: /* @__PURE__ */ jsx(ToolResultImageProvider, {
13491
+ value: resultImages,
13492
+ children: /* @__PURE__ */ jsx("div", {
13493
+ ref: panelRef,
13494
+ "data-slot": "session-panel",
13495
+ "data-agent-font": transcriptFont,
13496
+ onClick: handleClick,
13497
+ className: cn("relative flex h-full min-h-0 flex-col overflow-hidden bg-bg", className),
13498
+ style: fontSize !== void 0 ? { "--wd-font-size": `${Math.round(fontSize)}px` } : void 0,
13499
+ children: /* @__PURE__ */ jsxs(ImageViewerProvider, { children: [
13500
+ headerTakesActions ? header({ actions: menu }) : header,
13501
+ statusPlacement === "top" ? statusBar : null,
13502
+ protocolMismatch !== void 0 ? /* @__PURE__ */ jsxs(Notice, {
13503
+ level: "warning",
13504
+ children: [
13505
+ "Server speaks protocol v",
13506
+ protocolMismatch,
13507
+ ", this build renders v",
13508
+ PROTOCOL_VERSION,
13509
+ ". Some events may not render."
13510
+ ]
13511
+ }) : null,
13512
+ protocolError ? /* @__PURE__ */ jsx(Notice, {
13513
+ level: "error",
13514
+ onDismiss: () => setProtocolError(void 0),
13515
+ children: protocolError
13516
+ }) : null,
13517
+ framedShellId !== void 0 ? /* @__PURE__ */ jsx(ShellStrip, {
13518
+ shell: framedShell,
13519
+ label: framedShellLabel,
13520
+ onBack: leaveShell,
13521
+ onKill: () => void killShell(framedShellId),
13522
+ onAgentWrite: agentWrite ? (enabled) => void agentWrite(framedShellId, enabled) : void 0,
13523
+ terminal,
13524
+ fontSize: effectiveTermFontSize,
13525
+ lineHeight: effectiveTermLineHeight
13526
+ }) : null,
13527
+ framedShellId === void 0 && subagentId !== void 0 ? /* @__PURE__ */ jsx(SubagentStrip, {
13528
+ task: subagentTask,
13529
+ items: subagentFrameItems,
13530
+ label: subagentFallbackLabel,
13531
+ onBack: leaveSubagent,
13532
+ terminal,
13533
+ fontSize: effectiveTermFontSize,
13534
+ lineHeight: effectiveTermLineHeight
13535
+ }) : null,
13536
+ framedShellId !== void 0 ? /* @__PURE__ */ jsx(ShellTerminal, {
13537
+ handle,
13538
+ shellId: framedShellId,
13539
+ fontSize: effectiveTermFontSize
13540
+ }, framedShellId) : /* @__PURE__ */ jsx(BookmarkProvider, {
13541
+ value: bookmarkHandle,
13542
+ children: /* @__PURE__ */ jsx(Transcript, {
13543
+ state,
13544
+ fileUrl: sessionId ? (path) => client.sessionFileUrl(sessionId, path) : void 0,
13545
+ attachmentUrl: sessionId ? (id) => client.attachmentUrl(sessionId, id) : void 0,
13546
+ canBrowseFiles: hostFiles.available,
13547
+ sessionNames: peers.names,
13548
+ hostImage,
13549
+ variant: transcriptVariant,
13550
+ fontSize: effectiveTermFontSize,
13551
+ lineHeight: effectiveTermLineHeight,
13552
+ affordances,
13553
+ stickyPrompt,
13554
+ scrubber,
13555
+ bookmarks,
13556
+ replaying,
13557
+ catchUp: catchUp && newCount > 0 ? {
13558
+ from: catchUp.itemCount,
13559
+ since: catchUp.since
13560
+ } : void 0,
13561
+ reveal: taskReveal ?? frameReturnReveal ?? reveal,
13562
+ frame: subagentId === void 0 ? void 0 : { parentToolUseId: subagentId },
13563
+ onOpenSubagent: enterSubagent,
13564
+ emptyState,
13565
+ jumpToRecapRef: jumpToRecap,
13566
+ repinRef: repinTranscript
13567
+ }, subagentId ?? "session")
13568
+ }),
13569
+ catchUp && newCount > 0 && !replaying && subagentId === void 0 && framedShellId === void 0 ? /* @__PURE__ */ jsx("div", {
13570
+ className: "px-3 pb-1",
13571
+ children: /* @__PURE__ */ jsxs("div", {
13572
+ "data-slot": "catch-up",
13573
+ className: "mx-auto flex w-full max-w-[var(--wd-transcript-max-width)] items-center gap-2 text-label text-fg-3",
12789
13574
  children: [
12790
- "Server speaks protocol v",
12791
- protocolMismatch,
12792
- ", this build renders v",
12793
- PROTOCOL_VERSION,
12794
- ". Some events may not render."
13575
+ /* @__PURE__ */ jsx("span", {
13576
+ "aria-hidden": true,
13577
+ className: cn("select-none", terminal ? "text-fg-3" : "text-accent"),
13578
+ children: "※"
13579
+ }),
13580
+ /* @__PURE__ */ jsxs("span", {
13581
+ className: "min-w-0 flex-1 truncate",
13582
+ children: [
13583
+ newCount,
13584
+ " new ",
13585
+ newCount === 1 ? "row" : "rows",
13586
+ catchUp.since !== void 0 ? ` since you were last here` : ""
13587
+ ]
13588
+ }),
13589
+ /* @__PURE__ */ jsx("button", {
13590
+ type: "button",
13591
+ onClick: () => jumpToRecap.current?.(),
13592
+ className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
13593
+ children: "jump"
13594
+ }),
13595
+ /* @__PURE__ */ jsx("button", {
13596
+ type: "button",
13597
+ onClick: () => setCaughtUp(true),
13598
+ className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
13599
+ children: "dismiss"
13600
+ })
12795
13601
  ]
12796
- }) : null,
12797
- protocolError ? /* @__PURE__ */ jsx(Notice, {
12798
- level: "error",
12799
- onDismiss: () => setProtocolError(void 0),
12800
- children: protocolError
12801
- }) : null,
12802
- framedShellId !== void 0 ? /* @__PURE__ */ jsx(ShellStrip, {
12803
- shell: framedShell,
12804
- label: framedShellLabel,
12805
- onBack: leaveShell,
12806
- onKill: () => void killShell(framedShellId),
12807
- terminal,
12808
- fontSize: effectiveTermFontSize,
12809
- lineHeight: effectiveTermLineHeight
12810
- }) : null,
12811
- framedShellId === void 0 && subagentId !== void 0 ? /* @__PURE__ */ jsx(SubagentStrip, {
12812
- task: subagentTask,
12813
- items: subagentFrameItems,
12814
- label: subagentFallbackLabel,
12815
- onBack: leaveSubagent,
13602
+ })
13603
+ }) : null,
13604
+ !readOnly && capabilities.interactiveApprovals && state.pendingApprovals.length > 0 ? /* @__PURE__ */ jsx("div", {
13605
+ className: cn(terminal ? "pb-2" : "px-3 pb-2"),
13606
+ children: /* @__PURE__ */ jsx(PromptSurface, {
12816
13607
  terminal,
12817
- fontSize: effectiveTermFontSize,
12818
- lineHeight: effectiveTermLineHeight
12819
- }) : null,
12820
- framedShellId !== void 0 ? /* @__PURE__ */ jsx(ShellTerminal, {
12821
- handle,
12822
- shellId: framedShellId,
12823
- fontSize: effectiveTermFontSize
12824
- }, framedShellId) : /* @__PURE__ */ jsx(BookmarkProvider, {
12825
- value: bookmarkHandle,
12826
- children: /* @__PURE__ */ jsx(Transcript, {
12827
- state,
12828
- fileUrl: sessionId ? (path) => client.sessionFileUrl(sessionId, path) : void 0,
12829
- attachmentUrl: sessionId ? (id) => client.attachmentUrl(sessionId, id) : void 0,
12830
- canBrowseFiles: hostFiles.available,
12831
- sessionNames: peers.names,
12832
- hostImage,
12833
- variant: transcriptVariant,
12834
- density: transcriptDensity,
13608
+ metrics: {
12835
13609
  fontSize: effectiveTermFontSize,
12836
- lineHeight: effectiveTermLineHeight,
12837
- affordances,
12838
- stickyPrompt,
12839
- scrubber,
12840
- bookmarks,
12841
- replaying,
12842
- catchUp: catchUp && newCount > 0 ? {
12843
- from: catchUp.itemCount,
12844
- since: catchUp.since
12845
- } : void 0,
12846
- reveal: taskReveal ?? frameReturnReveal ?? reveal,
12847
- frame: subagentId === void 0 ? void 0 : { parentToolUseId: subagentId },
12848
- onOpenSubagent: enterSubagent,
12849
- emptyState,
12850
- jumpToRecapRef: jumpToRecap,
12851
- repinRef: repinTranscript
12852
- }, subagentId ?? "session")
12853
- }),
12854
- catchUp && newCount > 0 && !replaying && subagentId === void 0 && framedShellId === void 0 ? /* @__PURE__ */ jsx("div", {
12855
- className: "px-3 pb-1",
12856
- children: /* @__PURE__ */ jsxs("div", {
12857
- "data-slot": "catch-up",
12858
- className: "mx-auto flex w-full max-w-[var(--wd-transcript-max-width)] items-center gap-2 text-label text-fg-3",
12859
- children: [
12860
- /* @__PURE__ */ jsx("span", {
12861
- "aria-hidden": true,
12862
- className: cn("select-none", terminal ? "text-fg-3" : "text-accent"),
12863
- children: "※"
12864
- }),
12865
- /* @__PURE__ */ jsxs("span", {
12866
- className: "min-w-0 flex-1 truncate",
12867
- children: [
12868
- newCount,
12869
- " new ",
12870
- newCount === 1 ? "row" : "rows",
12871
- catchUp.since !== void 0 ? ` since you were last here` : ""
12872
- ]
12873
- }),
12874
- /* @__PURE__ */ jsx("button", {
12875
- type: "button",
12876
- onClick: () => jumpToRecap.current?.(),
12877
- className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
12878
- children: "jump"
12879
- }),
12880
- /* @__PURE__ */ jsx("button", {
12881
- type: "button",
12882
- onClick: () => setCaughtUp(true),
12883
- className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
12884
- children: "dismiss"
12885
- })
12886
- ]
12887
- })
12888
- }) : null,
12889
- !readOnly && capabilities.interactiveApprovals && state.pendingApprovals.length > 0 ? /* @__PURE__ */ jsx("div", {
12890
- className: cn(terminal ? "pb-2" : "px-3 pb-2"),
12891
- children: /* @__PURE__ */ jsx(PromptSurface, {
12892
- terminal,
12893
- metrics: {
12894
- fontSize: effectiveTermFontSize,
12895
- lineHeight: effectiveTermLineHeight
12896
- },
12897
- affordances,
12898
- children: state.pendingApprovals.map((request) => {
12899
- const HostPrompt = approvalPrompts?.[request.toolName];
12900
- if (HostPrompt) return /* @__PURE__ */ jsx(HostPrompt, {
12901
- request,
12902
- onApprove: approve,
12903
- onDeny: deny
12904
- }, request.id);
12905
- const isQuestion = request.toolName === "AskUserQuestion" && parseUserQuestions(request.input).length > 0;
12906
- if (terminal) return isQuestion ? /* @__PURE__ */ jsx(TerminalQuestionPrompt, {
12907
- request,
12908
- onAnswer: approve,
12909
- onDismiss: (id) => deny(id, "Question dismissed by user")
12910
- }, request.id) : /* @__PURE__ */ jsx(TerminalPermissionPrompt, {
12911
- request,
12912
- onApprove: approve,
12913
- onDeny: deny
12914
- }, request.id);
12915
- return isQuestion ? /* @__PURE__ */ jsx(QuestionPrompt, {
12916
- request,
12917
- onAnswer: approve,
12918
- onDismiss: (id) => deny(id, "Question dismissed by user")
12919
- }, request.id) : /* @__PURE__ */ jsx(PermissionPrompt, {
12920
- request,
12921
- onApprove: approve,
12922
- onDeny: deny
12923
- }, request.id);
12924
- })
12925
- })
12926
- }) : null,
12927
- readOnly || subagentId !== void 0 || framedShellId !== void 0 ? null : /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(Composer, {
12928
- ref: composerRef,
12929
- onSend: handleSend,
12930
- onInterrupt: interrupt,
12931
- busy,
12932
- disabled: ended || !sessionId,
12933
- commands: capabilities.slashCommands ? state.commands : void 0,
12934
- skills: capabilities.skillsList ? state.skills : void 0,
12935
- clientCommands,
12936
- attachments,
12937
- draft,
12938
- onSearchFiles: hostFiles.available ? searchComposerFiles : void 0,
12939
- peers: peers.peers,
12940
- onShellCommand: shell ? runShell : void 0,
12941
- layout: controlsExternal ? "inline" : "stacked",
12942
- toolbar: controlsExternal ? void 0 : sessionControls,
12943
- fontSize: effectiveTermFontSize,
12944
- lineHeight: effectiveTermLineHeight,
12945
- affordances
12946
- }) }),
12947
- statusPlacement === "bottom" ? statusBar : null,
12948
- !external ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
12949
- /* @__PURE__ */ jsx(SessionInfoDialog, {
12950
- state,
12951
- client,
12952
- sessionId,
12953
- open: panel === "info",
12954
- onOpenChange: (next) => setPanel(next ? "info" : void 0)
12955
- }),
12956
- /* @__PURE__ */ jsx(ContextDialog, {
12957
- usage: state.contextUsage,
12958
- engine: state.engine ?? "claude",
12959
- open: panel === "context",
12960
- onOpenChange: (next) => setPanel(next ? "context" : void 0)
12961
- }),
12962
- /* @__PURE__ */ jsx(UsageDialog, {
12963
- rateLimits: windows,
12964
- subscriptionType: state.subscriptionType,
12965
- engine: state.engine ?? "claude",
12966
- totalCostUsd: state.totalCostUsd,
12967
- costUsd: state.costUsd,
12968
- usageByModel: state.usageByModel,
12969
- pricing,
12970
- spend: profileSpend,
12971
- updatedAt: usageUpdatedAt,
12972
- open: panel === "usage",
12973
- onOpenChange: (next) => setPanel(next ? "usage" : void 0)
12974
- }),
12975
- /* @__PURE__ */ jsx(McpDialog, {
12976
- client,
12977
- sessionId,
12978
- canManageServers: capabilities.mcpServerActions,
12979
- open: panel === "mcp",
12980
- onOpenChange: (next) => setPanel(next ? "mcp" : void 0)
12981
- }),
12982
- /* @__PURE__ */ jsx(TasksDialog, {
12983
- tasks,
12984
- showCompleted: showCompletedTasks,
12985
- onShowCompletedChange: setShowCompletedTasks,
12986
- onSelectTask: (task) => {
12987
- if (task.toolUseId) setTaskReveal({
12988
- toolUseId: task.toolUseId,
12989
- nonce: Date.now()
12990
- });
12991
- },
12992
- open: panel === "tasks",
12993
- onOpenChange: (next) => setPanel(next ? "tasks" : void 0)
12994
- }),
12995
- /* @__PURE__ */ jsx(SkillsDialog, {
12996
- skills: state.skills,
12997
- open: panel === "skills",
12998
- onOpenChange: (next) => setPanel(next ? "skills" : void 0),
12999
- onUse: (skill) => composerRef.current?.insertText(skillPrompt(skill))
13000
- }),
13001
- /* @__PURE__ */ jsx(HostFilesDialog, {
13002
- client,
13003
- cwd: state.cwd,
13004
- open: panel === "files",
13005
- onOpenChange: (next) => setPanel(next ? "files" : void 0)
13610
+ lineHeight: effectiveTermLineHeight
13611
+ },
13612
+ affordances,
13613
+ children: state.pendingApprovals.map((request) => {
13614
+ const HostPrompt = approvalPrompts?.[request.toolName];
13615
+ if (HostPrompt) return /* @__PURE__ */ jsx(HostPrompt, {
13616
+ request,
13617
+ onApprove: approve,
13618
+ onDeny: deny
13619
+ }, request.id);
13620
+ const isQuestion = request.toolName === "AskUserQuestion" && parseUserQuestions(request.input).length > 0;
13621
+ if (terminal) return isQuestion ? /* @__PURE__ */ jsx(TerminalQuestionPrompt, {
13622
+ request,
13623
+ onAnswer: approve,
13624
+ onDismiss: (id) => deny(id, "Question dismissed by user")
13625
+ }, request.id) : /* @__PURE__ */ jsx(TerminalPermissionPrompt, {
13626
+ request,
13627
+ onApprove: approve,
13628
+ onDeny: deny
13629
+ }, request.id);
13630
+ return isQuestion ? /* @__PURE__ */ jsx(QuestionPrompt, {
13631
+ request,
13632
+ onAnswer: approve,
13633
+ onDismiss: (id) => deny(id, "Question dismissed by user")
13634
+ }, request.id) : /* @__PURE__ */ jsx(PermissionPrompt, {
13635
+ request,
13636
+ onApprove: approve,
13637
+ onDeny: deny
13638
+ }, request.id);
13006
13639
  })
13007
- ] }) : null
13008
- ]
13009
- })
13640
+ })
13641
+ }) : null,
13642
+ readOnly || subagentId !== void 0 || framedShellId !== void 0 ? null : /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(Composer, {
13643
+ ref: composerRef,
13644
+ onSend: handleSend,
13645
+ onInterrupt: interrupt,
13646
+ busy,
13647
+ disabled: ended || !sessionId,
13648
+ commands: capabilities.slashCommands ? state.commands : void 0,
13649
+ skills: capabilities.skillsList ? state.skills : void 0,
13650
+ clientCommands,
13651
+ attachments,
13652
+ draft,
13653
+ onSearchFiles: hostFiles.available ? searchComposerFiles : void 0,
13654
+ peers: peers.peers,
13655
+ onShellCommand: shell ? runShell : void 0,
13656
+ layout: controlsExternal ? "inline" : "stacked",
13657
+ toolbar: controlsExternal ? void 0 : sessionControls,
13658
+ fontSize: effectiveTermFontSize,
13659
+ lineHeight: effectiveTermLineHeight,
13660
+ affordances
13661
+ }) }),
13662
+ statusPlacement === "bottom" ? statusBar : null,
13663
+ !external ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
13664
+ /* @__PURE__ */ jsx(SessionInfoDialog, {
13665
+ state,
13666
+ client,
13667
+ sessionId,
13668
+ open: panel === "info",
13669
+ onOpenChange: (next) => setPanel(next ? "info" : void 0)
13670
+ }),
13671
+ /* @__PURE__ */ jsx(ContextDialog, {
13672
+ usage: state.contextUsage,
13673
+ engine: state.engine ?? "claude",
13674
+ open: panel === "context",
13675
+ onOpenChange: (next) => setPanel(next ? "context" : void 0)
13676
+ }),
13677
+ /* @__PURE__ */ jsx(UsageDialog, {
13678
+ rateLimits: windows,
13679
+ subscriptionType: state.subscriptionType,
13680
+ engine: state.engine ?? "claude",
13681
+ totalCostUsd: state.totalCostUsd,
13682
+ costUsd: state.costUsd,
13683
+ usageByModel: state.usageByModel,
13684
+ pricing,
13685
+ spend: profileSpend,
13686
+ updatedAt: usageUpdatedAt,
13687
+ open: panel === "usage",
13688
+ onOpenChange: (next) => setPanel(next ? "usage" : void 0)
13689
+ }),
13690
+ /* @__PURE__ */ jsx(McpDialog, {
13691
+ client,
13692
+ sessionId,
13693
+ canManageServers: capabilities.mcpServerActions,
13694
+ open: panel === "mcp",
13695
+ onOpenChange: (next) => setPanel(next ? "mcp" : void 0)
13696
+ }),
13697
+ /* @__PURE__ */ jsx(TasksDialog, {
13698
+ tasks,
13699
+ showCompleted: showCompletedTasks,
13700
+ onShowCompletedChange: setShowCompletedTasks,
13701
+ onSelectTask: (task) => {
13702
+ if (task.toolUseId) setTaskReveal({
13703
+ toolUseId: task.toolUseId,
13704
+ nonce: Date.now()
13705
+ });
13706
+ },
13707
+ open: panel === "tasks",
13708
+ onOpenChange: (next) => setPanel(next ? "tasks" : void 0)
13709
+ }),
13710
+ /* @__PURE__ */ jsx(SkillsDialog, {
13711
+ skills: state.skills,
13712
+ open: panel === "skills",
13713
+ onOpenChange: (next) => setPanel(next ? "skills" : void 0),
13714
+ onUse: (skill) => composerRef.current?.insertText(skillPrompt(skill))
13715
+ }),
13716
+ /* @__PURE__ */ jsx(HostFilesDialog, {
13717
+ client,
13718
+ cwd: state.cwd,
13719
+ open: panel === "files",
13720
+ onOpenChange: (next) => setPanel(next ? "files" : void 0)
13721
+ })
13722
+ ] }) : null
13723
+ ] })
13010
13724
  })
13011
13725
  })
13012
13726
  })
@@ -13179,6 +13893,6 @@ function resolveAgainstCwd(path, cwd) {
13179
13893
  return parseFileLink(path, cwd)?.path ?? path;
13180
13894
  }
13181
13895
  //#endregion
13182
- export { useToolTitle as $, DialogContent as $t, ShellTerminal as A, rankComposerRows as At, SkillsDialog as B, segmentsToPlainText as Bt, SessionInfoDialog as C, toolIcon as Ct, QuestionPrompt as D, useTranscriptVariant as Dt, QUESTION_BEHAVIORS as E, useTranscriptDensity as Et, PERMISSION_MODES as F, launchTrigger as Ft, TaskList as G, CopyButton as Gt, HostFilesDialog as H, Splitter as Ht, PermissionModeSelect as I, mentionTrigger as It, Composer as J, TooltipContent as Jt, ContextDialog as K, copyText as Kt, permissionModeChoices as L, usePromptAreaState as Lt, SubagentStrip as M, skillPrompt as Mt, PermissionPrompt as N, commandTrigger as Nt, parseUserQuestions as O, matchClientCommand as Ot, Response as P, hashtagTrigger as Pt, ToolTitleProvider as Q, DialogClose as Qt, permissionModeMeta as R, PromptArea as Rt, STATUS_META as S, isMutatingTool as St, TerminalPermissionPrompt as T, TranscriptVariantProvider as Tt, EntryIcon as U, CodeBlock as Ut, McpDialog as V, ProgressRing as Vt, TasksDialog as W, Spinner as Wt, ShellActionsProvider as X, Dialog$1 as Xt, TerminalSurface as Y, TooltipProvider as Yt, useShellActions as Z, DialogBody as Zt, Loader as _, Badge as _n, shellInfoStatusText as _t, UsageDialog as a, MenuItem as an, previewPatch as at, ConversationScrollButton as b, buttonVariants as bn, SessionNamesProvider as bt, Transcript as c, Select$1 as cn, Ink as ct, Message as d, SelectItemText as dn, WithActions as dt, DialogHeader as en, TerminalMarkdown as et, MessageContent as f, SelectTrigger as fn, useAffordances as ft, SessionEmptyState as g, Input as gn, shellInfoLabel as gt, TerminalTranscript as h, Textarea as hn, shellInfoFailed as ht, SessionPanel as i, MenuContent as in, TerminalDiff as it, ShellStrip as j, rankPeerSessions as jt, useShellFrame as k, mergeComposerRows as kt, ToolCallCard as l, SelectContent as ln, Row as lt, TerminalItemView as m, PortalScope as mn, SHELL_KILL_GLYPH as mt, usePathLinks as n, DialogTrigger as nn, useFileLinks as nt, UsageMeters as o, MenuSeparator as on, Band as ot, FileCard as p, SelectValue as pn, SHELL_GLYPH as pt, contextNote as q, Tip as qt, matchPath as r, Menu$1 as rn, parseFileLink as rt, useMinuteClock as s, MenuTrigger as sn, Blank as st, resolveAgainstCwd as t, DialogRow as tn, FileLinkProvider as tt, Reasoning as u, SelectItem as un, CopyAction as ut, Conversation as v, badgeVariants as vn, shellTitle as vt, TerminalQuestionPrompt as w, TranscriptDensityProvider as wt, StatusBar as x, cn as xn, SkillNamesProvider as xt, ConversationContent as y, Button as yn, PromptTokenText as yt, ModelSelect as z, plainTextToSegments as zt };
13896
+ export { shellAgentWriteLabel as $, Tip as $t, ShellTerminal as A, isMutatingTool as At, SkillsDialog as B, hashtagTrigger as Bt, SessionInfoDialog as C, Badge as Cn, AgentWriteIcon as Ct, QuestionPrompt as D, cn as Dn, PromptTokenText as Dt, QUESTION_BEHAVIORS as E, buttonVariants as En, useAffordances as Et, PERMISSION_MODES as F, mergeComposerRows as Ft, TaskList as G, plainTextToSegments as Gt, HostFilesDialog as H, mentionTrigger as Ht, PermissionModeSelect as I, rankComposerRows as It, Composer as J, Splitter as Jt, ContextDialog as K, segmentsToPlainText as Kt, permissionModeChoices as L, rankPeerSessions as Lt, SubagentStrip as M, TranscriptVariantProvider as Mt, PermissionPrompt as N, useTranscriptVariant as Nt, parseUserQuestions as O, SessionNamesProvider as Ot, Response as P, matchClientCommand as Pt, SHELL_KILL_GLYPH as Q, copyText as Qt, permissionModeMeta as R, skillPrompt as Rt, STATUS_META as S, Input as Sn, Row as St, TerminalPermissionPrompt as T, Button as Tn, WithActions as Tt, EntryIcon as U, usePromptAreaState as Ut, McpDialog as V, launchTrigger as Vt, TasksDialog as W, PromptArea as Wt, SHELL_AGENT_WRITE_NOTE as X, Spinner as Xt, TerminalSurface as Y, CodeBlock as Yt, SHELL_GLYPH as Z, CopyButton as Zt, Loader as _, SelectItemText as _n, TerminalDiff as _t, UsageDialog as a, DialogContent as an, ShellActionsProvider as at, ConversationScrollButton as b, PortalScope as bn, Blank as bt, Transcript as c, DialogTrigger as cn, useToolTitle as ct, Message as d, MenuItem as dn, ImageViewerProvider as dt, TooltipContent as en, shellGrantable as et, MessageContent as f, MenuSeparator as fn, ViewableImage as ft, SessionEmptyState as g, SelectItem as gn, parseFileLink as gt, TerminalTranscript as h, SelectContent as hn, useFileLinks as ht, SessionPanel as i, DialogClose as in, shellTitle as it, ShellStrip as j, toolIcon as jt, useShellFrame as k, SkillNamesProvider as kt, ToolCallCard as l, Menu$1 as ln, TerminalMarkdown as lt, TerminalItemView as m, Select$1 as mn, FileLinkProvider as mt, usePathLinks as n, Dialog$1 as nn, shellInfoLabel as nt, UsageMeters as o, DialogHeader as on, useShellActions as ot, FileCard as p, MenuTrigger as pn, useOpenImage as pt, contextNote as q, ProgressRing as qt, matchPath as r, DialogBody as rn, shellInfoStatusText as rt, useMinuteClock as s, DialogRow as sn, ToolTitleProvider as st, resolveAgainstCwd as t, TooltipProvider as tn, shellInfoFailed as tt, Reasoning as u, MenuContent as un, ImageViewer as ut, Conversation as v, SelectTrigger as vn, previewPatch as vt, TerminalQuestionPrompt as w, badgeVariants as wn, CopyAction as wt, StatusBar as x, Textarea as xn, Ink as xt, ConversationContent as y, SelectValue as yn, Band as yt, ModelSelect as z, commandTrigger as zt };
13183
13897
 
13184
- //# sourceMappingURL=use-path-links-BgWXmfAS.mjs.map
13898
+ //# sourceMappingURL=use-path-links-BEWxt0ZM.mjs.map