@alpaca-editor/core 1.0.4191 → 1.0.4192
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.
- package/dist/agents-view/AgentCard.js +1 -1
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsView.js +7 -5
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/components/ui/PlaceholderInput.d.ts +41 -0
- package/dist/components/ui/PlaceholderInput.js +160 -0
- package/dist/components/ui/PlaceholderInput.js.map +1 -0
- package/dist/components/ui/PlaceholderInputTypes.d.ts +41 -0
- package/dist/components/ui/PlaceholderInputTypes.js +48 -0
- package/dist/components/ui/PlaceholderInputTypes.js.map +1 -0
- package/dist/components/ui/PlaceholderItemSelector.d.ts +7 -0
- package/dist/components/ui/PlaceholderItemSelector.js +154 -0
- package/dist/components/ui/PlaceholderItemSelector.js.map +1 -0
- package/dist/config/config.js +7 -14
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ItemInfo.js +3 -3
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/QuickItemSwitcher.js +1 -1
- package/dist/editor/QuickItemSwitcher.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +7 -1
- package/dist/editor/ai/AgentTerminal.js +256 -382
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +198 -84
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +3 -1
- package/dist/editor/ai/AiResponseMessage.js +65 -12
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/MediaImage.d.ts +6 -0
- package/dist/editor/ai/MediaImage.js +38 -0
- package/dist/editor/ai/MediaImage.js.map +1 -0
- package/dist/editor/ai/ToolCallDisplay.d.ts +2 -1
- package/dist/editor/ai/ToolCallDisplay.js +13 -5
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/client/EditorShell.js +6 -5
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +6 -4
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +2 -0
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/control-center/About.js +1 -1
- package/dist/editor/control-center/About.js.map +1 -1
- package/dist/editor/control-center/AllAgentsPanel.js +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +1 -1
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/PageWizard.js +3 -3
- package/dist/page-wizard/PageWizard.js.map +1 -1
- package/dist/page-wizard/WizardSteps.js +1 -1
- package/dist/page-wizard/WizardSteps.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/ModernSplashScreen.d.ts +8 -0
- package/dist/splash-screen/ModernSplashScreen.js +92 -0
- package/dist/splash-screen/ModernSplashScreen.js.map +1 -0
- package/dist/splash-screen/OpenPage.js +10 -6
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/splash-screen/ParheliaAssistantChat.d.ts +8 -0
- package/dist/splash-screen/ParheliaAssistantChat.js +155 -0
- package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -0
- package/dist/splash-screen/RecentAgents.d.ts +7 -0
- package/dist/splash-screen/RecentAgents.js +76 -0
- package/dist/splash-screen/RecentAgents.js.map +1 -0
- package/dist/splash-screen/RecentPages.js +2 -2
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/splash-screen/SplashScreen.js +1 -1
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +244 -12
- package/package.json +1 -1
- package/src/agents-view/AgentCard.tsx +1 -6
- package/src/agents-view/AgentsView.tsx +18 -30
- package/src/components/ui/PlaceholderInput.tsx +290 -0
- package/src/components/ui/PlaceholderInputTypes.tsx +97 -0
- package/src/components/ui/PlaceholderItemSelector.tsx +253 -0
- package/src/config/config.tsx +8 -17
- package/src/editor/ItemInfo.tsx +3 -2
- package/src/editor/QuickItemSwitcher.tsx +1 -1
- package/src/editor/ai/AgentTerminal.tsx +544 -649
- package/src/editor/ai/Agents.tsx +464 -250
- package/src/editor/ai/AiResponseMessage.tsx +134 -29
- package/src/editor/ai/MediaImage.tsx +75 -0
- package/src/editor/ai/ToolCallDisplay.tsx +18 -4
- package/src/editor/client/EditorShell.tsx +9 -6
- package/src/editor/client/hooks/useSocketMessageHandler.ts +6 -7
- package/src/editor/commands/componentCommands.tsx +1 -0
- package/src/editor/control-center/About.tsx +2 -2
- package/src/editor/control-center/AllAgentsPanel.tsx +1 -1
- package/src/editor/field-types/MultiLineText.tsx +1 -1
- package/src/editor/services/aiService.ts +2 -0
- package/src/editor/sidebar/Validation.tsx +1 -1
- package/src/index.ts +5 -0
- package/src/page-wizard/PageWizard.tsx +3 -3
- package/src/page-wizard/WizardSteps.tsx +1 -1
- package/src/revision.ts +2 -2
- package/src/splash-screen/ModernSplashScreen.tsx +229 -0
- package/src/splash-screen/OpenPage.tsx +12 -4
- package/src/splash-screen/ParheliaAssistantChat.tsx +273 -0
- package/src/splash-screen/RecentAgents.tsx +151 -0
- package/src/splash-screen/RecentPages.tsx +58 -61
- package/src/splash-screen/SplashScreen.tsx +1 -1
- package/styles.css +20 -0
package/src/editor/ai/Agents.tsx
CHANGED
|
@@ -34,8 +34,10 @@ import {
|
|
|
34
34
|
DropdownMenuItem,
|
|
35
35
|
DropdownMenuTrigger,
|
|
36
36
|
} from "../../components/ui/dropdown-menu";
|
|
37
|
-
import { ChevronDown } from "lucide-react";
|
|
37
|
+
import { ChevronDown, List } from "lucide-react";
|
|
38
38
|
import { AgentProfilesOverview } from "./AgentProfilesOverview";
|
|
39
|
+
import { AgentsView } from "../../agents-view/AgentsView";
|
|
40
|
+
import { SecretAgentIcon } from "../ui/Icons";
|
|
39
41
|
|
|
40
42
|
// function convertAgentMessagesToTerminalMessages(
|
|
41
43
|
// agentMessages: AgentChatMessage[],
|
|
@@ -72,12 +74,13 @@ export const Agents = React.memo(function Agents({
|
|
|
72
74
|
const [activeAgentId, setActiveAgentId] = useState<string | null>(null);
|
|
73
75
|
const activeAgentIdRef = useRef<string | null>(null);
|
|
74
76
|
const singleAgentModeRef = useRef<boolean>(false);
|
|
75
|
-
const [
|
|
77
|
+
const [profileTabDropdownOpen, setProfileTabDropdownOpen] = useState<
|
|
78
|
+
string | null
|
|
79
|
+
>(null);
|
|
76
80
|
const [menuPopoverOpen, setMenuPopoverOpen] = useState(false);
|
|
77
81
|
const [addPopoverOpen, setAddPopoverOpen] = useState(false);
|
|
78
82
|
const [loadingAgents, setLoadingAgents] = useState(false);
|
|
79
83
|
const [inactiveAgents, setInactiveAgents] = useState<Agent[]>([]);
|
|
80
|
-
const [historyFilter, setHistoryFilter] = useState("");
|
|
81
84
|
const [initialMetadataMap, setInitialMetadataMap] = useState<
|
|
82
85
|
Record<string, AgentMetadata | undefined>
|
|
83
86
|
>({});
|
|
@@ -93,6 +96,44 @@ export const Agents = React.memo(function Agents({
|
|
|
93
96
|
[editContext?.currentItemDescriptor?.id],
|
|
94
97
|
);
|
|
95
98
|
|
|
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
|
+
|
|
96
137
|
// Detect single-agent mode from query parameter once on mount
|
|
97
138
|
useEffect(() => {
|
|
98
139
|
try {
|
|
@@ -103,18 +144,6 @@ export const Agents = React.memo(function Agents({
|
|
|
103
144
|
} catch {}
|
|
104
145
|
}, []);
|
|
105
146
|
|
|
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
|
-
|
|
118
147
|
// Helper function to get the most recently updated agent
|
|
119
148
|
const getMostRecentAgent = (agentList: Agent[]): Agent | null => {
|
|
120
149
|
if (agentList.length === 0) return null;
|
|
@@ -125,6 +154,34 @@ export const Agents = React.memo(function Agents({
|
|
|
125
154
|
});
|
|
126
155
|
};
|
|
127
156
|
|
|
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
|
+
|
|
128
185
|
// Helper function to set active agent and persist to localStorage
|
|
129
186
|
const setActiveAgentIdWithStorage = (agentId: string | null) => {
|
|
130
187
|
setActiveAgentId(agentId);
|
|
@@ -289,7 +346,8 @@ export const Agents = React.memo(function Agents({
|
|
|
289
346
|
}
|
|
290
347
|
}
|
|
291
348
|
if (message.type === "agent:run:start") {
|
|
292
|
-
const { agentId, agentName, autoSelect } =
|
|
349
|
+
const { agentId, agentName, autoSelect, profileId, profileName } =
|
|
350
|
+
message.payload;
|
|
293
351
|
|
|
294
352
|
if (!agentId || !agentName) {
|
|
295
353
|
console.warn(
|
|
@@ -309,7 +367,7 @@ export const Agents = React.memo(function Agents({
|
|
|
309
367
|
);
|
|
310
368
|
|
|
311
369
|
if (existingAgentIndex !== -1) {
|
|
312
|
-
// Update existing agent name
|
|
370
|
+
// Update existing agent name and profile info
|
|
313
371
|
const updatedAgents = [...prevAgents];
|
|
314
372
|
const existingAgent = updatedAgents[existingAgentIndex]!;
|
|
315
373
|
updatedAgents[existingAgentIndex] = {
|
|
@@ -317,6 +375,8 @@ export const Agents = React.memo(function Agents({
|
|
|
317
375
|
name: agentName,
|
|
318
376
|
status: "running" as const,
|
|
319
377
|
updatedDate: new Date().toISOString(),
|
|
378
|
+
...(profileId && { profileId }),
|
|
379
|
+
...(profileName && { profileName }),
|
|
320
380
|
};
|
|
321
381
|
return updatedAgents;
|
|
322
382
|
} else {
|
|
@@ -327,6 +387,8 @@ export const Agents = React.memo(function Agents({
|
|
|
327
387
|
status: "running" as const,
|
|
328
388
|
userId: "", // Will be populated from backend if needed
|
|
329
389
|
updatedDate: new Date().toISOString(),
|
|
390
|
+
...(profileId && { profileId }),
|
|
391
|
+
...(profileName && { profileName }),
|
|
330
392
|
};
|
|
331
393
|
|
|
332
394
|
return [...prevAgents, newAgent];
|
|
@@ -477,13 +539,11 @@ export const Agents = React.memo(function Agents({
|
|
|
477
539
|
|
|
478
540
|
setAgents(activeAgentsResult);
|
|
479
541
|
|
|
480
|
-
//
|
|
542
|
+
// Try to restore the previously active agent from localStorage if one exists
|
|
481
543
|
let selectedAgentId: string | null = null;
|
|
482
544
|
|
|
483
545
|
if (activeAgentsResult.length > 0) {
|
|
484
|
-
// Try to restore the previously active agent from localStorage
|
|
485
546
|
const storedAgentId = localStorage.getItem(ACTIVE_AGENT_STORAGE_KEY);
|
|
486
|
-
|
|
487
547
|
const storedAgent = activeAgentsResult.find(
|
|
488
548
|
(agent) => agent.id === storedAgentId,
|
|
489
549
|
);
|
|
@@ -491,7 +551,7 @@ export const Agents = React.memo(function Agents({
|
|
|
491
551
|
if (storedAgent) {
|
|
492
552
|
selectedAgentId = storedAgent.id;
|
|
493
553
|
} else {
|
|
494
|
-
//
|
|
554
|
+
// If no stored agent or stored agent not found, select the most recent agent
|
|
495
555
|
const mostRecentAgent = getMostRecentAgent(activeAgentsResult);
|
|
496
556
|
selectedAgentId = mostRecentAgent?.id || null;
|
|
497
557
|
}
|
|
@@ -542,6 +602,8 @@ export const Agents = React.memo(function Agents({
|
|
|
542
602
|
name: `New Agent`,
|
|
543
603
|
updatedDate: new Date().toISOString(),
|
|
544
604
|
userId: "",
|
|
605
|
+
profileId: metadata?.additionalData?.profileId,
|
|
606
|
+
profileName: metadata?.additionalData?.profileName || metadata?.profile,
|
|
545
607
|
};
|
|
546
608
|
setAgents((prev) => [...prev, newAgent]);
|
|
547
609
|
// User-initiated creation should activate the new agent tab
|
|
@@ -668,6 +730,85 @@ export const Agents = React.memo(function Agents({
|
|
|
668
730
|
}
|
|
669
731
|
};
|
|
670
732
|
|
|
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
|
+
|
|
671
812
|
const closeOtherAgents = async () => {
|
|
672
813
|
if (!activeAgentIdRef.current) return;
|
|
673
814
|
|
|
@@ -730,37 +871,45 @@ export const Agents = React.memo(function Agents({
|
|
|
730
871
|
if (existingAgent) {
|
|
731
872
|
// Switch to existing agent
|
|
732
873
|
setActiveAgentIdWithStorage(existingAgent.id);
|
|
733
|
-
setHistoryPopoverOpen(false);
|
|
734
874
|
return;
|
|
735
875
|
}
|
|
736
876
|
|
|
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
|
+
|
|
737
905
|
// Add the closed agent to the active agents list
|
|
738
906
|
const reopenedAgent: Agent = {
|
|
739
|
-
...
|
|
907
|
+
...fullAgent,
|
|
740
908
|
// Keep the original status to allow AgentTerminal to load the full agent data
|
|
741
909
|
};
|
|
742
910
|
|
|
743
911
|
setAgents((prev) => [...prev, reopenedAgent]);
|
|
744
912
|
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
|
-
}
|
|
764
913
|
};
|
|
765
914
|
|
|
766
915
|
if (loadingAgents) {
|
|
@@ -774,32 +923,55 @@ export const Agents = React.memo(function Agents({
|
|
|
774
923
|
// Empty state when no agents are open
|
|
775
924
|
if (!loadingAgents && agents.length === 0) {
|
|
776
925
|
return (
|
|
777
|
-
<div className="flex
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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...
|
|
792
953
|
</div>
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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
|
|
972
|
+
</div>
|
|
973
|
+
)}
|
|
974
|
+
</div>
|
|
803
975
|
</div>
|
|
804
976
|
</div>
|
|
805
977
|
);
|
|
@@ -810,179 +982,213 @@ export const Agents = React.memo(function Agents({
|
|
|
810
982
|
{/* Tab Header */}
|
|
811
983
|
<div className="flex items-center border-b border-gray-200 bg-gray-50">
|
|
812
984
|
<div className="scrollbar-hide flex flex-1 overflow-x-auto">
|
|
813
|
-
{
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
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}
|
|
866
1081
|
/>
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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"
|
|
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"
|
|
922
1102
|
/>
|
|
923
1103
|
</div>
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
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
|
+
/>
|
|
971
1156
|
</div>
|
|
972
1157
|
</div>
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
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">
|
|
1183
|
+
<div
|
|
1184
|
+
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",
|
|
1187
|
+
)}
|
|
1188
|
+
>
|
|
1189
|
+
<List className="size-4 flex-shrink-0 text-gray-600" strokeWidth={1} />
|
|
1190
|
+
<span className="truncate font-medium">Overview</span>
|
|
1191
|
+
</div>
|
|
986
1192
|
</div>
|
|
987
1193
|
)}
|
|
988
1194
|
|
|
@@ -1072,22 +1278,30 @@ export const Agents = React.memo(function Agents({
|
|
|
1072
1278
|
|
|
1073
1279
|
{/* Agent Content */}
|
|
1074
1280
|
<div className="relative flex-1">
|
|
1075
|
-
{
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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
|
-
/>
|
|
1281
|
+
{activeAgentId === null ? (
|
|
1282
|
+
// Show AgentsView when no agent is selected
|
|
1283
|
+
<div className="absolute inset-0">
|
|
1284
|
+
<AgentsView />
|
|
1089
1285
|
</div>
|
|
1090
|
-
)
|
|
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
|
+
)}
|
|
1091
1305
|
</div>
|
|
1092
1306
|
</div>
|
|
1093
1307
|
);
|