apteva 0.2.3 → 0.2.6

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 (38) hide show
  1. package/dist/App.0mzj9cz9.js +213 -0
  2. package/dist/index.html +1 -1
  3. package/dist/styles.css +1 -1
  4. package/package.json +6 -6
  5. package/src/binary.ts +271 -1
  6. package/src/crypto.ts +53 -0
  7. package/src/db.ts +492 -3
  8. package/src/mcp-client.ts +599 -0
  9. package/src/providers.ts +31 -0
  10. package/src/routes/api.ts +832 -64
  11. package/src/server.ts +169 -5
  12. package/src/web/App.tsx +44 -2
  13. package/src/web/components/agents/AgentCard.tsx +53 -9
  14. package/src/web/components/agents/AgentPanel.tsx +381 -0
  15. package/src/web/components/agents/AgentsView.tsx +27 -10
  16. package/src/web/components/agents/CreateAgentModal.tsx +7 -7
  17. package/src/web/components/agents/index.ts +1 -1
  18. package/src/web/components/common/Icons.tsx +8 -0
  19. package/src/web/components/common/Modal.tsx +2 -2
  20. package/src/web/components/common/Select.tsx +1 -1
  21. package/src/web/components/common/index.ts +1 -0
  22. package/src/web/components/dashboard/Dashboard.tsx +74 -25
  23. package/src/web/components/index.ts +5 -2
  24. package/src/web/components/layout/Sidebar.tsx +22 -2
  25. package/src/web/components/mcp/McpPage.tsx +1144 -0
  26. package/src/web/components/mcp/index.ts +1 -0
  27. package/src/web/components/onboarding/OnboardingWizard.tsx +5 -1
  28. package/src/web/components/settings/SettingsPage.tsx +312 -82
  29. package/src/web/components/tasks/TasksPage.tsx +129 -0
  30. package/src/web/components/tasks/index.ts +1 -0
  31. package/src/web/components/telemetry/TelemetryPage.tsx +359 -0
  32. package/src/web/context/TelemetryContext.tsx +202 -0
  33. package/src/web/context/index.ts +2 -0
  34. package/src/web/hooks/useAgents.ts +23 -0
  35. package/src/web/styles.css +18 -0
  36. package/src/web/types.ts +75 -1
  37. package/dist/App.wfhmfhx7.js +0 -213
  38. package/src/web/components/agents/ChatPanel.tsx +0 -63
