apteva 0.4.20 → 0.4.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/ActivityPage.cycn14ck.js +3 -0
  2. package/dist/{ApiDocsPage.kf6bbwkk.js → ApiDocsPage.3q5x9hhg.js} +2 -2
  3. package/dist/App.0wwyytz2.js +4 -0
  4. package/dist/{App.c90t3dxg.js → App.2prdcxgq.js} +3 -3
  5. package/dist/{App.2yy66bnp.js → App.40azyqz6.js} +3 -3
  6. package/dist/App.6ftxk387.js +4 -0
  7. package/dist/{App.jfx3der4.js → App.9bzz8dqh.js} +3 -3
  8. package/dist/App.a7h91mxr.js +4 -0
  9. package/dist/{App.7v1w3ys9.js → App.e54ynjf2.js} +3 -3
  10. package/dist/{App.edwahsvz.js → App.fq11mvc7.js} +2 -2
  11. package/dist/{App.2jmkqm8c.js → App.h6k4j1w9.js} +3 -3
  12. package/dist/App.jq5tmjws.js +267 -0
  13. package/dist/{App.q3bpx15d.js → App.k377qek6.js} +2 -2
  14. package/dist/{App.039re6cf.js → App.r2c5nw36.js} +3 -3
  15. package/dist/{App.n4jb3c22.js → App.sb2fg71h.js} +3 -3
  16. package/dist/App.wnap3h7r.js +4 -0
  17. package/dist/ConnectionsPage.6fyhqfhz.js +3 -0
  18. package/dist/McpPage.hk2qt1qt.js +3 -0
  19. package/dist/SettingsPage.gwpx9v7v.js +3 -0
  20. package/dist/SkillsPage.j5zech2z.js +3 -0
  21. package/dist/TasksPage.65dcf4vw.js +3 -0
  22. package/dist/TelemetryPage.07xrbd7k.js +3 -0
  23. package/dist/TestsPage.q6zfephf.js +3 -0
  24. package/dist/index.html +1 -1
  25. package/dist/styles.css +1 -1
  26. package/package.json +2 -2
  27. package/src/integrations/agentdojo.ts +1 -1
  28. package/src/providers.ts +2 -0
  29. package/src/routes/api/triggers.ts +45 -5
  30. package/src/web/App.tsx +1 -0
  31. package/src/web/components/activity/ActivityPage.tsx +347 -212
  32. package/src/web/components/agents/AgentCard.tsx +32 -3
  33. package/src/web/components/agents/AgentPanel.tsx +188 -4
  34. package/src/web/components/connections/IntegrationsTab.tsx +57 -31
  35. package/src/web/components/connections/TriggersTab.tsx +336 -159
  36. package/src/web/components/dashboard/Dashboard.tsx +39 -7
  37. package/src/web/components/layout/Header.tsx +0 -34
  38. package/src/web/components/layout/Sidebar.tsx +43 -3
  39. package/src/web/components/tasks/TasksPage.tsx +32 -6
  40. package/dist/ActivityPage.h769ek3a.js +0 -3
  41. package/dist/App.3515wsb4.js +0 -4
  42. package/dist/App.r0a2nmqs.js +0 -267
  43. package/dist/App.s2yrcz15.js +0 -4
  44. package/dist/App.s5j82a5j.js +0 -4
  45. package/dist/App.tg1b94tx.js +0 -4
  46. package/dist/ConnectionsPage.a67fjgbf.js +0 -3
  47. package/dist/McpPage.d4p3xvtk.js +0 -3
  48. package/dist/SettingsPage.46sqpe39.js +0 -3
  49. package/dist/SkillsPage.j9hkqm99.js +0 -3
  50. package/dist/TasksPage.6pvkb7s7.js +0 -3
  51. package/dist/TelemetryPage.5zq9msb5.js +0 -3
  52. package/dist/TestsPage.24432yqt.js +0 -3
