apteva 0.2.11 → 0.3.7

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.
@@ -3,7 +3,8 @@ import { Modal } from "../common/Modal";
3
3
  import { Select } from "../common/Select";
4
4
  import { MemoryIcon, TasksIcon, FilesIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, MultiAgentIcon } from "../common/Icons";
5
5
  import { useProjects } from "../../context";
6
- import type { Provider, NewAgentForm, AgentFeatures } from "../../types";
6
+ import type { Provider, NewAgentForm, AgentFeatures, AgentMode, MultiAgentConfig } from "../../types";
7
+ import { getMultiAgentConfig } from "../../types";
7
8
 
8
9
  interface CreateAgentModalProps {
9
10
  form: NewAgentForm;
@@ -40,10 +41,12 @@ export function CreateAgentModal({
40
41
  const { projects, currentProjectId } = useProjects();
41
42
  const selectedProvider = providers.find(p => p.id === form.provider);
42
43
 
43
- const providerOptions = configuredProviders.map(p => ({
44
- value: p.id,
45
- label: p.name,
46
- }));
44
+ const providerOptions = configuredProviders
45
+ .filter(p => p.type === "llm")
46
+ .map(p => ({
47
+ value: p.id,
48
+ label: p.name,
49
+ }));
47
50
 
48
51
  const modelOptions = selectedProvider?.models.map(m => ({
49
52
  value: m.value,
@@ -51,10 +54,7 @@ export function CreateAgentModal({
51
54
  recommended: m.recommended,
52
55
  })) || [];
53
56
 
54
- const projectOptions = [
55
- { value: "", label: "No Project" },
56
- ...projects.map(p => ({ value: p.id, label: p.name })),
57
- ];
57
+ const projectOptions = projects.map(p => ({ value: p.id, label: p.name }));
58
58
 
59
59
  // Set default project from current selection (but not "unassigned" or "all")
60
60
  React.useEffect(() => {
@@ -64,11 +64,56 @@ export function CreateAgentModal({
64
64
  }, [currentProjectId]);
65
65
 
66
66
  const toggleFeature = (key: keyof AgentFeatures) => {
67
+ if (key === "agents") {
68
+ // Special handling for agents feature
69
+ const isEnabled = typeof form.features.agents === "boolean"
70
+ ? form.features.agents
71
+ : (form.features.agents as MultiAgentConfig)?.enabled ?? false;
72
+ if (isEnabled) {
73
+ // Turning off
74
+ onFormChange({ ...form, features: { ...form.features, agents: false } });
75
+ } else {
76
+ // Turning on with defaults - use project as group
77
+ onFormChange({
78
+ ...form,
79
+ features: {
80
+ ...form.features,
81
+ agents: { enabled: true, mode: "worker" as AgentMode, group: form.projectId || undefined },
82
+ },
83
+ });
84
+ }
85
+ } else {
86
+ onFormChange({
87
+ ...form,
88
+ features: {
89
+ ...form.features,
90
+ [key]: !form.features[key],
91
+ },
92
+ });
93
+ }
94
+ };
95
+
96
+ // Helper to check if agents feature is enabled
97
+ const isAgentsEnabled = () => {
98
+ const agentsVal = form.features.agents;
99
+ if (typeof agentsVal === "boolean") return agentsVal;
100
+ return (agentsVal as MultiAgentConfig)?.enabled ?? false;
101
+ };
102
+
103
+ // Get current agent mode
104
+ const getAgentMode = (): AgentMode => {
105
+ const config = getMultiAgentConfig(form.features, form.projectId);
106
+ return config.mode || "worker";
107
+ };
108
+
109
+ // Set multi-agent mode
110
+ const setAgentMode = (mode: AgentMode) => {
111
+ const currentConfig = getMultiAgentConfig(form.features, form.projectId);
67
112
  onFormChange({
68
113
  ...form,
69
114
  features: {
70
115
  ...form.features,
71
- [key]: !form.features[key],
116
+ agents: { ...currentConfig, enabled: true, mode },
72
117
  },
73
118
  });
74
119
  };
@@ -77,7 +122,7 @@ export function CreateAgentModal({
77
122
  <Modal>
78
123
  <h2 className="text-xl font-semibold mb-4">Create New Agent</h2>
79
124
 
80
- {configuredProviders.length === 0 ? (
125
+ {providerOptions.length === 0 ? (
81
126
  <NoProvidersMessage onGoToSettings={onGoToSettings} />
82
127
  ) : (
83
128
  <>
@@ -131,28 +176,129 @@ export function CreateAgentModal({
131
176
 
132
177
  <FormField label="Features">
133
178
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
134
- {FEATURE_CONFIG.map(({ key, label, description, icon: Icon }) => (
179
+ {FEATURE_CONFIG.map(({ key, label, description, icon: Icon }) => {
180
+ const isEnabled = key === "agents" ? isAgentsEnabled() : !!form.features[key];
181
+ return (
182
+ <button
183
+ key={key}
184
+ type="button"
185
+ onClick={() => toggleFeature(key)}
186
+ className={`flex items-center gap-3 p-3 rounded border text-left transition ${
187
+ isEnabled
188
+ ? "border-[#f97316] bg-[#f97316]/10"
189
+ : "border-[#222] hover:border-[#333]"
190
+ }`}
191
+ >
192
+ <Icon className={`w-5 h-5 flex-shrink-0 ${isEnabled ? "text-[#f97316]" : "text-[#666]"}`} />
193
+ <div className="flex-1 min-w-0">
194
+ <div className={`text-sm font-medium ${isEnabled ? "text-[#f97316]" : ""}`}>
195
+ {label}
196
+ </div>
197
+ <div className="text-xs text-[#666]">{description}</div>
198
+ </div>
199
+ </button>
200
+ );
201
+ })}
202
+ </div>
203
+ </FormField>
204
+
205
+ {/* Multi-Agent Mode Selection */}
206
+ {isAgentsEnabled() && (
207
+ <FormField label="Multi-Agent Mode">
208
+ <div className="flex gap-2">
135
209
  <button
136
- key={key}
137
210
  type="button"
138
- onClick={() => toggleFeature(key)}
139
- className={`flex items-center gap-3 p-3 rounded border text-left transition ${
140
- form.features[key]
211
+ onClick={() => setAgentMode("coordinator")}
212
+ className={`flex-1 p-3 rounded border text-left transition ${
213
+ getAgentMode() === "coordinator"
141
214
  ? "border-[#f97316] bg-[#f97316]/10"
142
215
  : "border-[#222] hover:border-[#333]"
143
216
  }`}
144
217
  >
145
- <Icon className={`w-5 h-5 flex-shrink-0 ${form.features[key] ? "text-[#f97316]" : "text-[#666]"}`} />
146
- <div className="flex-1 min-w-0">
147
- <div className={`text-sm font-medium ${form.features[key] ? "text-[#f97316]" : ""}`}>
148
- {label}
149
- </div>
150
- <div className="text-xs text-[#666]">{description}</div>
218
+ <div className={`text-sm font-medium ${getAgentMode() === "coordinator" ? "text-[#f97316]" : ""}`}>
219
+ Coordinator
151
220
  </div>
221
+ <div className="text-xs text-[#666]">Orchestrates and delegates</div>
152
222
  </button>
153
- ))}
223
+ <button
224
+ type="button"
225
+ onClick={() => setAgentMode("worker")}
226
+ className={`flex-1 p-3 rounded border text-left transition ${
227
+ getAgentMode() === "worker"
228
+ ? "border-[#f97316] bg-[#f97316]/10"
229
+ : "border-[#222] hover:border-[#333]"
230
+ }`}
231
+ >
232
+ <div className={`text-sm font-medium ${getAgentMode() === "worker" ? "text-[#f97316]" : ""}`}>
233
+ Worker
234
+ </div>
235
+ <div className="text-xs text-[#666]">Receives delegated tasks</div>
236
+ </button>
237
+ </div>
238
+ {form.projectId && (
239
+ <p className="text-xs text-[#555] mt-2">
240
+ Group: Using project as agent group
241
+ </p>
242
+ )}
243
+ </FormField>
244
+ )}
245
+
246
+ {/* Agent Built-in Tools - Anthropic only */}
247
+ {form.provider === "anthropic" && (
248
+ <FormField label="Agent Built-in Tools">
249
+ <div className="flex flex-wrap gap-2">
250
+ <button
251
+ type="button"
252
+ onClick={() => onFormChange({
253
+ ...form,
254
+ features: {
255
+ ...form.features,
256
+ builtinTools: {
257
+ ...form.features.builtinTools,
258
+ webSearch: !form.features.builtinTools?.webSearch,
259
+ },
260
+ },
261
+ })}
262
+ className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
263
+ form.features.builtinTools?.webSearch
264
+ ? "border-[#f97316] bg-[#f97316]/10 text-[#f97316]"
265
+ : "border-[#222] hover:border-[#333] text-[#888]"
266
+ }`}
267
+ >
268
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
269
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
270
+ </svg>
271
+ <span className="text-sm">Web Search</span>
272
+ </button>
273
+ <button
274
+ type="button"
275
+ onClick={() => onFormChange({
276
+ ...form,
277
+ features: {
278
+ ...form.features,
279
+ builtinTools: {
280
+ ...form.features.builtinTools,
281
+ webFetch: !form.features.builtinTools?.webFetch,
282
+ },
283
+ },
284
+ })}
285
+ className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
286
+ form.features.builtinTools?.webFetch
287
+ ? "border-[#f97316] bg-[#f97316]/10 text-[#f97316]"
288
+ : "border-[#222] hover:border-[#333] text-[#888]"
289
+ }`}
290
+ >
291
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
292
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
293
+ </svg>
294
+ <span className="text-sm">Web Fetch</span>
295
+ </button>
154
296
  </div>
297
+ <p className="text-xs text-[#555] mt-2">
298
+ Provider-native tools for real-time web access
299
+ </p>
155
300
  </FormField>
301
+ )}
156
302
  </div>
157
303
 
158
304
  <div className="flex gap-3 mt-6">
@@ -102,6 +102,14 @@ export function McpIcon({ className = "w-4 h-4" }: IconProps) {
102
102
  );
103
103
  }
104
104
 
105
+ export function SkillsIcon({ className = "w-4 h-4" }: IconProps) {
106
+ return (
107
+ <svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
108
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
109
+ </svg>
110
+ );
111
+ }
112
+
105
113
  export function RealtimeIcon({ className = "w-4 h-4" }: IconProps) {
106
114
  return (
107
115
  <svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -12,6 +12,7 @@ export {
12
12
  VisionIcon,
13
13
  OperatorIcon,
14
14
  McpIcon,
15
+ SkillsIcon,
15
16
  RealtimeIcon,
16
17
  TelemetryIcon,
17
18
  } from "./Icons";
@@ -14,4 +14,5 @@ export { AgentCard, CreateAgentModal, AgentPanel, AgentsView } from "./agents";
14
14
  export { Dashboard } from "./dashboard";
15
15
  export { TasksPage } from "./tasks";
16
16
  export { McpPage } from "./mcp";
17
+ export { SkillsPage } from "./skills/SkillsPage";
17
18
  export { TelemetryPage } from "./telemetry/TelemetryPage";
@@ -1,6 +1,7 @@
1
1
  import React, { useState } from "react";
2
2
  import { useTelemetryContext, useAuth, useProjects } from "../../context";
3
3
  import { MenuIcon, ChevronDownIcon } from "../common/Icons";
4
+ import { MetaAgentButton } from "../meta-agent/MetaAgent";
4
5
 
5
6
  interface HeaderProps {
6
7
  onMenuClick?: () => void;
@@ -9,7 +10,7 @@ interface HeaderProps {
9
10
  export function Header({ onMenuClick }: HeaderProps) {
10
11
  const { connected } = useTelemetryContext();
11
12
  const { user, logout } = useAuth();
12
- const { projects, currentProjectId, currentProject, setCurrentProjectId, unassignedCount } = useProjects();
13
+ const { projects, currentProjectId, currentProject, setCurrentProjectId, unassignedCount, projectsEnabled } = useProjects();
13
14
  const [showUserMenu, setShowUserMenu] = useState(false);
14
15
  const [showProjectMenu, setShowProjectMenu] = useState(false);
15
16
 
@@ -52,7 +53,7 @@ export function Header({ onMenuClick }: HeaderProps) {
52
53
  </div>
53
54
 
54
55
  {/* Project Selector */}
55
- {projects.length > 0 && (
56
+ {projectsEnabled && projects.length > 0 && (
56
57
  <div className="relative ml-2 md:ml-4">
57
58
  <button
58
59
  onClick={() => setShowProjectMenu(!showProjectMenu)}
@@ -122,6 +123,7 @@ export function Header({ onMenuClick }: HeaderProps) {
122
123
  {connected ? "Live" : "Offline"}
123
124
  </span>
124
125
  </div>
126
+ <MetaAgentButton />
125
127
  {user && (
126
128
  <div className="relative">
127
129
  <button
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { DashboardIcon, AgentsIcon, TasksIcon, McpIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
2
+ import { DashboardIcon, AgentsIcon, TasksIcon, McpIcon, SkillsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
3
3
  import type { Route } from "../../types";
4
4
 
5
5
  interface SidebarProps {
@@ -76,6 +76,12 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
76
76
  active={route === "mcp"}
77
77
  onClick={() => handleNavigate("mcp")}
78
78
  />
79
+ <NavButton
80
+ icon={<SkillsIcon />}
81
+ label="Skills"
82
+ active={route === "skills"}
83
+ onClick={() => handleNavigate("skills")}
84
+ />
79
85
  <NavButton
80
86
  icon={<TelemetryIcon />}
81
87
  label="Telemetry"