@parhelia/core 0.1.12856 → 0.1.12868

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 (115) hide show
  1. package/dist/agents-view/AgentCard.d.ts +1 -2
  2. package/dist/agents-view/AgentCard.js +9 -14
  3. package/dist/agents-view/AgentCard.js.map +1 -1
  4. package/dist/agents-view/AgentsSidebar.d.ts +1 -3
  5. package/dist/agents-view/AgentsSidebar.js +2 -3
  6. package/dist/agents-view/AgentsSidebar.js.map +1 -1
  7. package/dist/agents-view/AgentsView.js +13 -32
  8. package/dist/agents-view/AgentsView.js.map +1 -1
  9. package/dist/agents-view/AgentsWorkspaceView.js +3 -13
  10. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  11. package/dist/agents-view/DateAgentsGroup.d.ts +1 -2
  12. package/dist/agents-view/DateAgentsGroup.js +2 -2
  13. package/dist/agents-view/DateAgentsGroup.js.map +1 -1
  14. package/dist/agents-view/ProfileAgentsGroup.d.ts +1 -2
  15. package/dist/agents-view/ProfileAgentsGroup.js +2 -2
  16. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
  17. package/dist/components/MarkdownDisplay.js +6 -6
  18. package/dist/components/MarkdownDisplay.js.map +1 -1
  19. package/dist/components/ui/avatar.d.ts +1 -1
  20. package/dist/components/ui/avatar.js +1 -1
  21. package/dist/components/ui/avatar.js.map +1 -1
  22. package/dist/config/types.d.ts +1 -0
  23. package/dist/config/types.js.map +1 -1
  24. package/dist/editor/ConfirmationDialog.js +2 -2
  25. package/dist/editor/ConfirmationDialog.js.map +1 -1
  26. package/dist/editor/ai/Agents.js +1 -81
  27. package/dist/editor/ai/Agents.js.map +1 -1
  28. package/dist/editor/ai/terminal/components/AgentGreeting.js +2 -5
  29. package/dist/editor/ai/terminal/components/AgentGreeting.js.map +1 -1
  30. package/dist/editor/ai/terminal/components/AgentSummaryAssistantResponse.d.ts +1 -1
  31. package/dist/editor/ai/terminal/components/AgentSummaryAssistantResponse.js +1 -1
  32. package/dist/editor/ai/terminal/components/AgentSummaryAssistantResponse.js.map +1 -1
  33. package/dist/editor/ai/terminal/components/AgentTerminalFullUpperContent.js +1 -1
  34. package/dist/editor/ai/terminal/components/AgentTerminalFullUpperContent.js.map +1 -1
  35. package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.d.ts +1 -1
  36. package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.js +4 -2
  37. package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.js.map +1 -1
  38. package/dist/editor/ai/terminal/components/AgentTerminalSummaryLayout.js +1 -1
  39. package/dist/editor/ai/terminal/components/AgentTerminalSummaryLayout.js.map +1 -1
  40. package/dist/editor/ai/terminal/components/AgentThinkingDots.d.ts +1 -1
  41. package/dist/editor/ai/terminal/components/AgentThinkingDots.js +4 -8
  42. package/dist/editor/ai/terminal/components/AgentThinkingDots.js.map +1 -1
  43. package/dist/editor/ai/terminal/components/AiResponseMessage.d.ts +2 -2
  44. package/dist/editor/ai/terminal/components/AiResponseMessage.js +366 -196
  45. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  46. package/dist/editor/ai/terminal/components/InitialThinkingSplash.d.ts +1 -3
  47. package/dist/editor/ai/terminal/components/InitialThinkingSplash.js +2 -6
  48. package/dist/editor/ai/terminal/components/InitialThinkingSplash.js.map +1 -1
  49. package/dist/editor/ai/terminal/components/ToolCallDisplay.js +26 -52
  50. package/dist/editor/ai/terminal/components/ToolCallDisplay.js.map +1 -1
  51. package/dist/editor/ai/terminal/components/UserMessage.d.ts +2 -1
  52. package/dist/editor/ai/terminal/components/UserMessage.js +2 -2
  53. package/dist/editor/ai/terminal/components/UserMessage.js.map +1 -1
  54. package/dist/editor/client/AboutDialog.js +3 -3
  55. package/dist/editor/client/AboutDialog.js.map +1 -1
  56. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +7 -0
  57. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +160 -15
  58. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  59. package/dist/editor/page-editor-chrome/CommentHighlightings.js +99 -42
  60. package/dist/editor/page-editor-chrome/CommentHighlightings.js.map +1 -1
  61. package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.d.ts +17 -0
  62. package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.js +33 -0
  63. package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.js.map +1 -0
  64. package/dist/editor/page-editor-chrome/SuggestionHighlightings.js +50 -17
  65. package/dist/editor/page-editor-chrome/SuggestionHighlightings.js.map +1 -1
  66. package/dist/editor/reviews/CommentDisplayPopover.js +3 -2
  67. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  68. package/dist/editor/reviews/CommentEditor.js +20 -2
  69. package/dist/editor/reviews/CommentEditor.js.map +1 -1
  70. package/dist/editor/reviews/CommentView.js +7 -9
  71. package/dist/editor/reviews/CommentView.js.map +1 -1
  72. package/dist/editor/reviews/Comments.js +6 -5
  73. package/dist/editor/reviews/Comments.js.map +1 -1
  74. package/dist/editor/reviews/FeedbackCard.d.ts +2 -1
  75. package/dist/editor/reviews/FeedbackCard.js +8 -4
  76. package/dist/editor/reviews/FeedbackCard.js.map +1 -1
  77. package/dist/editor/reviews/LatestFeedback.js +2 -3
  78. package/dist/editor/reviews/LatestFeedback.js.map +1 -1
  79. package/dist/editor/reviews/MentionText.js +2 -1
  80. package/dist/editor/reviews/MentionText.js.map +1 -1
  81. package/dist/editor/reviews/SuggestedEdit.js +47 -27
  82. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  83. package/dist/editor/reviews/SuggestionCommentThread.js +3 -3
  84. package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
  85. package/dist/editor/reviews/SuggestionDisplayPopover.js +7 -16
  86. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  87. package/dist/editor/reviews/feedbackAvatar.d.ts +7 -0
  88. package/dist/editor/reviews/feedbackAvatar.js +41 -0
  89. package/dist/editor/reviews/feedbackAvatar.js.map +1 -1
  90. package/dist/editor/reviews/feedbackSidebarText.d.ts +2 -0
  91. package/dist/editor/reviews/feedbackSidebarText.js +3 -0
  92. package/dist/editor/reviews/feedbackSidebarText.js.map +1 -0
  93. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.d.ts +2 -1
  94. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js +3 -2
  95. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js.map +1 -1
  96. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +9 -1
  97. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  98. package/dist/editor/ui/SimpleIconButton.d.ts +1 -0
  99. package/dist/editor/ui/SimpleIconButton.js +2 -2
  100. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  101. package/dist/editor/ui/SimpleMenu.js +1 -1
  102. package/dist/editor/ui/SimpleMenu.js.map +1 -1
  103. package/dist/editor/ui/SimpleTabs.d.ts +2 -1
  104. package/dist/editor/ui/SimpleTabs.js +2 -2
  105. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  106. package/dist/editor/utils.js +0 -2
  107. package/dist/editor/utils.js.map +1 -1
  108. package/dist/revision.d.ts +2 -2
  109. package/dist/revision.js +2 -2
  110. package/dist/splash-screen/DialogWrappers.js +2 -2
  111. package/dist/splash-screen/DialogWrappers.js.map +1 -1
  112. package/dist/task-board/TaskBoardWorkspace.js +1 -1
  113. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  114. package/package.json +1 -1
  115. package/styles.css +5 -0
@@ -8,15 +8,19 @@ import { X, Brain, ChevronDown, ChevronRight, Loader2, Undo2, Check, } from "luc
8
8
  import { Button } from "../../../../components/ui/button";
