apteva 0.4.1 โ 0.4.2
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/App.hzycvt6c.js +227 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/web/components/tasks/TasksPage.tsx +294 -69
- package/src/web/types.ts +10 -0
- package/dist/App.e10kd3t7.js +0 -227
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from "react";
|
|
2
|
-
import { TasksIcon } from "../common/Icons";
|
|
2
|
+
import { TasksIcon, CloseIcon } from "../common/Icons";
|
|
3
3
|
import { useAuth } from "../../context";
|
|
4
|
-
import type { Task } from "../../types";
|
|
4
|
+
import type { Task, TaskTrajectoryStep } from "../../types";
|
|
5
5
|
|
|
6
6
|
interface TasksPageProps {
|
|
7
7
|
onSelectAgent?: (agentId: string) => void;
|
|
@@ -12,6 +12,7 @@ export function TasksPage({ onSelectAgent }: TasksPageProps) {
|
|
|
12
12
|
const [tasks, setTasks] = useState<Task[]>([]);
|
|
13
13
|
const [loading, setLoading] = useState(true);
|
|
14
14
|
const [filter, setFilter] = useState<string>("all");
|
|
15
|
+
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
|
|
15
16
|
|
|
16
17
|
const fetchTasks = useCallback(async () => {
|
|
17
18
|
try {
|
|
@@ -49,83 +50,307 @@ export function TasksPage({ onSelectAgent }: TasksPageProps) {
|
|
|
49
50
|
];
|
|
50
51
|
|
|
51
52
|
return (
|
|
52
|
-
<div className="flex-1
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
<div className="flex-1 flex overflow-hidden">
|
|
54
|
+
{/* Task List */}
|
|
55
|
+
<div className={`flex-1 p-4 md:p-6 overflow-auto ${selectedTask ? 'hidden md:block md:w-1/2 lg:w-2/3' : ''}`}>
|
|
56
|
+
<div className="max-w-4xl">
|
|
57
|
+
<div className="mb-6">
|
|
58
|
+
<div className="mb-4">
|
|
59
|
+
<h1 className="text-xl md:text-2xl font-semibold mb-1">Tasks</h1>
|
|
60
|
+
<p className="text-sm text-[#666]">
|
|
61
|
+
View tasks from all running agents
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="flex gap-2 overflow-x-auto scrollbar-hide pb-1">
|
|
65
|
+
{filterOptions.map(opt => (
|
|
66
|
+
<button
|
|
67
|
+
key={opt.value}
|
|
68
|
+
onClick={() => setFilter(opt.value)}
|
|
69
|
+
className={`px-3 py-1.5 rounded text-sm transition whitespace-nowrap ${
|
|
70
|
+
filter === opt.value
|
|
71
|
+
? "bg-[#f97316] text-black"
|
|
72
|
+
: "bg-[#1a1a1a] hover:bg-[#222]"
|
|
73
|
+
}`}
|
|
74
|
+
>
|
|
75
|
+
{opt.label}
|
|
76
|
+
</button>
|
|
77
|
+
))}
|
|
78
|
+
</div>
|
|
60
79
|
</div>
|
|
61
|
-
<div className="flex gap-2 overflow-x-auto scrollbar-hide pb-1">
|
|
62
|
-
{filterOptions.map(opt => (
|
|
63
|
-
<button
|
|
64
|
-
key={opt.value}
|
|
65
|
-
onClick={() => setFilter(opt.value)}
|
|
66
|
-
className={`px-3 py-1.5 rounded text-sm transition whitespace-nowrap ${
|
|
67
|
-
filter === opt.value
|
|
68
|
-
? "bg-[#f97316] text-black"
|
|
69
|
-
: "bg-[#1a1a1a] hover:bg-[#222]"
|
|
70
|
-
}`}
|
|
71
|
-
>
|
|
72
|
-
{opt.label}
|
|
73
|
-
</button>
|
|
74
|
-
))}
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
81
|
+
{loading ? (
|
|
82
|
+
<div className="text-center py-12 text-[#666]">Loading tasks...</div>
|
|
83
|
+
) : tasks.length === 0 ? (
|
|
84
|
+
<div className="text-center py-12">
|
|
85
|
+
<TasksIcon className="w-12 h-12 mx-auto mb-4 text-[#333]" />
|
|
86
|
+
<p className="text-[#666]">No tasks found</p>
|
|
87
|
+
<p className="text-sm text-[#444] mt-1">
|
|
88
|
+
Tasks will appear here when agents create them
|
|
89
|
+
</p>
|
|
90
|
+
</div>
|
|
91
|
+
) : (
|
|
92
|
+
<div className="space-y-3">
|
|
93
|
+
{tasks.map(task => (
|
|
94
|
+
<div
|
|
95
|
+
key={`${task.agentId}-${task.id}`}
|
|
96
|
+
onClick={() => setSelectedTask(task)}
|
|
97
|
+
className={`bg-[#111] border rounded-lg p-4 cursor-pointer transition ${
|
|
98
|
+
selectedTask?.id === task.id && selectedTask?.agentId === task.agentId
|
|
99
|
+
? "border-[#f97316]"
|
|
100
|
+
: "border-[#1a1a1a] hover:border-[#333]"
|
|
101
|
+
}`}
|
|
102
|
+
>
|
|
103
|
+
<div className="flex items-start justify-between mb-2">
|
|
104
|
+
<div className="flex-1">
|
|
105
|
+
<h3 className="font-medium">{task.title}</h3>
|
|
106
|
+
<p className="text-sm text-[#666]">
|
|
107
|
+
{task.agentName}
|
|
108
|
+
{task.execute_at && (
|
|
109
|
+
<span className="ml-2">
|
|
110
|
+
ยท Scheduled: {new Date(task.execute_at).toLocaleString()}
|
|
111
|
+
</span>
|
|
112
|
+
)}
|
|
113
|
+
</p>
|
|
114
|
+
</div>
|
|
115
|
+
<span className={`px-2 py-1 rounded text-xs font-medium ${statusColors[task.status] || statusColors.pending}`}>
|
|
116
|
+
{task.status}
|
|
117
|
+
</span>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{task.description && (
|
|
121
|
+
<p className="text-sm text-[#888] mb-2 line-clamp-2">
|
|
122
|
+
{task.description}
|
|
105
123
|
</p>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-[#555]">
|
|
127
|
+
<span>Type: {task.type}</span>
|
|
128
|
+
<span>Priority: {task.priority}</span>
|
|
129
|
+
{task.recurrence && <span>Recurrence: {task.recurrence}</span>}
|
|
130
|
+
<span>Created: {new Date(task.created_at).toLocaleString()}</span>
|
|
106
131
|
</div>
|
|
107
|
-
<span className={`px-2 py-1 rounded text-xs font-medium ${statusColors[task.status] || statusColors.pending}`}>
|
|
108
|
-
{task.status}
|
|
109
|
-
</span>
|
|
110
132
|
</div>
|
|
133
|
+
))}
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
111
138
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
{/* Task Detail Panel */}
|
|
140
|
+
{selectedTask && (
|
|
141
|
+
<TaskDetailPanel
|
|
142
|
+
task={selectedTask}
|
|
143
|
+
statusColors={statusColors}
|
|
144
|
+
onClose={() => setSelectedTask(null)}
|
|
145
|
+
onSelectAgent={onSelectAgent}
|
|
146
|
+
/>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
117
151
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
152
|
+
interface TaskDetailPanelProps {
|
|
153
|
+
task: Task;
|
|
154
|
+
statusColors: Record<string, string>;
|
|
155
|
+
onClose: () => void;
|
|
156
|
+
onSelectAgent?: (agentId: string) => void;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function TaskDetailPanel({ task, statusColors, onClose, onSelectAgent }: TaskDetailPanelProps) {
|
|
160
|
+
return (
|
|
161
|
+
<div className="w-full md:w-1/2 lg:w-1/3 border-l border-[#1a1a1a] bg-[#0a0a0a] flex flex-col overflow-hidden">
|
|
162
|
+
{/* Header */}
|
|
163
|
+
<div className="flex items-center justify-between p-4 border-b border-[#1a1a1a]">
|
|
164
|
+
<h2 className="font-medium truncate pr-2">Task Details</h2>
|
|
165
|
+
<button onClick={onClose} className="text-[#666] hover:text-[#e0e0e0] transition">
|
|
166
|
+
<CloseIcon />
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{/* Content */}
|
|
171
|
+
<div className="flex-1 overflow-auto p-4 space-y-4">
|
|
172
|
+
{/* Title & Status */}
|
|
173
|
+
<div>
|
|
174
|
+
<div className="flex items-start justify-between gap-2 mb-2">
|
|
175
|
+
<h3 className="text-lg font-medium">{task.title}</h3>
|
|
176
|
+
<span className={`px-2 py-1 rounded text-xs font-medium flex-shrink-0 ${statusColors[task.status]}`}>
|
|
177
|
+
{task.status}
|
|
178
|
+
</span>
|
|
179
|
+
</div>
|
|
180
|
+
<button
|
|
181
|
+
onClick={() => onSelectAgent?.(task.agentId)}
|
|
182
|
+
className="text-sm text-[#f97316] hover:underline"
|
|
183
|
+
>
|
|
184
|
+
{task.agentName}
|
|
185
|
+
</button>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{/* Description */}
|
|
189
|
+
{task.description && (
|
|
190
|
+
<div>
|
|
191
|
+
<h4 className="text-xs text-[#666] uppercase tracking-wider mb-1">Description</h4>
|
|
192
|
+
<p className="text-sm text-[#888] whitespace-pre-wrap">{task.description}</p>
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{/* Metadata */}
|
|
197
|
+
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
198
|
+
<div>
|
|
199
|
+
<span className="text-[#666]">Type</span>
|
|
200
|
+
<p className="capitalize">{task.type}</p>
|
|
201
|
+
</div>
|
|
202
|
+
<div>
|
|
203
|
+
<span className="text-[#666]">Priority</span>
|
|
204
|
+
<p>{task.priority}</p>
|
|
205
|
+
</div>
|
|
206
|
+
<div>
|
|
207
|
+
<span className="text-[#666]">Source</span>
|
|
208
|
+
<p className="capitalize">{task.source}</p>
|
|
209
|
+
</div>
|
|
210
|
+
{task.recurrence && (
|
|
211
|
+
<div>
|
|
212
|
+
<span className="text-[#666]">Recurrence</span>
|
|
213
|
+
<p>{task.recurrence}</p>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
{/* Timestamps */}
|
|
219
|
+
<div className="space-y-2 text-sm">
|
|
220
|
+
<div className="flex justify-between">
|
|
221
|
+
<span className="text-[#666]">Created</span>
|
|
222
|
+
<span>{new Date(task.created_at).toLocaleString()}</span>
|
|
223
|
+
</div>
|
|
224
|
+
{task.execute_at && (
|
|
225
|
+
<div className="flex justify-between">
|
|
226
|
+
<span className="text-[#666]">Scheduled</span>
|
|
227
|
+
<span>{new Date(task.execute_at).toLocaleString()}</span>
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
{task.executed_at && (
|
|
231
|
+
<div className="flex justify-between">
|
|
232
|
+
<span className="text-[#666]">Started</span>
|
|
233
|
+
<span>{new Date(task.executed_at).toLocaleString()}</span>
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
{task.completed_at && (
|
|
237
|
+
<div className="flex justify-between">
|
|
238
|
+
<span className="text-[#666]">Completed</span>
|
|
239
|
+
<span>{new Date(task.completed_at).toLocaleString()}</span>
|
|
240
|
+
</div>
|
|
241
|
+
)}
|
|
242
|
+
{task.next_run && (
|
|
243
|
+
<div className="flex justify-between">
|
|
244
|
+
<span className="text-[#666]">Next Run</span>
|
|
245
|
+
<span>{new Date(task.next_run).toLocaleString()}</span>
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{/* Error */}
|
|
251
|
+
{task.status === "failed" && task.error && (
|
|
252
|
+
<div>
|
|
253
|
+
<h4 className="text-xs text-red-400 uppercase tracking-wider mb-1">Error</h4>
|
|
254
|
+
<div className="bg-red-500/10 border border-red-500/20 rounded p-3">
|
|
255
|
+
<p className="text-sm text-red-400 whitespace-pre-wrap">{task.error}</p>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{/* Result */}
|
|
261
|
+
{task.status === "completed" && task.result && (
|
|
262
|
+
<div>
|
|
263
|
+
<h4 className="text-xs text-green-400 uppercase tracking-wider mb-1">Result</h4>
|
|
264
|
+
<div className="bg-green-500/10 border border-green-500/20 rounded p-3">
|
|
265
|
+
<p className="text-sm text-green-400 whitespace-pre-wrap">
|
|
266
|
+
{typeof task.result === "string" ? task.result : JSON.stringify(task.result, null, 2)}
|
|
267
|
+
</p>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
|
|
272
|
+
{/* Trajectory */}
|
|
273
|
+
{task.trajectory && task.trajectory.length > 0 && (
|
|
274
|
+
<div>
|
|
275
|
+
<h4 className="text-xs text-[#666] uppercase tracking-wider mb-2">
|
|
276
|
+
Trajectory ({task.trajectory.length} steps)
|
|
277
|
+
</h4>
|
|
278
|
+
<TrajectoryView trajectory={task.trajectory} />
|
|
126
279
|
</div>
|
|
127
280
|
)}
|
|
128
281
|
</div>
|
|
129
282
|
</div>
|
|
130
283
|
);
|
|
131
284
|
}
|
|
285
|
+
|
|
286
|
+
function TrajectoryView({ trajectory }: { trajectory: TaskTrajectoryStep[] }) {
|
|
287
|
+
const [expanded, setExpanded] = useState<Set<number>>(new Set());
|
|
288
|
+
|
|
289
|
+
const toggleStep = (index: number) => {
|
|
290
|
+
setExpanded(prev => {
|
|
291
|
+
const next = new Set(prev);
|
|
292
|
+
if (next.has(index)) {
|
|
293
|
+
next.delete(index);
|
|
294
|
+
} else {
|
|
295
|
+
next.add(index);
|
|
296
|
+
}
|
|
297
|
+
return next;
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const stepColors: Record<string, { bg: string; text: string; icon: string }> = {
|
|
302
|
+
thought: { bg: "bg-purple-500/10", text: "text-purple-400", icon: "๐ญ" },
|
|
303
|
+
action: { bg: "bg-blue-500/10", text: "text-blue-400", icon: "โก" },
|
|
304
|
+
observation: { bg: "bg-green-500/10", text: "text-green-400", icon: "๐" },
|
|
305
|
+
tool_call: { bg: "bg-orange-500/10", text: "text-orange-400", icon: "๐ง" },
|
|
306
|
+
tool_result: { bg: "bg-teal-500/10", text: "text-teal-400", icon: "๐" },
|
|
307
|
+
message: { bg: "bg-gray-500/10", text: "text-gray-400", icon: "๐ฌ" },
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<div className="space-y-2">
|
|
312
|
+
{trajectory.map((step, index) => {
|
|
313
|
+
const colors = stepColors[step.type] || stepColors.message;
|
|
314
|
+
const isExpanded = expanded.has(index);
|
|
315
|
+
const isLong = step.content.length > 150;
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<div
|
|
319
|
+
key={index}
|
|
320
|
+
className={`${colors.bg} border border-[#1a1a1a] rounded overflow-hidden`}
|
|
321
|
+
>
|
|
322
|
+
<button
|
|
323
|
+
onClick={() => isLong && toggleStep(index)}
|
|
324
|
+
className={`w-full p-2 text-left flex items-start gap-2 ${isLong ? 'cursor-pointer' : ''}`}
|
|
325
|
+
>
|
|
326
|
+
<span className="flex-shrink-0">{colors.icon}</span>
|
|
327
|
+
<div className="flex-1 min-w-0">
|
|
328
|
+
<div className="flex items-center gap-2 mb-1">
|
|
329
|
+
<span className={`text-xs font-medium ${colors.text} capitalize`}>
|
|
330
|
+
{step.type.replace("_", " ")}
|
|
331
|
+
</span>
|
|
332
|
+
{step.tool && (
|
|
333
|
+
<span className="text-xs text-[#666]">ยท {step.tool}</span>
|
|
334
|
+
)}
|
|
335
|
+
{step.timestamp && (
|
|
336
|
+
<span className="text-xs text-[#555]">
|
|
337
|
+
ยท {new Date(step.timestamp).toLocaleTimeString()}
|
|
338
|
+
</span>
|
|
339
|
+
)}
|
|
340
|
+
</div>
|
|
341
|
+
<p className={`text-sm text-[#888] whitespace-pre-wrap ${!isExpanded && isLong ? 'line-clamp-3' : ''}`}>
|
|
342
|
+
{step.content}
|
|
343
|
+
</p>
|
|
344
|
+
{isLong && (
|
|
345
|
+
<span className="text-xs text-[#666] mt-1 inline-block">
|
|
346
|
+
{isExpanded ? "Click to collapse" : "Click to expand..."}
|
|
347
|
+
</span>
|
|
348
|
+
)}
|
|
349
|
+
</div>
|
|
350
|
+
</button>
|
|
351
|
+
</div>
|
|
352
|
+
);
|
|
353
|
+
})}
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
}
|
package/src/web/types.ts
CHANGED
|
@@ -145,6 +145,13 @@ export interface OnboardingStatus {
|
|
|
145
145
|
|
|
146
146
|
export type Route = "dashboard" | "agents" | "tasks" | "mcp" | "skills" | "telemetry" | "settings" | "api";
|
|
147
147
|
|
|
148
|
+
export interface TaskTrajectoryStep {
|
|
149
|
+
type: "thought" | "action" | "observation" | "tool_call" | "tool_result" | "message";
|
|
150
|
+
content: string;
|
|
151
|
+
tool?: string;
|
|
152
|
+
timestamp?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
148
155
|
export interface Task {
|
|
149
156
|
id: string;
|
|
150
157
|
title: string;
|
|
@@ -156,9 +163,12 @@ export interface Task {
|
|
|
156
163
|
created_at: string;
|
|
157
164
|
execute_at?: string;
|
|
158
165
|
executed_at?: string;
|
|
166
|
+
completed_at?: string;
|
|
159
167
|
recurrence?: string;
|
|
160
168
|
next_run?: string;
|
|
161
169
|
result?: any;
|
|
170
|
+
error?: string;
|
|
171
|
+
trajectory?: TaskTrajectoryStep[];
|
|
162
172
|
agentId: string;
|
|
163
173
|
agentName: string;
|
|
164
174
|
}
|