@@ -1,6 +1,6 @@
1
- import React from "react";
2
- import { MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, FilesIcon, MultiAgentIcon, SkillsIcon } from "../common/Icons";
3
- import { useAgentActivity, useProjects } from "../../context";
1
+ import React, { useState, useEffect } from "react";
2
+ import { MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, FilesIcon, MultiAgentIcon, SkillsIcon, ActivityIcon } from "../common/Icons";
3
+ import { useAgentActivity, useProjects, useAuth } from "../../context";
4
4
  import type { Agent, AgentFeatures } from "../../types";
5
5
 
6
6
  interface AgentCardProps {
@@ -28,7 +28,16 @@ export function AgentCard({ agent, selected, onSelect, onToggle, showProject }:
28
28
  const skills = agent.skillDetails || [];
29
29
  const { isActive, type } = useAgentActivity(agent.id);
30
30
  const { projects } = useProjects();
31
+ const { authFetch } = useAuth();
31
32
  const project = agent.projectId ? projects.find(p => p.id === agent.projectId) : null;
33
+ const [subscriptions, setSubscriptions] = useState<{ id: string; trigger_slug: string; enabled: boolean }[]>([]);
34
+
35
+ useEffect(() => {
36
+ authFetch(`/api/subscriptions?agent_id=${agent.id}`)
37
+ .then(res => res.ok ? res.json() : { subscriptions: [] })
38
+ .then(data => setSubscriptions(data.subscriptions || []))
39
+ .catch(() => {});
40
+ }, [agent.id, authFetch]);
32
41
 
33
42
  return (
34
43
  <div
@@ -115,6 +124,26 @@ export function AgentCard({ agent, selected, onSelect, onToggle, showProject }:
115
124
  </div>
116
125
  )}
117
126
 
127
+ {/* Subscriptions (triggers listening to) */}
128
+ {subscriptions.length > 0 && (
129
+ <div className="flex flex-wrap gap-1.5 mb-3">
130
+ {subscriptions.map((sub) => (
131
+ <span
132
+ key={sub.id}
133
+ className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
134
+ sub.enabled
135
+ ? "bg-cyan-500/10 text-cyan-400"
136
+ : "bg-[#222] text-[#666]"
137
+ }`}
138
+ title={`Trigger: ${sub.trigger_slug.replace(/_/g, " ")}`}
139
+ >
140
+ <ActivityIcon className="w-3 h-3" />
141
+ {sub.trigger_slug.replace(/_/g, " ")}
142
+ </span>
143
+ ))}
144
+ </div>
145
+ )}
146
+
118
147
  <p className="text-sm text-[#666] line-clamp-2 mb-4 flex-1">
119
148
  {agent.systemPrompt}
120
149
  </p>
@@ -1,12 +1,12 @@
1
1
  import React, { useState, useEffect } from "react";
2
2
  import { Chat, convertApiMessages } from "@apteva/apteva-kit";
3
3
  import { CloseIcon, MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, FilesIcon, MultiAgentIcon, RecurringIcon, ScheduledIcon, TaskOnceIcon } from "../common/Icons";
4
- import { formatCron, formatRelativeTime } from "../tasks/TasksPage";
4
+ import { formatCron, formatRelativeTime, TrajectoryView } from "../tasks/TasksPage";
5
5
  import { Select } from "../common/Select";
6
6
  import { useConfirm } from "../common/Modal";
7
7
  import { useTelemetry } from "../../context";
8
8
  import { useAuth } from "../../context";
9
- import type { Agent, Provider, AgentFeatures, McpServer, SkillSummary, AgentMode, MultiAgentConfig } from "../../types";
9
+ import type { Agent, Provider, AgentFeatures, McpServer, SkillSummary, AgentMode, MultiAgentConfig, Task } from "../../types";
10
10
  import { getMultiAgentConfig } from "../../types";
11
11
 
12
12
  type Tab = "chat" | "threads" | "tasks" | "memory" | "files" | "settings";
@@ -314,10 +314,13 @@ function ThreadsTab({ agent }: { agent: Agent }) {
314
314
  }
315
315
 
316
316
  function TasksTab({ agent }: { agent: Agent }) {
317
- const [tasks, setTasks] = useState<any[]>([]);
317
+ const { authFetch } = useAuth();
318
+ const [tasks, setTasks] = useState<Task[]>([]);
318
319
  const [loading, setLoading] = useState(true);
319
320
  const [error, setError] = useState<string | null>(null);
320
321
  const [filter, setFilter] = useState<string>("all");
322
+ const [selectedTask, setSelectedTask] = useState<Task | null>(null);
323
+ const [loadingTask, setLoadingTask] = useState(false);
321
324
  const { events } = useTelemetry({ agent_id: agent.id, category: "task" });
322
325
 
323
326
  // Reset state when agent changes
@@ -325,6 +328,7 @@ function TasksTab({ agent }: { agent: Agent }) {
325
328
  setTasks([]);
326
329
  setError(null);
327
330
  setLoading(true);
331
+ setSelectedTask(null);
328
332
  }, [agent.id]);
329
333
 
330
334
  const fetchTasks = async () => {
@@ -346,6 +350,24 @@ function TasksTab({ agent }: { agent: Agent }) {
346
350
  }
347
351
  };
348
352
 
353
+ const selectTask = async (task: Task) => {
354
+ setSelectedTask(task);
355
+ setLoadingTask(true);
356
+ try {
357
+ const res = await authFetch(`/api/tasks/${task.agentId || agent.id}/${task.id}`);
358
+ if (res.ok) {
359
+ const data = await res.json();
360
+ if (data.task) {
361
+ setSelectedTask({ ...data.task, agentId: task.agentId || agent.id, agentName: task.agentName || agent.name });
362
+ }
363
+ }
364
+ } catch (e) {
365
+ console.error("Failed to fetch task details:", e);
366
+ } finally {
367
+ setLoadingTask(false);
368
+ }
369
+ };
370
+
349
371
  // Refetch when agent changes, filter changes, or task telemetry arrives
350
372
  useEffect(() => {
351
373
  setLoading(true);
@@ -403,6 +425,133 @@ function TasksTab({ agent }: { agent: Agent }) {
403
425
  { value: "failed", label: "Failed" },
404
426
  ];
405
427
 
428
+ // Show task detail view when a task is selected
429
+ if (selectedTask) {
430
+ return (
431
+ <div className="flex-1 flex flex-col overflow-hidden">
432
+ {/* Back button */}
433
+ <div className="px-4 pt-3 pb-2 border-b border-[#1a1a1a] shrink-0">
434
+ <button
435
+ onClick={() => setSelectedTask(null)}
436
+ className="text-sm text-[#666] hover:text-[#e0e0e0] transition flex items-center gap-1"
437
+ >
438
+ <span>←</span> Back to tasks
439
+ </button>
440
+ </div>
441
+
442
+ {/* Task detail content */}
443
+ <div className="flex-1 overflow-auto p-4 space-y-4">
444
+ {/* Title & Status */}
445
+ <div>
446
+ <div className="flex items-start justify-between gap-2 mb-1">
447
+ <h3 className="text-lg font-medium">{selectedTask.title}</h3>
448
+ <span className={`px-2 py-1 rounded text-xs font-medium flex-shrink-0 ${statusColors[selectedTask.status]}`}>
449
+ {selectedTask.status}
450
+ </span>
451
+ </div>
452
+ </div>
453
+
454
+ {/* Description */}
455
+ {selectedTask.description && (
456
+ <div>
457
+ <h4 className="text-xs text-[#666] uppercase tracking-wider mb-1">Description</h4>
458
+ <p className="text-sm text-[#888] whitespace-pre-wrap">{selectedTask.description}</p>
459
+ </div>
460
+ )}
461
+
462
+ {/* Metadata */}
463
+ <div className="grid grid-cols-2 gap-3 text-sm">
464
+ <div>
465
+ <span className="text-[#666]">Type</span>
466
+ <p className="capitalize">{selectedTask.type}</p>
467
+ </div>
468
+ <div>
469
+ <span className="text-[#666]">Priority</span>
470
+ <p>{selectedTask.priority}</p>
471
+ </div>
472
+ {selectedTask.recurrence && (
473
+ <div>
474
+ <span className="text-[#666]">Recurrence</span>
475
+ <p>{formatCron(selectedTask.recurrence)}</p>
476
+ <p className="text-xs text-[#444] mt-0.5 font-mono">{selectedTask.recurrence}</p>
477
+ </div>
478
+ )}
479
+ </div>
480
+
481
+ {/* Timestamps */}
482
+ <div className="space-y-2 text-sm">
483
+ <div className="flex justify-between">
484
+ <span className="text-[#666]">Created</span>
485
+ <span>{new Date(selectedTask.created_at).toLocaleString()}</span>
486
+ </div>
487
+ {selectedTask.execute_at && (
488
+ <div className="flex justify-between">
489
+ <span className="text-[#666]">Scheduled</span>
490
+ <span className="text-[#f97316]">{formatRelativeTime(selectedTask.execute_at)}</span>
491
+ </div>
492
+ )}
493
+ {selectedTask.executed_at && (
494
+ <div className="flex justify-between">
495
+ <span className="text-[#666]">Started</span>
496
+ <span>{new Date(selectedTask.executed_at).toLocaleString()}</span>
497
+ </div>
498
+ )}
499
+ {selectedTask.completed_at && (
500
+ <div className="flex justify-between">
501
+ <span className="text-[#666]">Completed</span>
502
+ <span>{new Date(selectedTask.completed_at).toLocaleString()}</span>
503
+ </div>
504
+ )}
505
+ {selectedTask.next_run && (
506
+ <div className="flex justify-between">
507
+ <span className="text-[#666]">Next Run</span>
508
+ <span className="text-[#f97316]">{formatRelativeTime(selectedTask.next_run)}</span>
509
+ </div>
510
+ )}
511
+ </div>
512
+
513
+ {/* Error */}
514
+ {selectedTask.status === "failed" && selectedTask.error && (
515
+ <div className="min-w-0">
516
+ <h4 className="text-xs text-red-400 uppercase tracking-wider mb-1">Error</h4>
517
+ <div className="bg-red-500/10 border border-red-500/20 rounded p-3 overflow-x-auto">
518
+ <pre className="text-sm text-red-400 whitespace-pre-wrap break-words">{selectedTask.error}</pre>
519
+ </div>
520
+ </div>
521
+ )}
522
+
523
+ {/* Result */}
524
+ {selectedTask.status === "completed" && selectedTask.result && (
525
+ <div className="min-w-0">
526
+ <h4 className="text-xs text-green-400 uppercase tracking-wider mb-1">Result</h4>
527
+ <div className="bg-green-500/10 border border-green-500/20 rounded p-3 overflow-x-auto">
528
+ <pre className="text-sm text-green-400 whitespace-pre-wrap break-words">
529
+ {typeof selectedTask.result === "string" ? selectedTask.result : JSON.stringify(selectedTask.result, null, 2)}
530
+ </pre>
531
+ </div>
532
+ </div>
533
+ )}
534
+
535
+ {/* Trajectory */}
536
+ {loadingTask && !selectedTask.trajectory && (
537
+ <div>
538
+ <h4 className="text-xs text-[#666] uppercase tracking-wider mb-2">Trajectory</h4>
539
+ <div className="text-sm text-[#555]">Loading trajectory...</div>
540
+ </div>
541
+ )}
542
+ {selectedTask.trajectory && selectedTask.trajectory.length > 0 && (
543
+ <div>
544
+ <h4 className="text-xs text-[#666] uppercase tracking-wider mb-2">
545
+ Trajectory ({selectedTask.trajectory.length} steps)
546
+ </h4>
547
+ <TrajectoryView trajectory={selectedTask.trajectory} />
548
+ </div>
549
+ )}
550
+ </div>
551
+ </div>
552
+ );
553
+ }
554
+
406
555
  return (
407
556
  <div className="flex-1 overflow-auto p-4">
408
557
  {/* Filter tabs */}
@@ -431,7 +580,11 @@ function TasksTab({ agent }: { agent: Agent }) {
431
580
  ) : (
432
581
  <div className="space-y-3">
433
582
  {tasks.map(task => (
434
- <div key={task.id} className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
583
+ <div
584
+ key={task.id}
585
+ onClick={() => selectTask(task)}
586
+ className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4 cursor-pointer hover:border-[#333] transition"
587
+ >
435
588
  <div className="flex items-start justify-between mb-2">
436
589
  <div className="flex-1 min-w-0">
437
590
  <h3 className="font-medium">{task.title || task.name}</h3>
@@ -971,6 +1124,15 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
971
1124
  const [availableSkills, setAvailableSkills] = useState<AvailableSkill[]>([]);
972
1125
  const [apiKey, setApiKey] = useState<string | null>(null);
973
1126
  const [showApiKey, setShowApiKey] = useState(false);
1127
+ const [subscriptions, setSubscriptions] = useState<{ id: string; trigger_slug: string; enabled: boolean }[]>([]);
1128
+
1129
+ // Fetch subscriptions for this agent
1130
+ useEffect(() => {
1131
+ authFetch(`/api/subscriptions?agent_id=${agent.id}`)
1132
+ .then(res => res.ok ? res.json() : { subscriptions: [] })
1133
+ .then(data => setSubscriptions(data.subscriptions || []))
1134
+ .catch(() => {});
1135
+ }, [agent.id, authFetch]);
974
1136
 
975
1137
  // Fetch available MCP servers
976
1138
  useEffect(() => {
@@ -1443,6 +1605,28 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
1443
1605
  </p>
1444
1606
  )}
1445
1607
 
1608
+ {/* Subscriptions */}
1609
+ <div className="mt-8 pt-6 border-t border-[#222]">
1610
+ <p className="text-sm text-[#666] mb-3">Subscriptions</p>
1611
+ {subscriptions.length === 0 ? (
1612
+ <p className="text-xs text-[#555]">No subscriptions. Set up triggers in Connections to have this agent listen to external events.</p>
1613
+ ) : (
1614
+ <div className="space-y-2">
1615
+ {subscriptions.map(sub => (
1616
+ <div key={sub.id} className="flex items-center gap-2 px-3 py-2 bg-[#111] rounded border border-[#1a1a1a]">
1617
+ <span className={`w-2 h-2 rounded-full shrink-0 ${sub.enabled ? "bg-cyan-400" : "bg-[#444]"}`} />
1618
+ <span className={`text-sm flex-1 ${sub.enabled ? "text-cyan-400" : "text-[#666]"}`}>
1619
+ {sub.trigger_slug.replace(/_/g, " ")}
1620
+ </span>
1621
+ <span className={`text-[10px] px-1.5 py-0.5 rounded ${sub.enabled ? "bg-cyan-500/10 text-cyan-400" : "bg-[#222] text-[#555]"}`}>
1622
+ {sub.enabled ? "active" : "disabled"}
1623
+ </span>
1624
+ </div>
1625
+ ))}
1626
+ </div>
1627
+ )}
1628
+ </div>
1629
+
1446
1630
  {/* Developer Info (dev mode only) */}
1447
1631
  {isDev && apiKey && (
1448
1632
  <div className="mt-8 pt-6 border-t border-[#222]">
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback } from "react";
1
+ import React, { useState, useEffect, useCallback } from "react";
2
2
  import { useAuth, useProjects } from "../../context";
3
3
  import { IntegrationsPanel } from "../mcp/IntegrationsPanel";
4
4
 
@@ -12,18 +12,35 @@ interface TriggerType {
12
12
  logo: string | null;
13
13
  }
14
14
 
15
+ interface ProviderInfo {
16
+ id: string;
17
+ name: string;
18
+ connected: boolean;
19
+ }
20
+
15
21
  export function IntegrationsTab() {
16
22
  const { authFetch } = useAuth();
17
23
  const { currentProjectId } = useProjects();
18
24
 
19
25
  const projectId = currentProjectId && currentProjectId !== "unassigned" ? currentProjectId : null;
26
+ const projectParam = projectId ? `?project_id=${projectId}` : "";
20
27
 
21
- // Provider selection
22
- const [selectedProvider, setSelectedProvider] = useState("composio");
23
- const providerOptions = [
24
- { id: "composio", name: "Composio" },
25
- { id: "agentdojo", name: "AgentDojo" },
26
- ];
28
+ // Provider selection — only show configured providers
29
+ const [providers, setProviders] = useState<ProviderInfo[]>([]);
30
+ const [selectedProvider, setSelectedProvider] = useState("");
31
+
32
+ useEffect(() => {
33
+ authFetch(`/api/triggers/providers${projectParam}`)
34
+ .then(r => r.json())
35
+ .then(data => {
36
+ const connected = (data.providers || []).filter((p: ProviderInfo) => p.connected);
37
+ setProviders(connected);
38
+ if (connected.length > 0 && !connected.find((p: ProviderInfo) => p.id === selectedProvider)) {
39
+ setSelectedProvider(connected[0].id);
40
+ }
41
+ })
42
+ .catch(() => {});
43
+ }, [authFetch]);
27
44
 
28
45
  // Trigger type browsing
29
46
  const [browsingToolkit, setBrowsingToolkit] = useState<string | null>(null);
@@ -53,32 +70,41 @@ export function IntegrationsTab() {
53
70
  Connect external apps via OAuth or API Key. Connected apps can be used for triggers and MCP integrations.
54
71
  </p>
55
72
 
56
- {/* Provider Selector */}
57
- <div className="flex items-center gap-2 mb-4">
58
- <span className="text-xs text-[#666]">Provider:</span>
59
- <div className="flex gap-1 bg-[#111] border border-[#1a1a1a] rounded-lg p-0.5">
60
- {providerOptions.map(p => (
61
- <button
62
- key={p.id}
63
- onClick={() => setSelectedProvider(p.id)}
64
- className={`px-3 py-1 rounded text-xs font-medium transition ${
65
- selectedProvider === p.id
66
- ? "bg-[#1a1a1a] text-white"
67
- : "text-[#666] hover:text-[#888]"
68
- }`}
69
- >
70
- {p.name}
71
- </button>
72
- ))}
73
+ {/* Provider Selector — only show if multiple configured */}
74
+ {providers.length > 1 && (
75
+ <div className="flex items-center gap-2 mb-4">
76
+ <span className="text-xs text-[#666]">Provider:</span>
77
+ <div className="flex gap-1 bg-[#111] border border-[#1a1a1a] rounded-lg p-0.5">
78
+ {providers.map(p => (
79
+ <button
80
+ key={p.id}
81
+ onClick={() => setSelectedProvider(p.id)}
82
+ className={`px-3 py-1 rounded text-xs font-medium transition ${
83
+ selectedProvider === p.id
84
+ ? "bg-[#1a1a1a] text-white"
85
+ : "text-[#666] hover:text-[#888]"
86
+ }`}
87
+ >
88
+ {p.name}
89
+ </button>
90
+ ))}
91
+ </div>
73
92
  </div>
74
- </div>
93
+ )}
75
94
 
76
- <IntegrationsPanel
77
- providerId={selectedProvider}
78
- projectId={projectId}
79
- hideMcpConfig
80
- onBrowseTriggers={handleBrowseTriggers}
81
- />
95
+ {providers.length === 0 ? (
96
+ <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-8 text-center">
97
+ <p className="text-[#666]">No integration providers configured.</p>
98
+ <p className="text-sm text-[#555] mt-1">Add API keys for Composio or AgentDojo in Settings.</p>
99
+ </div>
100
+ ) : (
101
+ <IntegrationsPanel
102
+ providerId={selectedProvider}
103
+ projectId={projectId}
104
+ hideMcpConfig
105
+ onBrowseTriggers={handleBrowseTriggers}
106
+ />
107
+ )}
82
108
 
83
109
  {/* Trigger Types Panel */}
84
110
  {browsingToolkit && (