@@ -0,0 +1,381 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Chat } from "@apteva/apteva-kit";
3
+ import { CloseIcon, MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon } from "../common/Icons";
4
+ import { Select } from "../common/Select";
5
+ import type { Agent, Provider, AgentFeatures, McpServer } from "../../types";
6
+
7
+ type Tab = "chat" | "settings";
8
+
9
+ interface AgentPanelProps {
10
+ agent: Agent;
11
+ providers: Provider[];
12
+ onClose: () => void;
13
+ onStartAgent: (e?: React.MouseEvent) => void;
14
+ onUpdateAgent: (updates: Partial<Agent>) => Promise<{ error?: string }>;
15
+ onDeleteAgent: () => void;
16
+ }
17
+
18
+ const FEATURE_CONFIG = [
19
+ { key: "memory" as keyof AgentFeatures, label: "Memory", description: "Persistent recall", icon: MemoryIcon },
20
+ { key: "tasks" as keyof AgentFeatures, label: "Tasks", description: "Schedule and execute tasks", icon: TasksIcon },
21
+ { key: "vision" as keyof AgentFeatures, label: "Vision", description: "Process images and PDFs", icon: VisionIcon },
22
+ { key: "operator" as keyof AgentFeatures, label: "Operator", description: "Browser automation", icon: OperatorIcon },
23
+ { key: "mcp" as keyof AgentFeatures, label: "MCP", description: "External tools/services", icon: McpIcon },
24
+ { key: "realtime" as keyof AgentFeatures, label: "Realtime", description: "Voice conversations", icon: RealtimeIcon },
25
+ ];
26
+
27
+ export function AgentPanel({ agent, providers, onClose, onStartAgent, onUpdateAgent, onDeleteAgent }: AgentPanelProps) {
28
+ const [activeTab, setActiveTab] = useState<Tab>("chat");
29
+
30
+ return (
31
+ <div className="w-full h-full flex flex-col overflow-hidden bg-[#0a0a0a] border-l border-[#1a1a1a]">
32
+ {/* Header with tabs */}
33
+ <div className="border-b border-[#1a1a1a] flex items-center justify-between px-4">
34
+ <div className="flex gap-1">
35
+ <TabButton active={activeTab === "chat"} onClick={() => setActiveTab("chat")}>
36
+ Chat
37
+ </TabButton>
38
+ <TabButton active={activeTab === "settings"} onClick={() => setActiveTab("settings")}>
39
+ Settings
40
+ </TabButton>
41
+ </div>
42
+ <button
43
+ onClick={onClose}
44
+ className="text-[#666] hover:text-[#e0e0e0] transition p-2"
45
+ >
46
+ <CloseIcon />
47
+ </button>
48
+ </div>
49
+
50
+ {/* Tab content */}
51
+ <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
52
+ {activeTab === "chat" && (
53
+ <ChatTab agent={agent} onStartAgent={onStartAgent} />
54
+ )}
55
+ {activeTab === "settings" && (
56
+ <SettingsTab agent={agent} providers={providers} onUpdateAgent={onUpdateAgent} onDeleteAgent={onDeleteAgent} />
57
+ )}
58
+ </div>
59
+ </div>
60
+ );
61
+ }
62
+
63
+ function TabButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {
64
+ return (
65
+ <button
66
+ onClick={onClick}
67
+ className={`px-4 py-3 text-sm font-medium border-b-2 transition ${
68
+ active
69
+ ? "border-[#f97316] text-[#e0e0e0]"
70
+ : "border-transparent text-[#666] hover:text-[#888]"
71
+ }`}
72
+ >
73
+ {children}
74
+ </button>
75
+ );
76
+ }
77
+
78
+ function ChatTab({ agent, onStartAgent }: { agent: Agent; onStartAgent: (e?: React.MouseEvent) => void }) {
79
+ if (agent.status === "running" && agent.port) {
80
+ return (
81
+ <Chat
82
+ agentId="default"
83
+ apiUrl={`/api/agents/${agent.id}`}
84
+ placeholder="Message this agent..."
85
+ context={agent.systemPrompt}
86
+ variant="terminal"
87
+ headerTitle={agent.name}
88
+ />
89
+ );
90
+ }
91
+
92
+ return (
93
+ <div className="flex-1 flex items-center justify-center text-[#666]">
94
+ <div className="text-center">
95
+ <p className="text-lg mb-2">Agent is not running</p>
96
+ <button
97
+ onClick={onStartAgent}
98
+ className="bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30 px-4 py-2 rounded font-medium transition"
99
+ >
100
+ Start Agent
101
+ </button>
102
+ </div>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
108
+ agent: Agent;
109
+ providers: Provider[];
110
+ onUpdateAgent: (updates: Partial<Agent>) => Promise<{ error?: string }>;
111
+ onDeleteAgent: () => void;
112
+ }) {
113
+ const [form, setForm] = useState({
114
+ name: agent.name,
115
+ provider: agent.provider,
116
+ model: agent.model,
117
+ systemPrompt: agent.systemPrompt,
118
+ features: { ...agent.features },
119
+ mcpServers: [...(agent.mcpServers || [])],
120
+ });
121
+ const [saving, setSaving] = useState(false);
122
+ const [confirmDelete, setConfirmDelete] = useState(false);
123
+ const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
124
+ const [availableMcpServers, setAvailableMcpServers] = useState<McpServer[]>([]);
125
+
126
+ // Fetch available MCP servers
127
+ useEffect(() => {
128
+ const fetchMcpServers = async () => {
129
+ try {
130
+ const res = await fetch("/api/mcp/servers");
131
+ const data = await res.json();
132
+ setAvailableMcpServers(data.servers || []);
133
+ } catch (e) {
134
+ console.error("Failed to fetch MCP servers:", e);
135
+ }
136
+ };
137
+ fetchMcpServers();
138
+ }, []);
139
+
140
+ // Reset form when agent changes
141
+ useEffect(() => {
142
+ setForm({
143
+ name: agent.name,
144
+ provider: agent.provider,
145
+ model: agent.model,
146
+ systemPrompt: agent.systemPrompt,
147
+ features: { ...agent.features },
148
+ mcpServers: [...(agent.mcpServers || [])],
149
+ });
150
+ setMessage(null);
151
+ }, [agent.id]);
152
+
153
+ const selectedProvider = providers.find(p => p.id === form.provider);
154
+
155
+ const providerOptions = providers
156
+ .filter(p => p.configured)
157
+ .map(p => ({ value: p.id, label: p.name }));
158
+
159
+ const modelOptions = selectedProvider?.models.map(m => ({
160
+ value: m.value,
161
+ label: m.label,
162
+ recommended: m.recommended,
163
+ })) || [];
164
+
165
+ const handleProviderChange = (providerId: string) => {
166
+ const provider = providers.find(p => p.id === providerId);
167
+ const defaultModel = provider?.models.find(m => m.recommended)?.value || provider?.models[0]?.value || "";
168
+ setForm(prev => ({ ...prev, provider: providerId, model: defaultModel }));
169
+ };
170
+
171
+ const toggleFeature = (key: keyof AgentFeatures) => {
172
+ setForm(prev => ({
173
+ ...prev,
174
+ features: { ...prev.features, [key]: !prev.features[key] },
175
+ }));
176
+ };
177
+
178
+ const toggleMcpServer = (serverId: string) => {
179
+ setForm(prev => ({
180
+ ...prev,
181
+ mcpServers: prev.mcpServers.includes(serverId)
182
+ ? prev.mcpServers.filter(id => id !== serverId)
183
+ : [...prev.mcpServers, serverId],
184
+ }));
185
+ };
186
+
187
+ const handleSave = async () => {
188
+ setSaving(true);
189
+ setMessage(null);
190
+ const result = await onUpdateAgent(form);
191
+ setSaving(false);
192
+ if (result.error) {
193
+ setMessage({ type: "error", text: result.error });
194
+ } else {
195
+ setMessage({ type: "success", text: "Settings saved" });
196
+ setTimeout(() => setMessage(null), 2000);
197
+ }
198
+ };
199
+
200
+ const hasChanges =
201
+ form.name !== agent.name ||
202
+ form.provider !== agent.provider ||
203
+ form.model !== agent.model ||
204
+ form.systemPrompt !== agent.systemPrompt ||
205
+ JSON.stringify(form.features) !== JSON.stringify(agent.features) ||
206
+ JSON.stringify(form.mcpServers.sort()) !== JSON.stringify((agent.mcpServers || []).sort());
207
+
208
+ return (
209
+ <div className="flex-1 overflow-auto p-4">
210
+ <div className="space-y-4">
211
+ <FormField label="Name">
212
+ <input
213
+ type="text"
214
+ value={form.name}
215
+ onChange={(e) => setForm(prev => ({ ...prev, name: e.target.value }))}
216
+ className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 focus:outline-none focus:border-[#f97316] text-[#e0e0e0]"
217
+ />
218
+ </FormField>
219
+
220
+ <FormField label="Provider">
221
+ <Select
222
+ value={form.provider}
223
+ options={providerOptions}
224
+ onChange={handleProviderChange}
225
+ />
226
+ </FormField>
227
+
228
+ <FormField label="Model">
229
+ <Select
230
+ value={form.model}
231
+ options={modelOptions}
232
+ onChange={(value) => setForm(prev => ({ ...prev, model: value }))}
233
+ />
234
+ </FormField>
235
+
236
+ <FormField label="System Prompt">
237
+ <textarea
238
+ value={form.systemPrompt}
239
+ onChange={(e) => setForm(prev => ({ ...prev, systemPrompt: e.target.value }))}
240
+ className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 h-24 resize-none focus:outline-none focus:border-[#f97316] text-[#e0e0e0]"
241
+ />
242
+ </FormField>
243
+
244
+ <FormField label="Features">
245
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
246
+ {FEATURE_CONFIG.map(({ key, label, description, icon: Icon }) => (
247
+ <button
248
+ key={key}
249
+ type="button"
250
+ onClick={() => toggleFeature(key)}
251
+ className={`flex items-center gap-3 p-3 rounded border text-left transition ${
252
+ form.features[key]
253
+ ? "border-[#f97316] bg-[#f97316]/10"
254
+ : "border-[#222] hover:border-[#333]"
255
+ }`}
256
+ >
257
+ <Icon className={`w-5 h-5 flex-shrink-0 ${form.features[key] ? "text-[#f97316]" : "text-[#666]"}`} />
258
+ <div className="flex-1 min-w-0">
259
+ <div className={`text-sm font-medium ${form.features[key] ? "text-[#f97316]" : ""}`}>
260
+ {label}
261
+ </div>
262
+ <div className="text-xs text-[#666]">{description}</div>
263
+ </div>
264
+ </button>
265
+ ))}
266
+ </div>
267
+ </FormField>
268
+
269
+ {/* MCP Server Selection - shown when MCP is enabled */}
270
+ {form.features.mcp && (
271
+ <FormField label="MCP Servers">
272
+ {availableMcpServers.length === 0 ? (
273
+ <p className="text-sm text-[#666]">
274
+ No MCP servers configured. Add servers in the MCP page first.
275
+ </p>
276
+ ) : (
277
+ <div className="space-y-2">
278
+ {availableMcpServers.map(server => (
279
+ <button
280
+ key={server.id}
281
+ type="button"
282
+ onClick={() => toggleMcpServer(server.id)}
283
+ className={`w-full flex items-center gap-3 p-3 rounded border text-left transition ${
284
+ form.mcpServers.includes(server.id)
285
+ ? "border-[#f97316] bg-[#f97316]/10"
286
+ : "border-[#222] hover:border-[#333]"
287
+ }`}
288
+ >
289
+ <div className={`w-2 h-2 rounded-full flex-shrink-0 ${
290
+ server.status === "running" ? "bg-green-400" : "bg-[#444]"
291
+ }`} />
292
+ <div className="flex-1 min-w-0">
293
+ <div className={`text-sm font-medium ${form.mcpServers.includes(server.id) ? "text-[#f97316]" : ""}`}>
294
+ {server.name}
295
+ </div>
296
+ <div className="text-xs text-[#666]">
297
+ {server.type} • {server.package || server.command || "custom"}
298
+ {server.status === "running" && server.port && ` • :${server.port}`}
299
+ </div>
300
+ </div>
301
+ <div className={`text-xs px-2 py-0.5 rounded ${
302
+ server.status === "running"
303
+ ? "bg-green-500/20 text-green-400"
304
+ : "bg-[#222] text-[#666]"
305
+ }`}>
306
+ {server.status}
307
+ </div>
308
+ </button>
309
+ ))}
310
+ <p className="text-xs text-[#666] mt-2">
311
+ Only running servers will be connected to the agent.
312
+ </p>
313
+ </div>
314
+ )}
315
+ </FormField>
316
+ )}
317
+
318
+ {message && (
319
+ <div className={`text-sm px-3 py-2 rounded ${
320
+ message.type === "success"
321
+ ? "bg-green-500/10 text-green-400"
322
+ : "bg-red-500/10 text-red-400"
323
+ }`}>
324
+ {message.text}
325
+ </div>
326
+ )}
327
+
328
+ <button
329
+ onClick={handleSave}
330
+ disabled={!hasChanges || saving || !form.name}
331
+ className="w-full bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
332
+ >
333
+ {saving ? "Saving..." : "Save Changes"}
334
+ </button>
335
+
336
+ {agent.status === "running" && hasChanges && (
337
+ <p className="text-xs text-[#666] text-center">
338
+ Changes will be applied to the running agent
339
+ </p>
340
+ )}
341
+
342
+ {/* Danger Zone */}
343
+ <div className="mt-8 pt-6 border-t border-[#222]">
344
+ <p className="text-sm text-[#666] mb-3">Danger Zone</p>
345
+ {confirmDelete ? (
346
+ <div className="flex gap-2">
347
+ <button
348
+ onClick={() => setConfirmDelete(false)}
349
+ className="flex-1 border border-[#333] hover:border-[#444] px-4 py-2 rounded font-medium transition"
350
+ >
351
+ Cancel
352
+ </button>
353
+ <button
354
+ onClick={onDeleteAgent}
355
+ className="flex-1 bg-red-500/20 text-red-400 hover:bg-red-500/30 px-4 py-2 rounded font-medium transition"
356
+ >
357
+ Confirm Delete
358
+ </button>
359
+ </div>
360
+ ) : (
361
+ <button
362
+ onClick={() => setConfirmDelete(true)}
363
+ className="w-full border border-red-500/30 text-red-400/70 hover:border-red-500/50 hover:text-red-400 px-4 py-2 rounded font-medium transition"
364
+ >
365
+ Delete Agent
366
+ </button>
367
+ )}
368
+ </div>
369
+ </div>
370
+ </div>
371
+ );
372
+ }
373
+
374
+ function FormField({ label, children }: { label: string; children: React.ReactNode }) {
375
+ return (
376
+ <div>
377
+ <label className="block text-sm text-[#666] mb-1">{label}</label>
378
+ {children}
379
+ </div>
380
+ );
381
+ }
@@ -1,38 +1,42 @@
1
1
  import React from "react";
2
2
  import { AgentCard } from "./AgentCard";
3
- import { ChatPanel } from "./ChatPanel";
3
+ import { AgentPanel } from "./AgentPanel";
4
4
  import { LoadingSpinner } from "../common/LoadingSpinner";
5
- import type { Agent } from "../../types";
5
+ import type { Agent, Provider } from "../../types";
6
6
 
7
7
  interface AgentsViewProps {
8
8
  agents: Agent[];
9
9
  loading: boolean;
10
10
  selectedAgent: Agent | null;
11
+ providers: Provider[];
11
12
  onSelectAgent: (agent: Agent) => void;
12
13
  onCloseAgent: () => void;
13
14
  onToggleAgent: (agent: Agent, e?: React.MouseEvent) => void;
14
15
  onDeleteAgent: (id: string, e?: React.MouseEvent) => void;
16
+ onUpdateAgent: (id: string, updates: Partial<Agent>) => Promise<{ error?: string }>;
15
17
  }
16
18
 
17
19
  export function AgentsView({
18
20
  agents,
19
21
  loading,
20
22
  selectedAgent,
23
+ providers,
21
24
  onSelectAgent,
22
25
  onCloseAgent,
23
26
  onToggleAgent,
24
27
  onDeleteAgent,
28
+ onUpdateAgent,
25
29
  }: AgentsViewProps) {
26
30
  return (
27
- <div className="flex-1 flex overflow-hidden">
31
+ <div className="flex-1 flex overflow-hidden relative">
28
32
  {/* Agents list */}
29
- <div className={`${selectedAgent ? 'w-1/2 border-r border-[#1a1a1a]' : 'flex-1'} overflow-auto p-6 transition-all`}>
33
+ <div className="flex-1 overflow-auto p-6">
30
34
  {loading ? (
31
35
  <LoadingSpinner message="Loading agents..." />
32
36
  ) : agents.length === 0 ? (
33
37
  <EmptyState />
34
38
  ) : (
35
- <div className={`grid gap-4 ${selectedAgent ? 'grid-cols-1 xl:grid-cols-2' : 'md:grid-cols-2 xl:grid-cols-3'}`}>
39
+ <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
36
40
  {agents.map((agent) => (
37
41
  <AgentCard
38
42
  key={agent.id}
@@ -47,14 +51,27 @@ export function AgentsView({
47
51
  )}
48
52
  </div>
49
53
 
50
- {/* Chat Panel */}
54
+ {/* Overlay backdrop */}
51
55
  {selectedAgent && (
52
- <ChatPanel
53
- agent={selectedAgent}
54
- onClose={onCloseAgent}
55
- onStartAgent={(e) => onToggleAgent(selectedAgent, e)}
56
+ <div
57
+ className="absolute inset-0 bg-black/40 backdrop-blur-[2px] z-10"
58
+ onClick={onCloseAgent}
56
59
  />
57
60
  )}
61
+
62
+ {/* Agent Panel - slides in from right */}
63
+ {selectedAgent && (
64
+ <div className="absolute right-0 top-0 bottom-0 w-[600px] z-20">
65
+ <AgentPanel
66
+ agent={selectedAgent}
67
+ providers={providers}
68
+ onClose={onCloseAgent}
69
+ onStartAgent={(e) => onToggleAgent(selectedAgent, e)}
70
+ onUpdateAgent={(updates) => onUpdateAgent(selectedAgent.id, updates)}
71
+ onDeleteAgent={() => onDeleteAgent(selectedAgent.id)}
72
+ />
73
+ </div>
74
+ )}
58
75
  </div>
59
76
  );
60
77
  }
@@ -16,11 +16,11 @@ interface CreateAgentModalProps {
16
16
  }
17
17
 
18
18
  const FEATURE_CONFIG = [
19
- { key: "memory" as keyof AgentFeatures, label: "Memory", description: "Remember information across conversations", icon: MemoryIcon },
20
- { key: "tasks" as keyof AgentFeatures, label: "Tasks", description: "Create and execute scheduled tasks", icon: TasksIcon },
19
+ { key: "memory" as keyof AgentFeatures, label: "Memory", description: "Persistent recall", icon: MemoryIcon },
20
+ { key: "tasks" as keyof AgentFeatures, label: "Tasks", description: "Schedule and execute tasks", icon: TasksIcon },
21
21
  { key: "vision" as keyof AgentFeatures, label: "Vision", description: "Process images and PDFs", icon: VisionIcon },
22
- { key: "operator" as keyof AgentFeatures, label: "Operator", description: "Browser automation (computer use)", icon: OperatorIcon },
23
- { key: "mcp" as keyof AgentFeatures, label: "MCP", description: "Connect to external tools and services", icon: McpIcon },
22
+ { key: "operator" as keyof AgentFeatures, label: "Operator", description: "Browser automation", icon: OperatorIcon },
23
+ { key: "mcp" as keyof AgentFeatures, label: "MCP", description: "External tools/services", icon: McpIcon },
24
24
  { key: "realtime" as keyof AgentFeatures, label: "Realtime", description: "Voice conversations", icon: RealtimeIcon },
25
25
  ];
26
26
 
@@ -103,7 +103,7 @@ export function CreateAgentModal({
103
103
  </FormField>
104
104
 
105
105
  <FormField label="Features">
106
- <div className="grid grid-cols-2 gap-2">
106
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
107
107
  {FEATURE_CONFIG.map(({ key, label, description, icon: Icon }) => (
108
108
  <button
109
109
  key={key}
@@ -115,12 +115,12 @@ export function CreateAgentModal({
115
115
  : "border-[#222] hover:border-[#333]"
116
116
  }`}
117
117
  >
118
- <Icon className={`w-5 h-5 ${form.features[key] ? "text-[#f97316]" : "text-[#666]"}`} />
118
+ <Icon className={`w-5 h-5 flex-shrink-0 ${form.features[key] ? "text-[#f97316]" : "text-[#666]"}`} />
119
119
  <div className="flex-1 min-w-0">
120
120
  <div className={`text-sm font-medium ${form.features[key] ? "text-[#f97316]" : ""}`}>
121
121
  {label}
122
122
  </div>
123
- <div className="text-xs text-[#666] truncate">{description}</div>
123
+ <div className="text-xs text-[#666]">{description}</div>
124
124
  </div>
125
125
  </button>
126
126
  ))}
@@ -1,4 +1,4 @@
1
1
  export { AgentCard } from "./AgentCard";
2
2
  export { CreateAgentModal } from "./CreateAgentModal";
3
- export { ChatPanel } from "./ChatPanel";
3
+ export { AgentPanel } from "./AgentPanel";
4
4
  export { AgentsView } from "./AgentsView";
@@ -109,3 +109,11 @@ export function RealtimeIcon({ className = "w-4 h-4" }: IconProps) {
109
109
  </svg>
110
110
  );
111
111
  }
112
+
113
+ export function TelemetryIcon({ className = "w-4 h-4" }: IconProps) {
114
+ return (
115
+ <svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
116
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
117
+ </svg>
118
+ );
119
+ }
@@ -7,8 +7,8 @@ interface ModalProps {
7
7
 
8
8
  export function Modal({ children, onClose }: ModalProps) {
9
9
  return (
10
- <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50">
11
- <div className="bg-[#111] rounded p-6 w-full max-w-md border border-[#1a1a1a]">
10
+ <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
11
+ <div className="bg-[#111] rounded p-6 w-full max-w-xl border border-[#1a1a1a] max-h-[90vh] overflow-y-auto">
12
12
  {children}
13
13
  </div>
14
14
  </div>
@@ -50,7 +50,7 @@ export function Select({ value, options, onChange, placeholder = "Select..." }:
50
50
  </button>
51
51
 
52
52
  {isOpen && (
53
- <div className="absolute z-50 w-full mt-1 bg-[#111] border border-[#222] rounded shadow-lg max-h-60 overflow-auto">
53
+ <div className="absolute z-50 w-full min-w-max mt-1 bg-[#111] border border-[#222] rounded shadow-lg max-h-60 overflow-y-auto scrollbar-hide">
54
54
  {options.map((option) => (
55
55
  <button
56
56
  key={option.value}
@@ -13,4 +13,5 @@ export {
13
13
  OperatorIcon,
14
14
  McpIcon,
15
15
  RealtimeIcon,
16
+ TelemetryIcon,
16
17
  } from "./Icons";