@alpaca-editor/core 1.0.4192 → 1.0.4193

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 (106) hide show
  1. package/dist/agents-view/AgentCard.js +1 -1
  2. package/dist/agents-view/AgentCard.js.map +1 -1
  3. package/dist/agents-view/AgentsView.js +5 -7
  4. package/dist/agents-view/AgentsView.js.map +1 -1
  5. package/dist/config/config.js +14 -7
  6. package/dist/config/config.js.map +1 -1
  7. package/dist/editor/ItemInfo.js +3 -3
  8. package/dist/editor/ItemInfo.js.map +1 -1
  9. package/dist/editor/QuickItemSwitcher.js +1 -1
  10. package/dist/editor/QuickItemSwitcher.js.map +1 -1
  11. package/dist/editor/ai/AgentTerminal.d.ts +1 -7
  12. package/dist/editor/ai/AgentTerminal.js +382 -256
  13. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  14. package/dist/editor/ai/Agents.js +84 -198
  15. package/dist/editor/ai/Agents.js.map +1 -1
  16. package/dist/editor/ai/AiResponseMessage.d.ts +1 -3
  17. package/dist/editor/ai/AiResponseMessage.js +12 -65
  18. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  19. package/dist/editor/ai/ToolCallDisplay.d.ts +1 -2
  20. package/dist/editor/ai/ToolCallDisplay.js +5 -13
  21. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  22. package/dist/editor/client/EditorShell.js +5 -6
  23. package/dist/editor/client/EditorShell.js.map +1 -1
  24. package/dist/editor/client/hooks/useSocketMessageHandler.js +4 -6
  25. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  26. package/dist/editor/commands/componentCommands.js +0 -2
  27. package/dist/editor/commands/componentCommands.js.map +1 -1
  28. package/dist/editor/control-center/About.js +1 -1
  29. package/dist/editor/control-center/About.js.map +1 -1
  30. package/dist/editor/control-center/AllAgentsPanel.js +1 -1
  31. package/dist/editor/field-types/MultiLineText.js +1 -1
  32. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  33. package/dist/editor/services/aiService.d.ts +0 -1
  34. package/dist/editor/services/aiService.js.map +1 -1
  35. package/dist/editor/sidebar/Validation.js +1 -1
  36. package/dist/editor/sidebar/Validation.js.map +1 -1
  37. package/dist/index.d.ts +0 -3
  38. package/dist/index.js +0 -4
  39. package/dist/index.js.map +1 -1
  40. package/dist/page-wizard/PageWizard.js +3 -3
  41. package/dist/page-wizard/PageWizard.js.map +1 -1
  42. package/dist/page-wizard/WizardSteps.js +1 -1
  43. package/dist/page-wizard/WizardSteps.js.map +1 -1
  44. package/dist/revision.d.ts +2 -2
  45. package/dist/revision.js +2 -2
  46. package/dist/splash-screen/OpenPage.js +6 -10
  47. package/dist/splash-screen/OpenPage.js.map +1 -1
  48. package/dist/splash-screen/RecentPages.js +2 -2
  49. package/dist/splash-screen/RecentPages.js.map +1 -1
  50. package/dist/splash-screen/SplashScreen.js +1 -1
  51. package/dist/splash-screen/SplashScreen.js.map +1 -1
  52. package/dist/styles.css +12 -244
  53. package/package.json +1 -1
  54. package/src/agents-view/AgentCard.tsx +6 -1
  55. package/src/agents-view/AgentsView.tsx +30 -18
  56. package/src/config/config.tsx +17 -8
  57. package/src/editor/ItemInfo.tsx +2 -3
  58. package/src/editor/QuickItemSwitcher.tsx +1 -1
  59. package/src/editor/ai/AgentTerminal.tsx +649 -544
  60. package/src/editor/ai/Agents.tsx +250 -464
  61. package/src/editor/ai/AiResponseMessage.tsx +29 -134
  62. package/src/editor/ai/ToolCallDisplay.tsx +4 -18
  63. package/src/editor/client/EditorShell.tsx +6 -9
  64. package/src/editor/client/hooks/useSocketMessageHandler.ts +7 -6
  65. package/src/editor/commands/componentCommands.tsx +0 -1
  66. package/src/editor/control-center/About.tsx +2 -2
  67. package/src/editor/control-center/AllAgentsPanel.tsx +1 -1
  68. package/src/editor/field-types/MultiLineText.tsx +1 -1
  69. package/src/editor/services/aiService.ts +0 -2
  70. package/src/editor/sidebar/Validation.tsx +1 -1
  71. package/src/index.ts +0 -5
  72. package/src/page-wizard/PageWizard.tsx +3 -3
  73. package/src/page-wizard/WizardSteps.tsx +1 -1
  74. package/src/revision.ts +2 -2
  75. package/src/splash-screen/OpenPage.tsx +4 -12
  76. package/src/splash-screen/RecentPages.tsx +61 -58
  77. package/src/splash-screen/SplashScreen.tsx +1 -1
  78. package/styles.css +0 -20
  79. package/dist/components/ui/PlaceholderInput.d.ts +0 -41
  80. package/dist/components/ui/PlaceholderInput.js +0 -160
  81. package/dist/components/ui/PlaceholderInput.js.map +0 -1
  82. package/dist/components/ui/PlaceholderInputTypes.d.ts +0 -41
  83. package/dist/components/ui/PlaceholderInputTypes.js +0 -48
  84. package/dist/components/ui/PlaceholderInputTypes.js.map +0 -1
  85. package/dist/components/ui/PlaceholderItemSelector.d.ts +0 -7
  86. package/dist/components/ui/PlaceholderItemSelector.js +0 -154
  87. package/dist/components/ui/PlaceholderItemSelector.js.map +0 -1
  88. package/dist/editor/ai/MediaImage.d.ts +0 -6
  89. package/dist/editor/ai/MediaImage.js +0 -38
  90. package/dist/editor/ai/MediaImage.js.map +0 -1
  91. package/dist/splash-screen/ModernSplashScreen.d.ts +0 -8
  92. package/dist/splash-screen/ModernSplashScreen.js +0 -92
  93. package/dist/splash-screen/ModernSplashScreen.js.map +0 -1
  94. package/dist/splash-screen/ParheliaAssistantChat.d.ts +0 -8
  95. package/dist/splash-screen/ParheliaAssistantChat.js +0 -155
  96. package/dist/splash-screen/ParheliaAssistantChat.js.map +0 -1
  97. package/dist/splash-screen/RecentAgents.d.ts +0 -7
  98. package/dist/splash-screen/RecentAgents.js +0 -76
  99. package/dist/splash-screen/RecentAgents.js.map +0 -1
  100. package/src/components/ui/PlaceholderInput.tsx +0 -290
  101. package/src/components/ui/PlaceholderInputTypes.tsx +0 -97
  102. package/src/components/ui/PlaceholderItemSelector.tsx +0 -253
  103. package/src/editor/ai/MediaImage.tsx +0 -75
  104. package/src/splash-screen/ModernSplashScreen.tsx +0 -229
  105. package/src/splash-screen/ParheliaAssistantChat.tsx +0 -273
  106. package/src/splash-screen/RecentAgents.tsx +0 -151
