apteva 0.4.56 → 0.7.0

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 (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +12 -79
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2370
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. package/src/web/types.ts +0 -298
@@ -1,314 +0,0 @@
1
- import React, { useState, useMemo, useEffect, useCallback, useRef } from "react";
2
- import { useAuth, useProjects, useTelemetryContext } from "../../context";
3
- import type { TelemetryEvent } from "../../context";
4
- import type { Agent, Route } from "../../types";
5
- import { Select } from "../common/Select";
6
-
7
- interface ActivityPageProps {
8
- agents: Agent[];
9
- loading: boolean;
10
- onNavigate?: (route: Route) => void;
11
- }
12
-
13
- // Event types we show in the timeline (skip noisy internal ones)
14
- const VISIBLE_TYPES = new Set([
15
- "thread_activity",
16
- "agent_started",
17
- "agent_stopped",
18
- "agent_error",
19
- "task_created",
20
- "task_updated",
21
- "task_deleted",
22
- "task_executed",
23
- "llm_request",
24
- "tool_invocation",
25
- "mcp_request",
26
- "mcp_tool_execution",
27
- ]);
28
-
29
- // Category colors for the timeline dot
30
- const CATEGORY_COLORS: Record<string, string> = {
31
- CHAT: "bg-green-400",
32
- LLM: "bg-purple-400",
33
- TOOL: "bg-blue-400",
34
- TASK: "bg-yellow-400",
35
- MEMORY: "bg-cyan-400",
36
- MCP: "bg-orange-400",
37
- SYSTEM: "bg-gray-400",
38
- ERROR: "bg-red-400",
39
- };
40
-
41
- function describeEvent(evt: TelemetryEvent, agentName: string): string {
42
- const data = evt.data || {};
43
- switch (evt.type) {
44
- case "thread_activity":
45
- return (data.activity as string) || "Working...";
46
- case "agent_started":
47
- return "Agent started";
48
- case "agent_stopped":
49
- return data.reason ? `Agent stopped (${data.reason})` : "Agent stopped";
50
- case "agent_error":
51
- return evt.error || "Agent error";
52
- case "llm_request":
53
- return "Thinking...";
54
- case "tool_invocation": {
55
- const toolRaw = (data.tool_name || "") as string;
56
- if (!toolRaw) return "Using tools";
57
- const toolFormatted = toolRaw.replace(/[-_]/g, " ").replace(/\b\w/g, c => c.toUpperCase());
58
- return `Tool: ${toolFormatted}`;
59
- }
60
- case "task_created":
61
- return data.title ? `Task created: ${data.title}` : "Task created";
62
- case "task_updated": {
63
- const status = data.status as string | undefined;
64
- const title = data.title as string | undefined;
65
- const statusLabel = status ? status.charAt(0).toUpperCase() + status.slice(1) : null;
66
- if (title && statusLabel) return `Task ${statusLabel}: ${title}`;
67
- if (statusLabel) return `Task ${statusLabel}`;
68
- if (title) return `Task updated: ${title}`;
69
- return "Task updated";
70
- }
71
- case "task_deleted":
72
- return "Task deleted";
73
- case "task_executed": {
74
- const title = data.title as string | undefined;
75
- return title ? `Task started: ${title}` : "Task started";
76
- }
77
- case "memory_stored":
78
- return "Memory stored";
79
- case "memory_retrieved":
80
- return "Memory retrieved";
81
- case "mcp_request":
82
- return data.server ? `MCP request to ${data.server}` : "MCP request";
83
- case "mcp_tool_execution": {
84
- const rawName = (data.tool_name || data.tool || "") as string;
85
- if (!rawName) return "MCP tool call";
86
- // "Server__tool-name-here" -> take part after __, format dashes/underscores to spaces, title case
87
- const toolPart = rawName.includes("__") ? rawName.split("__").slice(1).join("__") : rawName;
88
- const formatted = toolPart.replace(/[-_]/g, " ").replace(/\b\w/g, c => c.toUpperCase());
89
- return `MCP: ${formatted}`;
90
- }
91
- default:
92
- return evt.type.replace(/_/g, " ");
93
- }
94
- }
95
-
96
- export function ActivityPage({ agents, loading, onNavigate }: ActivityPageProps) {
97
- const { authFetch } = useAuth();
98
- const { currentProjectId } = useProjects();
99
- const { events: realtimeEvents, statusChangeCounter } = useTelemetryContext();
100
- const [historicalEvents, setHistoricalEvents] = useState<TelemetryEvent[]>([]);
101
- const [filterAgentId, setFilterAgentId] = useState<string>("");
102
- const [seenIds] = useState(() => new Set<string>());
103
- const hasLoadedHistory = useRef(false);
104
-
105
- const filteredAgents = useMemo(() => {
106
- if (currentProjectId === null) return agents;
107
- if (currentProjectId === "unassigned") return agents.filter(a => !a.projectId);
108
- return agents.filter(a => a.projectId === currentProjectId);
109
- }, [agents, currentProjectId]);
110
-
111
- const agentIds = useMemo(() => new Set(filteredAgents.map(a => a.id)), [filteredAgents]);
112
- const agentNameMap = useMemo(() => {
113
- const map = new Map<string, string>();
114
- filteredAgents.forEach(a => map.set(a.id, a.name));
115
- return map;
116
- }, [filteredAgents]);
117
-
118
- // Fetch historical events
119
- const fetchHistory = useCallback(async () => {
120
- const projectParam = currentProjectId ? `&project_id=${encodeURIComponent(currentProjectId)}` : "";
121
- try {
122
- const res = await authFetch(`/api/telemetry/events?limit=200${projectParam}`);
123
- if (res.ok) {
124
- const data = await res.json();
125
- setHistoricalEvents(data.events || []);
126
- }
127
- } catch {}
128
- }, [authFetch, currentProjectId]);
129
-
130
- useEffect(() => {
131
- fetchHistory();
132
- }, [fetchHistory, statusChangeCounter]);
133
-
134
- // Mark historical events as seen so they don't animate
135
- useEffect(() => {
136
- if (historicalEvents.length > 0 && !hasLoadedHistory.current) {
137
- for (const evt of historicalEvents) {
138
- seenIds.add(evt.id);
139
- }
140
- // Also mark any realtime events already present at mount
141
- for (const evt of realtimeEvents) {
142
- seenIds.add(evt.id);
143
- }
144
- hasLoadedHistory.current = true;
145
- }
146
- }, [historicalEvents, realtimeEvents, seenIds]);
147
-
148
- // Merge realtime + historical, filter, sort
149
- const timeline = useMemo(() => {
150
- const seen = new Set<string>();
151
- const merged: TelemetryEvent[] = [];
152
-
153
- const shouldShow = (evt: TelemetryEvent) => {
154
- if (!VISIBLE_TYPES.has(evt.type) || !agentIds.has(evt.agent_id) || evt.data?.parent_id) return false;
155
- // MCP tool: only show completed calls (with duration), skip the start event
156
- if (evt.type === "mcp_tool_execution" && !evt.duration_ms) return false;
157
- return true;
158
- };
159
-
160
- for (const evt of realtimeEvents) {
161
- if (!seen.has(evt.id) && shouldShow(evt)) {
162
- merged.push(evt);
163
- seen.add(evt.id);
164
- }
165
- }
166
- for (const evt of historicalEvents) {
167
- if (!seen.has(evt.id) && shouldShow(evt)) {
168
- merged.push(evt);
169
- seen.add(evt.id);
170
- }
171
- }
172
-
173
- let filtered = merged;
174
- if (filterAgentId) {
175
- filtered = filtered.filter(e => e.agent_id === filterAgentId);
176
- }
177
-
178
- filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
179
- return filtered.slice(0, 200);
180
- }, [realtimeEvents, historicalEvents, agentIds, filterAgentId]);
181
-
182
- // Group by date for section headers
183
- const groupedTimeline = useMemo(() => {
184
- const groups: { label: string; events: TelemetryEvent[] }[] = [];
185
- let currentLabel = "";
186
- const today = new Date().toDateString();
187
- const yesterday = new Date(Date.now() - 86400000).toDateString();
188
-
189
- for (const evt of timeline) {
190
- const dateStr = new Date(evt.timestamp).toDateString();
191
- const label = dateStr === today ? "Today" : dateStr === yesterday ? "Yesterday" : dateStr;
192
- if (label !== currentLabel) {
193
- currentLabel = label;
194
- groups.push({ label, events: [] });
195
- }
196
- groups[groups.length - 1].events.push(evt);
197
- }
198
- return groups;
199
- }, [timeline]);
200
-
201
- if (loading) {
202
- return <div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">Loading...</div>;
203
- }
204
-
205
- const runningCount = filteredAgents.filter(a => a.status === "running").length;
206
-
207
- return (
208
- <div className="flex-1 flex flex-col overflow-hidden">
209
- {/* Header */}
210
- <div className="px-6 pt-6 pb-4 shrink-0">
211
- <div className="flex items-center justify-between gap-4">
212
- <div>
213
- <h1 className="text-xl font-semibold">Activity</h1>
214
- <p className="text-sm text-[var(--color-text-muted)] mt-0.5">
215
- {runningCount} of {filteredAgents.length} agents running
216
- </p>
217
- </div>
218
- <div className="w-48">
219
- <Select
220
- value={filterAgentId}
221
- onChange={setFilterAgentId}
222
- placeholder="All agents"
223
- options={[
224
- { value: "", label: "All agents" },
225
- ...filteredAgents.map(a => ({ value: a.id, label: a.name })),
226
- ]}
227
- />
228
- </div>
229
- </div>
230
- </div>
231
-
232
- {/* Timeline */}
233
- <div className="flex-1 overflow-auto px-6 pb-6">
234
- {timeline.length === 0 ? (
235
- <div className="flex flex-col items-center justify-center py-20 text-[var(--color-text-faint)]">
236
- <p className="text-lg mb-2">No activity yet</p>
237
- <p className="text-sm">Agent activity will appear here in real-time.</p>
238
- </div>
239
- ) : (
240
- <div className="max-w-2xl">
241
- {groupedTimeline.map(group => (
242
- <div key={group.label}>
243
- {/* Date header */}
244
- <div className="sticky top-0 z-10 bg-[var(--color-bg)] backdrop-blur-sm py-2 mb-1">
245
- <span className="text-xs font-semibold text-[var(--color-text-muted)] uppercase tracking-wider">
246
- {group.label}
247
- </span>
248
- </div>
249
-
250
- {/* Events */}
251
- <div className="relative ml-3 border-l border-[var(--color-border)]">
252
- {group.events.map(evt => {
253
- const isNew = !seenIds.has(evt.id);
254
- if (isNew) seenIds.add(evt.id); // mark seen after first render
255
- const agentName = agentNameMap.get(evt.agent_id) || evt.agent_id.slice(0, 8);
256
- const dotColor = evt.level === "error"
257
- ? "bg-red-400"
258
- : CATEGORY_COLORS[evt.category] || "bg-[var(--color-text-faint)]";
259
-
260
- return (
261
- <div
262
- key={evt.id}
263
- className={`relative pl-6 pr-3 py-2.5 hover:bg-[var(--color-surface-hover)] transition-colors ${
264
- isNew ? "animate-slideIn" : ""
265
- }`}
266
- >
267
- {/* Timeline dot */}
268
- <span className={`absolute left-[-4.5px] top-[14px] w-[9px] h-[9px] rounded-full ${dotColor} ring-2 ring-[#0a0a0a]`} />
269
-
270
- <div className="flex items-start justify-between gap-3">
271
- <div className="flex-1 min-w-0">
272
- <p className={`text-sm ${evt.level === "error" ? "text-red-400" : ""}`}>
273
- {describeEvent(evt, agentName)}
274
- </p>
275
- <div className="flex items-center gap-2 text-[11px] text-[var(--color-text-faint)] mt-0.5">
276
- <span className="text-[var(--color-text-secondary)] font-medium">{agentName}</span>
277
- <span className="text-[var(--color-border-light)]">&middot;</span>
278
- <span className="text-[var(--color-text-faint)]">{evt.category}</span>
279
- {evt.duration_ms != null && evt.duration_ms > 0 && (
280
- <>
281
- <span className="text-[var(--color-border-light)]">&middot;</span>
282
- <span>{evt.duration_ms < 1000 ? `${evt.duration_ms}ms` : `${(evt.duration_ms / 1000).toFixed(1)}s`}</span>
283
- </>
284
- )}
285
- </div>
286
- </div>
287
- <span className="text-[11px] text-[var(--color-text-faint)] shrink-0 pt-0.5">
288
- {timeAgo(evt.timestamp)}
289
- </span>
290
- </div>
291
- </div>
292
- );
293
- })}
294
- </div>
295
- </div>
296
- ))}
297
- </div>
298
- )}
299
- </div>
300
- </div>
301
- );
302
- }
303
-
304
- function timeAgo(timestamp: string): string {
305
- const seconds = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000);
306
- if (seconds < 5) return "just now";
307
- if (seconds < 60) return `${seconds}s ago`;
308
- const minutes = Math.floor(seconds / 60);
309
- if (minutes < 60) return `${minutes}m ago`;
310
- const hours = Math.floor(minutes / 60);
311
- if (hours < 24) return `${hours}h ago`;
312
- const days = Math.floor(hours / 24);
313
- return `${days}d ago`;
314
- }
@@ -1 +0,0 @@
1
- export { ActivityPage } from "./ActivityPage";
@@ -1,189 +0,0 @@
1
- import React from "react";
2
- import { MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, FilesIcon, MultiAgentIcon, SkillsIcon, ActivityIcon } from "../common/Icons";
3
- import { useAgentActivity, useProjects, useUIMode } from "../../context";
4
- import type { Agent, AgentFeatures } from "../../types";
5
-
6
- interface AgentCardProps {
7
- agent: Agent;
8
- selected: boolean;
9
- onSelect: () => void;
10
- onToggle: (e?: React.MouseEvent) => void;
11
- showProject?: boolean;
12
- }
13
-
14
- const FEATURE_ICONS: { key: keyof AgentFeatures; icon: React.ComponentType<{ className?: string }>; label: string }[] = [
15
- { key: "memory", icon: MemoryIcon, label: "Memory" },
16
- { key: "tasks", icon: TasksIcon, label: "Tasks" },
17
- { key: "files", icon: FilesIcon, label: "Files" },
18
- { key: "vision", icon: VisionIcon, label: "Vision" },
19
- { key: "operator", icon: OperatorIcon, label: "Operator" },
20
- { key: "mcp", icon: McpIcon, label: "MCP" },
21
- { key: "realtime", icon: RealtimeIcon, label: "Realtime" },
22
- { key: "agents", icon: MultiAgentIcon, label: "Multi-Agent" },
23
- ];
24
-
25
- export const AgentCard = React.memo(function AgentCard({ agent, selected, onSelect, onToggle, showProject }: AgentCardProps) {
26
- const enabledFeatures = FEATURE_ICONS.filter(f => agent.features?.[f.key]);
27
- const mcpServers = agent.mcpServerDetails || [];
28
- const skills = agent.skillDetails || [];
29
- const { isActive, label: activityLabel } = useAgentActivity(agent.id);
30
- const { projects } = useProjects();
31
- const { isDev } = useUIMode();
32
- const project = agent.projectId ? projects.find(p => p.id === agent.projectId) : null;
33
- const subscriptions = agent.subscriptions || [];
34
-
35
- return (
36
- <div
37
- onClick={onSelect}
38
- className={`bg-[var(--color-surface)] card p-5 transition cursor-pointer flex flex-col h-full ${
39
- selected
40
- ? '!border-[var(--color-accent)]'
41
- : 'hover:border-[var(--color-border-light)]'
42
- }`}
43
- >
44
- <div className="flex items-start justify-between mb-3">
45
- <div>
46
- <h3 className="font-semibold text-lg">{agent.name}</h3>
47
- {isDev && (
48
- <p className="text-sm text-[var(--color-text-muted)]">
49
- {agent.provider} / {agent.model}
50
- {agent.port && <span className="text-[var(--color-text-faint)]"> · :{agent.port}</span>}
51
- </p>
52
- )}
53
- {showProject && project && (
54
- <p className="text-sm text-[var(--color-text-muted)] flex items-center gap-1.5 mt-1">
55
- <span className="w-2 h-2 rounded-full" style={{ backgroundColor: project.color }} />
56
- {project.name}
57
- </p>
58
- )}
59
- </div>
60
- <StatusBadge status={agent.status} isActive={isActive && agent.status === "running"} activityLabel={activityLabel} />
61
- </div>
62
-
63
- {isDev && enabledFeatures.length > 0 && (
64
- <div className="flex flex-wrap gap-1.5 mb-3">
65
- {enabledFeatures.map(({ key, icon: Icon, label }) => (
66
- <span
67
- key={key}
68
- className="inline-flex items-center gap-1 px-2 py-0.5 bg-[var(--color-accent-10)] text-[var(--color-accent-70)] text-xs"
69
- style={{ borderRadius: "var(--radius-badge)" }}
70
- title={label}
71
- >
72
- <Icon className="w-3 h-3" />
73
- {label}
74
- </span>
75
- ))}
76
- </div>
77
- )}
78
-
79
- {/* MCP Servers */}
80
- {isDev && mcpServers.length > 0 && (
81
- <div className="flex flex-wrap gap-1.5 mb-3">
82
- {mcpServers.map((server) => {
83
- // HTTP/remote servers are always available
84
- const isAvailable = (server.type === "http" && server.url) || server.status === "running";
85
- return (
86
- <span
87
- key={server.id}
88
- className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
89
- isAvailable
90
- ? "bg-green-500/10 text-green-400"
91
- : "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
92
- }`}
93
- title={`MCP: ${server.name} (${isAvailable ? "available" : server.status})`}
94
- >
95
- <McpIcon className="w-3 h-3" />
96
- {server.name}
97
- </span>
98
- );
99
- })}
100
- </div>
101
- )}
102
-
103
- {/* Skills */}
104
- {isDev && skills.length > 0 && (
105
- <div className="flex flex-wrap gap-1.5 mb-3">
106
- {skills.map((skill) => (
107
- <span
108
- key={skill.id}
109
- className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
110
- skill.enabled
111
- ? "bg-purple-500/10 text-purple-400"
112
- : "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
113
- }`}
114
- title={`Skill: ${skill.name} v${skill.version}`}
115
- >
116
- <SkillsIcon className="w-3 h-3" />
117
- {skill.name}
118
- </span>
119
- ))}
120
- </div>
121
- )}
122
-
123
- {/* Subscriptions (triggers listening to) */}
124
- {isDev && subscriptions.length > 0 && (
125
- <div className="flex flex-wrap gap-1.5 mb-3">
126
- {subscriptions.map((sub) => (
127
- <span
128
- key={sub.id}
129
- className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
130
- sub.enabled
131
- ? "bg-cyan-500/10 text-cyan-400"
132
- : "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
133
- }`}
134
- title={`Trigger: ${sub.trigger_slug.replace(/_/g, " ")}`}
135
- >
136
- <ActivityIcon className="w-3 h-3" />
137
- {sub.trigger_slug.replace(/_/g, " ")}
138
- </span>
139
- ))}
140
- </div>
141
- )}
142
-
143
- <p className="text-sm text-[var(--color-text-muted)] line-clamp-2 mb-4 flex-1">
144
- {agent.systemPrompt}
145
- </p>
146
-
147
- <button
148
- onClick={onToggle}
149
- disabled={agent.status === "starting" || agent.status === "stopping"}
150
- className={`w-full px-3 py-1.5 btn text-sm font-medium transition mt-auto ${
151
- agent.status === "starting" || agent.status === "stopping"
152
- ? "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)] cursor-wait"
153
- : agent.status === "running"
154
- ? "bg-red-500/15 text-red-500 hover:bg-red-500/25 border border-red-500/20"
155
- : "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
156
- }`}
157
- >
158
- {agent.status === "starting" ? "Starting..." : agent.status === "stopping" ? "Stopping..." : agent.status === "running" ? "Stop" : "Start"}
159
- </button>
160
- </div>
161
- );
162
- });
163
-
164
- function StatusBadge({ status, isActive, activityLabel }: { status: Agent["status"]; isActive?: boolean; activityLabel?: string }) {
165
- if (status === "running" && isActive && activityLabel) {
166
- return (
167
- <span className="px-2 py-1 text-xs font-medium bg-green-500/20 text-green-400 animate-pulse" style={{ borderRadius: "var(--radius-badge)" }}>
168
- {activityLabel}
169
- </span>
170
- );
171
- }
172
-
173
- const isTransitioning = status === "starting" || status === "stopping";
174
-
175
- return (
176
- <span
177
- className={`px-2 py-1 text-xs font-medium ${
178
- isTransitioning
179
- ? "bg-yellow-500/20 text-yellow-400 animate-pulse"
180
- : status === "running"
181
- ? "bg-[#3b82f6]/20 text-[#3b82f6]"
182
- : "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
183
- }`}
184
- style={{ borderRadius: "var(--radius-badge)" }}
185
- >
186
- {status}
187
- </span>
188
- );
189
- }