apteva 0.4.31 → 0.4.41
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/ActivityPage.7907h64p.js +3 -0
- package/dist/ApiDocsPage.k3jjenpq.js +4 -0
- package/dist/App.01nq20st.js +4 -0
- package/dist/App.1maqvamf.js +4 -0
- package/dist/App.2yjrh32f.js +4 -0
- package/dist/App.3qw8nben.js +20 -0
- package/dist/App.7fb3e7mp.js +4 -0
- package/dist/App.7sy3wq8c.js +4 -0
- package/dist/App.apjrmctz.js +57 -0
- package/dist/App.av6t2yhe.js +4 -0
- package/dist/App.jqj5a094.js +46 -0
- package/dist/App.mc7xf85h.js +4 -0
- package/dist/App.myxqcj9x.js +4 -0
- package/dist/App.nm91r1mp.js +13 -0
- package/dist/App.qcknavjz.js +221 -0
- package/dist/App.vc7vfhg4.js +4 -0
- package/dist/App.z4s9zkw5.js +4 -0
- package/dist/ConnectionsPage.z1pw5xe2.js +3 -0
- package/dist/McpPage.8vc97z0b.js +3 -0
- package/dist/SettingsPage.p61bz8kd.js +3 -0
- package/dist/SkillsPage.r9x43g3g.js +3 -0
- package/dist/TasksPage.1e0zkye4.js +3 -0
- package/dist/TelemetryPage.p9vbe4gf.js +3 -0
- package/dist/TestsPage.d4xy504e.js +3 -0
- package/dist/ThreadsPage.m016am3x.js +3 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -7
- package/src/crypto.ts +4 -3
- package/src/db.ts +153 -28
- package/src/integrations/agentdojo.ts +94 -12
- package/src/integrations/index.ts +7 -0
- package/src/mcp-platform.ts +494 -121
- package/src/providers.ts +12 -12
- package/src/routes/api/agent-utils.ts +59 -46
- package/src/routes/api/agents.ts +52 -1
- package/src/routes/api/integrations.ts +11 -5
- package/src/routes/api/mcp.ts +5 -4
- package/src/routes/api/meta-agent.ts +35 -1
- package/src/routes/api/projects.ts +3 -3
- package/src/routes/api/providers.ts +121 -30
- package/src/routes/api/skills.ts +2 -3
- package/src/routes/api/system.ts +8 -13
- package/src/server.ts +31 -32
- package/src/triggers/agentdojo.ts +2 -2
- package/src/web/App.tsx +18 -10
- package/src/web/components/activity/ActivityPage.tsx +241 -388
- package/src/web/components/agents/AgentCard.tsx +5 -13
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/Select.tsx +4 -3
- package/src/web/components/dashboard/Dashboard.tsx +155 -30
- package/src/web/components/index.ts +1 -1
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/mcp/IntegrationsPanel.tsx +126 -35
- package/src/web/components/mcp/McpPage.tsx +10 -1
- package/src/web/components/meta-agent/MetaAgent.tsx +4 -2
- package/src/web/components/settings/SettingsPage.tsx +133 -48
- package/src/web/components/tasks/TasksPage.tsx +48 -16
- package/src/web/components/telemetry/TelemetryPage.tsx +184 -0
- package/src/web/components/threads/ThreadsPage.tsx +313 -0
- package/src/web/context/AuthContext.tsx +3 -3
- package/src/web/context/ProjectContext.tsx +3 -3
- package/src/web/context/TelemetryContext.tsx +24 -6
- package/src/web/context/index.ts +1 -1
- package/src/web/styles.css +20 -4
- package/src/web/types.ts +4 -3
- package/dist/ActivityPage.41nbye4r.js +0 -3
- package/dist/ApiDocsPage.4smnt8m3.js +0 -4
- package/dist/App.0sbax9et.js +0 -4
- package/dist/App.0ws427h8.js +0 -4
- package/dist/App.6q6bar8b.js +0 -4
- package/dist/App.80301vdb.js +0 -4
- package/dist/App.af2wg84v.js +0 -267
- package/dist/App.ca1rz1ph.js +0 -4
- package/dist/App.ensa6z0r.js +0 -4
- package/dist/App.f8g7tych.js +0 -13
- package/dist/App.mvtqv6qc.js +0 -20
- package/dist/App.ncgc9cxy.js +0 -4
- package/dist/App.p0fb1pds.js +0 -4
- package/dist/App.pmaq48sj.js +0 -4
- package/dist/App.yv87t9m5.js +0 -4
- package/dist/App.zjmfm8p6.js +0 -4
- package/dist/ConnectionsPage.anb3rv9a.js +0 -3
- package/dist/McpPage.y396h6fy.js +0 -3
- package/dist/SettingsPage.p1hc60gk.js +0 -3
- package/dist/SkillsPage.yj3xdsay.js +0 -3
- package/dist/TasksPage.sjv0khtv.js +0 -3
- package/dist/TelemetryPage.2qm4w16r.js +0 -3
- package/dist/TestsPage.zzs4qfj8.js +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, FilesIcon, MultiAgentIcon, SkillsIcon, ActivityIcon } from "../common/Icons";
|
|
3
|
-
import { useAgentActivity, useProjects
|
|
3
|
+
import { useAgentActivity, useProjects } from "../../context";
|
|
4
4
|
import type { Agent, AgentFeatures } from "../../types";
|
|
5
5
|
|
|
6
6
|
interface AgentCardProps {
|
|
@@ -22,22 +22,14 @@ const FEATURE_ICONS: { key: keyof AgentFeatures; icon: React.ComponentType<{ cla
|
|
|
22
22
|
{ key: "agents", icon: MultiAgentIcon, label: "Multi-Agent" },
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
-
export function AgentCard({ agent, selected, onSelect, onToggle, showProject }: AgentCardProps) {
|
|
25
|
+
export const AgentCard = React.memo(function AgentCard({ agent, selected, onSelect, onToggle, showProject }: AgentCardProps) {
|
|
26
26
|
const enabledFeatures = FEATURE_ICONS.filter(f => agent.features?.[f.key]);
|
|
27
27
|
const mcpServers = agent.mcpServerDetails || [];
|
|
28
28
|
const skills = agent.skillDetails || [];
|
|
29
29
|
const { isActive, label: activityLabel } = useAgentActivity(agent.id);
|
|
30
30
|
const { projects } = useProjects();
|
|
31
|
-
const { authFetch } = useAuth();
|
|
32
31
|
const project = agent.projectId ? projects.find(p => p.id === agent.projectId) : null;
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
authFetch(`/api/subscriptions?agent_id=${agent.id}`)
|
|
37
|
-
.then(res => res.ok ? res.json() : { subscriptions: [] })
|
|
38
|
-
.then(data => setSubscriptions(data.subscriptions || []))
|
|
39
|
-
.catch(() => {});
|
|
40
|
-
}, [agent.id, authFetch]);
|
|
32
|
+
const subscriptions = agent.subscriptions || [];
|
|
41
33
|
|
|
42
34
|
return (
|
|
43
35
|
<div
|
|
@@ -163,7 +155,7 @@ export function AgentCard({ agent, selected, onSelect, onToggle, showProject }:
|
|
|
163
155
|
</button>
|
|
164
156
|
</div>
|
|
165
157
|
);
|
|
166
|
-
}
|
|
158
|
+
});
|
|
167
159
|
|
|
168
160
|
function StatusBadge({ status, isActive, activityLabel }: { status: Agent["status"]; isActive?: boolean; activityLabel?: string }) {
|
|
169
161
|
if (status === "running" && isActive && activityLabel) {
|
|
@@ -233,6 +233,14 @@ export function TaskOnceIcon({ className = "w-4 h-4" }: IconProps) {
|
|
|
233
233
|
);
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
export function ThreadsIcon({ className = "w-5 h-5" }: IconProps) {
|
|
237
|
+
return (
|
|
238
|
+
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
239
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
240
|
+
</svg>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
236
244
|
export function BellIcon({ className = "w-5 h-5" }: IconProps) {
|
|
237
245
|
return (
|
|
238
246
|
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -11,9 +11,10 @@ interface SelectProps {
|
|
|
11
11
|
options: SelectOption[];
|
|
12
12
|
onChange: (value: string) => void;
|
|
13
13
|
placeholder?: string;
|
|
14
|
+
compact?: boolean;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function Select({ value, options, onChange, placeholder = "Select..." }: SelectProps) {
|
|
17
|
+
export function Select({ value, options, onChange, placeholder = "Select...", compact }: SelectProps) {
|
|
17
18
|
const [isOpen, setIsOpen] = useState(false);
|
|
18
19
|
const ref = useRef<HTMLDivElement>(null);
|
|
19
20
|
|
|
@@ -34,7 +35,7 @@ export function Select({ value, options, onChange, placeholder = "Select..." }:
|
|
|
34
35
|
<button
|
|
35
36
|
type="button"
|
|
36
37
|
onClick={() => setIsOpen(!isOpen)}
|
|
37
|
-
className=
|
|
38
|
+
className={`w-full bg-[#0a0a0a] border border-[#222] rounded ${compact ? "px-2.5 py-1.5 text-sm" : "px-3 py-2"} text-left flex items-center justify-between focus:outline-none focus:border-[#f97316] text-[#e0e0e0] hover:border-[#333] transition`}
|
|
38
39
|
>
|
|
39
40
|
<span className={selectedOption ? "text-[#e0e0e0]" : "text-[#666]"}>
|
|
40
41
|
{selectedOption ? (
|
|
@@ -59,7 +60,7 @@ export function Select({ value, options, onChange, placeholder = "Select..." }:
|
|
|
59
60
|
onChange(option.value);
|
|
60
61
|
setIsOpen(false);
|
|
61
62
|
}}
|
|
62
|
-
className={`w-full px-3 py-2 text-left flex items-center justify-between hover:bg-[#1a1a1a] transition ${
|
|
63
|
+
className={`w-full ${compact ? "px-2.5 py-1.5 text-sm" : "px-3 py-2"} text-left flex items-center justify-between hover:bg-[#1a1a1a] transition ${
|
|
63
64
|
option.value === value ? "bg-[#1a1a1a] text-[#f97316]" : "text-[#e0e0e0]"
|
|
64
65
|
}`}
|
|
65
66
|
>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
|
1
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
2
2
|
import { useAgentActivity, useAuth, useProjects, useTelemetryContext } from "../../context";
|
|
3
|
+
import { useTelemetry } from "../../context/TelemetryContext";
|
|
3
4
|
import type { TelemetryEvent } from "../../context";
|
|
4
5
|
import type { Agent, Provider, Route, DashboardStats, Task } from "../../types";
|
|
6
|
+
import { CloseIcon } from "../common/Icons";
|
|
5
7
|
|
|
6
8
|
interface DashboardProps {
|
|
7
9
|
agents: Agent[];
|
|
@@ -23,9 +25,12 @@ export function Dashboard({
|
|
|
23
25
|
const { authFetch } = useAuth();
|
|
24
26
|
const { currentProjectId } = useProjects();
|
|
25
27
|
const { events: realtimeEvents, statusChangeCounter } = useTelemetryContext();
|
|
28
|
+
const { events: taskTelemetryEvents } = useTelemetry({ category: "TASK" });
|
|
29
|
+
const lastProcessedTaskEventRef = useRef<string | null>(null);
|
|
26
30
|
const [stats, setStats] = useState<DashboardStats | null>(null);
|
|
27
31
|
const [recentTasks, setRecentTasks] = useState<Task[]>([]);
|
|
28
32
|
const [historicalActivities, setHistoricalActivities] = useState<TelemetryEvent[]>([]);
|
|
33
|
+
const [quickMessageAgent, setQuickMessageAgent] = useState<Agent | null>(null);
|
|
29
34
|
|
|
30
35
|
// Filter agents by current project
|
|
31
36
|
const filteredAgents = useMemo(() => {
|
|
@@ -75,6 +80,17 @@ export function Dashboard({
|
|
|
75
80
|
fetchDashboardData();
|
|
76
81
|
}, [fetchDashboardData, statusChangeCounter]);
|
|
77
82
|
|
|
83
|
+
// Real-time task updates from telemetry
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!taskTelemetryEvents.length) return;
|
|
86
|
+
const latestEvent = taskTelemetryEvents[0];
|
|
87
|
+
if (!latestEvent || latestEvent.id === lastProcessedTaskEventRef.current) return;
|
|
88
|
+
if (latestEvent.type === "task_created" || latestEvent.type === "task_updated" || latestEvent.type === "task_deleted") {
|
|
89
|
+
lastProcessedTaskEventRef.current = latestEvent.id;
|
|
90
|
+
fetchDashboardData();
|
|
91
|
+
}
|
|
92
|
+
}, [taskTelemetryEvents, fetchDashboardData]);
|
|
93
|
+
|
|
78
94
|
// Filter tasks by project agents and sort by next execution (soonest first)
|
|
79
95
|
const filteredTasks = useMemo(() => {
|
|
80
96
|
let list = currentProjectId
|
|
@@ -98,11 +114,11 @@ export function Dashboard({
|
|
|
98
114
|
|
|
99
115
|
// Merge real-time + historical thread_activity events, deduplicate
|
|
100
116
|
const activities = useMemo(() => {
|
|
101
|
-
const realtimeActivities = realtimeEvents.filter(e => e.type === "thread_activity");
|
|
117
|
+
const realtimeActivities = realtimeEvents.filter(e => e.type === "thread_activity" && !e.data?.parent_id);
|
|
102
118
|
const seen = new Set(realtimeActivities.map(e => e.id));
|
|
103
119
|
const merged = [...realtimeActivities];
|
|
104
120
|
for (const evt of historicalActivities) {
|
|
105
|
-
if (!seen.has(evt.id)) {
|
|
121
|
+
if (!seen.has(evt.id) && !evt.data?.parent_id) {
|
|
106
122
|
merged.push(evt);
|
|
107
123
|
seen.add(evt.id);
|
|
108
124
|
}
|
|
@@ -114,7 +130,7 @@ export function Dashboard({
|
|
|
114
130
|
}
|
|
115
131
|
// Sort newest first
|
|
116
132
|
filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
117
|
-
return filtered.slice(0,
|
|
133
|
+
return filtered.slice(0, 12);
|
|
118
134
|
}, [realtimeEvents, historicalActivities, currentProjectId, projectAgentIds]);
|
|
119
135
|
|
|
120
136
|
// Build agent name lookup
|
|
@@ -150,7 +166,13 @@ export function Dashboard({
|
|
|
150
166
|
) : (
|
|
151
167
|
<div className="divide-y divide-[#1a1a1a]">
|
|
152
168
|
{filteredAgents.slice(0, 5).map((agent) => (
|
|
153
|
-
<AgentListItem
|
|
169
|
+
<AgentListItem
|
|
170
|
+
key={agent.id}
|
|
171
|
+
agent={agent}
|
|
172
|
+
onSelect={() => onSelectAgent(agent)}
|
|
173
|
+
onMessage={agent.status === "running" ? () => setQuickMessageAgent(agent) : undefined}
|
|
174
|
+
showProject={!currentProjectId}
|
|
175
|
+
/>
|
|
154
176
|
))}
|
|
155
177
|
</div>
|
|
156
178
|
)}
|
|
@@ -221,6 +243,14 @@ export function Dashboard({
|
|
|
221
243
|
)}
|
|
222
244
|
</DashboardCard>
|
|
223
245
|
</div>
|
|
246
|
+
|
|
247
|
+
{/* Quick Message Modal */}
|
|
248
|
+
{quickMessageAgent && (
|
|
249
|
+
<QuickMessageModal
|
|
250
|
+
agent={quickMessageAgent}
|
|
251
|
+
onClose={() => setQuickMessageAgent(null)}
|
|
252
|
+
/>
|
|
253
|
+
)}
|
|
224
254
|
</div>
|
|
225
255
|
);
|
|
226
256
|
}
|
|
@@ -266,40 +296,57 @@ function DashboardCard({ title, actionLabel, onAction, children }: DashboardCard
|
|
|
266
296
|
);
|
|
267
297
|
}
|
|
268
298
|
|
|
269
|
-
function AgentListItem({ agent, onSelect, showProject }: { agent: Agent; onSelect: () => void; showProject?: boolean }) {
|
|
270
|
-
const { isActive } = useAgentActivity(agent.id);
|
|
299
|
+
function AgentListItem({ agent, onSelect, onMessage, showProject }: { agent: Agent; onSelect: () => void; onMessage?: () => void; showProject?: boolean }) {
|
|
300
|
+
const { isActive, label } = useAgentActivity(agent.id);
|
|
271
301
|
const { projects } = useProjects();
|
|
272
302
|
const project = agent.projectId ? projects.find(p => p.id === agent.projectId) : null;
|
|
273
303
|
|
|
274
304
|
return (
|
|
275
305
|
<div
|
|
276
306
|
onClick={onSelect}
|
|
277
|
-
className="px-4 py-3 hover:bg-[#1a1a1a] cursor-pointer flex items-center justify-between"
|
|
307
|
+
className="px-4 py-3 hover:bg-[#1a1a1a] cursor-pointer flex items-center justify-between group"
|
|
278
308
|
>
|
|
279
|
-
<div className="flex-1 min-w-0">
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
<div className="flex items-center gap-3 flex-1 min-w-0">
|
|
310
|
+
<span
|
|
311
|
+
className={`w-2 h-2 rounded-full flex-shrink-0 ${
|
|
312
|
+
agent.status === "running"
|
|
313
|
+
? isActive
|
|
314
|
+
? "bg-green-400 animate-pulse"
|
|
315
|
+
: "bg-[#3b82f6]"
|
|
316
|
+
: "bg-[#444]"
|
|
317
|
+
}`}
|
|
318
|
+
/>
|
|
319
|
+
<div className="flex-1 min-w-0">
|
|
320
|
+
<p className="font-medium truncate">{agent.name}</p>
|
|
321
|
+
<div className="flex items-center gap-2 text-sm text-[#666]">
|
|
322
|
+
{isActive && label ? (
|
|
323
|
+
<span className="text-green-400 truncate">{label}</span>
|
|
324
|
+
) : (
|
|
325
|
+
<span>{agent.provider} · {agent.status === "running" ? "idle" : "stopped"}</span>
|
|
326
|
+
)}
|
|
327
|
+
{showProject && project && (
|
|
328
|
+
<>
|
|
329
|
+
<span className="text-[#444]">·</span>
|
|
330
|
+
<span className="flex items-center gap-1">
|
|
331
|
+
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: project.color }} />
|
|
332
|
+
{project.name}
|
|
333
|
+
</span>
|
|
334
|
+
</>
|
|
335
|
+
)}
|
|
336
|
+
</div>
|
|
292
337
|
</div>
|
|
293
338
|
</div>
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
339
|
+
{onMessage && (
|
|
340
|
+
<button
|
|
341
|
+
onClick={(e) => { e.stopPropagation(); onMessage(); }}
|
|
342
|
+
className="opacity-0 group-hover:opacity-100 transition px-2 py-1 text-xs text-[#f97316] hover:bg-[#f97316]/10 rounded"
|
|
343
|
+
title="Send message"
|
|
344
|
+
>
|
|
345
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
346
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
347
|
+
</svg>
|
|
348
|
+
</button>
|
|
349
|
+
)}
|
|
303
350
|
</div>
|
|
304
351
|
);
|
|
305
352
|
}
|
|
@@ -345,6 +392,84 @@ function TaskStatusBadge({ status }: { status: Task["status"] }) {
|
|
|
345
392
|
);
|
|
346
393
|
}
|
|
347
394
|
|
|
395
|
+
// --- Quick Message Modal ---
|
|
396
|
+
|
|
397
|
+
function QuickMessageModal({ agent, onClose }: { agent: Agent; onClose: () => void }) {
|
|
398
|
+
const { authFetch } = useAuth();
|
|
399
|
+
const [message, setMessage] = useState("");
|
|
400
|
+
const [sending, setSending] = useState(false);
|
|
401
|
+
const [sent, setSent] = useState(false);
|
|
402
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
403
|
+
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
inputRef.current?.focus();
|
|
406
|
+
}, []);
|
|
407
|
+
|
|
408
|
+
const handleSend = async () => {
|
|
409
|
+
if (!message.trim() || sending) return;
|
|
410
|
+
setSending(true);
|
|
411
|
+
try {
|
|
412
|
+
const res = await authFetch(`/api/agents/${agent.id}/chat`, {
|
|
413
|
+
method: "POST",
|
|
414
|
+
headers: { "Content-Type": "application/json" },
|
|
415
|
+
body: JSON.stringify({ message: message.trim(), agent_id: agent.id }),
|
|
416
|
+
});
|
|
417
|
+
if (res.ok) {
|
|
418
|
+
setSent(true);
|
|
419
|
+
setTimeout(onClose, 1200);
|
|
420
|
+
}
|
|
421
|
+
} catch {
|
|
422
|
+
// ignore
|
|
423
|
+
} finally {
|
|
424
|
+
setSending(false);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
return (
|
|
429
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
430
|
+
<div className="absolute inset-0 bg-black/60" onClick={onClose} />
|
|
431
|
+
<div className="relative bg-[#111] border border-[#222] rounded-xl shadow-2xl w-full max-w-md mx-4 p-5">
|
|
432
|
+
<div className="flex items-center justify-between mb-4">
|
|
433
|
+
<div className="flex items-center gap-3">
|
|
434
|
+
<span className="w-2.5 h-2.5 rounded-full bg-green-400 animate-pulse" />
|
|
435
|
+
<h3 className="font-medium">{agent.name}</h3>
|
|
436
|
+
</div>
|
|
437
|
+
<button onClick={onClose} className="text-[#666] hover:text-[#e0e0e0] transition">
|
|
438
|
+
<CloseIcon />
|
|
439
|
+
</button>
|
|
440
|
+
</div>
|
|
441
|
+
|
|
442
|
+
{sent ? (
|
|
443
|
+
<div className="py-6 text-center">
|
|
444
|
+
<p className="text-green-400 font-medium">Message sent</p>
|
|
445
|
+
<p className="text-sm text-[#555] mt-1">The agent will process your message</p>
|
|
446
|
+
</div>
|
|
447
|
+
) : (
|
|
448
|
+
<div className="flex gap-2">
|
|
449
|
+
<input
|
|
450
|
+
ref={inputRef}
|
|
451
|
+
type="text"
|
|
452
|
+
value={message}
|
|
453
|
+
onChange={e => setMessage(e.target.value)}
|
|
454
|
+
onKeyDown={e => e.key === "Enter" && handleSend()}
|
|
455
|
+
placeholder={`Message ${agent.name}...`}
|
|
456
|
+
disabled={sending}
|
|
457
|
+
className="flex-1 bg-[#0a0a0a] border border-[#222] rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:border-[#f97316] placeholder-[#444] disabled:opacity-50"
|
|
458
|
+
/>
|
|
459
|
+
<button
|
|
460
|
+
onClick={handleSend}
|
|
461
|
+
disabled={sending || !message.trim()}
|
|
462
|
+
className="px-4 py-2.5 bg-[#f97316] text-black rounded-lg text-sm font-medium hover:bg-[#fb923c] transition disabled:opacity-30"
|
|
463
|
+
>
|
|
464
|
+
{sending ? "..." : "Send"}
|
|
465
|
+
</button>
|
|
466
|
+
</div>
|
|
467
|
+
)}
|
|
468
|
+
</div>
|
|
469
|
+
</div>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
348
473
|
// --- Task sorting helper ---
|
|
349
474
|
|
|
350
475
|
function statusPriority(task: Task): number {
|
|
@@ -12,7 +12,7 @@ export { OnboardingWizard } from "./onboarding";
|
|
|
12
12
|
export { SettingsPage } from "./settings";
|
|
13
13
|
export { AgentCard, CreateAgentModal, AgentPanel, AgentsView } from "./agents";
|
|
14
14
|
export { Dashboard } from "./dashboard";
|
|
15
|
-
export {
|
|
15
|
+
export { ThreadsPage } from "./threads/ThreadsPage";
|
|
16
16
|
export { TasksPage } from "./tasks";
|
|
17
17
|
export { McpPage } from "./mcp";
|
|
18
18
|
export { SkillsPage } from "./skills/SkillsPage";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { DashboardIcon,
|
|
2
|
+
import { DashboardIcon, ThreadsIcon, AgentsIcon, ActivityIcon, TasksIcon, ConnectionsIcon, McpIcon, SkillsIcon, TestsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
|
|
3
3
|
import { useAuth } from "../../context";
|
|
4
4
|
import type { Route } from "../../types";
|
|
5
5
|
|
|
@@ -72,6 +72,12 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
|
|
|
72
72
|
onClick={() => handleNavigate("agents")}
|
|
73
73
|
badge={agentCount > 0 ? String(agentCount) : undefined}
|
|
74
74
|
/>
|
|
75
|
+
<NavButton
|
|
76
|
+
icon={<ThreadsIcon />}
|
|
77
|
+
label="Threads"
|
|
78
|
+
active={route === "threads"}
|
|
79
|
+
onClick={() => handleNavigate("threads")}
|
|
80
|
+
/>
|
|
75
81
|
<NavButton
|
|
76
82
|
icon={<ActivityIcon />}
|
|
77
83
|
label="Activity"
|