apteva 0.4.11 → 0.4.14
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.jdzxkzm1.js +228 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/auth/middleware.ts +42 -26
- package/src/crypto.ts +2 -2
- package/src/db-tests.ts +174 -0
- package/src/db.ts +302 -5
- package/src/integrations/agentdojo.ts +168 -42
- package/src/mcp-client.ts +15 -9
- package/src/mcp-platform.ts +244 -33
- package/src/openapi.ts +416 -21
- package/src/routes/api/agent-utils.ts +2 -2
- package/src/routes/api/api-keys.ts +95 -0
- package/src/routes/api/mcp.ts +2 -2
- package/src/routes/api/meta-agent.ts +25 -17
- package/src/routes/api/system.ts +10 -1
- package/src/routes/api/tests.ts +148 -0
- package/src/routes/api.ts +4 -0
- package/src/server.ts +2 -1
- package/src/test-runner.ts +598 -0
- package/src/web/App.tsx +23 -10
- package/src/web/components/agents/AgentPanel.tsx +4 -8
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/dashboard/Dashboard.tsx +2 -4
- package/src/web/components/index.ts +1 -0
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/settings/SettingsPage.tsx +288 -5
- package/src/web/components/skills/SkillsPage.tsx +1 -1
- package/src/web/components/tasks/TasksPage.tsx +8 -3
- package/src/web/components/telemetry/TelemetryPage.tsx +2 -5
- package/src/web/components/tests/TestsPage.tsx +580 -0
- package/src/web/context/index.ts +1 -1
- package/src/web/types.ts +1 -1
- package/dist/App.9ph8javh.js +0 -228
|
@@ -47,23 +47,31 @@ export async function handleMetaAgentRoutes(
|
|
|
47
47
|
name: "Apteva Assistant",
|
|
48
48
|
model: defaultModel,
|
|
49
49
|
provider: providerId,
|
|
50
|
-
system_prompt: `You are the Apteva Assistant, an AI that manages the Apteva agent platform.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Create, configure, start, and
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Use
|
|
65
|
-
|
|
66
|
-
|
|
50
|
+
system_prompt: `You are the Apteva Assistant, an AI that manages the Apteva agent platform. You have full control over the platform via your tools.
|
|
51
|
+
|
|
52
|
+
WHAT YOU CAN DO:
|
|
53
|
+
- **Agents**: Create, configure, start, stop, and delete AI agents
|
|
54
|
+
- **Projects**: Create projects and organize agents into them
|
|
55
|
+
- **MCP Servers**: Add tool integrations (HTTP, npm, pip) and assign them to agents
|
|
56
|
+
- **Skills**: List, enable/disable, and assign skills to agents
|
|
57
|
+
- **Providers**: Check which LLM providers have API keys configured
|
|
58
|
+
- **Communication**: Send messages to running agents
|
|
59
|
+
|
|
60
|
+
WORKFLOW FOR CREATING AGENTS:
|
|
61
|
+
1. Use list_providers to check which providers have API keys
|
|
62
|
+
2. Use create_agent with a provider that has a key, pick a model, write a good system prompt
|
|
63
|
+
3. Optionally assign MCP servers (for tools) and skills (for behavior)
|
|
64
|
+
4. Use start_agent to run it
|
|
65
|
+
|
|
66
|
+
AGENT FEATURES (enable when creating/updating):
|
|
67
|
+
- **memory**: Persistent memory across conversations (needs OpenAI key for embeddings)
|
|
68
|
+
- **tasks**: Scheduling and task tracking
|
|
69
|
+
- **vision**: Image and PDF understanding
|
|
70
|
+
- **mcp**: Required if assigning MCP servers — gives the agent tool-use capability
|
|
71
|
+
- **files**: File read/write in agent workspace
|
|
72
|
+
|
|
73
|
+
ALWAYS use your tools proactively. When a user says "create an agent", don't explain how — just do it. Confirm what you did after.
|
|
74
|
+
Be concise. Use markdown formatting.`,
|
|
67
75
|
features: {
|
|
68
76
|
memory: false,
|
|
69
77
|
tasks: false,
|
package/src/routes/api/system.ts
CHANGED
|
@@ -126,8 +126,17 @@ export async function handleSystemRoutes(
|
|
|
126
126
|
if (path === "/api/tasks" && method === "GET") {
|
|
127
127
|
const url = new URL(req.url);
|
|
128
128
|
const status = url.searchParams.get("status") || "all";
|
|
129
|
+
const projectId = url.searchParams.get("project_id");
|
|
130
|
+
|
|
131
|
+
let runningAgents = AgentDB.findAll().filter(a => a.status === "running" && a.port);
|
|
132
|
+
|
|
133
|
+
// Filter agents by project if requested
|
|
134
|
+
if (projectId === "unassigned") {
|
|
135
|
+
runningAgents = runningAgents.filter(a => !a.project_id);
|
|
136
|
+
} else if (projectId) {
|
|
137
|
+
runningAgents = runningAgents.filter(a => a.project_id === projectId);
|
|
138
|
+
}
|
|
129
139
|
|
|
130
|
-
const runningAgents = AgentDB.findAll().filter(a => a.status === "running" && a.port);
|
|
131
140
|
const allTasks: any[] = [];
|
|
132
141
|
|
|
133
142
|
for (const agent of runningAgents) {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { json } from "./helpers";
|
|
2
|
+
import { TestCaseDB, TestRunDB } from "../../db-tests";
|
|
3
|
+
import { AgentDB } from "../../db";
|
|
4
|
+
import { runTest, runAll } from "../../test-runner";
|
|
5
|
+
|
|
6
|
+
export async function handleTestRoutes(
|
|
7
|
+
req: Request,
|
|
8
|
+
path: string,
|
|
9
|
+
method: string,
|
|
10
|
+
): Promise<Response | null> {
|
|
11
|
+
// GET /api/tests - List test cases
|
|
12
|
+
if (path === "/api/tests" && method === "GET") {
|
|
13
|
+
const url = new URL(req.url);
|
|
14
|
+
const projectId = url.searchParams.get("project_id") || undefined;
|
|
15
|
+
const tests = TestCaseDB.findAll(projectId);
|
|
16
|
+
|
|
17
|
+
// Enrich with agent name and latest run
|
|
18
|
+
const enriched = tests.map(tc => {
|
|
19
|
+
const agent = tc.agent_id ? AgentDB.findById(tc.agent_id) : null;
|
|
20
|
+
const lastRun = TestRunDB.getLatestByTestCase(tc.id);
|
|
21
|
+
return {
|
|
22
|
+
...tc,
|
|
23
|
+
agent_name: agent?.name || null,
|
|
24
|
+
agent_status: agent?.status || null,
|
|
25
|
+
last_run: lastRun ? {
|
|
26
|
+
id: lastRun.id,
|
|
27
|
+
status: lastRun.status,
|
|
28
|
+
score: lastRun.score,
|
|
29
|
+
duration_ms: lastRun.duration_ms,
|
|
30
|
+
judge_reasoning: lastRun.judge_reasoning,
|
|
31
|
+
generated_message: lastRun.generated_message,
|
|
32
|
+
selected_agent_id: lastRun.selected_agent_id,
|
|
33
|
+
selected_agent_name: lastRun.selected_agent_name,
|
|
34
|
+
planner_reasoning: lastRun.planner_reasoning,
|
|
35
|
+
created_at: lastRun.created_at,
|
|
36
|
+
} : null,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return json(enriched);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// POST /api/tests - Create test case
|
|
44
|
+
if (path === "/api/tests" && method === "POST") {
|
|
45
|
+
const body = await req.json() as any;
|
|
46
|
+
|
|
47
|
+
// Behavior-driven: only name + behavior required
|
|
48
|
+
// Legacy: name + agent_id + input_message + eval_criteria required
|
|
49
|
+
if (!body.name) {
|
|
50
|
+
return json({ error: "Missing required field: name" }, 400);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!body.behavior && (!body.agent_id || !body.input_message)) {
|
|
54
|
+
return json({ error: "Either 'behavior' or both 'agent_id' and 'input_message' are required" }, 400);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Validate agent if explicitly specified
|
|
58
|
+
if (body.agent_id) {
|
|
59
|
+
const agent = AgentDB.findById(body.agent_id);
|
|
60
|
+
if (!agent) {
|
|
61
|
+
return json({ error: "Agent not found" }, 404);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const testCase = TestCaseDB.create({
|
|
66
|
+
name: body.name,
|
|
67
|
+
description: body.description,
|
|
68
|
+
behavior: body.behavior,
|
|
69
|
+
agent_id: body.agent_id || null,
|
|
70
|
+
input_message: body.input_message || null,
|
|
71
|
+
eval_criteria: body.eval_criteria,
|
|
72
|
+
project_id: body.project_id,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return json(testCase, 201);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// PUT /api/tests/:id - Update test case
|
|
79
|
+
const updateMatch = path.match(/^\/api\/tests\/([^/]+)$/);
|
|
80
|
+
if (updateMatch && method === "PUT") {
|
|
81
|
+
const body = await req.json() as any;
|
|
82
|
+
const updated = TestCaseDB.update(updateMatch[1], body);
|
|
83
|
+
if (!updated) {
|
|
84
|
+
return json({ error: "Test case not found" }, 404);
|
|
85
|
+
}
|
|
86
|
+
return json(updated);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// DELETE /api/tests/:id - Delete test case
|
|
90
|
+
if (updateMatch && method === "DELETE") {
|
|
91
|
+
const deleted = TestCaseDB.delete(updateMatch[1]);
|
|
92
|
+
if (!deleted) {
|
|
93
|
+
return json({ error: "Test case not found" }, 404);
|
|
94
|
+
}
|
|
95
|
+
return json({ success: true });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// POST /api/tests/:id/run - Run single test
|
|
99
|
+
const runSingleMatch = path.match(/^\/api\/tests\/([^/]+)\/run$/);
|
|
100
|
+
if (runSingleMatch && method === "POST") {
|
|
101
|
+
const testCase = TestCaseDB.findById(runSingleMatch[1]);
|
|
102
|
+
if (!testCase) {
|
|
103
|
+
return json({ error: "Test case not found" }, 404);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const result = await runTest(testCase);
|
|
107
|
+
return json(result);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// POST /api/tests/run - Run all (or filtered) tests
|
|
111
|
+
if (path === "/api/tests/run" && method === "POST") {
|
|
112
|
+
const body = await req.json().catch(() => ({})) as any;
|
|
113
|
+
const testCaseIds = body.test_case_ids as string[] | undefined;
|
|
114
|
+
|
|
115
|
+
const results = await runAll(testCaseIds);
|
|
116
|
+
const passed = results.filter(r => r.status === "passed").length;
|
|
117
|
+
const failed = results.filter(r => r.status === "failed").length;
|
|
118
|
+
const errors = results.filter(r => r.status === "error").length;
|
|
119
|
+
|
|
120
|
+
return json({
|
|
121
|
+
summary: { total: results.length, passed, failed, errors },
|
|
122
|
+
results,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// GET /api/tests/:id/runs - Get run history for a test
|
|
127
|
+
const runsMatch = path.match(/^\/api\/tests\/([^/]+)\/runs$/);
|
|
128
|
+
if (runsMatch && method === "GET") {
|
|
129
|
+
const testCase = TestCaseDB.findById(runsMatch[1]);
|
|
130
|
+
if (!testCase) {
|
|
131
|
+
return json({ error: "Test case not found" }, 404);
|
|
132
|
+
}
|
|
133
|
+
const runs = TestRunDB.findByTestCase(runsMatch[1]);
|
|
134
|
+
return json(runs);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// GET /api/tests/runs/:runId - Get single run details
|
|
138
|
+
const runDetailMatch = path.match(/^\/api\/tests\/runs\/([^/]+)$/);
|
|
139
|
+
if (runDetailMatch && method === "GET") {
|
|
140
|
+
const run = TestRunDB.findById(runDetailMatch[1]);
|
|
141
|
+
if (!run) {
|
|
142
|
+
return json({ error: "Test run not found" }, 404);
|
|
143
|
+
}
|
|
144
|
+
return json(run);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
}
|
package/src/routes/api.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { handleSkillRoutes } from "./api/skills";
|
|
|
10
10
|
import { handleIntegrationRoutes } from "./api/integrations";
|
|
11
11
|
import { handleMetaAgentRoutes } from "./api/meta-agent";
|
|
12
12
|
import { handleTelemetryRoutes } from "./api/telemetry";
|
|
13
|
+
import { handleTestRoutes } from "./api/tests";
|
|
14
|
+
import { handleApiKeyRoutes } from "./api/api-keys";
|
|
13
15
|
import { handlePlatformMcpRequest } from "../mcp-platform";
|
|
14
16
|
|
|
15
17
|
// Re-export for backward compatibility (server.ts dynamic import)
|
|
@@ -29,6 +31,7 @@ export async function handleApiRequest(
|
|
|
29
31
|
|
|
30
32
|
return (
|
|
31
33
|
(await handleSystemRoutes(req, path, method, authContext)) ??
|
|
34
|
+
(await handleApiKeyRoutes(req, path, method, authContext)) ?? // Must be before provider routes to handle /api/keys/personal
|
|
32
35
|
(await handleProviderRoutes(req, path, method, authContext)) ??
|
|
33
36
|
(await handleUserRoutes(req, path, method, authContext)) ??
|
|
34
37
|
(await handleProjectRoutes(req, path, method, authContext)) ??
|
|
@@ -38,6 +41,7 @@ export async function handleApiRequest(
|
|
|
38
41
|
(await handleIntegrationRoutes(req, path, method)) ??
|
|
39
42
|
(await handleMetaAgentRoutes(req, path, method)) ??
|
|
40
43
|
(await handleTelemetryRoutes(req, path, method)) ??
|
|
44
|
+
(await handleTestRoutes(req, path, method)) ??
|
|
41
45
|
json({ error: "Not found" }, 404)
|
|
42
46
|
);
|
|
43
47
|
}
|
package/src/server.ts
CHANGED
|
@@ -456,7 +456,8 @@ if (hasRestarts) {
|
|
|
456
456
|
continue;
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
-
|
|
459
|
+
// Use permanently assigned port from DB, fallback to dynamic
|
|
460
|
+
const port = server.port || await getNextPort();
|
|
460
461
|
const result = await startMcpProcess(server.id, cmd, serverEnv, port);
|
|
461
462
|
|
|
462
463
|
if (result.success) {
|