apteva 0.4.41 → 0.4.44
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.c48n83h2.js +3 -0
- package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
- package/dist/App.09yb8t0b.js +1 -0
- package/dist/App.152mbs1r.js +4 -0
- package/dist/App.3a67nx9w.js +4 -0
- package/dist/App.9epx6785.js +4 -0
- package/dist/App.d8955awp.js +4 -0
- package/dist/App.drwb57jq.js +4 -0
- package/dist/App.gssbmajb.js +4 -0
- package/dist/App.qw70pc29.js +53 -0
- package/dist/{App.7fb3e7mp.js → App.qzbx5wtj.js} +1 -1
- package/dist/App.r5serxkt.js +8 -0
- package/dist/App.tpmp9020.js +20 -0
- package/dist/App.v2wb4d7d.js +61 -0
- package/dist/App.vxmaaj0m.js +13 -0
- package/dist/App.w4p2tda9.js +4 -0
- package/dist/App.wv2ng55q.js +221 -0
- package/dist/App.yncnrn0f.js +4 -0
- package/dist/ConnectionsPage.k6cspyqq.js +3 -0
- package/dist/McpPage.cdxm48xj.js +3 -0
- package/dist/SettingsPage.evpv7c2y.js +3 -0
- package/dist/SkillsPage.pvzp6c1a.js +3 -0
- package/dist/TasksPage.6jnvbpsy.js +3 -0
- package/dist/TelemetryPage.t7vk24zc.js +3 -0
- package/dist/TestsPage.5x6658aa.js +3 -0
- package/dist/ThreadsPage.3fvhtevh.js +3 -0
- package/dist/apteva-kit.css +1 -1
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -8
- package/src/db.ts +19 -9
- package/src/integrations/agentdojo.ts +1 -0
- package/src/mcp-platform.ts +418 -63
- package/src/openapi.ts +96 -0
- package/src/providers.ts +50 -24
- package/src/routes/api/agent-utils.ts +0 -1
- package/src/routes/api/agents.ts +19 -1
- package/src/routes/api/meta-agent.ts +2 -0
- package/src/routes/api/system.ts +90 -1
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +12 -0
- package/src/web/App.tsx +89 -11
- package/src/web/components/activity/ActivityPage.tsx +14 -14
- package/src/web/components/agents/AgentCard.tsx +14 -14
- package/src/web/components/agents/AgentPanel.tsx +358 -198
- package/src/web/components/agents/AgentsView.tsx +4 -4
- package/src/web/components/agents/CreateAgentModal.tsx +21 -79
- package/src/web/components/api/ApiDocsPage.tsx +66 -66
- package/src/web/components/auth/CreateAccountStep.tsx +16 -16
- package/src/web/components/auth/LoginPage.tsx +10 -10
- package/src/web/components/common/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +9 -9
- package/src/web/components/connections/ConnectionsPage.tsx +4 -4
- package/src/web/components/connections/IntegrationsTab.tsx +18 -18
- package/src/web/components/connections/OverviewTab.tsx +13 -13
- package/src/web/components/connections/TriggersTab.tsx +99 -99
- package/src/web/components/dashboard/Dashboard.tsx +32 -32
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +34 -15
- package/src/web/components/mcp/IntegrationsPanel.tsx +40 -40
- package/src/web/components/mcp/McpPage.tsx +208 -208
- package/src/web/components/meta-agent/MetaAgent.tsx +12 -10
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +258 -175
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +339 -54
- package/src/web/components/telemetry/TelemetryPage.tsx +135 -64
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +23 -21
- package/src/web/context/ProjectContext.tsx +6 -1
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +2 -0
- package/src/web/styles.css +5 -3
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +0 -4
- package/dist/ActivityPage.7907h64p.js +0 -3
- package/dist/ApiDocsPage.k3jjenpq.js +0 -4
- package/dist/App.01nq20st.js +0 -4
- package/dist/App.1maqvamf.js +0 -4
- package/dist/App.2yjrh32f.js +0 -4
- package/dist/App.3qw8nben.js +0 -20
- package/dist/App.7sy3wq8c.js +0 -4
- package/dist/App.apjrmctz.js +0 -57
- package/dist/App.av6t2yhe.js +0 -4
- package/dist/App.jqj5a094.js +0 -46
- package/dist/App.mc7xf85h.js +0 -4
- package/dist/App.myxqcj9x.js +0 -4
- package/dist/App.nm91r1mp.js +0 -13
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.qcknavjz.js +0 -221
- package/dist/App.vc7vfhg4.js +0 -4
- package/dist/App.z4s9zkw5.js +0 -4
- package/dist/ConnectionsPage.z1pw5xe2.js +0 -3
- package/dist/McpPage.8vc97z0b.js +0 -3
- package/dist/SettingsPage.p61bz8kd.js +0 -3
- package/dist/SkillsPage.r9x43g3g.js +0 -3
- package/dist/TasksPage.1e0zkye4.js +0 -3
- package/dist/TelemetryPage.p9vbe4gf.js +0 -3
- package/dist/TestsPage.d4xy504e.js +0 -3
- package/dist/ThreadsPage.m016am3x.js +0 -3
|
@@ -4,9 +4,9 @@ import { CloseIcon, MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, Re
|
|
|
4
4
|
import { formatCron, formatRelativeTime, TrajectoryView } from "../tasks/TasksPage";
|
|
5
5
|
import { Select } from "../common/Select";
|
|
6
6
|
import { useConfirm } from "../common/Modal";
|
|
7
|
-
import { useTelemetry } from "../../context";
|
|
7
|
+
import { useTelemetry, useTheme } from "../../context";
|
|
8
8
|
import { useAuth } from "../../context";
|
|
9
|
-
import type { Agent, Provider, AgentFeatures, McpServer, SkillSummary,
|
|
9
|
+
import type { Agent, Provider, AgentFeatures, McpServer, SkillSummary, MultiAgentConfig, OperatorConfig, Task } from "../../types";
|
|
10
10
|
import { getMultiAgentConfig, getOperatorConfig } from "../../types";
|
|
11
11
|
|
|
12
12
|
type Tab = "chat" | "threads" | "tasks" | "memory" | "files" | "settings";
|
|
@@ -35,9 +35,9 @@ export function AgentPanel({ agent, providers, onClose, onStartAgent, onUpdateAg
|
|
|
35
35
|
const [activeTab, setActiveTab] = useState<Tab>("chat");
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
|
-
<div className="w-full h-full flex flex-col overflow-hidden bg-[
|
|
38
|
+
<div className="w-full h-full flex flex-col overflow-hidden bg-[var(--color-bg)] border-l border-[var(--color-border)]">
|
|
39
39
|
{/* Header with tabs */}
|
|
40
|
-
<div className="border-b border-[
|
|
40
|
+
<div className="border-b border-[var(--color-border)] flex items-center">
|
|
41
41
|
{/* Scrollable tabs */}
|
|
42
42
|
<div className="flex-1 overflow-x-auto scrollbar-hide px-2 md:px-4">
|
|
43
43
|
<div className="flex gap-1">
|
|
@@ -65,7 +65,7 @@ export function AgentPanel({ agent, providers, onClose, onStartAgent, onUpdateAg
|
|
|
65
65
|
{/* Close button - fixed on right */}
|
|
66
66
|
<button
|
|
67
67
|
onClick={onClose}
|
|
68
|
-
className="text-[
|
|
68
|
+
className="text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition p-2 flex-shrink-0 mr-2"
|
|
69
69
|
>
|
|
70
70
|
<CloseIcon />
|
|
71
71
|
</button>
|
|
@@ -102,8 +102,8 @@ function TabButton({ active, onClick, children }: { active: boolean; onClick: ()
|
|
|
102
102
|
onClick={onClick}
|
|
103
103
|
className={`px-4 py-3 text-sm font-medium border-b-2 transition ${
|
|
104
104
|
active
|
|
105
|
-
? "border-[
|
|
106
|
-
: "border-transparent text-[
|
|
105
|
+
? "border-[var(--color-accent)] text-[var(--color-text)]"
|
|
106
|
+
: "border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
|
|
107
107
|
}`}
|
|
108
108
|
>
|
|
109
109
|
{children}
|
|
@@ -112,6 +112,7 @@ function TabButton({ active, onClick, children }: { active: boolean; onClick: ()
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
function ChatTab({ agent, onStartAgent }: { agent: Agent; onStartAgent: (e?: React.MouseEvent) => void }) {
|
|
115
|
+
const { theme } = useTheme();
|
|
115
116
|
if (agent.status === "running" && agent.port) {
|
|
116
117
|
return (
|
|
117
118
|
<Chat
|
|
@@ -120,13 +121,14 @@ function ChatTab({ agent, onStartAgent }: { agent: Agent; onStartAgent: (e?: Rea
|
|
|
120
121
|
placeholder="Message this agent..."
|
|
121
122
|
context={agent.systemPrompt}
|
|
122
123
|
variant="terminal"
|
|
124
|
+
theme={theme.id as "light" | "dark"}
|
|
123
125
|
headerTitle={agent.name}
|
|
124
126
|
/>
|
|
125
127
|
);
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
return (
|
|
129
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
131
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
130
132
|
<div className="text-center">
|
|
131
133
|
<p className="text-lg mb-2">Agent is not running</p>
|
|
132
134
|
<button
|
|
@@ -149,6 +151,7 @@ interface Thread {
|
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
function ThreadsTab({ agent }: { agent: Agent }) {
|
|
154
|
+
const { theme: themeObj } = useTheme();
|
|
152
155
|
const [threads, setThreads] = useState<Thread[]>([]);
|
|
153
156
|
const [loading, setLoading] = useState(true);
|
|
154
157
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -223,7 +226,7 @@ function ThreadsTab({ agent }: { agent: Agent }) {
|
|
|
223
226
|
|
|
224
227
|
if (agent.status !== "running") {
|
|
225
228
|
return (
|
|
226
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
229
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
227
230
|
<p>Start the agent to view threads</p>
|
|
228
231
|
</div>
|
|
229
232
|
);
|
|
@@ -231,7 +234,7 @@ function ThreadsTab({ agent }: { agent: Agent }) {
|
|
|
231
234
|
|
|
232
235
|
if (loading) {
|
|
233
236
|
return (
|
|
234
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
237
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
235
238
|
<p>Loading threads...</p>
|
|
236
239
|
</div>
|
|
237
240
|
);
|
|
@@ -252,7 +255,7 @@ function ThreadsTab({ agent }: { agent: Agent }) {
|
|
|
252
255
|
{ConfirmDialog}
|
|
253
256
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
254
257
|
{loadingMessages ? (
|
|
255
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
258
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">Loading messages...</div>
|
|
256
259
|
) : (
|
|
257
260
|
<Chat
|
|
258
261
|
key={selectedThread}
|
|
@@ -263,6 +266,7 @@ function ThreadsTab({ agent }: { agent: Agent }) {
|
|
|
263
266
|
placeholder="Continue this conversation..."
|
|
264
267
|
context={agent.systemPrompt}
|
|
265
268
|
variant="terminal"
|
|
269
|
+
theme={themeObj.id as "light" | "dark"}
|
|
266
270
|
showHeader={true}
|
|
267
271
|
onHeaderBack={() => { setSelectedThread(null); setInitialMessages([]); }}
|
|
268
272
|
/>
|
|
@@ -278,29 +282,29 @@ function ThreadsTab({ agent }: { agent: Agent }) {
|
|
|
278
282
|
{ConfirmDialog}
|
|
279
283
|
<div className="flex-1 overflow-auto">
|
|
280
284
|
{threads.length === 0 ? (
|
|
281
|
-
<div className="flex items-center justify-center h-full text-[
|
|
285
|
+
<div className="flex items-center justify-center h-full text-[var(--color-text-muted)]">
|
|
282
286
|
<p>No conversation threads yet</p>
|
|
283
287
|
</div>
|
|
284
288
|
) : (
|
|
285
|
-
<div className="divide-y divide-[
|
|
289
|
+
<div className="divide-y divide-[var(--color-border)]">
|
|
286
290
|
{threads.map(thread => (
|
|
287
291
|
<div
|
|
288
292
|
key={thread.id}
|
|
289
293
|
onClick={() => openThread(thread.id)}
|
|
290
|
-
className="p-4 cursor-pointer hover:bg-[
|
|
294
|
+
className="p-4 cursor-pointer hover:bg-[var(--color-surface)] transition flex items-center justify-between"
|
|
291
295
|
>
|
|
292
296
|
<div className="flex-1 min-w-0">
|
|
293
297
|
<p className="text-sm font-medium truncate">
|
|
294
298
|
{thread.title || `Thread ${thread.id.slice(0, 8)}`}
|
|
295
299
|
</p>
|
|
296
|
-
<p className="text-xs text-[
|
|
300
|
+
<p className="text-xs text-[var(--color-text-muted)] mt-1">
|
|
297
301
|
{new Date(thread.updated_at || thread.created_at).toLocaleString()}
|
|
298
302
|
{thread.message_count !== undefined && ` • ${thread.message_count} messages`}
|
|
299
303
|
</p>
|
|
300
304
|
</div>
|
|
301
305
|
<button
|
|
302
306
|
onClick={(e) => deleteThread(thread.id, e)}
|
|
303
|
-
className="text-[
|
|
307
|
+
className="text-[var(--color-text-muted)] hover:text-red-400 text-lg ml-4"
|
|
304
308
|
>
|
|
305
309
|
×
|
|
306
310
|
</button>
|
|
@@ -321,6 +325,10 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
321
325
|
const [filter, setFilter] = useState<string>("all");
|
|
322
326
|
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
|
|
323
327
|
const [loadingTask, setLoadingTask] = useState(false);
|
|
328
|
+
const [showCreateForm, setShowCreateForm] = useState(false);
|
|
329
|
+
const [executing, setExecuting] = useState(false);
|
|
330
|
+
const [deleting, setDeleting] = useState(false);
|
|
331
|
+
const { confirm, ConfirmDialog } = useConfirm();
|
|
324
332
|
const { events } = useTelemetry({ agent_id: agent.id, category: "task" });
|
|
325
333
|
|
|
326
334
|
// Reset state when agent changes
|
|
@@ -368,6 +376,58 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
368
376
|
}
|
|
369
377
|
};
|
|
370
378
|
|
|
379
|
+
const handleExecuteTask = async () => {
|
|
380
|
+
if (!selectedTask || executing) return;
|
|
381
|
+
setExecuting(true);
|
|
382
|
+
try {
|
|
383
|
+
await authFetch(`/api/tasks/${agent.id}/${selectedTask.id}/execute`, { method: "POST" });
|
|
384
|
+
setSelectedTask(null);
|
|
385
|
+
fetchTasks();
|
|
386
|
+
} catch (e) {
|
|
387
|
+
console.error("Failed to execute task:", e);
|
|
388
|
+
} finally {
|
|
389
|
+
setExecuting(false);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const handleDeleteTask = async () => {
|
|
394
|
+
if (!selectedTask || deleting) return;
|
|
395
|
+
const ok = await confirm(`Are you sure you want to delete "${selectedTask.title}"?`, {
|
|
396
|
+
title: "Delete Task",
|
|
397
|
+
confirmText: "Delete",
|
|
398
|
+
confirmVariant: "danger",
|
|
399
|
+
});
|
|
400
|
+
if (!ok) return;
|
|
401
|
+
setDeleting(true);
|
|
402
|
+
try {
|
|
403
|
+
await authFetch(`/api/tasks/${agent.id}/${selectedTask.id}`, { method: "DELETE" });
|
|
404
|
+
setSelectedTask(null);
|
|
405
|
+
fetchTasks();
|
|
406
|
+
} catch (e) {
|
|
407
|
+
console.error("Failed to delete task:", e);
|
|
408
|
+
} finally {
|
|
409
|
+
setDeleting(false);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const handleCreateTask = async (data: { title: string; description: string; type: string; priority: number; execute_at?: string; recurrence?: string }) => {
|
|
414
|
+
try {
|
|
415
|
+
const body: Record<string, unknown> = { ...data };
|
|
416
|
+
if (data.execute_at) body.execute_at = new Date(data.execute_at).toISOString();
|
|
417
|
+
const res = await authFetch(`/api/tasks/${agent.id}`, {
|
|
418
|
+
method: "POST",
|
|
419
|
+
headers: { "Content-Type": "application/json" },
|
|
420
|
+
body: JSON.stringify(body),
|
|
421
|
+
});
|
|
422
|
+
if (res.ok) {
|
|
423
|
+
setShowCreateForm(false);
|
|
424
|
+
fetchTasks();
|
|
425
|
+
}
|
|
426
|
+
} catch (e) {
|
|
427
|
+
console.error("Failed to create task:", e);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
371
431
|
// Refetch when agent changes, filter changes, or task telemetry arrives
|
|
372
432
|
useEffect(() => {
|
|
373
433
|
setLoading(true);
|
|
@@ -384,7 +444,7 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
384
444
|
|
|
385
445
|
if (agent.status !== "running") {
|
|
386
446
|
return (
|
|
387
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
447
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
388
448
|
<p>Start the agent to view tasks</p>
|
|
389
449
|
</div>
|
|
390
450
|
);
|
|
@@ -392,7 +452,7 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
392
452
|
|
|
393
453
|
if (!agent.features?.tasks) {
|
|
394
454
|
return (
|
|
395
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
455
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
396
456
|
<div className="text-center">
|
|
397
457
|
<p className="mb-2">Tasks feature is not enabled</p>
|
|
398
458
|
<p className="text-sm">Enable it in Settings to schedule tasks</p>
|
|
@@ -403,7 +463,7 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
403
463
|
|
|
404
464
|
if (loading) {
|
|
405
465
|
return (
|
|
406
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
466
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
407
467
|
<p>Loading tasks...</p>
|
|
408
468
|
</div>
|
|
409
469
|
);
|
|
@@ -429,14 +489,35 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
429
489
|
if (selectedTask) {
|
|
430
490
|
return (
|
|
431
491
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
432
|
-
{
|
|
433
|
-
|
|
492
|
+
{ConfirmDialog}
|
|
493
|
+
{/* Back button + actions */}
|
|
494
|
+
<div className="px-4 pt-3 pb-2 border-b border-[var(--color-border)] shrink-0 flex items-center justify-between">
|
|
434
495
|
<button
|
|
435
496
|
onClick={() => setSelectedTask(null)}
|
|
436
|
-
className="text-sm text-[
|
|
497
|
+
className="text-sm text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition flex items-center gap-1"
|
|
437
498
|
>
|
|
438
499
|
<span>←</span> Back to tasks
|
|
439
500
|
</button>
|
|
501
|
+
<div className="flex items-center gap-2">
|
|
502
|
+
{(selectedTask.status === "pending" || selectedTask.status === "completed") && (
|
|
503
|
+
<button
|
|
504
|
+
onClick={handleExecuteTask}
|
|
505
|
+
disabled={executing}
|
|
506
|
+
title="Execute now"
|
|
507
|
+
className="text-[var(--color-accent)] hover:opacity-80 transition disabled:opacity-50"
|
|
508
|
+
>
|
|
509
|
+
<svg className="w-4.5 h-4.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
510
|
+
</button>
|
|
511
|
+
)}
|
|
512
|
+
<button
|
|
513
|
+
onClick={handleDeleteTask}
|
|
514
|
+
disabled={deleting}
|
|
515
|
+
title="Delete task"
|
|
516
|
+
className="text-red-400 hover:text-red-300 transition disabled:opacity-50"
|
|
517
|
+
>
|
|
518
|
+
<svg className="w-4.5 h-4.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
|
519
|
+
</button>
|
|
520
|
+
</div>
|
|
440
521
|
</div>
|
|
441
522
|
|
|
442
523
|
{/* Task detail content */}
|
|
@@ -454,26 +535,26 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
454
535
|
{/* Description */}
|
|
455
536
|
{selectedTask.description && (
|
|
456
537
|
<div>
|
|
457
|
-
<h4 className="text-xs text-[
|
|
458
|
-
<p className="text-sm text-[
|
|
538
|
+
<h4 className="text-xs text-[var(--color-text-muted)] uppercase tracking-wider mb-1">Description</h4>
|
|
539
|
+
<p className="text-sm text-[var(--color-text-secondary)] whitespace-pre-wrap">{selectedTask.description}</p>
|
|
459
540
|
</div>
|
|
460
541
|
)}
|
|
461
542
|
|
|
462
543
|
{/* Metadata */}
|
|
463
544
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
464
545
|
<div>
|
|
465
|
-
<span className="text-[
|
|
546
|
+
<span className="text-[var(--color-text-muted)]">Type</span>
|
|
466
547
|
<p className="capitalize">{selectedTask.type}</p>
|
|
467
548
|
</div>
|
|
468
549
|
<div>
|
|
469
|
-
<span className="text-[
|
|
550
|
+
<span className="text-[var(--color-text-muted)]">Priority</span>
|
|
470
551
|
<p>{selectedTask.priority}</p>
|
|
471
552
|
</div>
|
|
472
553
|
{selectedTask.recurrence && (
|
|
473
554
|
<div>
|
|
474
|
-
<span className="text-[
|
|
555
|
+
<span className="text-[var(--color-text-muted)]">Recurrence</span>
|
|
475
556
|
<p>{formatCron(selectedTask.recurrence)}</p>
|
|
476
|
-
<p className="text-xs text-[
|
|
557
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-0.5 font-mono">{selectedTask.recurrence}</p>
|
|
477
558
|
</div>
|
|
478
559
|
)}
|
|
479
560
|
</div>
|
|
@@ -481,31 +562,31 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
481
562
|
{/* Timestamps */}
|
|
482
563
|
<div className="space-y-2 text-sm">
|
|
483
564
|
<div className="flex justify-between">
|
|
484
|
-
<span className="text-[
|
|
565
|
+
<span className="text-[var(--color-text-muted)]">Created</span>
|
|
485
566
|
<span>{new Date(selectedTask.created_at).toLocaleString()}</span>
|
|
486
567
|
</div>
|
|
487
568
|
{selectedTask.execute_at && (
|
|
488
569
|
<div className="flex justify-between">
|
|
489
|
-
<span className="text-[
|
|
490
|
-
<span className="text-[
|
|
570
|
+
<span className="text-[var(--color-text-muted)]">Scheduled</span>
|
|
571
|
+
<span className="text-[var(--color-accent)]">{formatRelativeTime(selectedTask.execute_at)}</span>
|
|
491
572
|
</div>
|
|
492
573
|
)}
|
|
493
574
|
{selectedTask.executed_at && (
|
|
494
575
|
<div className="flex justify-between">
|
|
495
|
-
<span className="text-[
|
|
576
|
+
<span className="text-[var(--color-text-muted)]">Started</span>
|
|
496
577
|
<span>{new Date(selectedTask.executed_at).toLocaleString()}</span>
|
|
497
578
|
</div>
|
|
498
579
|
)}
|
|
499
580
|
{selectedTask.completed_at && (
|
|
500
581
|
<div className="flex justify-between">
|
|
501
|
-
<span className="text-[
|
|
582
|
+
<span className="text-[var(--color-text-muted)]">Completed</span>
|
|
502
583
|
<span>{new Date(selectedTask.completed_at).toLocaleString()}</span>
|
|
503
584
|
</div>
|
|
504
585
|
)}
|
|
505
586
|
{selectedTask.next_run && (
|
|
506
587
|
<div className="flex justify-between">
|
|
507
|
-
<span className="text-[
|
|
508
|
-
<span className="text-[
|
|
588
|
+
<span className="text-[var(--color-text-muted)]">Next Run</span>
|
|
589
|
+
<span className="text-[var(--color-accent)]">{formatRelativeTime(selectedTask.next_run)}</span>
|
|
509
590
|
</div>
|
|
510
591
|
)}
|
|
511
592
|
</div>
|
|
@@ -535,13 +616,13 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
535
616
|
{/* Trajectory */}
|
|
536
617
|
{loadingTask && !selectedTask.trajectory && (
|
|
537
618
|
<div>
|
|
538
|
-
<h4 className="text-xs text-[
|
|
539
|
-
<div className="text-sm text-[
|
|
619
|
+
<h4 className="text-xs text-[var(--color-text-muted)] uppercase tracking-wider mb-2">Trajectory</h4>
|
|
620
|
+
<div className="text-sm text-[var(--color-text-faint)]">Loading trajectory...</div>
|
|
540
621
|
</div>
|
|
541
622
|
)}
|
|
542
623
|
{selectedTask.trajectory && selectedTask.trajectory.length > 0 && (
|
|
543
624
|
<div>
|
|
544
|
-
<h4 className="text-xs text-[
|
|
625
|
+
<h4 className="text-xs text-[var(--color-text-muted)] uppercase tracking-wider mb-2">
|
|
545
626
|
Trajectory ({selectedTask.trajectory.length} steps)
|
|
546
627
|
</h4>
|
|
547
628
|
<TrajectoryView trajectory={selectedTask.trajectory} />
|
|
@@ -554,28 +635,45 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
554
635
|
|
|
555
636
|
return (
|
|
556
637
|
<div className="flex-1 overflow-auto p-4">
|
|
557
|
-
{/* Filter tabs */}
|
|
558
|
-
<div className="flex
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
638
|
+
{/* Create Task Button + Filter tabs */}
|
|
639
|
+
<div className="flex items-center justify-between mb-4">
|
|
640
|
+
<div className="flex gap-2">
|
|
641
|
+
{filterOptions.map(opt => (
|
|
642
|
+
<button
|
|
643
|
+
key={opt.value}
|
|
644
|
+
onClick={() => setFilter(opt.value)}
|
|
563
645
|
className={`px-3 py-1.5 rounded text-sm transition ${
|
|
564
646
|
filter === opt.value
|
|
565
|
-
? "bg-[
|
|
566
|
-
: "bg-[
|
|
647
|
+
? "bg-[var(--color-accent)] text-black"
|
|
648
|
+
: "bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)]"
|
|
567
649
|
}`}
|
|
568
650
|
>
|
|
569
651
|
{opt.label}
|
|
570
652
|
</button>
|
|
571
653
|
))}
|
|
654
|
+
</div>
|
|
655
|
+
<button
|
|
656
|
+
onClick={() => setShowCreateForm(!showCreateForm)}
|
|
657
|
+
className="px-3 py-1.5 rounded text-sm bg-[var(--color-accent)] text-black hover:opacity-90 transition flex items-center gap-1 flex-shrink-0"
|
|
658
|
+
>
|
|
659
|
+
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
|
|
660
|
+
New
|
|
661
|
+
</button>
|
|
572
662
|
</div>
|
|
573
663
|
|
|
664
|
+
{/* Inline Create Form */}
|
|
665
|
+
{showCreateForm && (
|
|
666
|
+
<AgentCreateTaskForm
|
|
667
|
+
onSubmit={handleCreateTask}
|
|
668
|
+
onCancel={() => setShowCreateForm(false)}
|
|
669
|
+
/>
|
|
670
|
+
)}
|
|
671
|
+
|
|
574
672
|
{tasks.length === 0 ? (
|
|
575
673
|
<div className="text-center py-10">
|
|
576
|
-
<TasksIcon className="w-10 h-10 mx-auto mb-3 text-[
|
|
577
|
-
<p className="text-[
|
|
578
|
-
<p className="text-sm text-[
|
|
674
|
+
<TasksIcon className="w-10 h-10 mx-auto mb-3 text-[var(--color-border-light)]" />
|
|
675
|
+
<p className="text-[var(--color-text-muted)]">No {filter === "all" ? "" : filter + " "}tasks</p>
|
|
676
|
+
<p className="text-sm text-[var(--color-text-faint)] mt-1">Tasks will appear here when created</p>
|
|
579
677
|
</div>
|
|
580
678
|
) : (
|
|
581
679
|
<div className="space-y-3">
|
|
@@ -583,7 +681,7 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
583
681
|
<div
|
|
584
682
|
key={task.id}
|
|
585
683
|
onClick={() => selectTask(task)}
|
|
586
|
-
className="bg-[
|
|
684
|
+
className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 cursor-pointer hover:border-[var(--color-border-light)] transition"
|
|
587
685
|
>
|
|
588
686
|
<div className="flex items-start justify-between mb-2">
|
|
589
687
|
<div className="flex-1 min-w-0">
|
|
@@ -595,10 +693,10 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
595
693
|
</div>
|
|
596
694
|
|
|
597
695
|
{task.description && (
|
|
598
|
-
<p className="text-sm text-[
|
|
696
|
+
<p className="text-sm text-[var(--color-text-secondary)] mb-2 line-clamp-2">{task.description}</p>
|
|
599
697
|
)}
|
|
600
698
|
|
|
601
|
-
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-[
|
|
699
|
+
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-[var(--color-text-faint)]">
|
|
602
700
|
<span className="flex items-center gap-1">
|
|
603
701
|
{task.type === "recurring"
|
|
604
702
|
? <RecurringIcon className="w-3.5 h-3.5" />
|
|
@@ -612,10 +710,10 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
612
710
|
<span>Priority: {task.priority}</span>
|
|
613
711
|
)}
|
|
614
712
|
{task.next_run && (
|
|
615
|
-
<span className="text-[
|
|
713
|
+
<span className="text-[var(--color-accent)]">{formatRelativeTime(task.next_run)}</span>
|
|
616
714
|
)}
|
|
617
715
|
{!task.next_run && task.execute_at && (
|
|
618
|
-
<span className="text-[
|
|
716
|
+
<span className="text-[var(--color-accent)]">{formatRelativeTime(task.execute_at)}</span>
|
|
619
717
|
)}
|
|
620
718
|
<span>Created: {new Date(task.created_at).toLocaleDateString()}</span>
|
|
621
719
|
</div>
|
|
@@ -643,6 +741,82 @@ function TasksTab({ agent }: { agent: Agent }) {
|
|
|
643
741
|
);
|
|
644
742
|
}
|
|
645
743
|
|
|
744
|
+
function AgentCreateTaskForm({ onSubmit, onCancel }: {
|
|
745
|
+
onSubmit: (data: { title: string; description: string; type: string; priority: number; execute_at?: string; recurrence?: string }) => void;
|
|
746
|
+
onCancel: () => void;
|
|
747
|
+
}) {
|
|
748
|
+
const [title, setTitle] = useState("");
|
|
749
|
+
const [description, setDescription] = useState("");
|
|
750
|
+
const [type, setType] = useState("once");
|
|
751
|
+
const [priority, setPriority] = useState(5);
|
|
752
|
+
const [executeAt, setExecuteAt] = useState("");
|
|
753
|
+
const [recurrence, setRecurrence] = useState("");
|
|
754
|
+
|
|
755
|
+
return (
|
|
756
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-accent)]/30 rounded-lg p-3 mb-4 space-y-3">
|
|
757
|
+
<input
|
|
758
|
+
type="text"
|
|
759
|
+
value={title}
|
|
760
|
+
onChange={e => setTitle(e.target.value)}
|
|
761
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border)] rounded px-3 py-1.5 text-sm"
|
|
762
|
+
placeholder="Task title..."
|
|
763
|
+
autoFocus
|
|
764
|
+
/>
|
|
765
|
+
<textarea
|
|
766
|
+
value={description}
|
|
767
|
+
onChange={e => setDescription(e.target.value)}
|
|
768
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border)] rounded px-3 py-1.5 text-sm resize-none"
|
|
769
|
+
rows={2}
|
|
770
|
+
placeholder="Description (optional)..."
|
|
771
|
+
/>
|
|
772
|
+
<div className="grid grid-cols-2 gap-2">
|
|
773
|
+
<select
|
|
774
|
+
value={type}
|
|
775
|
+
onChange={e => setType(e.target.value)}
|
|
776
|
+
className="bg-[var(--color-bg)] border border-[var(--color-border)] rounded px-2 py-1.5 text-sm"
|
|
777
|
+
>
|
|
778
|
+
<option value="once">One-time</option>
|
|
779
|
+
<option value="recurring">Recurring</option>
|
|
780
|
+
</select>
|
|
781
|
+
<input
|
|
782
|
+
type="number"
|
|
783
|
+
min={1}
|
|
784
|
+
max={10}
|
|
785
|
+
value={priority}
|
|
786
|
+
onChange={e => setPriority(Number(e.target.value))}
|
|
787
|
+
className="bg-[var(--color-bg)] border border-[var(--color-border)] rounded px-2 py-1.5 text-sm"
|
|
788
|
+
placeholder="Priority"
|
|
789
|
+
/>
|
|
790
|
+
</div>
|
|
791
|
+
{type === "once" && (
|
|
792
|
+
<input
|
|
793
|
+
type="datetime-local"
|
|
794
|
+
value={executeAt}
|
|
795
|
+
onChange={e => setExecuteAt(e.target.value)}
|
|
796
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border)] rounded px-3 py-1.5 text-sm"
|
|
797
|
+
/>
|
|
798
|
+
)}
|
|
799
|
+
{type === "recurring" && (
|
|
800
|
+
<input
|
|
801
|
+
type="text"
|
|
802
|
+
value={recurrence}
|
|
803
|
+
onChange={e => setRecurrence(e.target.value)}
|
|
804
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border)] rounded px-3 py-1.5 text-sm font-mono"
|
|
805
|
+
placeholder="*/30 * * * * (cron)"
|
|
806
|
+
/>
|
|
807
|
+
)}
|
|
808
|
+
<div className="flex justify-end gap-2">
|
|
809
|
+
<button onClick={onCancel} className="px-3 py-1.5 rounded text-sm bg-[var(--color-surface-raised)] hover:bg-[var(--color-border)] transition">Cancel</button>
|
|
810
|
+
<button
|
|
811
|
+
onClick={() => title.trim() && onSubmit({ title: title.trim(), description: description.trim(), type, priority, execute_at: executeAt || undefined, recurrence: recurrence || undefined })}
|
|
812
|
+
disabled={!title.trim()}
|
|
813
|
+
className="px-3 py-1.5 rounded text-sm bg-[var(--color-accent)] text-black hover:opacity-90 transition disabled:opacity-50"
|
|
814
|
+
>Create</button>
|
|
815
|
+
</div>
|
|
816
|
+
</div>
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
|
|
646
820
|
interface Memory {
|
|
647
821
|
id: string;
|
|
648
822
|
content: string;
|
|
@@ -712,7 +886,7 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
712
886
|
|
|
713
887
|
if (!agent.features?.memory) {
|
|
714
888
|
return (
|
|
715
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
889
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
716
890
|
<div className="text-center">
|
|
717
891
|
<p className="mb-2">Memory feature is not enabled</p>
|
|
718
892
|
<p className="text-sm">Enable it in Settings to persist knowledge</p>
|
|
@@ -723,7 +897,7 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
723
897
|
|
|
724
898
|
if (agent.status !== "running") {
|
|
725
899
|
return (
|
|
726
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
900
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
727
901
|
<p>Start the agent to view memories</p>
|
|
728
902
|
</div>
|
|
729
903
|
);
|
|
@@ -731,7 +905,7 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
731
905
|
|
|
732
906
|
if (loading) {
|
|
733
907
|
return (
|
|
734
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
908
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
735
909
|
<p>Loading memories...</p>
|
|
736
910
|
</div>
|
|
737
911
|
);
|
|
@@ -747,7 +921,7 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
747
921
|
|
|
748
922
|
if (!enabled) {
|
|
749
923
|
return (
|
|
750
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
924
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
751
925
|
<div className="text-center">
|
|
752
926
|
<p className="mb-2">Memory system not initialized</p>
|
|
753
927
|
<p className="text-sm">Check OPENAI_API_KEY for embeddings</p>
|
|
@@ -761,7 +935,7 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
761
935
|
{ConfirmDialog}
|
|
762
936
|
<div className="flex-1 overflow-auto p-4">
|
|
763
937
|
<div className="flex items-center justify-between mb-4">
|
|
764
|
-
<h3 className="text-sm font-medium text-[
|
|
938
|
+
<h3 className="text-sm font-medium text-[var(--color-text-secondary)]">Stored Memories ({memories.length})</h3>
|
|
765
939
|
{memories.length > 0 && (
|
|
766
940
|
<button
|
|
767
941
|
onClick={clearAllMemories}
|
|
@@ -773,19 +947,19 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
773
947
|
</div>
|
|
774
948
|
|
|
775
949
|
{memories.length === 0 ? (
|
|
776
|
-
<div className="text-center py-10 text-[
|
|
950
|
+
<div className="text-center py-10 text-[var(--color-text-muted)]">
|
|
777
951
|
<p>No memories stored yet</p>
|
|
778
952
|
<p className="text-sm mt-1">The agent will remember important information from conversations</p>
|
|
779
953
|
</div>
|
|
780
954
|
) : (
|
|
781
955
|
<div className="space-y-3">
|
|
782
956
|
{memories.map(memory => (
|
|
783
|
-
<div key={memory.id} className="bg-[
|
|
957
|
+
<div key={memory.id} className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded p-3">
|
|
784
958
|
<div className="flex items-start justify-between gap-2">
|
|
785
|
-
<p className="text-sm text-[
|
|
959
|
+
<p className="text-sm text-[var(--color-text)] flex-1">{memory.content}</p>
|
|
786
960
|
<button
|
|
787
961
|
onClick={() => deleteMemory(memory.id)}
|
|
788
|
-
className="text-[
|
|
962
|
+
className="text-[var(--color-text-muted)] hover:text-red-400 text-sm flex-shrink-0"
|
|
789
963
|
>
|
|
790
964
|
×
|
|
791
965
|
</button>
|
|
@@ -800,11 +974,11 @@ function MemoryTab({ agent }: { agent: Agent }) {
|
|
|
800
974
|
}`}>
|
|
801
975
|
{memory.type}
|
|
802
976
|
</span>
|
|
803
|
-
<span className="text-xs text-[
|
|
977
|
+
<span className="text-xs text-[var(--color-text-muted)]">
|
|
804
978
|
{new Date(memory.created_at).toLocaleString()}
|
|
805
979
|
</span>
|
|
806
980
|
{memory.importance && (
|
|
807
|
-
<span className="text-xs text-[
|
|
981
|
+
<span className="text-xs text-[var(--color-text-faint)]">
|
|
808
982
|
importance: {memory.importance.toFixed(1)}
|
|
809
983
|
</span>
|
|
810
984
|
)}
|
|
@@ -961,7 +1135,7 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
961
1135
|
|
|
962
1136
|
if (!agent.features?.files) {
|
|
963
1137
|
return (
|
|
964
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
1138
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
965
1139
|
<div className="text-center">
|
|
966
1140
|
<p className="mb-2">Files feature is not enabled</p>
|
|
967
1141
|
<p className="text-sm">Enable it in Settings to manage files</p>
|
|
@@ -972,7 +1146,7 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
972
1146
|
|
|
973
1147
|
if (agent.status !== "running") {
|
|
974
1148
|
return (
|
|
975
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
1149
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
976
1150
|
<p>Start the agent to view files</p>
|
|
977
1151
|
</div>
|
|
978
1152
|
);
|
|
@@ -980,7 +1154,7 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
980
1154
|
|
|
981
1155
|
if (loading) {
|
|
982
1156
|
return (
|
|
983
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
1157
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">
|
|
984
1158
|
<p>Loading files...</p>
|
|
985
1159
|
</div>
|
|
986
1160
|
);
|
|
@@ -998,7 +1172,7 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
998
1172
|
<>
|
|
999
1173
|
{ConfirmDialog}
|
|
1000
1174
|
<div
|
|
1001
|
-
className={`flex-1 overflow-auto p-4 transition ${dragOver ? "bg-[
|
|
1175
|
+
className={`flex-1 overflow-auto p-4 transition ${dragOver ? "bg-[var(--color-accent-5)]" : ""}`}
|
|
1002
1176
|
onDrop={handleDrop}
|
|
1003
1177
|
onDragOver={handleDragOver}
|
|
1004
1178
|
onDragLeave={handleDragLeave}
|
|
@@ -1012,18 +1186,18 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
1012
1186
|
/>
|
|
1013
1187
|
|
|
1014
1188
|
<div className="flex items-center justify-between mb-4">
|
|
1015
|
-
<h3 className="text-sm font-medium text-[
|
|
1189
|
+
<h3 className="text-sm font-medium text-[var(--color-text-secondary)]">Agent Files ({files.length})</h3>
|
|
1016
1190
|
<div className="flex items-center gap-2">
|
|
1017
1191
|
<button
|
|
1018
1192
|
onClick={() => fileInputRef.current?.click()}
|
|
1019
1193
|
disabled={uploading}
|
|
1020
|
-
className="text-xs bg-[
|
|
1194
|
+
className="text-xs bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black px-3 py-1 rounded font-medium transition"
|
|
1021
1195
|
>
|
|
1022
1196
|
{uploading ? "Uploading..." : "Upload"}
|
|
1023
1197
|
</button>
|
|
1024
1198
|
<button
|
|
1025
1199
|
onClick={fetchFiles}
|
|
1026
|
-
className="text-xs text-[
|
|
1200
|
+
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
|
|
1027
1201
|
>
|
|
1028
1202
|
Refresh
|
|
1029
1203
|
</button>
|
|
@@ -1037,17 +1211,17 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
1037
1211
|
)}
|
|
1038
1212
|
|
|
1039
1213
|
{dragOver && (
|
|
1040
|
-
<div className="mb-4 border-2 border-dashed border-[
|
|
1041
|
-
<p className="text-[
|
|
1214
|
+
<div className="mb-4 border-2 border-dashed border-[var(--color-accent)] rounded-lg p-8 text-center">
|
|
1215
|
+
<p className="text-[var(--color-accent)]">Drop file to upload</p>
|
|
1042
1216
|
</div>
|
|
1043
1217
|
)}
|
|
1044
1218
|
|
|
1045
1219
|
{files.length === 0 && !dragOver && (
|
|
1046
|
-
<div className="text-center py-10 text-[
|
|
1220
|
+
<div className="text-center py-10 text-[var(--color-text-muted)]">
|
|
1047
1221
|
<p>No files stored yet</p>
|
|
1048
1222
|
<p className="text-sm mt-1">Drop files here, click Upload, or attach files in Chat</p>
|
|
1049
1223
|
{agent.features?.memory && (
|
|
1050
|
-
<p className="text-xs mt-2 text-[
|
|
1224
|
+
<p className="text-xs mt-2 text-[var(--color-text-faint)]">Files will be auto-ingested into memory</p>
|
|
1051
1225
|
)}
|
|
1052
1226
|
</div>
|
|
1053
1227
|
)}
|
|
@@ -1055,13 +1229,13 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
1055
1229
|
{files.length > 0 && (
|
|
1056
1230
|
<div className="space-y-2">
|
|
1057
1231
|
{files.map(file => (
|
|
1058
|
-
<div key={file.id} className="bg-[
|
|
1059
|
-
<div className="w-10 h-10 bg-[
|
|
1232
|
+
<div key={file.id} className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded p-3 flex items-center gap-3">
|
|
1233
|
+
<div className="w-10 h-10 bg-[var(--color-surface-raised)] rounded flex items-center justify-center text-[var(--color-text-muted)]">
|
|
1060
1234
|
{getFileIcon(file.mime_type)}
|
|
1061
1235
|
</div>
|
|
1062
1236
|
<div className="flex-1 min-w-0">
|
|
1063
|
-
<p className="text-sm text-[
|
|
1064
|
-
<p className="text-xs text-[
|
|
1237
|
+
<p className="text-sm text-[var(--color-text)] truncate">{file.filename}</p>
|
|
1238
|
+
<p className="text-xs text-[var(--color-text-muted)]">
|
|
1065
1239
|
{formatSize(file.size_bytes)} • {new Date(file.created_at).toLocaleString()}
|
|
1066
1240
|
{file.source && file.source !== "upload" && ` • ${file.source}`}
|
|
1067
1241
|
</p>
|
|
@@ -1069,13 +1243,13 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
1069
1243
|
<div className="flex items-center gap-2">
|
|
1070
1244
|
<button
|
|
1071
1245
|
onClick={() => downloadFile(file.id, file.filename)}
|
|
1072
|
-
className="text-xs text-[
|
|
1246
|
+
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-accent)] px-2 py-1"
|
|
1073
1247
|
>
|
|
1074
1248
|
↓
|
|
1075
1249
|
</button>
|
|
1076
1250
|
<button
|
|
1077
1251
|
onClick={() => deleteFile(file.id)}
|
|
1078
|
-
className="text-[
|
|
1252
|
+
className="text-[var(--color-text-muted)] hover:text-red-400 text-sm"
|
|
1079
1253
|
>
|
|
1080
1254
|
×
|
|
1081
1255
|
</button>
|
|
@@ -1126,6 +1300,8 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1126
1300
|
const [apiKeyFull, setApiKeyFull] = useState<string | null>(null);
|
|
1127
1301
|
const [showApiKey, setShowApiKey] = useState(false);
|
|
1128
1302
|
const [subscriptions, setSubscriptions] = useState<{ id: string; trigger_slug: string; enabled: boolean }[]>([]);
|
|
1303
|
+
const [shareToken, setShareToken] = useState<string | null>(null);
|
|
1304
|
+
const [shareCopied, setShareCopied] = useState(false);
|
|
1129
1305
|
|
|
1130
1306
|
// Fetch subscriptions for this agent
|
|
1131
1307
|
useEffect(() => {
|
|
@@ -1166,6 +1342,20 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1166
1342
|
fetchApiKey();
|
|
1167
1343
|
}, [agent.id, authFetch]);
|
|
1168
1344
|
|
|
1345
|
+
// Fetch share token
|
|
1346
|
+
useEffect(() => {
|
|
1347
|
+
const fetchShareToken = async () => {
|
|
1348
|
+
try {
|
|
1349
|
+
const res = await authFetch(`/api/agents/${agent.id}/share-token`);
|
|
1350
|
+
if (res.ok) {
|
|
1351
|
+
const data = await res.json();
|
|
1352
|
+
setShareToken(data.token || null);
|
|
1353
|
+
}
|
|
1354
|
+
} catch {}
|
|
1355
|
+
};
|
|
1356
|
+
fetchShareToken();
|
|
1357
|
+
}, [agent.id, authFetch]);
|
|
1358
|
+
|
|
1169
1359
|
// Fetch available skills
|
|
1170
1360
|
useEffect(() => {
|
|
1171
1361
|
const fetchSkills = async () => {
|
|
@@ -1229,7 +1419,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1229
1419
|
...prev,
|
|
1230
1420
|
features: {
|
|
1231
1421
|
...prev.features,
|
|
1232
|
-
agents: { enabled: true,
|
|
1422
|
+
agents: { enabled: true, group: agent.projectId || undefined },
|
|
1233
1423
|
},
|
|
1234
1424
|
};
|
|
1235
1425
|
}
|
|
@@ -1258,20 +1448,6 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1258
1448
|
}
|
|
1259
1449
|
};
|
|
1260
1450
|
|
|
1261
|
-
// Set multi-agent mode
|
|
1262
|
-
const setAgentMode = (mode: AgentMode) => {
|
|
1263
|
-
setForm(prev => {
|
|
1264
|
-
const currentConfig = getMultiAgentConfig(prev.features, agent.projectId);
|
|
1265
|
-
return {
|
|
1266
|
-
...prev,
|
|
1267
|
-
features: {
|
|
1268
|
-
...prev.features,
|
|
1269
|
-
agents: { ...currentConfig, enabled: true, mode },
|
|
1270
|
-
},
|
|
1271
|
-
};
|
|
1272
|
-
});
|
|
1273
|
-
};
|
|
1274
|
-
|
|
1275
1451
|
// Helper to check if agents feature is enabled
|
|
1276
1452
|
const isAgentsEnabled = () => {
|
|
1277
1453
|
const agentsVal = form.features.agents;
|
|
@@ -1279,12 +1455,6 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1279
1455
|
return (agentsVal as MultiAgentConfig)?.enabled ?? false;
|
|
1280
1456
|
};
|
|
1281
1457
|
|
|
1282
|
-
// Get current agent mode
|
|
1283
|
-
const getAgentMode = (): AgentMode => {
|
|
1284
|
-
const config = getMultiAgentConfig(form.features, agent.projectId);
|
|
1285
|
-
return config.mode || "worker";
|
|
1286
|
-
};
|
|
1287
|
-
|
|
1288
1458
|
// Helper to check if operator feature is enabled
|
|
1289
1459
|
const isOperatorEnabled = () => {
|
|
1290
1460
|
return getOperatorConfig(form.features).enabled;
|
|
@@ -1360,7 +1530,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1360
1530
|
type="text"
|
|
1361
1531
|
value={form.name}
|
|
1362
1532
|
onChange={(e) => setForm(prev => ({ ...prev, name: e.target.value }))}
|
|
1363
|
-
className="w-full bg-[
|
|
1533
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)] text-[var(--color-text)]"
|
|
1364
1534
|
/>
|
|
1365
1535
|
</FormField>
|
|
1366
1536
|
|
|
@@ -1384,7 +1554,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1384
1554
|
<textarea
|
|
1385
1555
|
value={form.systemPrompt}
|
|
1386
1556
|
onChange={(e) => setForm(prev => ({ ...prev, systemPrompt: e.target.value }))}
|
|
1387
|
-
className="w-full bg-[
|
|
1557
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 h-24 resize-none focus:outline-none focus:border-[var(--color-accent)] text-[var(--color-text)]"
|
|
1388
1558
|
/>
|
|
1389
1559
|
</FormField>
|
|
1390
1560
|
|
|
@@ -1402,16 +1572,16 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1402
1572
|
onClick={() => toggleFeature(key)}
|
|
1403
1573
|
className={`flex items-center gap-3 p-3 rounded border text-left transition ${
|
|
1404
1574
|
isEnabled
|
|
1405
|
-
? "border-[
|
|
1406
|
-
: "border-[
|
|
1575
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)]"
|
|
1576
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)]"
|
|
1407
1577
|
}`}
|
|
1408
1578
|
>
|
|
1409
|
-
<Icon className={`w-5 h-5 flex-shrink-0 ${isEnabled ? "text-[
|
|
1579
|
+
<Icon className={`w-5 h-5 flex-shrink-0 ${isEnabled ? "text-[var(--color-accent)]" : "text-[var(--color-text-muted)]"}`} />
|
|
1410
1580
|
<div className="flex-1 min-w-0">
|
|
1411
|
-
<div className={`text-sm font-medium ${isEnabled ? "text-[
|
|
1581
|
+
<div className={`text-sm font-medium ${isEnabled ? "text-[var(--color-accent)]" : ""}`}>
|
|
1412
1582
|
{label}
|
|
1413
1583
|
</div>
|
|
1414
|
-
<div className="text-xs text-[
|
|
1584
|
+
<div className="text-xs text-[var(--color-text-muted)]">{description}</div>
|
|
1415
1585
|
</div>
|
|
1416
1586
|
</button>
|
|
1417
1587
|
);
|
|
@@ -1419,47 +1589,6 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1419
1589
|
</div>
|
|
1420
1590
|
</FormField>
|
|
1421
1591
|
|
|
1422
|
-
{/* Multi-Agent Mode Selection - shown when agents is enabled */}
|
|
1423
|
-
{isAgentsEnabled() && (
|
|
1424
|
-
<FormField label="Multi-Agent Mode">
|
|
1425
|
-
<div className="flex gap-2">
|
|
1426
|
-
<button
|
|
1427
|
-
type="button"
|
|
1428
|
-
onClick={() => setAgentMode("coordinator")}
|
|
1429
|
-
className={`flex-1 p-3 rounded border text-left transition ${
|
|
1430
|
-
getAgentMode() === "coordinator"
|
|
1431
|
-
? "border-[#f97316] bg-[#f97316]/10"
|
|
1432
|
-
: "border-[#222] hover:border-[#333]"
|
|
1433
|
-
}`}
|
|
1434
|
-
>
|
|
1435
|
-
<div className={`text-sm font-medium ${getAgentMode() === "coordinator" ? "text-[#f97316]" : ""}`}>
|
|
1436
|
-
Coordinator
|
|
1437
|
-
</div>
|
|
1438
|
-
<div className="text-xs text-[#666]">Orchestrates and delegates to other agents</div>
|
|
1439
|
-
</button>
|
|
1440
|
-
<button
|
|
1441
|
-
type="button"
|
|
1442
|
-
onClick={() => setAgentMode("worker")}
|
|
1443
|
-
className={`flex-1 p-3 rounded border text-left transition ${
|
|
1444
|
-
getAgentMode() === "worker"
|
|
1445
|
-
? "border-[#f97316] bg-[#f97316]/10"
|
|
1446
|
-
: "border-[#222] hover:border-[#333]"
|
|
1447
|
-
}`}
|
|
1448
|
-
>
|
|
1449
|
-
<div className={`text-sm font-medium ${getAgentMode() === "worker" ? "text-[#f97316]" : ""}`}>
|
|
1450
|
-
Worker
|
|
1451
|
-
</div>
|
|
1452
|
-
<div className="text-xs text-[#666]">Receives tasks from coordinators</div>
|
|
1453
|
-
</button>
|
|
1454
|
-
</div>
|
|
1455
|
-
{agent.projectId && (
|
|
1456
|
-
<p className="text-xs text-[#555] mt-2">
|
|
1457
|
-
Group: Using project as agent group
|
|
1458
|
-
</p>
|
|
1459
|
-
)}
|
|
1460
|
-
</FormField>
|
|
1461
|
-
)}
|
|
1462
|
-
|
|
1463
1592
|
{/* Operator Browser Provider - shown when operator is enabled */}
|
|
1464
1593
|
{isOperatorEnabled() && (
|
|
1465
1594
|
<FormField label="Browser Provider">
|
|
@@ -1476,7 +1605,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1476
1605
|
onChange={(value) => setOperatorBrowserProvider(value)}
|
|
1477
1606
|
/>
|
|
1478
1607
|
) : (
|
|
1479
|
-
<p className="text-sm text-[
|
|
1608
|
+
<p className="text-sm text-[var(--color-text-muted)] p-3 border border-[var(--color-border-light)] rounded bg-[var(--color-bg)]">
|
|
1480
1609
|
No browser providers configured. Go to Settings → Providers to add one.
|
|
1481
1610
|
</p>
|
|
1482
1611
|
)}
|
|
@@ -1501,8 +1630,8 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1501
1630
|
}))}
|
|
1502
1631
|
className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
|
|
1503
1632
|
form.features.builtinTools?.webSearch
|
|
1504
|
-
? "border-[
|
|
1505
|
-
: "border-[
|
|
1633
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)] text-[var(--color-accent)]"
|
|
1634
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)] text-[var(--color-text-secondary)]"
|
|
1506
1635
|
}`}
|
|
1507
1636
|
>
|
|
1508
1637
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -1524,8 +1653,8 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1524
1653
|
}))}
|
|
1525
1654
|
className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
|
|
1526
1655
|
form.features.builtinTools?.webFetch
|
|
1527
|
-
? "border-[
|
|
1528
|
-
: "border-[
|
|
1656
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)] text-[var(--color-accent)]"
|
|
1657
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)] text-[var(--color-text-secondary)]"
|
|
1529
1658
|
}`}
|
|
1530
1659
|
>
|
|
1531
1660
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -1534,7 +1663,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1534
1663
|
<span className="text-sm">Web Fetch</span>
|
|
1535
1664
|
</button>
|
|
1536
1665
|
</div>
|
|
1537
|
-
<p className="text-xs text-[
|
|
1666
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-2">
|
|
1538
1667
|
Provider-native tools for real-time web access
|
|
1539
1668
|
</p>
|
|
1540
1669
|
</FormField>
|
|
@@ -1544,7 +1673,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1544
1673
|
{form.features.mcp && (
|
|
1545
1674
|
<FormField label="MCP Servers">
|
|
1546
1675
|
{availableMcpServers.length === 0 ? (
|
|
1547
|
-
<p className="text-sm text-[
|
|
1676
|
+
<p className="text-sm text-[var(--color-text-muted)]">
|
|
1548
1677
|
No MCP servers configured. Add servers in the MCP page first.
|
|
1549
1678
|
</p>
|
|
1550
1679
|
) : (
|
|
@@ -1564,35 +1693,35 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1564
1693
|
onClick={() => toggleMcpServer(server.id)}
|
|
1565
1694
|
className={`w-full flex items-center gap-3 p-3 rounded border text-left transition ${
|
|
1566
1695
|
form.mcpServers.includes(server.id)
|
|
1567
|
-
? "border-[
|
|
1568
|
-
: "border-[
|
|
1696
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)]"
|
|
1697
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)]"
|
|
1569
1698
|
}`}
|
|
1570
1699
|
>
|
|
1571
1700
|
<div className={`w-2 h-2 rounded-full flex-shrink-0 ${
|
|
1572
|
-
isAvailable ? "bg-green-400" : "bg-[
|
|
1701
|
+
isAvailable ? "bg-green-400" : "bg-[var(--color-scrollbar)]"
|
|
1573
1702
|
}`} />
|
|
1574
1703
|
<div className="flex-1 min-w-0">
|
|
1575
1704
|
<div className="flex items-center gap-2">
|
|
1576
|
-
<span className={`text-sm font-medium ${form.mcpServers.includes(server.id) ? "text-[
|
|
1705
|
+
<span className={`text-sm font-medium ${form.mcpServers.includes(server.id) ? "text-[var(--color-accent)]" : ""}`}>
|
|
1577
1706
|
{server.name}
|
|
1578
1707
|
</span>
|
|
1579
1708
|
{server.project_id === null && (
|
|
1580
|
-
<span className="text-[10px] text-[
|
|
1709
|
+
<span className="text-[10px] text-[var(--color-text-muted)] bg-[var(--color-surface-raised)] px-1.5 py-0.5 rounded">Global</span>
|
|
1581
1710
|
)}
|
|
1582
1711
|
</div>
|
|
1583
|
-
<div className="text-xs text-[
|
|
1712
|
+
<div className="text-xs text-[var(--color-text-muted)]">{serverInfo}</div>
|
|
1584
1713
|
</div>
|
|
1585
1714
|
<div className={`text-xs px-2 py-0.5 rounded ${
|
|
1586
1715
|
isAvailable
|
|
1587
1716
|
? "bg-green-500/20 text-green-400"
|
|
1588
|
-
: "bg-[
|
|
1717
|
+
: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
|
|
1589
1718
|
}`}>
|
|
1590
1719
|
{isRemote ? "remote" : server.status}
|
|
1591
1720
|
</div>
|
|
1592
1721
|
</button>
|
|
1593
1722
|
);
|
|
1594
1723
|
})}
|
|
1595
|
-
<p className="text-xs text-[
|
|
1724
|
+
<p className="text-xs text-[var(--color-text-muted)] mt-2">
|
|
1596
1725
|
Remote servers are always available. Local servers must be running.
|
|
1597
1726
|
</p>
|
|
1598
1727
|
</div>
|
|
@@ -1603,7 +1732,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1603
1732
|
{/* Skills Selection */}
|
|
1604
1733
|
<FormField label="Skills">
|
|
1605
1734
|
{availableSkills.length === 0 ? (
|
|
1606
|
-
<p className="text-sm text-[
|
|
1735
|
+
<p className="text-sm text-[var(--color-text-muted)]">
|
|
1607
1736
|
No skills configured. Add skills in the Skills page first.
|
|
1608
1737
|
</p>
|
|
1609
1738
|
) : (
|
|
@@ -1617,27 +1746,27 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1617
1746
|
onClick={() => toggleSkill(skill.id)}
|
|
1618
1747
|
className={`w-full flex items-center gap-3 p-3 rounded border text-left transition ${
|
|
1619
1748
|
form.skills.includes(skill.id)
|
|
1620
|
-
? "border-[
|
|
1621
|
-
: "border-[
|
|
1749
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)]"
|
|
1750
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)]"
|
|
1622
1751
|
}`}
|
|
1623
1752
|
>
|
|
1624
1753
|
<div className="flex-1 min-w-0">
|
|
1625
1754
|
<div className="flex items-center gap-2">
|
|
1626
|
-
<span className={`text-sm font-medium ${form.skills.includes(skill.id) ? "text-[
|
|
1755
|
+
<span className={`text-sm font-medium ${form.skills.includes(skill.id) ? "text-[var(--color-accent)]" : ""}`}>
|
|
1627
1756
|
{skill.name}
|
|
1628
1757
|
</span>
|
|
1629
1758
|
{skill.project_id === null && (
|
|
1630
|
-
<span className="text-[10px] text-[
|
|
1759
|
+
<span className="text-[10px] text-[var(--color-text-muted)] bg-[var(--color-surface-raised)] px-1.5 py-0.5 rounded">Global</span>
|
|
1631
1760
|
)}
|
|
1632
1761
|
</div>
|
|
1633
|
-
<div className="text-xs text-[
|
|
1762
|
+
<div className="text-xs text-[var(--color-text-muted)]">{skill.description}</div>
|
|
1634
1763
|
</div>
|
|
1635
|
-
<div className="text-xs px-2 py-0.5 rounded bg-[
|
|
1764
|
+
<div className="text-xs px-2 py-0.5 rounded bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]">
|
|
1636
1765
|
v{skill.version}
|
|
1637
1766
|
</div>
|
|
1638
1767
|
</button>
|
|
1639
1768
|
))}
|
|
1640
|
-
<p className="text-xs text-[
|
|
1769
|
+
<p className="text-xs text-[var(--color-text-muted)] mt-2">
|
|
1641
1770
|
Skills provide reusable instructions for the agent.
|
|
1642
1771
|
</p>
|
|
1643
1772
|
</div>
|
|
@@ -1657,31 +1786,31 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1657
1786
|
<button
|
|
1658
1787
|
onClick={handleSave}
|
|
1659
1788
|
disabled={!hasChanges || saving || !form.name}
|
|
1660
|
-
className="w-full bg-[
|
|
1789
|
+
className="w-full bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
|
|
1661
1790
|
>
|
|
1662
1791
|
{saving ? "Saving..." : "Save Changes"}
|
|
1663
1792
|
</button>
|
|
1664
1793
|
|
|
1665
1794
|
{agent.status === "running" && hasChanges && (
|
|
1666
|
-
<p className="text-xs text-[
|
|
1795
|
+
<p className="text-xs text-[var(--color-text-muted)] text-center">
|
|
1667
1796
|
Changes will be applied to the running agent
|
|
1668
1797
|
</p>
|
|
1669
1798
|
)}
|
|
1670
1799
|
|
|
1671
1800
|
{/* Subscriptions */}
|
|
1672
|
-
<div className="mt-8 pt-6 border-t border-[
|
|
1673
|
-
<p className="text-sm text-[
|
|
1801
|
+
<div className="mt-8 pt-6 border-t border-[var(--color-border-light)]">
|
|
1802
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-3">Subscriptions</p>
|
|
1674
1803
|
{subscriptions.length === 0 ? (
|
|
1675
|
-
<p className="text-xs text-[
|
|
1804
|
+
<p className="text-xs text-[var(--color-text-faint)]">No subscriptions. Set up triggers in Connections to have this agent listen to external events.</p>
|
|
1676
1805
|
) : (
|
|
1677
1806
|
<div className="space-y-2">
|
|
1678
1807
|
{subscriptions.map(sub => (
|
|
1679
|
-
<div key={sub.id} className="flex items-center gap-2 px-3 py-2 bg-[
|
|
1680
|
-
<span className={`w-2 h-2 rounded-full shrink-0 ${sub.enabled ? "bg-cyan-400" : "bg-[
|
|
1681
|
-
<span className={`text-sm flex-1 ${sub.enabled ? "text-cyan-400" : "text-[
|
|
1808
|
+
<div key={sub.id} className="flex items-center gap-2 px-3 py-2 bg-[var(--color-surface)] rounded border border-[var(--color-border)]">
|
|
1809
|
+
<span className={`w-2 h-2 rounded-full shrink-0 ${sub.enabled ? "bg-cyan-400" : "bg-[var(--color-scrollbar)]"}`} />
|
|
1810
|
+
<span className={`text-sm flex-1 ${sub.enabled ? "text-cyan-400" : "text-[var(--color-text-muted)]"}`}>
|
|
1682
1811
|
{sub.trigger_slug.replace(/_/g, " ")}
|
|
1683
1812
|
</span>
|
|
1684
|
-
<span className={`text-[10px] px-1.5 py-0.5 rounded ${sub.enabled ? "bg-cyan-500/10 text-cyan-400" : "bg-[
|
|
1813
|
+
<span className={`text-[10px] px-1.5 py-0.5 rounded ${sub.enabled ? "bg-cyan-500/10 text-cyan-400" : "bg-[var(--color-surface-raised)] text-[var(--color-text-faint)]"}`}>
|
|
1685
1814
|
{sub.enabled ? "active" : "disabled"}
|
|
1686
1815
|
</span>
|
|
1687
1816
|
</div>
|
|
@@ -1692,35 +1821,35 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1692
1821
|
|
|
1693
1822
|
{/* Developer Info (dev mode only) */}
|
|
1694
1823
|
{apiKey && (
|
|
1695
|
-
<div className="mt-8 pt-6 border-t border-[
|
|
1696
|
-
<p className="text-sm text-[
|
|
1824
|
+
<div className="mt-8 pt-6 border-t border-[var(--color-border-light)]">
|
|
1825
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-3">Developer Info</p>
|
|
1697
1826
|
<div className="space-y-2">
|
|
1698
1827
|
<div className="flex items-center justify-between">
|
|
1699
|
-
<span className="text-xs text-[
|
|
1700
|
-
<code className="text-xs bg-[
|
|
1828
|
+
<span className="text-xs text-[var(--color-text-muted)]">Agent ID</span>
|
|
1829
|
+
<code className="text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded text-[var(--color-text-secondary)]">{agent.id}</code>
|
|
1701
1830
|
</div>
|
|
1702
1831
|
<div className="flex items-center justify-between">
|
|
1703
|
-
<span className="text-xs text-[
|
|
1704
|
-
<code className="text-xs bg-[
|
|
1832
|
+
<span className="text-xs text-[var(--color-text-muted)]">Port</span>
|
|
1833
|
+
<code className="text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded text-[var(--color-text-secondary)]">{agent.port || "N/A"}</code>
|
|
1705
1834
|
</div>
|
|
1706
1835
|
<div className="flex flex-col gap-1">
|
|
1707
1836
|
<div className="flex items-center justify-between">
|
|
1708
|
-
<span className="text-xs text-[
|
|
1837
|
+
<span className="text-xs text-[var(--color-text-muted)]">API Key</span>
|
|
1709
1838
|
<button
|
|
1710
1839
|
onClick={() => setShowApiKey(!showApiKey)}
|
|
1711
|
-
className="text-xs text-[
|
|
1840
|
+
className="text-xs text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
|
|
1712
1841
|
>
|
|
1713
1842
|
{showApiKey ? "Hide" : "Show"}
|
|
1714
1843
|
</button>
|
|
1715
1844
|
</div>
|
|
1716
|
-
<code className="text-xs bg-[
|
|
1845
|
+
<code className="text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded text-[var(--color-text-secondary)] break-all">
|
|
1717
1846
|
{showApiKey ? (apiKeyFull || apiKey) : apiKey}
|
|
1718
1847
|
</code>
|
|
1719
1848
|
</div>
|
|
1720
1849
|
{agent.status === "running" && agent.port && (
|
|
1721
1850
|
<div className="flex flex-col gap-1 mt-2">
|
|
1722
|
-
<span className="text-xs text-[
|
|
1723
|
-
<code className="text-xs bg-[
|
|
1851
|
+
<span className="text-xs text-[var(--color-text-muted)]">Test with curl</span>
|
|
1852
|
+
<code className="text-xs bg-[var(--color-surface-raised)] px-2 py-1.5 rounded text-[var(--color-text-muted)] break-all">
|
|
1724
1853
|
curl -H "X-API-Key: {showApiKey ? (apiKeyFull || apiKey) : "***"}" http://localhost:{agent.port}/config
|
|
1725
1854
|
</code>
|
|
1726
1855
|
</div>
|
|
@@ -1729,14 +1858,45 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1729
1858
|
</div>
|
|
1730
1859
|
)}
|
|
1731
1860
|
|
|
1861
|
+
{/* Share Link */}
|
|
1862
|
+
{shareToken && (
|
|
1863
|
+
<div className="mt-8 pt-6 border-t border-[var(--color-border-light)]">
|
|
1864
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-3">Share Link</p>
|
|
1865
|
+
<p className="text-xs text-[var(--color-text-faint)] mb-3">
|
|
1866
|
+
Anyone with this link can chat with this agent. No login required. Regenerate the API key to invalidate.
|
|
1867
|
+
</p>
|
|
1868
|
+
<div className="flex gap-2">
|
|
1869
|
+
<code className="flex-1 text-xs bg-[var(--color-surface-raised)] px-3 py-2 rounded text-[var(--color-text-secondary)] break-all border border-[var(--color-border-light)]">
|
|
1870
|
+
{`${window.location.origin}/share/${shareToken}`}
|
|
1871
|
+
</code>
|
|
1872
|
+
<button
|
|
1873
|
+
onClick={() => {
|
|
1874
|
+
navigator.clipboard.writeText(`${window.location.origin}/share/${shareToken}`);
|
|
1875
|
+
setShareCopied(true);
|
|
1876
|
+
setTimeout(() => setShareCopied(false), 2000);
|
|
1877
|
+
}}
|
|
1878
|
+
className="px-3 py-2 text-xs bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] rounded text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition flex-shrink-0"
|
|
1879
|
+
>
|
|
1880
|
+
{shareCopied ? "Copied!" : "Copy"}
|
|
1881
|
+
</button>
|
|
1882
|
+
</div>
|
|
1883
|
+
<div className="mt-3">
|
|
1884
|
+
<p className="text-xs text-[var(--color-text-faint)] mb-1">Embed</p>
|
|
1885
|
+
<code className="block text-xs bg-[var(--color-surface-raised)] px-3 py-2 rounded text-[var(--color-text-muted)] break-all border border-[var(--color-border-light)]">
|
|
1886
|
+
{`<iframe src="${window.location.origin}/share/${shareToken}" width="400" height="600" style="border:none; border-radius:12px;" />`}
|
|
1887
|
+
</code>
|
|
1888
|
+
</div>
|
|
1889
|
+
</div>
|
|
1890
|
+
)}
|
|
1891
|
+
|
|
1732
1892
|
{/* Danger Zone */}
|
|
1733
|
-
<div className="mt-8 pt-6 border-t border-[
|
|
1734
|
-
<p className="text-sm text-[
|
|
1893
|
+
<div className="mt-8 pt-6 border-t border-[var(--color-border-light)]">
|
|
1894
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-3">Danger Zone</p>
|
|
1735
1895
|
{confirmDelete ? (
|
|
1736
1896
|
<div className="flex gap-2">
|
|
1737
1897
|
<button
|
|
1738
1898
|
onClick={() => setConfirmDelete(false)}
|
|
1739
|
-
className="flex-1 border border-[
|
|
1899
|
+
className="flex-1 border border-[var(--color-border-light)] hover:border-[var(--color-scrollbar)] px-4 py-2 rounded font-medium transition"
|
|
1740
1900
|
>
|
|
1741
1901
|
Cancel
|
|
1742
1902
|
</button>
|
|
@@ -1764,7 +1924,7 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1764
1924
|
function FormField({ label, children }: { label: string; children: React.ReactNode }) {
|
|
1765
1925
|
return (
|
|
1766
1926
|
<div>
|
|
1767
|
-
<label className="block text-sm text-[
|
|
1927
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">{label}</label>
|
|
1768
1928
|
{children}
|
|
1769
1929
|
</div>
|
|
1770
1930
|
);
|