9
9
  import { Checkbox } from "../../../../components/ui/checkbox";
10
10
  import { Tooltip, TooltipTrigger, TooltipContent, } from "../../../../components/ui/tooltip";
11
- import { ParheliaAgentIcon } from "../../../ui/Icons";
12
11
  import { CopyButton } from "../../../../components/ui/copy-button";
13
- import { sanitizeHtml, sanitizeSvg } from "../../../../lib/sanitize";
12
+ import { sanitizeHtml } from "../../../../lib/sanitize";
14
13
  // Helper to extract partial quick actions from potentially incomplete JSON during streaming
15
14
  const extractPartialQuickActions = (jsonText) => {
16
15
  const actions = [];
16
+ let headline;
17
17
  // First try to parse complete JSON
18
18
  try {
19
19
  const parsed = JSON.parse(jsonText);
20
+ headline =
21
+ !Array.isArray(parsed) && typeof parsed?.headline === "string"
22
+ ? parsed.headline.trim() || undefined
23
+ : undefined;
20
24
  const rawActions = Array.isArray(parsed)
21
25
  ? parsed
22
26
  : parsed?.actions || parsed?.buttons || parsed?.choices || [];
@@ -46,11 +50,20 @@ const extractPartialQuickActions = (jsonText) => {
46
50
  }
47
51
  });
48
52
  }
49
- return actions;
53
+ return { headline, actions };
50
54
  }
51
55
  catch (e) {
52
56
  // JSON is incomplete, try to extract whatever complete action objects we can find
53
57
  }
58
+ const headlineMatch = jsonText.match(/"(?:headline|title)"\s*:\s*"([^"\\]*(\\.[^"\\]*)*)"/);
59
+ if (headlineMatch?.[1]) {
60
+ try {
61
+ headline = JSON.parse(`"${headlineMatch[1]}"`).trim() || undefined;
62
+ }
63
+ catch {
64
+ headline = headlineMatch[1].trim() || undefined;
65
+ }
66
+ }
54
67
  // Look for individual action objects in the partial JSON
55
68
  // Match patterns like: { "label": "...", ... }
56
69
  const labelPattern = /"(?:label|text)"\s*:\s*"([^"\\]*(\\.[^"\\]*)*)"/g;
@@ -123,7 +136,7 @@ const extractPartialQuickActions = (jsonText) => {
123
136
  // Skip malformed objects
124
137
  }
125
138
  }
126
- return actions;
139
+ return { headline, actions };
127
140
  };
128
141
  function parseContentSegments(content, hideIncomplete) {
129
142
  if (!content)
@@ -255,9 +268,9 @@ function parseContentSegments(content, hideIncomplete) {
255
268
  // Incomplete fenced block during streaming
256
269
  if (hideIncomplete && pick.type === "quick") {
257
270
  // Try to parse partial quick actions and show what we have so far
258
- const partialActions = extractPartialQuickActions(jsonText);
259
- if (partialActions.length > 0) {
260
- segments.push({ kind: "actions", actions: partialActions });
271
+ const partialActionGroup = extractPartialQuickActions(jsonText);
272
+ if (partialActionGroup.actions.length > 0) {
273
+ segments.push({ kind: "actions", ...partialActionGroup });
261
274
  }
262
275
  break;
263
276
  }
@@ -380,7 +393,10 @@ function parseContentSegments(content, hideIncomplete) {
380
393
  });
381
394
  }
382
395
  if (actions.length > 0) {
383
- segments.push({ kind: "actions", actions });
396
+ const headline = typeof parsed?.headline === "string"
397
+ ? parsed.headline.trim() || undefined
398
+ : undefined;
399
+ segments.push({ kind: "actions", headline, actions });
384
400
  }
385
401
  }
386
402
  else if (pick.type === "todo") {
@@ -448,9 +464,9 @@ function parseContentSegments(content, hideIncomplete) {
448
464
  // Incomplete plain JSON block during streaming
449
465
  if (hideIncomplete && pick.type === "quick") {
450
466
  // Try to parse partial quick actions and show what we have so far
451
- const partialActions = extractPartialQuickActions(jsonText);
452
- if (partialActions.length > 0) {
453
- segments.push({ kind: "actions", actions: partialActions });
467
+ const partialActionGroup = extractPartialQuickActions(jsonText);
468
+ if (partialActionGroup.actions.length > 0) {
469
+ segments.push({ kind: "actions", ...partialActionGroup });
454
470
  }
455
471
  break;
456
472
  }
@@ -498,7 +514,10 @@ function parseContentSegments(content, hideIncomplete) {
498
514
  });
499
515
  }
500
516
  if (actions.length > 0) {
501
- segments.push({ kind: "actions", actions });
517
+ const headline = typeof parsed?.headline === "string"
518
+ ? parsed.headline.trim() || undefined
519
+ : undefined;
520
+ segments.push({ kind: "actions", headline, actions });
502
521
  }
503
522
  }
504
523
  else {
@@ -618,17 +637,21 @@ function JsonBlockDisplay({ data, raw, className, defaultCollapsed = false, }) {
618
637
  return (_jsxs("div", { className: `border-border-default bg-neutral-grey-5 my-2 overflow-hidden rounded-lg border ${className || ""}`, children: [_jsxs("div", { className: "border-border-default bg-neutral-grey-5 flex items-center justify-between border-b px-3 py-2", children: [_jsxs("button", { onClick: () => setIsExpanded(!isExpanded), className: "text-neutral-grey-100 hover:text-neutral-grey-100 flex items-center gap-2 text-xs font-medium", children: [isExpanded ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 }), _jsx("span", { children: getTitle() })] }), _jsx("button", { onClick: handleCopy, className: "badge-pad text-neutral-grey-50 hover:bg-neutral-grey-10 hover:text-neutral-grey-100 rounded text-xs transition-colors", children: isCopied ? "Copied!" : "Copy" })] }), isExpanded && (_jsx("div", { className: "overflow-x-auto p-3", children: data ? (_jsx("pre", { className: "font-mono text-[11px] leading-relaxed", children: renderJsonValue(data) })) : (_jsx("pre", { className: "text-neutral-grey-50 font-mono text-[11px] leading-relaxed whitespace-pre-wrap", children: raw })) }))] }));
619
638
  }
