@alpaca-editor/core 1.0.4176 → 1.0.4179
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/AgentsView.d.ts +2 -2
- package/dist/agents-view/AgentsView.js +105 -62
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/editor/ai/Agents.js +15 -7
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/useAgentStatus.js +6 -1
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +24 -2
- package/dist/editor/services/agentService.js +25 -18
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -0
- package/dist/editor/services/aiService.js +9 -6
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +8 -1
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +7 -3
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/page-wizard/steps/usePageCreator.js +5 -0
- package/dist/page-wizard/steps/usePageCreator.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +12 -0
- package/package.json +1 -1
- package/src/agents-view/AgentsView.tsx +303 -208
- package/src/editor/ai/Agents.tsx +17 -7
- package/src/editor/ai/useAgentStatus.ts +6 -1
- package/src/editor/services/agentService.ts +54 -23
- package/src/editor/services/aiService.ts +16 -6
- package/src/page-wizard/steps/ContentStep.tsx +9 -4
- package/src/page-wizard/steps/SelectStep.tsx +8 -3
- package/src/page-wizard/steps/usePageCreator.ts +5 -0
- package/src/revision.ts +2 -2
|
@@ -1,61 +1,106 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
Agent,
|
|
4
4
|
getActiveAgents,
|
|
5
|
+
GetAgentsParams,
|
|
5
6
|
getAgent,
|
|
6
7
|
closeAgent,
|
|
7
8
|
deleteAgent,
|
|
8
9
|
} from "../editor/services/agentService";
|
|
9
10
|
import { getAgentStatusConfig } from "../editor/ai/AgentStatusBadge";
|
|
10
11
|
import { cn } from "../lib/utils";
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
Trash,
|
|
14
|
+
X,
|
|
15
|
+
RefreshCw,
|
|
16
|
+
Play,
|
|
17
|
+
Search,
|
|
18
|
+
Users,
|
|
19
|
+
XCircle,
|
|
20
|
+
} from "lucide-react";
|
|
12
21
|
import { SimpleIconButton } from "../editor/ui/SimpleIconButton";
|
|
13
22
|
import { useEditContext } from "../editor/client/editContext";
|
|
14
23
|
import { SecretAgentIcon } from "../editor/ui/Icons";
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
|
-
* Agents overview view that displays all
|
|
18
|
-
* grouped by profile with interactive controls
|
|
26
|
+
* Agents overview view that displays all agents for the current user
|
|
27
|
+
* (including closed and shared agents) grouped by profile with interactive controls
|
|
19
28
|
*/
|
|
20
29
|
export function AgentsView() {
|
|
21
30
|
const editContext = useEditContext();
|
|
22
|
-
const [agents, setAgents] = useState<
|
|
31
|
+
const [agents, setAgents] = useState<Agent[]>([]);
|
|
32
|
+
const [totalCount, setTotalCount] = useState(0);
|
|
33
|
+
const [hasMore, setHasMore] = useState(false);
|
|
23
34
|
const [loading, setLoading] = useState(true);
|
|
35
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
24
36
|
const [error, setError] = useState<string | null>(null);
|
|
25
37
|
const [refreshing, setRefreshing] = useState(false);
|
|
26
38
|
const [searchTerm, setSearchTerm] = useState("");
|
|
39
|
+
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
|
40
|
+
const [showOnlyMyAgents, setShowOnlyMyAgents] = useState(false);
|
|
41
|
+
const agentsRef = useRef<Agent[]>([]);
|
|
27
42
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const basicAgents = await getActiveAgents(1000);
|
|
43
|
+
// Keep ref in sync with state
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
agentsRef.current = agents;
|
|
46
|
+
}, [agents]);
|
|
33
47
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
|
|
48
|
+
// Debounce search term
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
setDebouncedSearchTerm(searchTerm);
|
|
52
|
+
}, 400);
|
|
39
53
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
return () => clearTimeout(timer);
|
|
55
|
+
}, [searchTerm]);
|
|
56
|
+
|
|
57
|
+
const loadAgents = useCallback(
|
|
58
|
+
async (reset: boolean = false) => {
|
|
59
|
+
try {
|
|
60
|
+
if (reset) {
|
|
61
|
+
setLoading(true);
|
|
62
|
+
setError(null);
|
|
63
|
+
} else {
|
|
64
|
+
setLoadingMore(true);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const skip = reset ? 0 : agentsRef.current.length;
|
|
68
|
+
const params: GetAgentsParams = {
|
|
69
|
+
skip,
|
|
70
|
+
limit: 50,
|
|
71
|
+
includeShared: !showOnlyMyAgents,
|
|
72
|
+
includeOwned: true,
|
|
73
|
+
searchTerm: debouncedSearchTerm || undefined,
|
|
74
|
+
};
|
|
55
75
|
|
|
76
|
+
const response = await getActiveAgents(params);
|
|
77
|
+
|
|
78
|
+
if (reset) {
|
|
79
|
+
setAgents(response.agents);
|
|
80
|
+
} else {
|
|
81
|
+
setAgents((prev) => [...prev, ...response.agents]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setTotalCount(response.totalCount);
|
|
85
|
+
setHasMore(response.hasMore);
|
|
86
|
+
} catch (err: any) {
|
|
87
|
+
console.error("Failed to load agents:", err);
|
|
88
|
+
setError(
|
|
89
|
+
err?.message || "Failed to load agents. Please try again later.",
|
|
90
|
+
);
|
|
91
|
+
} finally {
|
|
92
|
+
setLoading(false);
|
|
93
|
+
setLoadingMore(false);
|
|
94
|
+
setRefreshing(false);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
[showOnlyMyAgents, debouncedSearchTerm],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Initial load
|
|
56
101
|
useEffect(() => {
|
|
57
|
-
loadAgents();
|
|
58
|
-
}, []);
|
|
102
|
+
loadAgents(true);
|
|
103
|
+
}, [showOnlyMyAgents, debouncedSearchTerm]);
|
|
59
104
|
|
|
60
105
|
// Subscribe to websocket messages for real-time agent updates
|
|
61
106
|
useEffect(() => {
|
|
@@ -97,25 +142,48 @@ export function AgentsView() {
|
|
|
97
142
|
return prevAgents;
|
|
98
143
|
}
|
|
99
144
|
});
|
|
100
|
-
} else if (message.type === "agent:
|
|
101
|
-
|
|
102
|
-
|
|
145
|
+
} else if (message.type === "agent:info:updated") {
|
|
146
|
+
const { agentId, agentName, agentDescription } =
|
|
147
|
+
message.payload || {};
|
|
103
148
|
if (!agentId) return;
|
|
104
|
-
|
|
105
|
-
setAgents((prevAgents) =>
|
|
149
|
+
// Update the agent name and/or description in the list
|
|
150
|
+
setAgents((prevAgents) => {
|
|
151
|
+
const existingIndex = prevAgents.findIndex((a) => a.id === agentId);
|
|
152
|
+
if (existingIndex === -1) return prevAgents;
|
|
153
|
+
const updatedAgents = [...prevAgents];
|
|
154
|
+
const existingAgent = updatedAgents[existingIndex]!;
|
|
155
|
+
updatedAgents[existingIndex] = {
|
|
156
|
+
...existingAgent,
|
|
157
|
+
...(agentName !== undefined && { name: agentName }),
|
|
158
|
+
...(agentDescription !== undefined && {
|
|
159
|
+
description: agentDescription,
|
|
160
|
+
}),
|
|
161
|
+
updatedDate: new Date().toISOString(),
|
|
162
|
+
};
|
|
163
|
+
return updatedAgents;
|
|
164
|
+
});
|
|
165
|
+
} else if (message.type === "agent:run:closed") {
|
|
166
|
+
// Reload agents when one is closed - it should still appear in the list
|
|
167
|
+
loadAgents(true);
|
|
106
168
|
}
|
|
107
169
|
},
|
|
108
170
|
);
|
|
109
171
|
|
|
110
172
|
return unsubscribe;
|
|
111
|
-
}, [editContext?.addSocketMessageListener]);
|
|
173
|
+
}, [editContext?.addSocketMessageListener, loadAgents]);
|
|
112
174
|
|
|
113
175
|
const handleRefresh = async () => {
|
|
114
176
|
setRefreshing(true);
|
|
115
|
-
await loadAgents();
|
|
177
|
+
await loadAgents(true);
|
|
116
178
|
};
|
|
117
179
|
|
|
118
|
-
const
|
|
180
|
+
const handleLoadMore = () => {
|
|
181
|
+
if (!loadingMore && hasMore) {
|
|
182
|
+
loadAgents(false);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const handleResumeAgent = (agent: Agent) => {
|
|
119
187
|
editContext?.switchView("page-editor");
|
|
120
188
|
editContext?.setShowAgentsPanel?.(true);
|
|
121
189
|
// Delay for panel mount, then dispatch event to open existing agent
|
|
@@ -183,35 +251,10 @@ export function AgentsView() {
|
|
|
183
251
|
}
|
|
184
252
|
};
|
|
185
253
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const search = searchTerm.toLowerCase();
|
|
189
|
-
return (
|
|
190
|
-
agent.name.toLowerCase().includes(search) ||
|
|
191
|
-
agent.profileName?.toLowerCase().includes(search) ||
|
|
192
|
-
agent.id.toLowerCase().includes(search)
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Group agents by profile
|
|
197
|
-
const groupedAgents = filteredAgents.reduce(
|
|
198
|
-
(acc, agent) => {
|
|
199
|
-
const profileName = agent.profileName || "Unknown Profile";
|
|
200
|
-
if (!acc[profileName]) {
|
|
201
|
-
acc[profileName] = [];
|
|
202
|
-
}
|
|
203
|
-
acc[profileName].push(agent);
|
|
204
|
-
return acc;
|
|
205
|
-
},
|
|
206
|
-
{} as Record<string, AgentDetails[]>,
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
// Sort profile groups by name
|
|
210
|
-
const sortedProfileGroups = Object.entries(groupedAgents).sort(([a], [b]) =>
|
|
211
|
-
a.localeCompare(b),
|
|
212
|
-
);
|
|
254
|
+
// No client-side filtering needed - backend handles it
|
|
255
|
+
// Just display the agents as returned from the API
|
|
213
256
|
|
|
214
|
-
if (loading) {
|
|
257
|
+
if (loading && (!agents || agents.length === 0)) {
|
|
215
258
|
return (
|
|
216
259
|
<div className="flex h-full items-center justify-center">
|
|
217
260
|
<div className="text-sm text-gray-500">Loading agents...</div>
|
|
@@ -241,171 +284,223 @@ export function AgentsView() {
|
|
|
241
284
|
<div className="border-b border-gray-200 bg-white p-4">
|
|
242
285
|
<div className="mb-3 flex items-center justify-between">
|
|
243
286
|
<div>
|
|
244
|
-
<h2 className="text-lg font-semibold text-gray-900">
|
|
287
|
+
<h2 className="text-lg font-semibold text-gray-900">Agents</h2>
|
|
245
288
|
<p className="text-xs text-gray-500">
|
|
246
|
-
Showing {
|
|
247
|
-
{
|
|
289
|
+
Showing {agents?.length || 0} of {totalCount} agent
|
|
290
|
+
{totalCount !== 1 ? "s" : ""}
|
|
248
291
|
</p>
|
|
249
292
|
</div>
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
293
|
+
<div className="flex items-center gap-2">
|
|
294
|
+
<SimpleIconButton
|
|
295
|
+
onClick={handleRefresh}
|
|
296
|
+
icon={
|
|
297
|
+
<RefreshCw
|
|
298
|
+
className={cn("size-4", refreshing && "animate-spin")}
|
|
299
|
+
strokeWidth={1}
|
|
300
|
+
/>
|
|
301
|
+
}
|
|
302
|
+
label="Refresh"
|
|
303
|
+
disabled={refreshing}
|
|
304
|
+
className="text-gray-600 hover:text-gray-800"
|
|
305
|
+
/>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
{/* Filter Toggle */}
|
|
310
|
+
<div className="mb-3">
|
|
311
|
+
<label className="flex cursor-pointer items-center gap-2 text-sm">
|
|
312
|
+
<input
|
|
313
|
+
type="checkbox"
|
|
314
|
+
checked={showOnlyMyAgents}
|
|
315
|
+
onChange={(e) => setShowOnlyMyAgents(e.target.checked)}
|
|
316
|
+
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
317
|
+
/>
|
|
318
|
+
<span className="text-gray-700">Show only my agents</span>
|
|
319
|
+
</label>
|
|
262
320
|
</div>
|
|
263
321
|
|
|
264
322
|
{/* Search */}
|
|
265
|
-
<
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
323
|
+
<div className="relative">
|
|
324
|
+
<Search
|
|
325
|
+
className="absolute top-1/2 left-3 size-4 -translate-y-1/2 text-gray-400"
|
|
326
|
+
strokeWidth={1}
|
|
327
|
+
/>
|
|
328
|
+
<input
|
|
329
|
+
type="text"
|
|
330
|
+
placeholder="Search by name or description..."
|
|
331
|
+
value={searchTerm}
|
|
332
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
333
|
+
className="w-full rounded border border-gray-200 py-2 pr-10 pl-10 text-sm focus:border-blue-500 focus:outline-none"
|
|
334
|
+
/>
|
|
335
|
+
{searchTerm && (
|
|
336
|
+
<button
|
|
337
|
+
onClick={() => setSearchTerm("")}
|
|
338
|
+
className="absolute top-1/2 right-3 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
|
339
|
+
>
|
|
340
|
+
<XCircle className="size-4" strokeWidth={1} />
|
|
341
|
+
</button>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
272
344
|
</div>
|
|
273
345
|
|
|
274
346
|
{/* Agents List */}
|
|
275
347
|
<div className="flex-1 overflow-auto">
|
|
276
|
-
{
|
|
348
|
+
{!agents || agents.length === 0 ? (
|
|
277
349
|
<div className="flex h-full items-center justify-center text-sm text-gray-500">
|
|
278
350
|
{searchTerm.trim()
|
|
279
351
|
? "No agents match your search"
|
|
280
|
-
: "No
|
|
352
|
+
: "No agents found"}
|
|
281
353
|
</div>
|
|
282
354
|
) : (
|
|
283
|
-
<div className="space-y-
|
|
284
|
-
{
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
{
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
355
|
+
<div className="space-y-2 p-4">
|
|
356
|
+
{agents.map((agent) => {
|
|
357
|
+
const statusConfig = getAgentStatusConfig(agent);
|
|
358
|
+
return (
|
|
359
|
+
<div
|
|
360
|
+
key={agent.id}
|
|
361
|
+
className="cursor-pointer rounded-lg border border-gray-200 bg-white p-3 shadow-sm transition-shadow hover:shadow-md"
|
|
362
|
+
onClick={() => {
|
|
363
|
+
// Only select the agent in the agents panel; do not switch views
|
|
364
|
+
window.dispatchEvent(
|
|
365
|
+
new CustomEvent("editor:selectAgent", {
|
|
366
|
+
detail: { agentId: agent.id },
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
}}
|
|
370
|
+
>
|
|
371
|
+
<div className="flex items-start justify-between">
|
|
372
|
+
{/* Agent Icon */}
|
|
373
|
+
<div className="mr-3 flex-shrink-0">
|
|
374
|
+
{agent.profileSvgIcon ? (
|
|
375
|
+
<div
|
|
376
|
+
className="flex h-6 w-6 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full"
|
|
377
|
+
dangerouslySetInnerHTML={{
|
|
378
|
+
__html: agent.profileSvgIcon,
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
) : (
|
|
382
|
+
<SecretAgentIcon
|
|
383
|
+
size={24}
|
|
384
|
+
strokeWidth={1}
|
|
385
|
+
className="text-gray-400"
|
|
386
|
+
/>
|
|
387
|
+
)}
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<div className="min-w-0 flex-1">
|
|
391
|
+
{/* Agent Name & Status */}
|
|
392
|
+
<div className="mb-1 flex items-center gap-2">
|
|
393
|
+
<div
|
|
394
|
+
className={cn(
|
|
395
|
+
"h-2 w-2 flex-shrink-0 rounded-full",
|
|
396
|
+
statusConfig.color,
|
|
397
|
+
statusConfig.shouldPulse && "animate-pulse",
|
|
398
|
+
)}
|
|
399
|
+
title={statusConfig.label}
|
|
400
|
+
/>
|
|
401
|
+
<h4 className="truncate font-medium text-gray-900">
|
|
402
|
+
{agent.name}
|
|
403
|
+
</h4>
|
|
404
|
+
{agent.isShared && (
|
|
405
|
+
<div className="flex items-center gap-1 rounded bg-blue-50 px-2 py-0.5 text-xs text-blue-700">
|
|
406
|
+
<Users className="size-3" strokeWidth={1} />
|
|
407
|
+
<span>Shared</span>
|
|
322
408
|
</div>
|
|
409
|
+
)}
|
|
410
|
+
</div>
|
|
323
411
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
statusConfig.color,
|
|
331
|
-
statusConfig.shouldPulse && "animate-pulse",
|
|
332
|
-
)}
|
|
333
|
-
title={statusConfig.label}
|
|
334
|
-
/>
|
|
335
|
-
<h4 className="truncate font-medium text-gray-900">
|
|
336
|
-
{agent.name}
|
|
337
|
-
</h4>
|
|
338
|
-
</div>
|
|
412
|
+
{/* Shared Agent Owner */}
|
|
413
|
+
{agent.isShared && agent.ownerName && (
|
|
414
|
+
<p className="mb-1 text-xs text-gray-500">
|
|
415
|
+
Created by {agent.ownerName}
|
|
416
|
+
</p>
|
|
417
|
+
)}
|
|
339
418
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
{agent.messageCount > 0 && (
|
|
347
|
-
<div className="flex items-center gap-2">
|
|
348
|
-
<span className="font-medium">Messages:</span>
|
|
349
|
-
<span>{agent.messageCount}</span>
|
|
350
|
-
</div>
|
|
351
|
-
)}
|
|
352
|
-
{agent.totalCost > 0 && (
|
|
353
|
-
<div className="flex items-center gap-2">
|
|
354
|
-
<span className="font-medium">Cost:</span>
|
|
355
|
-
<span>${agent.totalCost.toFixed(4)}</span>
|
|
356
|
-
<span className="text-gray-400">
|
|
357
|
-
({agent.totalTokensUsed.toLocaleString()}{" "}
|
|
358
|
-
tokens)
|
|
359
|
-
</span>
|
|
360
|
-
</div>
|
|
361
|
-
)}
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
419
|
+
{/* Agent Description */}
|
|
420
|
+
{agent.description && (
|
|
421
|
+
<p className="mb-2 truncate text-xs text-gray-500">
|
|
422
|
+
{agent.description}
|
|
423
|
+
</p>
|
|
424
|
+
)}
|
|
364
425
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
handleResumeAgent(agent);
|
|
371
|
-
}}
|
|
372
|
-
icon={
|
|
373
|
-
<Play className="size-3" strokeWidth={1.5} />
|
|
374
|
-
}
|
|
375
|
-
label="Resume Agent"
|
|
376
|
-
className="text-blue-600 opacity-60 hover:text-blue-700 hover:opacity-100"
|
|
377
|
-
/>
|
|
378
|
-
<SimpleIconButton
|
|
379
|
-
onClick={(e) => {
|
|
380
|
-
e.stopPropagation();
|
|
381
|
-
handleCloseAgent(agent.id, agent.name);
|
|
382
|
-
}}
|
|
383
|
-
icon={<X className="size-3" strokeWidth={1.5} />}
|
|
384
|
-
label="Close Agent"
|
|
385
|
-
className="text-gray-600 opacity-60 hover:text-gray-800 hover:opacity-100"
|
|
386
|
-
/>
|
|
387
|
-
<SimpleIconButton
|
|
388
|
-
onClick={(e) =>
|
|
389
|
-
handleDeleteAgent(agent.id, agent.name, e)
|
|
390
|
-
}
|
|
391
|
-
icon={
|
|
392
|
-
<Trash className="size-3" strokeWidth={1.5} />
|
|
393
|
-
}
|
|
394
|
-
label="Delete Agent"
|
|
395
|
-
className="text-red-600 opacity-60 hover:text-red-700 hover:opacity-100"
|
|
396
|
-
/>
|
|
397
|
-
</div>
|
|
426
|
+
{/* Agent Details */}
|
|
427
|
+
<div className="space-y-1 text-xs text-gray-600">
|
|
428
|
+
<div className="flex items-center gap-2">
|
|
429
|
+
<span className="font-medium">Updated:</span>
|
|
430
|
+
<span>{formatDateTime(agent.updatedDate)}</span>
|
|
398
431
|
</div>
|
|
432
|
+
{agent.messageCount !== undefined &&
|
|
433
|
+
agent.messageCount > 0 && (
|
|
434
|
+
<div className="flex items-center gap-2">
|
|
435
|
+
<span className="font-medium">Messages:</span>
|
|
436
|
+
<span>{agent.messageCount}</span>
|
|
437
|
+
</div>
|
|
438
|
+
)}
|
|
439
|
+
{agent.totalCost !== undefined &&
|
|
440
|
+
agent.totalCost > 0 && (
|
|
441
|
+
<div className="flex items-center gap-2">
|
|
442
|
+
<span className="font-medium">Cost:</span>
|
|
443
|
+
<span>${agent.totalCost.toFixed(4)}</span>
|
|
444
|
+
{agent.totalTokensUsed !== undefined && (
|
|
445
|
+
<span className="text-gray-400">
|
|
446
|
+
({agent.totalTokensUsed.toLocaleString()}{" "}
|
|
447
|
+
tokens)
|
|
448
|
+
</span>
|
|
449
|
+
)}
|
|
450
|
+
</div>
|
|
451
|
+
)}
|
|
399
452
|
</div>
|
|
400
|
-
|
|
401
|
-
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{/* Actions */}
|
|
456
|
+
<div className="ml-3 flex gap-1">
|
|
457
|
+
<SimpleIconButton
|
|
458
|
+
onClick={(e) => {
|
|
459
|
+
e.stopPropagation();
|
|
460
|
+
handleResumeAgent(agent);
|
|
461
|
+
}}
|
|
462
|
+
icon={<Play className="size-3" strokeWidth={1.5} />}
|
|
463
|
+
label="Resume Agent"
|
|
464
|
+
className="text-blue-600 opacity-60 hover:text-blue-700 hover:opacity-100"
|
|
465
|
+
/>
|
|
466
|
+
<SimpleIconButton
|
|
467
|
+
onClick={(e) => {
|
|
468
|
+
e.stopPropagation();
|
|
469
|
+
handleCloseAgent(agent.id, agent.name);
|
|
470
|
+
}}
|
|
471
|
+
icon={<X className="size-3" strokeWidth={1.5} />}
|
|
472
|
+
label="Close Agent"
|
|
473
|
+
className="text-gray-600 opacity-60 hover:text-gray-800 hover:opacity-100"
|
|
474
|
+
/>
|
|
475
|
+
<SimpleIconButton
|
|
476
|
+
onClick={(e) =>
|
|
477
|
+
handleDeleteAgent(agent.id, agent.name, e)
|
|
478
|
+
}
|
|
479
|
+
icon={<Trash className="size-3" strokeWidth={1.5} />}
|
|
480
|
+
label="Delete Agent"
|
|
481
|
+
className="text-red-600 opacity-60 hover:text-red-700 hover:opacity-100"
|
|
482
|
+
/>
|
|
483
|
+
</div>
|
|
484
|
+
</div>
|
|
402
485
|
</div>
|
|
486
|
+
);
|
|
487
|
+
})}
|
|
488
|
+
|
|
489
|
+
{/* Load More Button */}
|
|
490
|
+
{hasMore && (
|
|
491
|
+
<div className="flex justify-center pt-4">
|
|
492
|
+
<button
|
|
493
|
+
onClick={handleLoadMore}
|
|
494
|
+
disabled={loadingMore}
|
|
495
|
+
className="rounded-lg border border-gray-300 bg-white px-6 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
|
496
|
+
>
|
|
497
|
+
{loadingMore ? "Loading..." : "Load More"}
|
|
498
|
+
</button>
|
|
403
499
|
</div>
|
|
404
|
-
)
|
|
500
|
+
)}
|
|
405
501
|
</div>
|
|
406
502
|
)}
|
|
407
503
|
</div>
|
|
408
504
|
</div>
|
|
409
505
|
);
|
|
410
506
|
}
|
|
411
|
-
|
package/src/editor/ai/Agents.tsx
CHANGED
|
@@ -332,10 +332,11 @@ export const Agents = React.memo(function Agents({
|
|
|
332
332
|
return [...prevAgents, newAgent];
|
|
333
333
|
}
|
|
334
334
|
});
|
|
335
|
-
} else if (message.type === "agent:
|
|
336
|
-
const { agentId, agentName } =
|
|
337
|
-
|
|
338
|
-
|
|
335
|
+
} else if (message.type === "agent:info:updated") {
|
|
336
|
+
const { agentId, agentName, agentDescription } =
|
|
337
|
+
message.payload || {};
|
|
338
|
+
if (!agentId) return;
|
|
339
|
+
// Update the agent name and/or description in the tabs list
|
|
339
340
|
setAgents((prevAgents) => {
|
|
340
341
|
const existingIndex = prevAgents.findIndex((a) => a.id === agentId);
|
|
341
342
|
if (existingIndex === -1) return prevAgents;
|
|
@@ -343,7 +344,10 @@ export const Agents = React.memo(function Agents({
|
|
|
343
344
|
const existingAgent = updatedAgents[existingIndex]!;
|
|
344
345
|
updatedAgents[existingIndex] = {
|
|
345
346
|
...existingAgent,
|
|
346
|
-
name: agentName,
|
|
347
|
+
...(agentName !== undefined && { name: agentName }),
|
|
348
|
+
...(agentDescription !== undefined && {
|
|
349
|
+
description: agentDescription,
|
|
350
|
+
}),
|
|
347
351
|
updatedDate: new Date().toISOString(),
|
|
348
352
|
};
|
|
349
353
|
return updatedAgents;
|
|
@@ -457,8 +461,14 @@ export const Agents = React.memo(function Agents({
|
|
|
457
461
|
|
|
458
462
|
setLoadingAgents(true);
|
|
459
463
|
|
|
460
|
-
// Load active agents
|
|
461
|
-
const
|
|
464
|
+
// Load active agents (exclude shared agents for the agent panel)
|
|
465
|
+
const activeAgentsResponse = await getActiveAgents({
|
|
466
|
+
limit: 1000,
|
|
467
|
+
includeShared: false, // Agent panel only shows user's own agents
|
|
468
|
+
includeOwned: true,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const activeAgentsResult = activeAgentsResponse.agents;
|
|
462
472
|
|
|
463
473
|
setAgents(activeAgentsResult);
|
|
464
474
|
|
|
@@ -21,7 +21,12 @@ export function useAgentStatus(): AgentStatusSummary {
|
|
|
21
21
|
|
|
22
22
|
const fetchAgentStatus = useCallback(async () => {
|
|
23
23
|
try {
|
|
24
|
-
const
|
|
24
|
+
const response = await getActiveAgents({
|
|
25
|
+
limit: 1000,
|
|
26
|
+
includeShared: false, // Only show user's own agents in status
|
|
27
|
+
includeOwned: true,
|
|
28
|
+
});
|
|
29
|
+
const activeAgents = response.agents;
|
|
25
30
|
// Filter out completed, error, and closed agents to match WebSocket behavior
|
|
26
31
|
// Note: Backend sends status as both enum numbers (0-7) and strings
|
|
27
32
|
// Enum: New=0, Running=1, WaitingForApproval=2, Completed=3, Error=4, Closed=5, Idle=6, CostLimitReached=7
|