@@ -34,10 +34,8 @@ import {
34
34
  DropdownMenuItem,
35
35
  DropdownMenuTrigger,
36
36
  } from "../../components/ui/dropdown-menu";
37
- import { ChevronDown, List } from "lucide-react";
37
+ import { ChevronDown } from "lucide-react";
38
38
  import { AgentProfilesOverview } from "./AgentProfilesOverview";
39
- import { AgentsView } from "../../agents-view/AgentsView";
40
- import { SecretAgentIcon } from "../ui/Icons";
41
39
 
42
40
  // function convertAgentMessagesToTerminalMessages(
43
41
  // agentMessages: AgentChatMessage[],
@@ -74,13 +72,12 @@ export const Agents = React.memo(function Agents({
74
72
  const [activeAgentId, setActiveAgentId] = useState<string | null>(null);
75
73
  const activeAgentIdRef = useRef<string | null>(null);
76
74
  const singleAgentModeRef = useRef<boolean>(false);
77
- const [profileTabDropdownOpen, setProfileTabDropdownOpen] = useState<
78
- string | null
79
- >(null);
75
+ const [historyPopoverOpen, setHistoryPopoverOpen] = useState(false);
80
76
  const [menuPopoverOpen, setMenuPopoverOpen] = useState(false);
81
77
  const [addPopoverOpen, setAddPopoverOpen] = useState(false);
82
78
  const [loadingAgents, setLoadingAgents] = useState(false);
83
79
  const [inactiveAgents, setInactiveAgents] = useState<Agent[]>([]);
80
+ const [historyFilter, setHistoryFilter] = useState("");
84
81
  const [initialMetadataMap, setInitialMetadataMap] = useState<
85
82
  Record<string, AgentMetadata | undefined>
86
83
  >({});
@@ -96,44 +93,6 @@ export const Agents = React.memo(function Agents({
96
93
  [editContext?.currentItemDescriptor?.id],
97
94
  );
98
95
 
99
- // Group agents by profile for profile-based tabs
100
- const agentsGroupedByProfile = useMemo(() => {
101
- const profileMap = new Map<
102
- string,
103
- {
104
- profileId: string | null;
105
- profileName: string;
106
- profileSvgIcon: string | undefined;
107
- agents: Agent[];
108
- }
109
- >();
110
-
111
- agents.forEach((agent) => {
112
- const profileId = agent.profileId || null;
113
- const profileKey = profileId || "general";
114
- const profile = availableProfiles.find((p) => p.id === profileId);
115
-
116
- if (!profileMap.has(profileKey)) {
117
- profileMap.set(profileKey, {
118
- profileId,
119
- profileName: agent.profileName || "General",
120
- profileSvgIcon: profile?.svgIcon,
121
- agents: [],
122
- });
123
- } else {
124
- // Update icon if current group doesn't have one but this agent does
125
- const group = profileMap.get(profileKey)!;
126
- if (!group.profileSvgIcon && profile?.svgIcon) {
127
- group.profileSvgIcon = profile?.svgIcon;
128
- }
129
- }
130
-
131
- profileMap.get(profileKey)!.agents.push(agent);
132
- });
133
-
134
- return profileMap;
135
- }, [agents, availableProfiles]);
136
-
137
96
  // Detect single-agent mode from query parameter once on mount
138
97
  useEffect(() => {
139
98
  try {
@@ -144,6 +103,18 @@ export const Agents = React.memo(function Agents({
144
103
  } catch {}
145
104
  }, []);
146
105
 
106
+ // Helper function to filter agents by name
107
+ const getFilteredInactiveAgents = (): Agent[] => {
108
+ if (!historyFilter.trim()) {
109
+ return inactiveAgents;
110
+ }
111
+
112
+ const filterLower = historyFilter.toLowerCase();
113
+ return inactiveAgents.filter((agent) =>
114
+ agent.name.toLowerCase().includes(filterLower),
115
+ );
116
+ };
117
+
147
118
  // Helper function to get the most recently updated agent
148
119
  const getMostRecentAgent = (agentList: Agent[]): Agent | null => {
149
120
  if (agentList.length === 0) return null;
@@ -154,34 +125,6 @@ export const Agents = React.memo(function Agents({
154
125
  });
155
126
  };
156
127
 
157
- // Helper function to get highest priority status for a group of agents
158
- const getHighestPriorityStatus = (agentList: Agent[]): Agent["status"] => {
159
- const statusPriority: Record<string, number> = {
160
- waitingForApproval: 5,
161
- error: 4,
162
- running: 3,
163
- idle: 2,
164
- completed: 1,
165
- closed: 0,
166
- new: 0,
167
- };
168
-
169
- let highestPriority = -1;
170
- let highestStatus: Agent["status"] = "idle";
171
-
172
- agentList.forEach((agent) => {
173
- const statusKey =
174
- typeof agent.status === "string" ? agent.status : "idle";
175
- const priority = statusPriority[statusKey] ?? 0;
176
- if (priority > highestPriority) {
177
- highestPriority = priority;
178
- highestStatus = agent.status;
179
- }
180
- });
181
-
182
- return highestStatus;
183
- };
184
-
185
128
  // Helper function to set active agent and persist to localStorage
186
129
  const setActiveAgentIdWithStorage = (agentId: string | null) => {
187
130
  setActiveAgentId(agentId);
@@ -346,8 +289,7 @@ export const Agents = React.memo(function Agents({
346
289
  }
347
290
  }
348
291
  if (message.type === "agent:run:start") {
349
- const { agentId, agentName, autoSelect, profileId, profileName } =
350
- message.payload;
292
+ const { agentId, agentName, autoSelect } = message.payload;
351
293
 
352
294
  if (!agentId || !agentName) {
353
295
  console.warn(
@@ -367,7 +309,7 @@ export const Agents = React.memo(function Agents({
367
309
  );
368
310
 
369
311
  if (existingAgentIndex !== -1) {
370
- // Update existing agent name and profile info
312
+ // Update existing agent name
371
313
  const updatedAgents = [...prevAgents];
372
314
  const existingAgent = updatedAgents[existingAgentIndex]!;
373
315
  updatedAgents[existingAgentIndex] = {
@@ -375,8 +317,6 @@ export const Agents = React.memo(function Agents({
375
317
  name: agentName,
376
318
  status: "running" as const,
377
319
  updatedDate: new Date().toISOString(),
378
- ...(profileId && { profileId }),
379
- ...(profileName && { profileName }),
380
320
  };
381
321
  return updatedAgents;
382
322
  } else {
@@ -387,8 +327,6 @@ export const Agents = React.memo(function Agents({
387
327
  status: "running" as const,
388
328
  userId: "", // Will be populated from backend if needed
389
329
  updatedDate: new Date().toISOString(),
390
- ...(profileId && { profileId }),
391
- ...(profileName && { profileName }),
392
330
  };
393
331
 
394
332
  return [...prevAgents, newAgent];
@@ -539,11 +477,13 @@ export const Agents = React.memo(function Agents({
539
477
 
540
478
  setAgents(activeAgentsResult);
541
479
 
542
- // Try to restore the previously active agent from localStorage if one exists
480
+ // Determine which agent to select as active
543
481
  let selectedAgentId: string | null = null;
544
482
 
545
483
  if (activeAgentsResult.length > 0) {
484
+ // Try to restore the previously active agent from localStorage
546
485
  const storedAgentId = localStorage.getItem(ACTIVE_AGENT_STORAGE_KEY);
486
+
547
487
  const storedAgent = activeAgentsResult.find(
548
488
  (agent) => agent.id === storedAgentId,
549
489
  );
@@ -551,7 +491,7 @@ export const Agents = React.memo(function Agents({
551
491
  if (storedAgent) {
552
492
  selectedAgentId = storedAgent.id;
553
493
  } else {
554
- // If no stored agent or stored agent not found, select the most recent agent
494
+ // Fall back to the most recently updated agent
555
495
  const mostRecentAgent = getMostRecentAgent(activeAgentsResult);
556
496
  selectedAgentId = mostRecentAgent?.id || null;
557
497
  }
@@ -602,8 +542,6 @@ export const Agents = React.memo(function Agents({
602
542
  name: `New Agent`,
603
543
  updatedDate: new Date().toISOString(),
604
544
  userId: "",
605
- profileId: metadata?.additionalData?.profileId,
606
- profileName: metadata?.additionalData?.profileName || metadata?.profile,
607
545
  };
608
546
  setAgents((prev) => [...prev, newAgent]);
609
547
  // User-initiated creation should activate the new agent tab
@@ -730,85 +668,6 @@ export const Agents = React.memo(function Agents({
730
668
  }
731
669
  };
732
670
 
733
- const handleCloseProfileAgents = async (
734
- profileAgents: Agent[],
735
- profileName: string,
736
- e: React.MouseEvent,
737
- ) => {
738
- e.stopPropagation();
739
-
740
- const agentCount = profileAgents.length;
741
- const hasActiveAgents = profileAgents.some(
742
- (agent) =>
743
- agent.status === "running" ||
744
- agent.status === "waitingForApproval" ||
745
- agent.status === 1 ||
746
- agent.status === 2,
747
- );
748
-
749
- if (editContext?.confirm) {
750
- editContext.confirm({
751
- header: `Close ${agentCount} Agent${agentCount > 1 ? "s" : ""}`,
752
- message: hasActiveAgents
753
- ? `This will close all ${agentCount} ${profileName} agent${agentCount > 1 ? "s" : ""}, including ${profileAgents.filter((a) => a.status === "running" || a.status === 1).length} running agent(s). Are you sure?`
754
- : `Are you sure you want to close all ${agentCount} ${profileName} agent${agentCount > 1 ? "s" : ""}?`,
755
- acceptLabel: `Close ${agentCount > 1 ? "All" : "Agent"}`,
756
- accept: async () => {
757
- // Close all agents in this profile
758
- const closePromises = profileAgents.map((agent) =>
759
- closeAgentService(agent.id).catch((error) => {
760
- console.error(`Failed to close agent ${agent.id}:`, error);
761
- }),
762
- );
763
-
764
- await Promise.all(closePromises);
765
-
766
- // Update UI state
767
- setAgents((prev) => {
768
- const agentIds = new Set(profileAgents.map((a) => a.id));
769
- const filtered = prev.filter((a) => !agentIds.has(a.id));
770
-
771
- // Add closed agents to history
772
- const closedAgents = profileAgents.map((agent) => ({
773
- ...agent,
774
- status: "closed" as const,
775
- updatedDate: new Date().toISOString(),
776
- }));
777
-
778
- setInactiveAgents((prevInactive) => {
779
- const newAgents = closedAgents.filter(
780
- (closedAgent) =>
781
- !prevInactive.some((a) => a.id === closedAgent.id),
782
- );
783
- return [...newAgents, ...prevInactive];
784
- });
785
-
786
- // If we're closing the active agent, switch to most recent remaining or null
787
- if (
788
- activeAgentIdRef.current &&
789
- agentIds.has(activeAgentIdRef.current)
790
- ) {
791
- if (filtered.length > 0) {
792
- const mostRecentAgent = getMostRecentAgent(filtered);
793
- setActiveAgentIdWithStorage(mostRecentAgent?.id || null);
794
- } else {
795
- setActiveAgentIdWithStorage(null);
796
- }
797
- }
798
-
799
- // Reset profile selection if no agents remain
800
- if (filtered.length === 0) {
801
- setSelectedProfileId("");
802
- }
803
-
804
- return filtered;
805
- });
806
- },
807
- showCancel: true,
808
- });
809
- }
810
- };
811
-
812
671
  const closeOtherAgents = async () => {
813
672
  if (!activeAgentIdRef.current) return;
814
673
 
@@ -871,45 +730,37 @@ export const Agents = React.memo(function Agents({
871
730
  if (existingAgent) {
872
731
  // Switch to existing agent
873
732
  setActiveAgentIdWithStorage(existingAgent.id);
733
+ setHistoryPopoverOpen(false);
874
734
  return;
875
735
  }
876
736
 
877
- // If agent data is incomplete (e.g., only has ID), fetch full details from backend
878
- let fullAgent = agent;
879
- if (!agent.name || !agent.userId) {
880
- try {
881
- const { getAgent } = await import("../services/agentService");
882
- const agentDetails = await getAgent(agent.id);
883
- fullAgent = {
884
- id: agentDetails.id,
885
- name: agentDetails.name,
886
- description: agentDetails.description,
887
- userId: agentDetails.userId,
888
- updatedDate: agentDetails.updatedDate,
889
- status: agentDetails.status,
890
- isShared: agentDetails.isShared,
891
- ownerName: agentDetails.ownerName,
892
- profileId: agentDetails.profileId,
893
- profileName: agentDetails.profileName,
894
- profileSvgIcon: agentDetails.profileSvgIcon,
895
- messageCount: agentDetails.messageCount,
896
- totalCost: agentDetails.totalCost,
897
- totalTokensUsed: agentDetails.totalTokensUsed,
898
- };
899
- } catch (error) {
900
- console.error("Failed to fetch agent details:", error);
901
- // Fall back to using whatever data we have
902
- }
903
- }
904
-
905
737
  // Add the closed agent to the active agents list
906
738
  const reopenedAgent: Agent = {
907
- ...fullAgent,
739
+ ...agent,
908
740
  // Keep the original status to allow AgentTerminal to load the full agent data
909
741
  };
910
742
 
911
743
  setAgents((prev) => [...prev, reopenedAgent]);
912
744
  setActiveAgentIdWithStorage(reopenedAgent.id);
745
+ setHistoryPopoverOpen(false);
746
+ };
747
+
748
+ const deleteAgentFromHistory = async (
749
+ agentId: string,
750
+ event: React.MouseEvent,
751
+ ) => {
752
+ event.stopPropagation(); // Prevent opening the agent when clicking delete
753
+
754
+ try {
755
+ // Permanently delete the agent from the backend
756
+ await deleteAgent(agentId);
757
+
758
+ // Remove from inactive agents list
759
+ setInactiveAgents((prev) => prev.filter((agent) => agent.id !== agentId));
760
+ } catch (error) {
761
+ console.error("Failed to delete agent:", error);
762
+ // You might want to show a user-facing error message here
763
+ }
913
764
  };
914
765
 
915
766
  if (loadingAgents) {
@@ -923,55 +774,32 @@ export const Agents = React.memo(function Agents({
923
774
  // Empty state when no agents are open
924
775
  if (!loadingAgents && agents.length === 0) {
925
776
  return (
926
- <div className="flex h-full flex-col">
927
- {/* Header with Overview Button */}
928
- <div className="flex items-center justify-between border-b border-gray-200 bg-gray-50 px-4 py-2">
929
- <div className="text-xs font-medium text-gray-600">
930
- No Active Agents
931
- </div>
932
- <div className="flex items-center gap-2">
933
- {/* Overview Button */}
934
- <SimpleIconButton
935
- onClick={() => setActiveAgentIdWithStorage(null)}
936
- icon={<List className="size-4" strokeWidth={1} />}
937
- label="Agent Overview"
938
- className="text-gray-600 hover:text-gray-800"
939
- />
940
- {/* Main Close Button */}
941
- {closeButton && (
942
- <div className="flex items-center">{closeButton}</div>
943
- )}
944
- </div>
945
- </div>
946
-
947
- {/* Content Area */}
948
- <div className="flex flex-1 items-center justify-center p-4">
949
- <div className="w-full max-w-4xl">
950
- {loadingProfiles ? (
951
- <div className="text-center text-xs text-gray-500">
952
- Loading profiles...
953
- </div>
954
- ) : availableProfiles.length > 0 ? (
955
- <div className="flex flex-col gap-4">
956
- <div className="text-center">
957
- <h2 className="text-lg font-semibold text-gray-900">
958
- Select an AI Agent Profile
959
- </h2>
960
- <p className="text-xs text-gray-500">
961
- Choose a profile to start a new agent
962
- </p>
963
- </div>
964
- <AgentProfilesOverview
965
- profiles={availableProfiles}
966
- onSelectProfile={createAgentWithSelectedProfile}
967
- />
968
- </div>
969
- ) : (
970
- <div className="text-center text-xs text-gray-500">
971
- No profiles available
777
+ <div className="flex items-center justify-center p-4">
778
+ <div className="w-full max-w-4xl">
779
+ {loadingProfiles ? (
780
+ <div className="text-center text-xs text-gray-500">
781
+ Loading profiles...
782
+ </div>
783
+ ) : availableProfiles.length > 0 ? (
784
+ <div className="flex flex-col gap-4">
785
+ <div className="text-center">
786
+ <h2 className="text-lg font-semibold text-gray-900">
787
+ Select an AI Agent Profile
788
+ </h2>
789
+ <p className="text-xs text-gray-500">
790
+ Choose a profile to start a new agent
791
+ </p>
972
792
  </div>
973
- )}
974
- </div>
793
+ <AgentProfilesOverview
794
+ profiles={availableProfiles}
795
+ onSelectProfile={createAgentWithSelectedProfile}
796
+ />
797
+ </div>
798
+ ) : (
799
+ <div className="text-center text-xs text-gray-500">
800
+ No profiles available
801
+ </div>
802
+ )}
975
803
  </div>
976
804
  </div>
977
805
  );
@@ -982,213 +810,179 @@ export const Agents = React.memo(function Agents({
982
810
  {/* Tab Header */}
983
811
  <div className="flex items-center border-b border-gray-200 bg-gray-50">
984
812
  <div className="scrollbar-hide flex flex-1 overflow-x-auto">
985
- {Array.from(agentsGroupedByProfile.values()).map((profileGroup) => {
986
- const profileKey = profileGroup.profileId || "general";
987
- const activeAgent = profileGroup.agents.find(
988
- (a) => a.id === activeAgentId,
989
- );
990
- const isActive = !!activeAgent;
991
- const agentCount = profileGroup.agents.length;
992
- const singleAgent =
993
- agentCount === 1 ? profileGroup.agents[0] : null;
994
- const highestStatus = getHighestPriorityStatus(profileGroup.agents);
995
- const statusConfig = getAgentStatusConfig({
996
- status: highestStatus,
997
- } as Agent);
998
-
999
- // Use agent name if: single agent OR active agent from this profile
1000
- const displayName = activeAgent
1001
- ? activeAgent.name
1002
- : singleAgent
1003
- ? singleAgent.name
1004
- : profileGroup.profileName;
1005
-
1006
- // Always use profile group icon since all agents in the group share the same profile
1007
- const displayIcon = profileGroup.profileSvgIcon;
1008
-
1009
- return (
1010
- <Popover
1011
- key={profileKey}
1012
- open={profileTabDropdownOpen === profileKey}
1013
- onOpenChange={(open) => {
1014
- setProfileTabDropdownOpen(open ? profileKey : null);
1015
- }}
1016
- >
1017
- <PopoverTrigger asChild>
1018
- <div
1019
- className={cn(
1020
- "flex min-w-0 cursor-pointer items-center gap-2 border-r border-gray-200 p-2 text-xs",
1021
- isActive
1022
- ? "border-b-white bg-white"
1023
- : "hover:bg-gray-100",
1024
- )}
1025
- onClick={() => {
1026
- if (singleAgent) {
1027
- // Single agent: switch directly
1028
- setActiveAgentIdWithStorage(singleAgent.id);
1029
- }
1030
- // Multiple agents: popover will open via PopoverTrigger
1031
- }}
1032
- >
1033
- {/* Profile/Agent Icon */}
1034
- <div className="flex-shrink-0">
1035
- {displayIcon ? (
1036
- <div
1037
- className="flex h-[18px] w-[18px] items-center justify-center text-gray-500 [&>svg]:h-full [&>svg]:w-full"
1038
- dangerouslySetInnerHTML={{
1039
- __html: displayIcon,
1040
- }}
1041
- />
1042
- ) : (
1043
- <SecretAgentIcon
1044
- size={18}
1045
- strokeWidth={1}
1046
- className="text-gray-500"
1047
- />
1048
- )}
1049
- </div>
1050
-
1051
- {/* Profile/Agent Name */}
1052
- <span className="truncate font-medium">{displayName}</span>
1053
-
1054
- {/* Agent Count Badge */}
1055
- {agentCount > 1 && (
1056
- <div className="flex-shrink-0 rounded-full bg-gray-200 px-1.5 py-0.5 text-[10px] leading-none font-medium text-gray-700">
1057
- {agentCount}
1058
- </div>
1059
- )}
1060
-
1061
- {/* Status Indicator */}
1062
- <Tooltip>
1063
- <TooltipTrigger asChild>
1064
- <div
1065
- className={cn(
1066
- "h-1.5 w-1.5 flex-shrink-0 rounded-full",
1067
- statusConfig.color,
1068
- statusConfig.shouldPulse && "animate-pulse",
1069
- )}
1070
- title={statusConfig.label}
1071
- />
1072
- </TooltipTrigger>
1073
- <TooltipContent>{statusConfig.label}</TooltipContent>
1074
- </Tooltip>
1075
-
1076
- {/* Chevron for multiple agents */}
1077
- {agentCount > 1 && (
1078
- <ChevronDown
1079
- className="size-3 flex-shrink-0 text-gray-500"
1080
- strokeWidth={1}
1081
- />
1082
- )}
1083
-
1084
- {/* Close button - always shown */}
1085
- <SimpleIconButton
1086
- onClick={(e) =>
1087
- singleAgent
1088
- ? handleCloseAgent(singleAgent.id, e)
1089
- : handleCloseProfileAgents(
1090
- profileGroup.agents,
1091
- profileGroup.profileName,
1092
- e,
1093
- )
1094
- }
1095
- icon={<X className="size-3" strokeWidth={1} />}
1096
- label={
1097
- agentCount > 1
1098
- ? `Close all ${agentCount} agents`
1099
- : "Close"
1100
- }
1101
- className="opacity-60 hover:opacity-100"
1102
- />
1103
- </div>
1104
- </PopoverTrigger>
1105
-
1106
- {/* Dropdown for multiple agents */}
1107
- {agentCount > 1 && (
1108
- <PopoverContent className="w-80 p-0" align="start">
1109
- <div className="border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-500">
1110
- {profileGroup.profileName} Agents
1111
- </div>
1112
- <div className="max-h-64 overflow-y-auto">
1113
- {profileGroup.agents.map((agent) => {
1114
- const agentStatusConfig = getAgentStatusConfig(agent);
1115
- return (
1116
- <div
1117
- key={agent.id}
1118
- className={cn(
1119
- "cursor-pointer border-b border-gray-50 px-3 py-2 hover:bg-gray-50",
1120
- activeAgentId === agent.id && "bg-blue-50",
1121
- )}
1122
- onClick={() => {
1123
- setActiveAgentIdWithStorage(agent.id);
1124
- setProfileTabDropdownOpen(null);
1125
- }}
1126
- >
1127
- <div className="flex items-center gap-2">
1128
- {/* Status Indicator */}
1129
- <div
1130
- className={cn(
1131
- "h-2 w-2 flex-shrink-0 rounded-full",
1132
- agentStatusConfig.color,
1133
- agentStatusConfig.shouldPulse &&
1134
- "animate-pulse",
1135
- )}
1136
- title={agentStatusConfig.label}
1137
- />
1138
- {/* Agent Name */}
1139
- <div className="min-w-0 flex-1">
1140
- <div className="truncate text-xs font-medium text-gray-900">
1141
- {agent.name}
1142
- </div>
1143
- {agent.description && (
1144
- <div className="truncate text-[11px] leading-tight text-gray-400">
1145
- {agent.description}
1146
- </div>
1147
- )}
1148
- </div>
1149
- {/* Close Button */}
1150
- <SimpleIconButton
1151
- onClick={(e) => handleCloseAgent(agent.id, e)}
1152
- icon={<X className="size-3" strokeWidth={1} />}
1153
- label="Close"
1154
- className="opacity-60 hover:opacity-100"
1155
- />
1156
- </div>
1157
- </div>
1158
- );
1159
- })}
1160
- </div>
1161
- </PopoverContent>
1162
- )}
1163
- </Popover>
1164
- );
1165
- })}
1166
- </div>
1167
-
1168
- {/* Overview Button - only show when an agent is active (not showing overview) */}
1169
- {activeAgentId !== null && (
1170
- <div className="flex items-center px-1">
1171
- <SimpleIconButton
1172
- onClick={() => setActiveAgentIdWithStorage(null)}
1173
- icon={<List className="size-4" strokeWidth={1} />}
1174
- label="Agent Overview"
1175
- className="text-gray-600 hover:text-gray-800"
1176
- />
1177
- </div>
1178
- )}
1179
-
1180
- {/* Overview Tab - show as active tab when overview is displayed */}
1181
- {activeAgentId === null && (
1182
- <div className="flex items-center px-1">
813
+ {agents.map((agent) => (
1183
814
  <div
815
+ key={agent.id}
1184
816
  className={cn(
1185
- "flex min-w-0 cursor-default items-center gap-2 border-r border-gray-200 p-2 text-xs",
1186
- "border-b-white bg-white",
817
+ "flex min-w-0 cursor-pointer items-center gap-1 border-r border-gray-200 px-2 py-2 pr-1.5 text-xs",
818
+ activeAgentId === agent.id
819
+ ? "border-b-white bg-white"
820
+ : "hover:bg-gray-100",
1187
821
  )}
822
+ data-agent-id={agent.id}
823
+ data-active={activeAgentId === agent.id ? "true" : "false"}
824
+ onClick={() => setActiveAgentIdWithStorage(agent.id)}
825
+ onContextMenu={(e) => {
826
+ e.preventDefault();
827
+ e.stopPropagation();
828
+ // Capture coordinates before state update to avoid pooled/react event issues
829
+ const contextEvent = {
830
+ clientX: e.clientX,
831
+ clientY: e.clientY,
832
+ pageX: e.pageX,
833
+ pageY: e.pageY,
834
+ screenX: (e as any).screenX,
835
+ screenY: (e as any).screenY,
836
+ button: 2,
837
+ buttons: 2,
838
+ ctrlKey: e.ctrlKey,
839
+ shiftKey: e.shiftKey,
840
+ altKey: e.altKey,
841
+ preventDefault: () => {},
842
+ } as any;
843
+
844
+ // Show the menu first at the correct position
845
+ setTimeout(() => {
846
+ editContext?.showContextMenu(
847
+ contextEvent,
848
+ getTabsMenuItems(),
849
+ );
850
+ }, 150);
851
+ }}
1188
852
  >
1189
- <List className="size-4 flex-shrink-0 text-gray-600" strokeWidth={1} />
1190
- <span className="truncate font-medium">Overview</span>
853
+ {/* Status Indicator */}
854
+ {(() => {
855
+ const statusConfig = getAgentStatusConfig(agent);
856
+ return (
857
+ <Tooltip>
858
+ <TooltipTrigger asChild>
859
+ <div
860
+ className={cn(
861
+ "mr-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full",
862
+ statusConfig.color,
863
+ statusConfig.shouldPulse && "animate-pulse",
864
+ )}
865
+ title={statusConfig.label}
866
+ />
867
+ </TooltipTrigger>
868
+ <TooltipContent>{statusConfig.label}</TooltipContent>
869
+ </Tooltip>
870
+ );
871
+ })()}
872
+ <Tooltip>
873
+ <TooltipTrigger asChild>
874
+ <span className="truncate" title={agent.name}>
875
+ {agent.name}
876
+ </span>
877
+ </TooltipTrigger>
878
+ <TooltipContent>{agent.name}</TooltipContent>
879
+ </Tooltip>
880
+ <SimpleIconButton
881
+ onClick={(e) => handleCloseAgent(agent.id, e)}
882
+ icon={<X className="size-3" strokeWidth={1} />}
883
+ label="Close"
884
+ className="ml-1 opacity-60 hover:opacity-100"
885
+ />
1191
886
  </div>
887
+ ))}
888
+ </div>
889
+
890
+ {/* History Popover */}
891
+ {agents.length > 0 && (
892
+ <div className="flex items-center px-1">
893
+ <Popover
894
+ open={historyPopoverOpen}
895
+ onOpenChange={(open) => {
896
+ setHistoryPopoverOpen(open);
897
+ if (!open) {
898
+ setHistoryFilter(""); // Clear filter when popover closes
899
+ }
900
+ }}
901
+ >
902
+ <PopoverTrigger asChild>
903
+ <SimpleIconButton
904
+ onClick={() => {}}
905
+ icon={<History className="size-4" strokeWidth={1} />}
906
+ label="Agent History"
907
+ className="text-gray-600 hover:text-gray-800"
908
+ />
909
+ </PopoverTrigger>
910
+ <PopoverContent className="w-64 p-0" align="end">
911
+ <div className="border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-500">
912
+ Closed Agents
913
+ </div>
914
+ {inactiveAgents.length > 0 && (
915
+ <div className="border-b border-gray-100 px-3 py-2">
916
+ <input
917
+ type="text"
918
+ placeholder="Filter agents..."
919
+ value={historyFilter}
920
+ onChange={(e) => setHistoryFilter(e.target.value)}
921
+ className="w-full rounded border border-gray-200 px-2 py-1 text-xs focus:border-blue-500 focus:outline-none"
922
+ />
923
+ </div>
924
+ )}
925
+ <div className="max-h-80 overflow-y-auto">
926
+ {(() => {
927
+ const filteredAgents = getFilteredInactiveAgents();
928
+
929
+ if (inactiveAgents.length === 0) {
930
+ return (
931
+ <div className="px-3 py-2 text-xs text-gray-500">
932
+ No closed agents found
933
+ </div>
934
+ );
935
+ }
936
+
937
+ if (filteredAgents.length === 0) {
938
+ return (
939
+ <div className="px-3 py-2 text-xs text-gray-500">
940
+ No agents match your filter
941
+ </div>
942
+ );
943
+ }
944
+
945
+ return filteredAgents.map((agent) => (
946
+ <div
947
+ key={agent.id}
948
+ className="cursor-pointer border-b border-gray-50 px-3 py-2 text-xs hover:bg-gray-50"
949
+ onClick={() => openAgentFromHistory(agent)}
950
+ >
951
+ <div className="flex items-center justify-between">
952
+ <div className="min-w-0 flex-1">
953
+ <div className="truncate font-medium text-gray-900">
954
+ {agent.name}
955
+ </div>
956
+ <div className="text-xs text-gray-400">
957
+ {(() => {
958
+ try {
959
+ const {
960
+ formatDateTime,
961
+ } = require("../utils");
962
+ return formatDateTime(
963
+ new Date(agent.updatedDate),
964
+ );
965
+ } catch {
966
+ return new Date(
967
+ agent.updatedDate,
968
+ ).toLocaleString();
969
+ }
970
+ })()}
971
+ </div>
972
+ </div>
973
+ <SimpleIconButton
974
+ onClick={(e) => deleteAgentFromHistory(agent.id, e)}
975
+ icon={<Trash className="size-3" strokeWidth={1} />}
976
+ label="Delete Agent"
977
+ className="ml-2 text-red-600 opacity-60 hover:text-red-700 hover:opacity-100"
978
+ />
979
+ </div>
980
+ </div>
981
+ ));
982
+ })()}
983
+ </div>
984
+ </PopoverContent>
985
+ </Popover>
1192
986
  </div>
1193
987
  )}
1194
988
 
@@ -1278,30 +1072,22 @@ export const Agents = React.memo(function Agents({
1278
1072
 
1279
1073
  {/* Agent Content */}
1280
1074
  <div className="relative flex-1">
1281
- {activeAgentId === null ? (
1282
- // Show AgentsView when no agent is selected
1283
- <div className="absolute inset-0">
1284
- <AgentsView />
1075
+ {agents.map((agent) => (
1076
+ <div
1077
+ key={agent.id}
1078
+ className={cn(
1079
+ "absolute inset-0",
1080
+ activeAgentId === agent.id ? "block" : "hidden",
1081
+ )}
1082
+ >
1083
+ <AgentTerminal
1084
+ agentStub={agent}
1085
+ initialMetadata={initialMetadataMap[agent.id]}
1086
+ profiles={availableProfiles}
1087
+ isActive={activeAgentId === agent.id}
1088
+ />
1285
1089
  </div>
1286
- ) : (
1287
- // Show agent terminals
1288
- agents.map((agent) => (
1289
- <div
1290
- key={agent.id}
1291
- className={cn(
1292
- "absolute inset-0",
1293
- activeAgentId === agent.id ? "block" : "hidden",
1294
- )}
1295
- >
1296
- <AgentTerminal
1297
- agentStub={agent}
1298
- initialMetadata={initialMetadataMap[agent.id]}
1299
- profiles={availableProfiles}
1300
- isActive={activeAgentId === agent.id}
1301
- />
1302
- </div>
1303
- ))
1304
- )}
1090
+ ))}
1305
1091
  </div>
1306
1092
  </div>
1307
1093
  );