620
639
  function simpleFormatToHtml(text) {
640
+ const stripLegacyFontTags = (value) => value
641
+ .replace(/<\/?font\b[^>]*>/gi, "")
642
+ .replace(/&lt;\/?font\b(?:(?!&gt;)[\s\S])*?&gt;/gi, "");
643
+ const normalizedText = stripLegacyFontTags(text);
621
644
  //Inline formatting
622
645
  const format = (s) => s
623
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) => `<a href="${url}" style="color: var(--color-component-default); text-decoration: underline;" target="_blank" rel="noopener noreferrer">${text.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")}</a>`)
624
- .replace(/^######\s+(.+)$/, '<h6 style="font-size: 0.75rem; font-weight: 500; margin: 0.75rem 0 0.25rem 0; color: var(--color-neutral-grey-50); letter-spacing: 0.01em; line-height: 1.3;">$1</h6>')
625
- .replace(/^#####\s+(.+)$/, '<h5 style="font-size: 0.875rem; font-weight: 500; margin: 0.875rem 0 0.375rem 0; color: var(--color-neutral-grey-50); letter-spacing: 0.01em; line-height: 1.35;">$1</h5>')
626
- .replace(/^####\s+(.+)$/, '<h4 style="font-size: 1rem; font-weight: 600; margin: 1rem 0 0.5rem 0; color: var(--color-neutral-grey-100); letter-spacing: -0.01em; line-height: 1.4;">$1</h4>')
627
- .replace(/^###\s+(.+)$/, '<h3 style="font-size: 1.125rem; font-weight: 600; margin: 1.125rem 0 0.5rem 0; color: var(--color-neutral-grey-100); letter-spacing: -0.015em; line-height: 1.4;">$1</h3>')
628
- .replace(/^##\s+(.+)$/, '<h2 style="font-size: 1.375rem; font-weight: 600; margin: 1.25rem 0 0.625rem 0; color: var(--color-neutral-grey-100); letter-spacing: -0.02em; line-height: 1.3;">$1</h2>')
629
- .replace(/^#\s+(.+)$/, '<h1 style="font-size: 1.75rem; font-weight: 700; margin: 1.5rem 0 0.75rem 0; color: var(--color-neutral-grey-100); letter-spacing: -0.025em; line-height: 1.2;">$1</h1>')
646
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) => `<a href="${url}" target="_blank" rel="noopener noreferrer">${text.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")}</a>`)
647
+ .replace(/^######\s+(.+)$/, "<h6>$1</h6>")
648
+ .replace(/^#####\s+(.+)$/, "<h5>$1</h5>")
649
+ .replace(/^####\s+(.+)$/, "<h4>$1</h4>")
650
+ .replace(/^###\s+(.+)$/, "<h3>$1</h3>")
651
+ .replace(/^##\s+(.+)$/, "<h2>$1</h2>")
652
+ .replace(/^#\s+(.+)$/, "<h1>$1</h1>")
630
653
  .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
631
- const lines = text.split(/\r?\n/);
654
+ const lines = normalizedText.split(/\r?\n/);
632
655
  const blocks = [];
633
656
  let i = 0;
634
657
  while (i < lines.length) {
@@ -640,7 +663,7 @@ function simpleFormatToHtml(text) {
640
663
  const t = line.trim();
641
664
  // Sepperator Lines
642
665
  if (/^[\-\*\_]{3,}$/.test(t)) {
643
- blocks.push('<hr style="border: none; border-top: 1px solid var(--color-border-default); margin: 0.75rem 0;" />');
666
+ blocks.push("<hr />");
644
667
  i++;
645
668
  continue;
646
669
  }
@@ -668,15 +691,14 @@ function simpleFormatToHtml(text) {
668
691
  break;
669
692
  }
670
693
  if (rows.length > 0 && rows[0]) {
671
- let html = '<table style="border-collapse: collapse; width: 100%; margin: 0.5rem 0; font-size: 0.75rem;"><thead><tr style="border-bottom: 2px solid var(--color-border-default);">';
672
- rows[0].forEach((c) => (html += `<th style="padding: 0.375rem 0.5rem; text-align: left; font-weight: 600; color: var(--color-neutral-grey-100);">${format(c)}</th>`));
694
+ let html = "<table><thead><tr>";
695
+ rows[0].forEach((c) => (html += `<th>${format(c)}</th>`));
673
696
  html += "</tr></thead>";
674
697
  if (rows.length > 1) {
675
698
  html += "<tbody>";
676
699
  for (let k = 1; k < rows.length; k++) {
677
- html +=
678
- '<tr style="border-bottom: 1px solid var(--color-neutral-grey-5);">';
679
- rows[k]?.forEach((c) => (html += `<td style="padding: 0.375rem 0.5rem; color: var(--color-neutral-grey-50);">${format(c)}</td>`));
700
+ html += "<tr>";
701
+ rows[k]?.forEach((c) => (html += `<td>${format(c)}</td>`));
680
702
  html += "</tr>";
681
703
  }
682
704
  html += "</tbody>";
@@ -706,16 +728,12 @@ function simpleFormatToHtml(text) {
706
728
  j++;
707
729
  }
708
730
  if (listLines.length > 0) {
709
- const listStyleByTag = {
710
- ul: "list-style: disc;",
711
- ol: "list-style: decimal;",
712
- };
713
731
  const indentStack = [];
714
732
  const tagStack = [];
715
733
  let html = "";
716
734
  let itemOpen = false;
717
735
  const openList = (tag, indent) => {
718
- html += `<${tag} style="${listStyleByTag[tag]} padding-left: 1.5rem; margin: 0.25rem 0;">`;
736
+ html += `<${tag}>`;
719
737
  tagStack.push(tag);
720
738
  indentStack.push(indent);
721
739
  };
@@ -748,8 +766,7 @@ function simpleFormatToHtml(text) {
748
766
  if (currentIndent === undefined ||
749
767
  (entry.indent !== currentIndent && entry.indent > currentIndent)) {
750
768
  if (!itemOpen) {
751
- html +=
752
- '<li style="margin: 0.25rem 0; color: var(--color-neutral-grey-50);">';
769
+ html += "<li>";
753
770
  itemOpen = true;
754
771
  }
755
772
  openList(tag, entry.indent);
@@ -766,7 +783,7 @@ function simpleFormatToHtml(text) {
766
783
  }
767
784
  }
768
785
  closeItem();
769
- html += `<li style="margin: 0.25rem 0; color: var(--color-neutral-grey-50);">${format(entry.content)}`;
786
+ html += `<li>${format(entry.content)}`;
770
787
  itemOpen = true;
771
788
  }
772
789
  while (tagStack.length > 0)
@@ -801,6 +818,154 @@ function simpleFormatToHtml(text) {
801
818
  }
802
819
  return sanitizeHtml(result.replace(/<\/(h[1-6])><br\/>/g, "</$1>"));
803
820
  }
821
+ const assistantMessageTextClassName = [
822
+ "max-w-none text-xs leading-5 text-neutral-grey-100 select-text",
823
+ "[&_a]:text-neutral-grey-100 [&_a]:underline [&_a]:underline-offset-2",
824
+ "[&_strong]:font-semibold [&_strong]:text-neutral-grey-100",
825
+ "[&_h1]:mt-0 [&_h1]:mb-3 [&_h1]:text-[16px] [&_h1]:leading-6 [&_h1]:font-semibold [&_h1]:text-neutral-grey-100",
826
+ "[&_h2]:mt-3 [&_h2]:mb-1.5 [&_h2]:text-[15px] [&_h2]:leading-5 [&_h2]:font-semibold [&_h2]:text-neutral-grey-100",
827
+ "[&_h3]:mt-2.5 [&_h3]:mb-1 [&_h3]:text-[13px] [&_h3]:leading-5 [&_h3]:font-semibold [&_h3]:text-neutral-grey-100",
828
+ "[&_h4]:mt-2 [&_h4]:mb-1 [&_h4]:text-[12px] [&_h4]:leading-5 [&_h4]:font-semibold [&_h4]:text-neutral-grey-100",
829
+ "[&_h5]:mt-2 [&_h5]:mb-1 [&_h5]:text-[12px] [&_h5]:leading-5 [&_h5]:font-semibold [&_h5]:text-neutral-grey-100",
830
+ "[&_h6]:mt-2 [&_h6]:mb-1 [&_h6]:text-[12px] [&_h6]:leading-5 [&_h6]:font-semibold [&_h6]:text-neutral-grey-100",
831
+ "[&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-5",
832
+ "[&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-5",
833
+ "[&_li]:my-0.5 [&_li]:pl-1 [&_li]:leading-5 [&_li]:text-neutral-grey-100",
834
+ "[&_li::marker]:text-neutral-grey-100",
835
+ "[&_table]:my-2 [&_table]:text-neutral-grey-100",
836
+ "[&_th]:px-2 [&_th]:py-1.5 [&_th]:text-neutral-grey-100",
837
+ "[&_td]:px-2 [&_td]:py-1.5 [&_td]:text-neutral-grey-100",
838
+ ].join(" ");
839
+ const cleanToolFunctionName = (name) => {
840
+ return (name || "")
841
+ .replace(" (approved)", "")
842
+ .replace(" (rejected)", "")
843
+ .replace(" (pending approval)", "")
844
+ .trim();
845
+ };
846
+ const getToolFunctionName = (toolCall) => {
847
+ return cleanToolFunctionName(toolCall.function?.name || toolCall.displayName || "");
848
+ };
849
+ const isActivityItem = (item) => {
850
+ if (item.type === "toolcalls") {
851
+ return item.toolCalls.length > 0;
852
+ }
853
+ return (item.segment.kind === "reasoning" && item.segment.text.trim().length > 0);
854
+ };
855
+ const isVisibleUserDirectedItem = (item) => {
856
+ if (item.type !== "segment" || item.segment.kind === "reasoning") {
857
+ return false;
858
+ }
859
+ const segment = item.segment;
860
+ if (segment.kind === "text") {
861
+ return segment.text.trim().length > 0;
862
+ }
863
+ if (segment.kind === "actions") {
864
+ return segment.actions.length > 0;
865
+ }
866
+ if (segment.kind === "todo") {
867
+ return segment.items.length > 0 || !!segment.title?.trim();
868
+ }
869
+ if (segment.kind === "media") {
870
+ return segment.items.length > 0;
871
+ }
872
+ if (segment.kind === "json") {
873
+ return (!!segment.raw.trim() ||
874
+ (segment.data !== null && segment.data !== undefined));
875
+ }
876
+ return false;
877
+ };
878
+ const groupActivityItems = (flatItems) => {
879
+ const displayItems = [];
880
+ const activityEntries = [];
881
+ const flushActivityEntries = () => {
882
+ const entries = activityEntries;
883
+ if (entries.length === 0) {
884
+ return;
885
+ }
886
+ const first = entries[0];
887
+ if (entries.length === 1) {
888
+ displayItems.push({
889
+ type: "single",
890
+ entry: first,
891
+ key: `item-${first.index}`,
892
+ });
893
+ return;
894
+ }
895
+ const last = entries[entries.length - 1];
896
+ displayItems.push({
897
+ type: "activity-group",
898
+ entries,
899
+ key: `activity-${first.index}-${last.index}`,
900
+ });
901
+ };
902
+ flatItems.forEach((item, index) => {
903
+ if (isActivityItem(item)) {
904
+ activityEntries.push({ item, index });
905
+ return;
906
+ }
907
+ if (isVisibleUserDirectedItem(item)) {
908
+ flushActivityEntries();
909
+ displayItems.push({
910
+ type: "single",
911
+ entry: { item, index },
912
+ key: `item-${index}`,
913
+ });
914
+ }
915
+ });
916
+ flushActivityEntries();
917
+ return displayItems;
918
+ };
919
+ function ActivityBlockGroup({ entries, isLive, renderEntry, }) {
920
+ const toolCalls = entries.flatMap((entry) => entry.item.type === "toolcalls" ? entry.item.toolCalls : []);
921
+ const thinkingCount = entries.filter((entry) => entry.item.type === "segment" && entry.item.segment.kind === "reasoning").length;
922
+ const toolCallCount = toolCalls.length;
923
+ const toolNames = [
924
+ ...new Set(toolCalls.map(getToolFunctionName).filter(Boolean)),
925
+ ];
926
+ const errorCount = toolCalls.filter((toolCall) => {
927
+ const rawName = toolCall.function?.name || toolCall.displayName || "";
928
+ const isRejected = rawName.toLowerCase().includes("(rejected)");
929
+ return !!toolCall.function?.error || isRejected;
930
+ }).length;
931
+ const hasPendingApproval = toolCalls.some((toolCall) => {
932
+ const rawName = toolCall.function?.name || toolCall.displayName || "";
933
+ return (!!toolCall.requiresApproval &&
934
+ !rawName.includes("(approved)") &&
935
+ !rawName.includes("(rejected)"));
936
+ });
937
+ const hasIncompleteToolCall = toolCalls.some((toolCall) => {
938
+ return (!toolCall.isCompleted &&
939
+ !toolCall.function?.result &&
940
+ !toolCall.function?.error);
941
+ });
942
+ const [isExpanded, setIsExpanded] = useState(hasPendingApproval);
943
+ const summaryParts = [
944
+ toolCallCount > 0
945
+ ? `${toolCallCount} tool call${toolCallCount !== 1 ? "s" : ""}`
946
+ : null,
947
+ thinkingCount > 0
948
+ ? `${thinkingCount} thinking block${thinkingCount !== 1 ? "s" : ""}`
949
+ : null,
950
+ ].filter(Boolean);
951
+ const detailsText = toolNames.length <= 3
952
+ ? toolNames.join(", ")
953
+ : `${toolNames.slice(0, 2).join(", ")} +${toolNames.length - 2} more`;
954
+ const isActive = !errorCount && (isLive || hasIncompleteToolCall);
955
+ useEffect(() => {
956
+ if (hasPendingApproval) {
957
+ setIsExpanded(true);
958
+ }
959
+ }, [hasPendingApproval]);
960
+ return (_jsxs("div", { className: "border-border-default my-2 overflow-hidden rounded-lg border", "data-testid": "agent-activity-group", children: [_jsxs("button", { type: "button", className: "group text-neutral-grey-50 hover:bg-neutral-grey-5/60 hover:text-neutral-grey-100 flex w-full cursor-pointer items-center gap-1.5 px-2 py-1.5 text-left text-xs transition-all", onClick: () => {
961
+ setIsExpanded(!isExpanded);
962
+ }, "aria-expanded": isExpanded, "data-testid": "agent-activity-group-toggle", children: [_jsx("div", { className: "flex h-4 w-4 shrink-0 items-center justify-center transition-transform group-hover:scale-110", children: isExpanded ? (_jsx(ChevronDown, { size: 14, strokeWidth: 1.5, className: "text-neutral-grey-50" })) : (_jsx(ChevronRight, { size: 14, strokeWidth: 1.5, className: "text-neutral-grey-50" })) }), _jsx("span", { className: `shrink-0 leading-4 font-medium ${isActive ? "animate-text-shimmer" : ""}`, children: summaryParts.join(", ") }), detailsText && (_jsx("span", { className: "text-neutral-grey-50 min-w-0 flex-1 truncate text-[10px] leading-4", children: detailsText })), errorCount > 0 && (_jsxs("span", { className: "text-feedback-red shrink-0 text-[10px] leading-4", children: ["(", errorCount, " error", errorCount !== 1 ? "s" : "", ")"] })), _jsx("div", { className: "ml-auto flex h-4 w-4 shrink-0 items-center justify-center", children: errorCount > 0 ? (_jsx(XCircleLike, {})) : isActive ? (_jsx(Loader2, { className: "text-neutral-grey-50 h-3.5 w-3.5 animate-spin", strokeWidth: 1.5 })) : (_jsx(Check, { className: "text-feedback-green h-3.5 w-3.5", strokeWidth: 2 })) })] }), _jsx("div", { className: "grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none", style: {
963
+ gridTemplateRows: isExpanded ? "1fr" : "0fr",
964
+ }, "aria-hidden": !isExpanded, "data-testid": "agent-activity-group-content", children: _jsx("div", { className: "min-h-0 overflow-hidden", children: _jsx("div", { className: "border-border-default/60 border-t px-2 py-1", children: entries.map((entry) => renderEntry(entry)) }) }) })] }));
965
+ }
966
+ function XCircleLike() {
967
+ return (_jsx("span", { className: "border-feedback-red text-feedback-red flex h-3.5 w-3.5 items-center justify-center rounded-full border", children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 2 }) }));
968
+ }
804
969
  // Thinking block component with auto-scroll
805
970
  function ThinkingBlock({ text, isStreaming, isCollapsed, thinkingKey, onToggleCollapse, }) {
806
971
  const contentRef = useRef(null);
@@ -810,13 +975,9 @@ function ThinkingBlock({ text, isStreaming, isCollapsed, thinkingKey, onToggleCo
810
975
  contentRef.current.scrollTop = contentRef.current.scrollHeight;
811
976
  }
812
977
  }, [text, isStreaming, isCollapsed]);
813
- return (_jsxs("div", { className: `my-2 flex flex-col gap-1 rounded-lg border text-[11px] transition-colors duration-200 ${isStreaming
814
- ? "border-border-default bg-neutral-grey-5/50 text-neutral-grey-100"
815
- : "border-border-default bg-neutral-grey-5/50 text-neutral-grey-50"}`, children: [_jsxs("div", { className: `flex cursor-pointer items-center gap-1.5 p-2 text-[10px] font-medium tracking-wider transition-colors ${isStreaming
816
- ? "text-neutral-grey-50 hover:bg-neutral-grey-5/50"
817
- : "text-neutral-grey-50 hover:bg-neutral-grey-5/50"}`, onClick: onToggleCollapse, children: [isCollapsed ? _jsx(ChevronRight, { size: 12 }) : _jsx(ChevronDown, { size: 12 }), _jsx(Brain, { size: 12, className: isStreaming ? "animate-pulse" : "" }), _jsx("span", { children: "Thinking" }), isStreaming && (_jsx(Loader2, { className: "text-neutral-grey-50 ml-1 h-3 w-3 animate-spin", strokeWidth: 1.5 }))] }), !isCollapsed && (_jsxs("div", { ref: contentRef, className: `prose-sm max-h-[6rem] overflow-y-auto px-2 pb-2 leading-relaxed whitespace-pre-wrap italic ${isStreaming ? "text-neutral-grey-50" : ""}`, children: [text, isStreaming && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }))] }));
978
+ return (_jsxs("div", { className: `my-2 flex flex-col gap-1 text-[11px] transition-colors duration-200 ${isStreaming ? "text-neutral-grey-100" : "text-neutral-grey-50"}`, children: [_jsxs("div", { className: "text-neutral-grey-50 hover:text-neutral-grey-100 flex cursor-pointer items-center gap-1.5 p-2 text-[10px] font-medium tracking-wider transition-colors", onClick: onToggleCollapse, children: [isCollapsed ? _jsx(ChevronRight, { size: 12 }) : _jsx(ChevronDown, { size: 12 }), _jsx(Brain, { size: 12, className: isStreaming ? "animate-pulse" : "" }), _jsx("span", { children: "Thinking" }), isStreaming && (_jsx(Loader2, { className: "text-neutral-grey-50 ml-1 h-3 w-3 animate-spin", strokeWidth: 1.5 }))] }), !isCollapsed && (_jsxs("div", { ref: contentRef, className: `prose-sm max-h-[6rem] overflow-y-auto px-2 pb-2 leading-relaxed whitespace-pre-wrap italic ${isStreaming ? "text-neutral-grey-50" : ""}`, children: [text, isStreaming && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }))] }));
818
979
  }
819
- export function AiResponseMessage({ messages, finished, editOperations, error, onQuickAction, defaultCollapseJson = false, profileSvgIcon, agentId, agentName, allPendingApprovals, onSwitchToAutonomous, browserCaptureInlinePrompt, readOnly = false, }) {
980
+ export function AiResponseMessage({ messages, finished, editOperations, error, onQuickAction, defaultCollapseJson = false, agentId, agentName, allPendingApprovals, onSwitchToAutonomous, browserCaptureInlinePrompt, readOnly = false, followsUserMessage = false, }) {
820
981
  const editContext = useEditContext();
821
982
  // const [showFunctions, setShowFunctions] = useState(false);
822
983
  const [changesRejected, setChangesRejected] = useState(undefined);
@@ -917,6 +1078,7 @@ export function AiResponseMessage({ messages, finished, editOperations, error, o
917
1078
  const lastMessage = messages[messages.length - 1];
918
1079
  // Memoize filtered messages and their parsed content segments to avoid hooks inside loops
919
1080
  const nonToolAssistantMessages = useMemo(() => messages.filter((x) => x.role !== "tool" && x.role !== "user"), [messages]);
1081
+ // Create a flat structure of content items: text, tool calls, text, tool calls, etc.
920
1082
  const flatItems = useMemo(() => {
921
1083
  const items = [];
922
1084
  // Collect all unique tool call IDs and their latest versions across all messages in this group
@@ -1032,6 +1194,7 @@ export function AiResponseMessage({ messages, finished, editOperations, error, o
1032
1194
  preservedToolCalls,
1033
1195
  getToolArgumentsScore,
1034
1196
  ]);
1197
+ const displayItems = useMemo(() => groupActivityItems(flatItems), [flatItems]);
1035
1198
  // Check if any reasoning block is currently showing a spinner
1036
1199
  const isAnyReasoningStreaming = useMemo(() => {
1037
1200
  if (!isStreaming || allPendingApprovals?.length)
@@ -1076,165 +1239,172 @@ export function AiResponseMessage({ messages, finished, editOperations, error, o
1076
1239
  });
1077
1240
  return textParts.join("\n\n");
1078
1241
  }, [flatItems]);
1079
- return (_jsxs("div", { className: "flex gap-3 px-4 py-3", "data-testid": "agent-message", children: [_jsx("div", { className: "flex-shrink-0", children: profileSvgIcon ? (_jsx("div", { className: "text-neutral-grey-50 flex h-6 w-6 items-center justify-center [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
1080
- __html: sanitizeSvg(profileSvgIcon),
1081
- } })) : (_jsx(ParheliaAgentIcon, { size: 20, strokeWidth: 1, className: "text-neutral-grey-50" })) }), _jsxs("div", { className: "min-w-0 flex-1 select-text", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-neutral-grey-100 text-xs font-medium", children: agentName || "Agent" }), lastMessage && lastMessage.createdDate && (_jsx(TimeWithTooltip, { date: new Date(lastMessage.createdDate), className: "text-neutral-grey-50 text-xs", testId: "agent-message-timestamp", timestampAttr: lastMessage.createdDate }))] }), error && (_jsx("div", { className: "border-feedback-red bg-feedback-red-light mb-2 rounded-lg border-l-4 p-2.5", "data-testid": "agent-error", children: _jsxs("div", { className: "flex items-start", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(X, { className: "text-feedback-red h-5 w-5", strokeWidth: 1 }) }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-feedback-red text-sm font-medium", children: "Agent Error" }), _jsx("p", { className: "text-feedback-red mt-1 text-sm", children: error })] })] }) })), flatItems.map((item, itemIndex) => {
1082
- const prevItem = itemIndex > 0 ? flatItems[itemIndex - 1] : null;
1083
- const isLastItem = itemIndex === flatItems.length - 1;
1084
- const shouldAddSpacing = prevItem?.type === "toolcalls" && item.type === "segment";
1085
- if (item.type === "segment") {
1086
- const segment = item.segment;
1087
- const message = item.message;
1088
- if (segment.kind === "reasoning") {
1089
- const thinkingKey = `thinking-${message.id}-${itemIndex}`;
1090
- // Only show the "Thinking" spinner while we're still in the pre-output phase.
1091
- // Once any actual response content starts appearing, thinking has effectively finished,
1092
- // even if the overall response is still streaming.
1093
- const contentString = (message.content ||
1094
- message.formattedContent ||
1095
- "");
1096
- const hasToolCalls = (message.tool_calls && message.tool_calls.length > 0) ||
1097
- (preservedToolCalls[message.id] &&
1098
- preservedToolCalls[message.id].length > 0);
1099
- const hasStartedOutput = contentString.trim().length > 0 || hasToolCalls;
1100
- // Check if this reasoning block is still actively streaming
1101
- const isReasoningStreaming = !!(isStreaming &&
1102
- item.isLastMessage &&
1103
- !hasStartedOutput &&
1104
- !allPendingApprovals?.length);
1105
- // During streaming, default to expanded; after streaming, default to collapsed
1106
- const isCollapsed = collapsedThinking[thinkingKey] !== undefined
1107
- ? collapsedThinking[thinkingKey]
1108
- : !isReasoningStreaming; // Expanded during streaming, collapsed after
1109
- return (_jsx(ThinkingBlock, { text: segment.text, isStreaming: isReasoningStreaming, isCollapsed: isCollapsed, thinkingKey: thinkingKey, onToggleCollapse: () => {
1110
- setCollapsedThinking((prev) => ({
1111
- ...prev,
1112
- [thinkingKey]: !isCollapsed,
1113
- }));
1114
- } }, `item-${itemIndex}`));
1115
- }
1116
- if (segment.kind === "text") {
1117
- const trimmedText = segment.text.trim();
1118
- if (!trimmedText)
1119
- return null;
1120
- // Check for command links in the text
1121
- const { text, links } = extractCommandLinks(trimmedText);
1122
- if (links.length > 0) {
1123
- // Split text around command links and render buttons
1124
- const parts = [];
1125
- let lastIndex = 0;
1126
- links.forEach((link, linkIdx) => {
1127
- // Add text before the link
1128
- if (link.index > lastIndex) {
1129
- const textBefore = text.substring(lastIndex, link.index);
1130
- const html = simpleFormatToHtml(textBefore);
1131
- if (html) {
1132
- parts.push(_jsx("span", { dangerouslySetInnerHTML: { __html: html } }, `text-${itemIndex}-${linkIdx}`));
1133
- }
1134
- }
1135
- // Add the button
1136
- // Calculate the actual text that will be submitted (same logic as in AgentTerminal.tsx)
1137
- // For command links: prompt=command, value=command, so submitted text is link.command
1138
- const submittedText = (link.command ||
1139
- link.label ||
1140
- "").trim();
1141
- parts.push(_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { size: "sm", variant: "outline", className: "mx-1 inline-flex", onClick: () => onQuickAction?.({
1142
- label: link.label,
1143
- value: link.command,
1144
- prompt: link.command,
1145
- }, message), children: link.label }) }), submittedText && (_jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs break-words", children: submittedText }) }))] }, `btn-${itemIndex}-${linkIdx}`));
1146
- // Find the end of this match in the original text
1147
- const matchEnd = link.index +
1148
- `[${link.label}](command:${link.command})`.length;
1149
- lastIndex = matchEnd;
1150
- });
1151
- // Add any remaining text after the last link
1152
- if (lastIndex < text.length) {
1153
- const textAfter = text.substring(lastIndex);
1154
- const html = simpleFormatToHtml(textAfter);
1155
- if (html) {
1156
- parts.push(_jsx("span", { dangerouslySetInnerHTML: { __html: html } }, `text-${itemIndex}-end`));
1157
- }
1158
- }
1159
- return (_jsxs("div", { className: `prose prose-sm text-neutral-grey-100 max-w-none text-xs select-text ${shouldAddSpacing ? "mt-2" : ""}`, children: [parts, isStreaming && isLastItem && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }, `item-${itemIndex}`));
1160
- }
1161
- const html = simpleFormatToHtml(trimmedText);
1162
- if (!html)
1163
- return null;
1164
- return (_jsxs("div", { className: `prose prose-sm text-neutral-grey-100 max-w-none text-xs select-text ${shouldAddSpacing ? "mt-2" : ""}`, children: [_jsx("span", { dangerouslySetInnerHTML: { __html: html } }), isStreaming && isLastItem && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }, `item-${itemIndex}`));
1165
- }
1166
- if (segment.kind === "todo") {
1167
- const todo = segment;
1168
- return (_jsxs("div", { className: `my-1 ${shouldAddSpacing ? "mt-2" : ""}`, children: [todo.title && (_jsx("div", { className: "text-neutral-grey-100 mb-1 text-xs font-medium", children: todo.title })), _jsx("div", { className: "flex flex-col gap-1", children: todo.items.map((todoItem, iIdx) => (_jsxs("label", { className: "text-neutral-grey-100 flex items-start gap-2 text-xs", children: [_jsx(Checkbox, { checked: todoItem.status === "completed", onCheckedChange: () => { }, "aria-readonly": true, className: "mt-0.5" }), _jsxs("div", { children: [_jsx("div", { className: todoItem.status === "completed" ||
1169
- todoItem.status === "cancelled"
1170
- ? "line-through opacity-70"
1171
- : todoItem.status === "in_progress"
1172
- ? "text-feedback-blue font-medium"
1173
- : "", children: todoItem.title }), todoItem.instructions && (_jsx("div", { className: "text-neutral-grey-100 text-xs", children: todoItem.instructions }))] })] }, (todoItem.id || todoItem.title || "todo") + "-" + iIdx))) })] }, `item-${itemIndex}`));
1174
- }
1175
- if (segment.kind === "media") {
1176
- const media = segment;
1177
- return (_jsx("div", { className: `my-2 flex flex-col gap-3 ${shouldAddSpacing ? "mt-2" : ""}`, children: media.items.map((mediaItem, mIdx) => (_jsx(MediaImage, { itemId: mediaItem.itemId, label: mediaItem.label }, mediaItem.itemId || `media-${itemIndex}-${mIdx}`))) }, `item-${itemIndex}`));
1178
- }
1179
- if (segment.kind === "json") {
1180
- return (_jsx(JsonBlockDisplay, { data: segment.data, raw: segment.raw, defaultCollapsed: defaultCollapseJson, className: shouldAddSpacing ? "mt-2" : "" }, `item-${itemIndex}`));
1242
+ const renderFlatItem = (item, itemIndex) => {
1243
+ const prevItem = itemIndex > 0 ? flatItems[itemIndex - 1] : null;
1244
+ const isLastItem = itemIndex === flatItems.length - 1;
1245
+ const shouldAddSpacing = prevItem?.type === "toolcalls" && item.type === "segment";
1246
+ if (item.type === "segment") {
1247
+ const segment = item.segment;
1248
+ const message = item.message;
1249
+ if (segment.kind === "reasoning") {
1250
+ const thinkingKey = `thinking-${message.id}-${itemIndex}`;
1251
+ // Only show the "Thinking" spinner while we're still in the pre-output phase.
1252
+ // Once any actual response content starts appearing, thinking has effectively finished,
1253
+ // even if the overall response is still streaming.
1254
+ const contentString = (message.content ||
1255
+ message.formattedContent ||
1256
+ "");
1257
+ const hasToolCalls = (message.tool_calls && message.tool_calls.length > 0) ||
1258
+ (preservedToolCalls[message.id] &&
1259
+ preservedToolCalls[message.id].length > 0);
1260
+ const hasStartedOutput = contentString.trim().length > 0 || hasToolCalls;
1261
+ // Check if this reasoning block is still actively streaming
1262
+ const isReasoningStreaming = !!(isStreaming &&
1263
+ item.isLastMessage &&
1264
+ !hasStartedOutput &&
1265
+ !allPendingApprovals?.length);
1266
+ // During streaming, default to expanded; after streaming, default to collapsed
1267
+ const isCollapsed = collapsedThinking[thinkingKey] !== undefined
1268
+ ? collapsedThinking[thinkingKey]
1269
+ : !isReasoningStreaming; // Expanded during streaming, collapsed after
1270
+ return (_jsx(ThinkingBlock, { text: segment.text, isStreaming: isReasoningStreaming, isCollapsed: isCollapsed, thinkingKey: thinkingKey, onToggleCollapse: () => {
1271
+ setCollapsedThinking((prev) => ({
1272
+ ...prev,
1273
+ [thinkingKey]: !isCollapsed,
1274
+ }));
1275
+ } }, `item-${itemIndex}`));
1276
+ }
1277
+ if (segment.kind === "text") {
1278
+ const trimmedText = segment.text.trim();
1279
+ if (!trimmedText)
1280
+ return null;
1281
+ // Check for command links in the text
1282
+ const { text, links } = extractCommandLinks(trimmedText);
1283
+ if (links.length > 0) {
1284
+ // Split text around command links and render buttons
1285
+ const parts = [];
1286
+ let lastIndex = 0;
1287
+ links.forEach((link, linkIdx) => {
1288
+ // Add text before the link
1289
+ if (link.index > lastIndex) {
1290
+ const textBefore = text.substring(lastIndex, link.index);
1291
+ const html = simpleFormatToHtml(textBefore);
1292
+ if (html) {
1293
+ parts.push(_jsx("span", { dangerouslySetInnerHTML: { __html: html } }, `text-${itemIndex}-${linkIdx}`));
1181
1294
  }
1182
- // segment.kind === "actions"
1183
- const actions = segment.actions;
1184
- return (_jsx("div", { className: `my-3 flex flex-wrap gap-2 ${shouldAddSpacing ? "mt-2" : ""}`, children: actions.map((a, aIdx) => {
1185
- const variant = a.style === "destructive"
1186
- ? "destructive"
1187
- : a.style === "outline"
1188
- ? "outline"
1189
- : a.style === "secondary"
1190
- ? "secondary"
1191
- : "default"; // primary
1192
- // Calculate the actual text that will be submitted (same logic as in AgentTerminal.tsx)
1193
- const submittedText = (a.prompt ||
1194
- a.value ||
1195
- a.label ||
1196
- "").trim();
1197
- return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { size: "xs", variant: variant, onClick: () => onQuickAction?.({
1198
- label: a.label,
1199
- prompt: a.prompt,
1200
- value: a.value,
1201
- behavior: a.behavior,
1202
- placeholder: a.placeholder,
1203
- }, message), children: a.label }) }), submittedText && (_jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs break-words", children: submittedText }) }))] }, (a.id || a.label || "btn") + "-" + aIdx));
1204
- }) }, `item-${itemIndex}`));
1205
1295
  }
1206
- // item.type === "toolcalls"
1207
- return (_jsx("div", { className: shouldAddSpacing ? "mt-2" : "", children: _jsx(ToolCallDisplay, { toolCalls: item.toolCalls, finished: finished, openPopovers: openPopovers, setOpenPopovers: setOpenPopovers, messageId: item.messageId, agentId: agentId, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: onSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, readOnly: readOnly }) }, `item-${itemIndex}`));
1208
- }), isStreaming &&
1209
- !error &&
1210
- !allPendingApprovals?.length &&
1211
- !isAnyReasoningStreaming && (_jsxs("div", { className: "flex items-center gap-1 pt-2", "data-testid": "agent-streaming-dots", children: [_jsx("div", { className: "bg-neutral-grey-50 h-1 w-1 animate-bounce rounded-full [animation-delay:-0.3s]" }), _jsx("div", { className: "bg-neutral-grey-50 h-1 w-1 animate-bounce rounded-full [animation-delay:-0.15s]" }), _jsx("div", { className: "bg-neutral-grey-50 h-1 w-1 animate-bounce rounded-full" })] })), (copyableText || (finished && editOperations.length > 0)) && (_jsxs("div", { className: "mt-3 flex items-center justify-end gap-2", children: [copyableText && (_jsx(CopyButton, { textToCopy: copyableText, iconOnly: false })), finished && editOperations.length > 0 && (_jsxs(_Fragment, { children: [canUndo && !changesRejected && !confirmingUndo && (_jsxs("button", { type: "button", title: `Undo ${editOperations.length} change${editOperations.length !== 1 ? "s" : ""}`, className: "tour-ai-response-message-changes text-neutral-grey-50 hover:text-neutral-grey-50 flex cursor-pointer items-center gap-1 text-xs", onClick: (e) => {
1212
- e.stopPropagation();
1213
- setConfirmingUndo(true);
1214
- }, children: [_jsx(Undo2, { strokeWidth: 1.5, size: 12 }), " Undo"] })), canUndo && !changesRejected && confirmingUndo && (_jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsxs("span", { className: "text-neutral-grey-50", children: ["Undo ", editOperations.length, " change", editOperations.length !== 1 ? "s" : "", "?"] }), _jsx("button", { type: "button", className: "text-feedback-red hover:text-feedback-red cursor-pointer", onClick: async (e) => {
1215
- e.stopPropagation();
1216
- const operationIds = undoableOperations.map((op) => op.id);
1217
- if (!editContext?.operations?.undoOperations) {
1218
- setConfirmingUndo(false);
1219
- return;
1220
- }
1221
- if (operationIds.length === 0) {
1222
- setConfirmingUndo(false);
1223
- return;
1224
- }
1225
- try {
1226
- const success = await editContext.operations.undoOperations(operationIds);
1227
- if (success) {
1228
- setChangesRejected(true);
1229
- }
1230
- }
1231
- catch (error) {
1232
- // Undo operation failed
1233
- }
1296
+ // Add the button
1297
+ // Calculate the actual text that will be submitted (same logic as in AgentTerminal.tsx)
1298
+ // For command links: prompt=command, value=command, so submitted text is link.command
1299
+ const submittedText = (link.command || link.label || "").trim();
1300
+ parts.push(_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { size: "sm", variant: "outline", className: "mx-1 inline-flex", onClick: () => onQuickAction?.({
1301
+ label: link.label,
1302
+ value: link.command,
1303
+ prompt: link.command,
1304
+ }, message), children: link.label }) }), submittedText && (_jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs break-words", children: submittedText }) }))] }, `btn-${itemIndex}-${linkIdx}`));
1305
+ // Find the end of this match in the original text
1306
+ const matchEnd = link.index + `[${link.label}](command:${link.command})`.length;
1307
+ lastIndex = matchEnd;
1308
+ });
1309
+ // Add any remaining text after the last link
1310
+ if (lastIndex < text.length) {
1311
+ const textAfter = text.substring(lastIndex);
1312
+ const html = simpleFormatToHtml(textAfter);
1313
+ if (html) {
1314
+ parts.push(_jsx("span", { dangerouslySetInnerHTML: { __html: html } }, `text-${itemIndex}-end`));
1315
+ }
1316
+ }
1317
+ return (_jsxs("div", { className: `${assistantMessageTextClassName} ${shouldAddSpacing ? "mt-2" : ""}`, children: [parts, isStreaming && isLastItem && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }, `item-${itemIndex}`));
1318
+ }
1319
+ const html = simpleFormatToHtml(trimmedText);
1320
+ if (!html)
1321
+ return null;
1322
+ return (_jsxs("div", { className: `${assistantMessageTextClassName} ${shouldAddSpacing ? "mt-2" : ""}`, children: [_jsx("span", { dangerouslySetInnerHTML: { __html: html } }), isStreaming && isLastItem && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }, `item-${itemIndex}`));
1323
+ }
1324
+ if (segment.kind === "todo") {
1325
+ const todo = segment;
1326
+ return (_jsxs("div", { className: `my-1 ${shouldAddSpacing ? "mt-2" : ""}`, children: [todo.title && (_jsx("div", { className: "text-neutral-grey-100 mb-1 text-xs font-medium", children: todo.title })), _jsx("div", { className: "flex flex-col gap-1", children: todo.items.map((todoItem, iIdx) => (_jsxs("label", { className: "text-neutral-grey-100 flex items-start gap-2 text-xs", children: [_jsx(Checkbox, { checked: todoItem.status === "completed", onCheckedChange: () => { }, "aria-readonly": true, className: "mt-0.5" }), _jsxs("div", { children: [_jsx("div", { className: todoItem.status === "completed" ||
1327
+ todoItem.status === "cancelled"
1328
+ ? "line-through opacity-70"
1329
+ : todoItem.status === "in_progress"
1330
+ ? "text-feedback-blue font-medium"
1331
+ : "", children: todoItem.title }), todoItem.instructions && (_jsx("div", { className: "text-neutral-grey-100 text-xs", children: todoItem.instructions }))] })] }, (todoItem.id || todoItem.title || "todo") + "-" + iIdx))) })] }, `item-${itemIndex}`));
1332
+ }
1333
+ if (segment.kind === "media") {
1334
+ const media = segment;
1335
+ return (_jsx("div", { className: `my-2 flex flex-col gap-3 ${shouldAddSpacing ? "mt-2" : ""}`, children: media.items.map((mediaItem, mIdx) => (_jsx(MediaImage, { itemId: mediaItem.itemId, label: mediaItem.label }, mediaItem.itemId || `media-${itemIndex}-${mIdx}`))) }, `item-${itemIndex}`));
1336
+ }
1337
+ if (segment.kind === "json") {
1338
+ return (_jsx(JsonBlockDisplay, { data: segment.data, raw: segment.raw, defaultCollapsed: defaultCollapseJson, className: shouldAddSpacing ? "mt-2" : "" }, `item-${itemIndex}`));
1339
+ }
1340
+ // segment.kind === "actions"
1341
+ const actions = segment.actions;
1342
+ const headline = segment.headline?.trim();
1343
+ return (_jsxs("div", { className: `border-neutral-grey-10 my-3 rounded-xl border bg-white px-3 py-2.5 ${shouldAddSpacing ? "mt-2" : ""}`, children: [headline && (_jsx("div", { className: "text-neutral-grey-100 mb-2.5 text-[13px] leading-4 font-semibold", children: headline })), _jsx("div", { className: "flex flex-wrap gap-2", children: actions.map((a, aIdx) => {
1344
+ const variant = a.style === "destructive"
1345
+ ? "destructive"
1346
+ : a.style === "outline"
1347
+ ? "outline"
1348
+ : a.style === "secondary"
1349
+ ? "secondary"
1350
+ : "default"; // primary
1351
+ const buttonClassName = variant === "outline"
1352
+ ? "h-7 rounded-full border-neutral-grey-10 bg-white px-3 text-xs font-medium text-neutral-grey-100 shadow-none hover:bg-neutral-grey-5"
1353
+ : "h-7 rounded-full border border-transparent bg-neutral-grey-10 px-3 text-xs font-medium text-neutral-grey-100 shadow-none hover:bg-neutral-grey-5";
1354
+ // Calculate the actual text that will be submitted (same logic as in AgentTerminal.tsx)
1355
+ const submittedText = (a.prompt ||
1356
+ a.value ||
1357
+ a.label ||
1358
+ "").trim();
1359
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { size: "xs", variant: variant, className: buttonClassName, onClick: () => onQuickAction?.({
1360
+ label: a.label,
1361
+ prompt: a.prompt,
1362
+ value: a.value,
1363
+ behavior: a.behavior,
1364
+ placeholder: a.placeholder,
1365
+ }, message), children: a.label }) }), submittedText && (_jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs break-words", children: submittedText }) }))] }, (a.id || a.label || "btn") + "-" + aIdx));
1366
+ }) })] }, `item-${itemIndex}`));
1367
+ }
1368
+ // item.type === "toolcalls"
1369
+ return (_jsx("div", { className: shouldAddSpacing ? "mt-2" : "", children: _jsx(ToolCallDisplay, { toolCalls: item.toolCalls, finished: finished, openPopovers: openPopovers, setOpenPopovers: setOpenPopovers, messageId: item.messageId, agentId: agentId, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: onSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, readOnly: readOnly }) }, `item-${itemIndex}`));
1370
+ };
1371
+ return (_jsx("div", { className: `px-4 pb-3 ${followsUserMessage ? "pt-6" : "pt-3"}`, "data-testid": "agent-message", children: _jsxs("div", { className: "min-w-0 flex-1 select-text", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-neutral-grey-100 text-xs font-medium", children: agentName || "Agent" }), lastMessage && lastMessage.createdDate && (_jsx(TimeWithTooltip, { date: new Date(lastMessage.createdDate), className: "text-neutral-grey-50 text-xs", testId: "agent-message-timestamp", timestampAttr: lastMessage.createdDate }))] }), error && (_jsx("div", { className: "border-feedback-red bg-feedback-red-light mb-2 rounded-lg border-l-4 p-2.5", "data-testid": "agent-error", children: _jsxs("div", { className: "flex items-start", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(X, { className: "text-feedback-red h-5 w-5", strokeWidth: 1 }) }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-feedback-red text-sm font-medium", children: "Agent Error" }), _jsx("p", { className: "text-feedback-red mt-1 text-sm", children: error })] })] }) })), displayItems.map((displayItem) => {
1372
+ if (displayItem.type === "activity-group") {
1373
+ const isLive = !finished &&
1374
+ displayItem.entries.some((entry) => entry.item.isLastMessage);
1375
+ return (_jsx(ActivityBlockGroup, { entries: displayItem.entries, isLive: isLive, renderEntry: (entry) => renderFlatItem(entry.item, entry.index) }, displayItem.key));
1376
+ }
1377
+ return renderFlatItem(displayItem.entry.item, displayItem.entry.index);
1378
+ }), isStreaming &&
1379
+ !error &&
1380
+ !allPendingApprovals?.length &&
1381
+ !isAnyReasoningStreaming && (_jsxs("div", { className: "flex items-center gap-1 pt-2", "data-testid": "agent-streaming-dots", children: [_jsx("div", { className: "bg-neutral-grey-50 h-1 w-1 animate-bounce rounded-full [animation-delay:-0.3s]" }), _jsx("div", { className: "bg-neutral-grey-50 h-1 w-1 animate-bounce rounded-full [animation-delay:-0.15s]" }), _jsx("div", { className: "bg-neutral-grey-50 h-1 w-1 animate-bounce rounded-full" })] })), (copyableText || (finished && editOperations.length > 0)) && (_jsxs("div", { className: "mt-3 flex items-center justify-end gap-2", children: [copyableText && (_jsx(CopyButton, { textToCopy: copyableText, iconOnly: false, className: "text-neutral-grey-50 hover:text-neutral-grey-50 flex cursor-pointer items-center gap-1 p-0 text-xs" })), finished && editOperations.length > 0 && (_jsxs(_Fragment, { children: [canUndo && !changesRejected && !confirmingUndo && (_jsxs("button", { type: "button", title: `Undo ${editOperations.length} change${editOperations.length !== 1 ? "s" : ""}`, className: "tour-ai-response-message-changes text-neutral-grey-50 hover:text-neutral-grey-50 flex cursor-pointer items-center gap-1 text-xs", onClick: (e) => {
1382
+ e.stopPropagation();
1383
+ setConfirmingUndo(true);
1384
+ }, children: [_jsx(Undo2, { strokeWidth: 1.5, size: 12 }), " Undo"] })), canUndo && !changesRejected && confirmingUndo && (_jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsxs("span", { className: "text-neutral-grey-50", children: ["Undo ", editOperations.length, " change", editOperations.length !== 1 ? "s" : "", "?"] }), _jsx("button", { type: "button", className: "text-feedback-red hover:text-feedback-red cursor-pointer", onClick: async (e) => {
1385
+ e.stopPropagation();
1386
+ const operationIds = undoableOperations.map((op) => op.id);
1387
+ if (!editContext?.operations?.undoOperations) {
1234
1388
  setConfirmingUndo(false);
1235
- }, children: "Yes" }), _jsx("button", { type: "button", className: "text-neutral-grey-50 hover:text-neutral-grey-50 cursor-pointer", onClick: (e) => {
1236
- e.stopPropagation();
1389
+ return;
1390
+ }
1391
+ if (operationIds.length === 0) {
1237
1392
  setConfirmingUndo(false);
1238
- }, children: "No" })] })), (changesRejected || allOperationsUndone) && (_jsxs("span", { className: "text-neutral-grey-50 inline-flex items-center gap-1 text-xs", children: [_jsx(Check, { strokeWidth: 2, size: 12 }), " Undone"] }))] }))] }))] })] }));
1393
+ return;
1394
+ }
1395
+ try {
1396
+ const success = await editContext.operations.undoOperations(operationIds);
1397
+ if (success) {
1398
+ setChangesRejected(true);
1399
+ }
1400
+ }
1401
+ catch (error) {
1402
+ // Undo operation failed
1403
+ }
1404
+ setConfirmingUndo(false);
1405
+ }, children: "Yes" }), _jsx("button", { type: "button", className: "text-neutral-grey-50 hover:text-neutral-grey-50 cursor-pointer", onClick: (e) => {
1406
+ e.stopPropagation();
1407
+ setConfirmingUndo(false);
1408
+ }, children: "No" })] })), (changesRejected || allOperationsUndone) && (_jsxs("span", { className: "text-neutral-grey-50 inline-flex items-center gap-1 text-xs", children: [_jsx(Check, { strokeWidth: 2, size: 12 }), " Undone"] }))] }))] }))] }) }));
1239
1409
  }
1240
1410
  //# sourceMappingURL=AiResponseMessage.js.map