apteva 0.4.32 → 0.4.41
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.7907h64p.js +3 -0
- package/dist/ApiDocsPage.k3jjenpq.js +4 -0
- package/dist/App.01nq20st.js +4 -0
- package/dist/App.1maqvamf.js +4 -0
- package/dist/App.2yjrh32f.js +4 -0
- package/dist/App.3qw8nben.js +20 -0
- package/dist/App.7fb3e7mp.js +4 -0
- package/dist/App.7sy3wq8c.js +4 -0
- package/dist/App.apjrmctz.js +57 -0
- package/dist/App.av6t2yhe.js +4 -0
- package/dist/App.jqj5a094.js +46 -0
- package/dist/App.mc7xf85h.js +4 -0
- package/dist/App.myxqcj9x.js +4 -0
- package/dist/App.nm91r1mp.js +13 -0
- package/dist/App.qcknavjz.js +221 -0
- package/dist/App.vc7vfhg4.js +4 -0
- package/dist/App.z4s9zkw5.js +4 -0
- package/dist/ConnectionsPage.z1pw5xe2.js +3 -0
- package/dist/McpPage.8vc97z0b.js +3 -0
- package/dist/SettingsPage.p61bz8kd.js +3 -0
- package/dist/SkillsPage.r9x43g3g.js +3 -0
- package/dist/TasksPage.1e0zkye4.js +3 -0
- package/dist/TelemetryPage.p9vbe4gf.js +3 -0
- package/dist/TestsPage.d4xy504e.js +3 -0
- package/dist/ThreadsPage.m016am3x.js +3 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -7
- package/src/crypto.ts +4 -3
- package/src/db.ts +153 -28
- package/src/integrations/agentdojo.ts +94 -12
- package/src/integrations/index.ts +7 -0
- package/src/mcp-platform.ts +494 -121
- package/src/providers.ts +12 -12
- package/src/routes/api/agent-utils.ts +59 -46
- package/src/routes/api/agents.ts +52 -1
- package/src/routes/api/integrations.ts +11 -5
- package/src/routes/api/mcp.ts +5 -4
- package/src/routes/api/meta-agent.ts +35 -1
- package/src/routes/api/projects.ts +3 -3
- package/src/routes/api/providers.ts +121 -30
- package/src/routes/api/skills.ts +2 -3
- package/src/routes/api/system.ts +8 -13
- package/src/server.ts +31 -32
- package/src/triggers/agentdojo.ts +2 -2
- package/src/web/App.tsx +18 -10
- package/src/web/components/activity/ActivityPage.tsx +241 -388
- package/src/web/components/agents/AgentCard.tsx +5 -13
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/Select.tsx +4 -3
- package/src/web/components/dashboard/Dashboard.tsx +155 -30
- package/src/web/components/index.ts +1 -1
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/mcp/IntegrationsPanel.tsx +126 -35
- package/src/web/components/mcp/McpPage.tsx +10 -1
- package/src/web/components/meta-agent/MetaAgent.tsx +4 -2
- package/src/web/components/settings/SettingsPage.tsx +133 -48
- package/src/web/components/tasks/TasksPage.tsx +48 -16
- package/src/web/components/telemetry/TelemetryPage.tsx +184 -0
- package/src/web/components/threads/ThreadsPage.tsx +313 -0
- package/src/web/context/AuthContext.tsx +3 -3
- package/src/web/context/ProjectContext.tsx +3 -3
- package/src/web/context/TelemetryContext.tsx +24 -6
- package/src/web/context/index.ts +1 -1
- package/src/web/styles.css +20 -4
- package/src/web/types.ts +4 -3
- package/dist/ActivityPage.41nbye4r.js +0 -3
- package/dist/ApiDocsPage.4smnt8m3.js +0 -4
- package/dist/App.0sbax9et.js +0 -4
- package/dist/App.0ws427h8.js +0 -4
- package/dist/App.6q6bar8b.js +0 -4
- package/dist/App.80301vdb.js +0 -4
- package/dist/App.af2wg84v.js +0 -267
- package/dist/App.ca1rz1ph.js +0 -4
- package/dist/App.ensa6z0r.js +0 -4
- package/dist/App.f8g7tych.js +0 -13
- package/dist/App.mvtqv6qc.js +0 -20
- package/dist/App.ncgc9cxy.js +0 -4
- package/dist/App.p0fb1pds.js +0 -4
- package/dist/App.pmaq48sj.js +0 -4
- package/dist/App.yv87t9m5.js +0 -4
- package/dist/App.zjmfm8p6.js +0 -4
- package/dist/ConnectionsPage.anb3rv9a.js +0 -3
- package/dist/McpPage.y396h6fy.js +0 -3
- package/dist/SettingsPage.p1hc60gk.js +0 -3
- package/dist/SkillsPage.yj3xdsay.js +0 -3
- package/dist/TasksPage.sjv0khtv.js +0 -3
- package/dist/TelemetryPage.2qm4w16r.js +0 -3
- package/dist/TestsPage.zzs4qfj8.js +0 -3
package/src/providers.ts
CHANGED
|
@@ -12,7 +12,8 @@ export const PROVIDERS = {
|
|
|
12
12
|
docsUrl: "https://console.anthropic.com/settings/keys",
|
|
13
13
|
testEndpoint: "https://api.anthropic.com/v1/messages",
|
|
14
14
|
models: [
|
|
15
|
-
{ value: "claude-sonnet-4-
|
|
15
|
+
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", recommended: true },
|
|
16
|
+
{ value: "claude-sonnet-4-5", label: "Claude Sonnet 4.5" },
|
|
16
17
|
{ value: "claude-haiku-4-5", label: "Claude Haiku 4.5 (Fast)" },
|
|
17
18
|
],
|
|
18
19
|
},
|
|
@@ -152,22 +153,21 @@ export const PROVIDERS = {
|
|
|
152
153
|
browserengine: {
|
|
153
154
|
id: "browserengine",
|
|
154
155
|
name: "BrowserEngine",
|
|
155
|
-
displayName: "BrowserEngine
|
|
156
|
+
displayName: "BrowserEngine",
|
|
156
157
|
type: "browser" as const,
|
|
157
|
-
envVar: "
|
|
158
|
-
docsUrl: "",
|
|
159
|
-
description: "
|
|
160
|
-
isLocal: true,
|
|
158
|
+
envVar: "BROWSERENGINE_API_KEY",
|
|
159
|
+
docsUrl: "https://browserengine.co",
|
|
160
|
+
description: "Cloud browser automation with stealth browsing and proxies",
|
|
161
161
|
models: [],
|
|
162
162
|
},
|
|
163
|
-
|
|
164
|
-
id: "
|
|
165
|
-
name: "
|
|
166
|
-
displayName: "
|
|
163
|
+
cdp: {
|
|
164
|
+
id: "cdp",
|
|
165
|
+
name: "CDP",
|
|
166
|
+
displayName: "Direct CDP",
|
|
167
167
|
type: "browser" as const,
|
|
168
|
-
envVar: "
|
|
168
|
+
envVar: "CDP_URL",
|
|
169
169
|
docsUrl: "",
|
|
170
|
-
description: "
|
|
170
|
+
description: "Connect directly to any browser via Chrome DevTools Protocol",
|
|
171
171
|
isLocal: true,
|
|
172
172
|
models: [],
|
|
173
173
|
},
|
|
@@ -3,7 +3,7 @@ import { join } from "path";
|
|
|
3
3
|
import { homedir } from "os";
|
|
4
4
|
import { mkdirSync, existsSync, rmSync } from "fs";
|
|
5
5
|
import { agentProcesses, agentsStarting, getBinaryPathForAgent, getBinaryStatus, BIN_DIR, telemetryBroadcaster, isShuttingDown, type TelemetryEvent } from "../../server";
|
|
6
|
-
import { AgentDB, McpServerDB, SkillDB, TelemetryDB, generateId, getMultiAgentConfig, getOperatorConfig, type Agent, type Project } from "../../db";
|
|
6
|
+
import { AgentDB, McpServerDB, SkillDB, SubscriptionDB, TelemetryDB, generateId, getMultiAgentConfig, getOperatorConfig, type Agent, type Project } from "../../db";
|
|
7
7
|
import { ProviderKeys, PROVIDERS, type ProviderId } from "../../providers";
|
|
8
8
|
import { binaryExists } from "../../binary";
|
|
9
9
|
|
|
@@ -93,32 +93,29 @@ function buildOperatorConfig(features: Agent["features"], projectId: string | nu
|
|
|
93
93
|
if (!opConfig.enabled) {
|
|
94
94
|
return {
|
|
95
95
|
enabled: false,
|
|
96
|
-
virtual_browser: "http://localhost:8098",
|
|
97
96
|
display_width: 1024,
|
|
98
97
|
display_height: 768,
|
|
99
98
|
max_actions_per_turn: 5,
|
|
100
99
|
};
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
const browserProvider = opConfig.browser_provider || "";
|
|
102
|
+
const browserProvider = opConfig.browser_provider || "browserengine";
|
|
104
103
|
const displayWidth = opConfig.display_width || 1024;
|
|
105
104
|
const displayHeight = opConfig.display_height || 768;
|
|
106
105
|
const maxActions = opConfig.max_actions_per_turn || 5;
|
|
107
106
|
|
|
108
|
-
// Map browser provider IDs to agent binary config
|
|
109
107
|
const operatorResult: Record<string, unknown> = {
|
|
110
108
|
enabled: true,
|
|
109
|
+
browser_provider: browserProvider,
|
|
111
110
|
display_width: displayWidth,
|
|
112
111
|
display_height: displayHeight,
|
|
113
112
|
max_actions_per_turn: maxActions,
|
|
114
113
|
};
|
|
115
114
|
|
|
115
|
+
// Only include the active provider's config
|
|
116
116
|
if (browserProvider === "browserbase") {
|
|
117
117
|
const raw = ProviderKeys.getDecryptedForProject("browserbase", projectId);
|
|
118
|
-
operatorResult.browser_provider = "browserbase";
|
|
119
|
-
operatorResult.virtual_browser = "http://localhost:8098"; // fallback
|
|
120
118
|
if (raw) {
|
|
121
|
-
// Parse JSON object {api_key, project_id} or plain string (backwards compat)
|
|
122
119
|
try {
|
|
123
120
|
const parsed = JSON.parse(raw);
|
|
124
121
|
operatorResult.browserbase = { api_key: parsed.api_key || raw, project_id: parsed.project_id || "" };
|
|
@@ -128,26 +125,21 @@ function buildOperatorConfig(features: Agent["features"], projectId: string | nu
|
|
|
128
125
|
}
|
|
129
126
|
} else if (browserProvider === "steel") {
|
|
130
127
|
const apiKey = ProviderKeys.getDecryptedForProject("steel", projectId);
|
|
131
|
-
operatorResult.browser_provider = "steel";
|
|
132
|
-
operatorResult.virtual_browser = "http://localhost:8098"; // fallback
|
|
133
128
|
if (apiKey) {
|
|
134
129
|
operatorResult.steel = { api_key: apiKey, base_url: "https://api.steel.dev" };
|
|
135
130
|
}
|
|
136
|
-
} else if (browserProvider === "
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
operatorResult.chrome = { debug_url: debugUrl };
|
|
131
|
+
} else if (browserProvider === "cdp") {
|
|
132
|
+
// CDP uses a URL, not an API key — stored as the provider key value
|
|
133
|
+
const cdpUrl = ProviderKeys.getDecryptedForProject("cdp", projectId);
|
|
134
|
+
if (cdpUrl) {
|
|
135
|
+
operatorResult.cdp = { url: cdpUrl };
|
|
142
136
|
}
|
|
143
|
-
} else if (browserProvider === "browserengine") {
|
|
144
|
-
const url = ProviderKeys.getDecryptedForProject("browserengine", projectId);
|
|
145
|
-
operatorResult.browser_provider = "self";
|
|
146
|
-
operatorResult.virtual_browser = url || "http://localhost:8098";
|
|
147
137
|
} else {
|
|
148
|
-
// Default:
|
|
149
|
-
|
|
150
|
-
|
|
138
|
+
// Default: browserengine
|
|
139
|
+
const apiKey = ProviderKeys.getDecryptedForProject("browserengine", projectId);
|
|
140
|
+
if (apiKey) {
|
|
141
|
+
operatorResult.browserengine = { api_key: apiKey, base_url: "https://api.browserengine.co" };
|
|
142
|
+
}
|
|
151
143
|
}
|
|
152
144
|
|
|
153
145
|
return operatorResult;
|
|
@@ -173,8 +165,12 @@ export function buildAgentConfig(agent: Agent, providerKey: string) {
|
|
|
173
165
|
enabled: boolean;
|
|
174
166
|
}> = [];
|
|
175
167
|
|
|
168
|
+
// Batch load skills and MCP servers (2 queries instead of N+M)
|
|
169
|
+
const skillMap = SkillDB.findByIds(agent.skills || []);
|
|
170
|
+
const mcpMap = McpServerDB.findByIds(agent.mcp_servers || []);
|
|
171
|
+
|
|
176
172
|
for (const skillId of agent.skills || []) {
|
|
177
|
-
const skill =
|
|
173
|
+
const skill = skillMap.get(skillId);
|
|
178
174
|
if (!skill || !skill.enabled) continue;
|
|
179
175
|
|
|
180
176
|
skillDefinitions.push({
|
|
@@ -190,7 +186,7 @@ export function buildAgentConfig(agent: Agent, providerKey: string) {
|
|
|
190
186
|
}
|
|
191
187
|
|
|
192
188
|
for (const id of agent.mcp_servers || []) {
|
|
193
|
-
const server =
|
|
189
|
+
const server = mcpMap.get(id);
|
|
194
190
|
if (!server) continue;
|
|
195
191
|
|
|
196
192
|
if (server.type === "local" && server.status === "running") {
|
|
@@ -387,9 +383,8 @@ export async function pushSkillsToAgent(agentId: string, port: number, skills: A
|
|
|
387
383
|
}
|
|
388
384
|
|
|
389
385
|
try {
|
|
390
|
-
// Push
|
|
391
|
-
|
|
392
|
-
// First try PUT to update existing skill
|
|
386
|
+
// Push all skills in parallel - try PUT first (update), then POST (create) if not found
|
|
387
|
+
await Promise.allSettled(skills.map(async (skill) => {
|
|
393
388
|
let res = await agentFetch(agentId, port, "/skills", {
|
|
394
389
|
method: "PUT",
|
|
395
390
|
headers: { "Content-Type": "application/json" },
|
|
@@ -397,7 +392,6 @@ export async function pushSkillsToAgent(agentId: string, port: number, skills: A
|
|
|
397
392
|
signal: AbortSignal.timeout(5000),
|
|
398
393
|
});
|
|
399
394
|
|
|
400
|
-
// If skill doesn't exist (404), create it with POST
|
|
401
395
|
if (res.status === 404) {
|
|
402
396
|
res = await agentFetch(agentId, port, "/skills", {
|
|
403
397
|
method: "POST",
|
|
@@ -411,7 +405,7 @@ export async function pushSkillsToAgent(agentId: string, port: number, skills: A
|
|
|
411
405
|
const data = await res.json().catch(() => ({}));
|
|
412
406
|
console.error(`[pushSkillsToAgent] Failed to push skill ${skill.name}:`, data.error || res.status);
|
|
413
407
|
}
|
|
414
|
-
}
|
|
408
|
+
}));
|
|
415
409
|
|
|
416
410
|
// Enable skills globally via POST /skills/status
|
|
417
411
|
const statusRes = await agentFetch(agentId, port, "/skills/status", {
|
|
@@ -611,6 +605,8 @@ export async function startAgentProcess(
|
|
|
611
605
|
console.log(` Pushing configuration...`);
|
|
612
606
|
}
|
|
613
607
|
const config = buildAgentConfig(agent, providerKey);
|
|
608
|
+
console.log(`[DEBUG] operator config being pushed:`, JSON.stringify(config.operator, null, 2));
|
|
609
|
+
console.log(`[DEBUG] builtin_tools:`, JSON.stringify(config.operator?.builtin_tools));
|
|
614
610
|
const configResult = await pushConfigToAgent(agent.id, port, config);
|
|
615
611
|
if (!configResult.success) {
|
|
616
612
|
if (!silent) {
|
|
@@ -650,24 +646,27 @@ export async function startAgentProcess(
|
|
|
650
646
|
}
|
|
651
647
|
|
|
652
648
|
// Transform DB agent to API response format (camelCase for frontend compatibility)
|
|
649
|
+
// Uses batch queries + light MCP loading (no decryption) for performance
|
|
653
650
|
export function toApiAgent(agent: Agent) {
|
|
654
|
-
//
|
|
651
|
+
// Batch load MCP servers (light = no decryption) and skills in 2 queries
|
|
652
|
+
const mcpMap = McpServerDB.findByIdsLight(agent.mcp_servers || []);
|
|
653
|
+
const skillMap = SkillDB.findByIds(agent.skills || []);
|
|
654
|
+
|
|
655
655
|
const mcpServerDetails = (agent.mcp_servers || [])
|
|
656
|
-
.map(id =>
|
|
657
|
-
.filter((s): s is NonNullable<typeof s> => s
|
|
656
|
+
.map(id => mcpMap.get(id))
|
|
657
|
+
.filter((s): s is NonNullable<typeof s> => !!s)
|
|
658
658
|
.map(s => ({
|
|
659
659
|
id: s.id,
|
|
660
660
|
name: s.name,
|
|
661
661
|
type: s.type,
|
|
662
662
|
status: s.status,
|
|
663
663
|
port: s.port,
|
|
664
|
-
url: s.url,
|
|
664
|
+
url: s.url,
|
|
665
665
|
}));
|
|
666
666
|
|
|
667
|
-
// Look up skill details
|
|
668
667
|
const skillDetails = (agent.skills || [])
|
|
669
|
-
.map(id =>
|
|
670
|
-
.filter((s): s is NonNullable<typeof s> => s
|
|
668
|
+
.map(id => skillMap.get(id))
|
|
669
|
+
.filter((s): s is NonNullable<typeof s> => !!s)
|
|
671
670
|
.map(s => ({
|
|
672
671
|
id: s.id,
|
|
673
672
|
name: s.name,
|
|
@@ -676,6 +675,11 @@ export function toApiAgent(agent: Agent) {
|
|
|
676
675
|
enabled: s.enabled,
|
|
677
676
|
}));
|
|
678
677
|
|
|
678
|
+
// Look up subscriptions
|
|
679
|
+
const subscriptions = SubscriptionDB.findByAgentId(agent.id).map(s => ({
|
|
680
|
+
id: s.id, trigger_slug: s.trigger_slug, enabled: s.enabled,
|
|
681
|
+
}));
|
|
682
|
+
|
|
679
683
|
return {
|
|
680
684
|
id: agent.id,
|
|
681
685
|
name: agent.name,
|
|
@@ -685,29 +689,33 @@ export function toApiAgent(agent: Agent) {
|
|
|
685
689
|
status: agent.status,
|
|
686
690
|
port: agent.port,
|
|
687
691
|
features: agent.features,
|
|
688
|
-
mcpServers: agent.mcp_servers,
|
|
689
|
-
mcpServerDetails,
|
|
690
|
-
skills: agent.skills,
|
|
691
|
-
skillDetails,
|
|
692
|
+
mcpServers: agent.mcp_servers,
|
|
693
|
+
mcpServerDetails,
|
|
694
|
+
skills: agent.skills,
|
|
695
|
+
skillDetails,
|
|
696
|
+
subscriptions,
|
|
692
697
|
projectId: agent.project_id,
|
|
693
698
|
createdAt: agent.created_at,
|
|
694
699
|
updatedAt: agent.updated_at,
|
|
695
700
|
};
|
|
696
701
|
}
|
|
697
702
|
|
|
698
|
-
// Batch transform: fetch all MCP servers + skills in
|
|
703
|
+
// Batch transform: fetch all MCP servers + skills + subscriptions in 3 queries instead of N per agent
|
|
699
704
|
export function toApiAgentsBatch(agents: Agent[]) {
|
|
700
705
|
// Collect all unique IDs
|
|
701
706
|
const allMcpIds = new Set<string>();
|
|
702
707
|
const allSkillIds = new Set<string>();
|
|
708
|
+
const allAgentIds: string[] = [];
|
|
703
709
|
for (const agent of agents) {
|
|
710
|
+
allAgentIds.push(agent.id);
|
|
704
711
|
for (const id of agent.mcp_servers || []) allMcpIds.add(id);
|
|
705
712
|
for (const id of agent.skills || []) allSkillIds.add(id);
|
|
706
713
|
}
|
|
707
714
|
|
|
708
|
-
// Batch load in
|
|
709
|
-
const mcpMap = McpServerDB.
|
|
715
|
+
// Batch load in 3 queries (Light = no decryption for MCP servers)
|
|
716
|
+
const mcpMap = McpServerDB.findByIdsLight([...allMcpIds]);
|
|
710
717
|
const skillMap = SkillDB.findByIds([...allSkillIds]);
|
|
718
|
+
const subsMap = SubscriptionDB.findByAgentIds(allAgentIds);
|
|
711
719
|
|
|
712
720
|
return agents.map(agent => {
|
|
713
721
|
const mcpServerDetails = (agent.mcp_servers || [])
|
|
@@ -720,11 +728,15 @@ export function toApiAgentsBatch(agents: Agent[]) {
|
|
|
720
728
|
.filter((s): s is NonNullable<typeof s> => !!s)
|
|
721
729
|
.map(s => ({ id: s.id, name: s.name, description: s.description, version: s.version, enabled: s.enabled }));
|
|
722
730
|
|
|
731
|
+
const subscriptions = (subsMap.get(agent.id) || []).map(s => ({
|
|
732
|
+
id: s.id, trigger_slug: s.trigger_slug, enabled: s.enabled,
|
|
733
|
+
}));
|
|
734
|
+
|
|
723
735
|
return {
|
|
724
736
|
id: agent.id, name: agent.name, model: agent.model, provider: agent.provider,
|
|
725
737
|
systemPrompt: agent.system_prompt, status: agent.status, port: agent.port,
|
|
726
738
|
features: agent.features, mcpServers: agent.mcp_servers, mcpServerDetails,
|
|
727
|
-
skills: agent.skills, skillDetails, projectId: agent.project_id,
|
|
739
|
+
skills: agent.skills, skillDetails, subscriptions, projectId: agent.project_id,
|
|
728
740
|
createdAt: agent.created_at, updatedAt: agent.updated_at,
|
|
729
741
|
};
|
|
730
742
|
});
|
|
@@ -742,11 +754,12 @@ export function toApiProject(project: Project) {
|
|
|
742
754
|
};
|
|
743
755
|
}
|
|
744
756
|
|
|
745
|
-
// Helper to fetch from a running agent (with authentication)
|
|
746
|
-
export async function fetchFromAgent(agentId: string, port: number, endpoint: string): Promise<any> {
|
|
757
|
+
// Helper to fetch from a running agent (with authentication + timeout)
|
|
758
|
+
export async function fetchFromAgent(agentId: string, port: number, endpoint: string, timeoutMs = 3000): Promise<any> {
|
|
747
759
|
try {
|
|
748
760
|
const response = await agentFetch(agentId, port, endpoint, {
|
|
749
761
|
headers: { "Accept": "application/json" },
|
|
762
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
750
763
|
});
|
|
751
764
|
if (response.ok) {
|
|
752
765
|
return await response.json();
|
package/src/routes/api/agents.ts
CHANGED
|
@@ -62,7 +62,7 @@ export async function handleAgentRoutes(
|
|
|
62
62
|
const agent = AgentDB.create({
|
|
63
63
|
id: generateId(),
|
|
64
64
|
name,
|
|
65
|
-
model: model || "claude-sonnet-4-
|
|
65
|
+
model: model || "claude-sonnet-4-6",
|
|
66
66
|
provider: provider || "anthropic",
|
|
67
67
|
system_prompt: systemPrompt || "You are a helpful assistant.",
|
|
68
68
|
features: features || DEFAULT_FEATURES,
|
|
@@ -358,6 +358,7 @@ export async function handleAgentRoutes(
|
|
|
358
358
|
try {
|
|
359
359
|
const body = await req.json();
|
|
360
360
|
|
|
361
|
+
|
|
361
362
|
// Proxy to the agent's /chat endpoint with authentication
|
|
362
363
|
const response = await agentFetch(agent.id, agent.port, "/chat", {
|
|
363
364
|
method: "POST",
|
|
@@ -451,6 +452,56 @@ export async function handleAgentRoutes(
|
|
|
451
452
|
|
|
452
453
|
// ==================== THREAD & MESSAGE PROXY ====================
|
|
453
454
|
|
|
455
|
+
// GET /api/threads - Consolidated threads from all running agents
|
|
456
|
+
if (path === "/api/threads" && method === "GET") {
|
|
457
|
+
const url = new URL(req.url);
|
|
458
|
+
const projectId = url.searchParams.get("project_id");
|
|
459
|
+
|
|
460
|
+
let agents;
|
|
461
|
+
if (projectId === "unassigned") {
|
|
462
|
+
agents = AgentDB.findByProject(null);
|
|
463
|
+
} else if (projectId) {
|
|
464
|
+
agents = AgentDB.findByProject(projectId);
|
|
465
|
+
} else {
|
|
466
|
+
agents = AgentDB.findAll();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Only query running agents (excluding meta agent)
|
|
470
|
+
const runningAgents = agents.filter(a => a.id !== META_AGENT_ID && a.status === "running" && a.port);
|
|
471
|
+
|
|
472
|
+
const results = await Promise.allSettled(
|
|
473
|
+
runningAgents.map(async (agent) => {
|
|
474
|
+
const response = await agentFetch(agent.id, agent.port!, "/threads", {
|
|
475
|
+
method: "GET",
|
|
476
|
+
headers: { "Accept": "application/json" },
|
|
477
|
+
signal: AbortSignal.timeout(3000),
|
|
478
|
+
});
|
|
479
|
+
if (!response.ok) return [];
|
|
480
|
+
const data = await response.json() as any;
|
|
481
|
+
const threads = Array.isArray(data) ? data : (data.threads ?? []);
|
|
482
|
+
return threads.map((t: any) => ({
|
|
483
|
+
...t,
|
|
484
|
+
agent_id: agent.id,
|
|
485
|
+
agent_name: agent.name,
|
|
486
|
+
}));
|
|
487
|
+
})
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
const allThreads = results
|
|
491
|
+
.filter((r): r is PromiseFulfilledResult<any[]> => r.status === "fulfilled")
|
|
492
|
+
.flatMap(r => r.value)
|
|
493
|
+
.filter((t: any) => !t.parent_id);
|
|
494
|
+
|
|
495
|
+
// Sort by most recent first
|
|
496
|
+
allThreads.sort((a, b) => {
|
|
497
|
+
const ta = a.updated_at || a.created_at || "";
|
|
498
|
+
const tb = b.updated_at || b.created_at || "";
|
|
499
|
+
return tb.localeCompare(ta);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
return json({ threads: allThreads });
|
|
503
|
+
}
|
|
504
|
+
|
|
454
505
|
// GET/POST /api/agents/:id/threads
|
|
455
506
|
const threadsListMatch = path.match(/^\/api\/agents\/([^/]+)\/threads$/);
|
|
456
507
|
if (threadsListMatch && method === "GET") {
|
|
@@ -111,8 +111,11 @@ export async function handleIntegrationRoutes(
|
|
|
111
111
|
|
|
112
112
|
try {
|
|
113
113
|
const body = await req.json();
|
|
114
|
-
const { appSlug, redirectUrl, credentials
|
|
115
|
-
|
|
114
|
+
const { appSlug, redirectUrl, credentials } = body;
|
|
115
|
+
// Read project_id from query param (frontend sends it there), fall back to body
|
|
116
|
+
const url = new URL(req.url);
|
|
117
|
+
const projectId = url.searchParams.get("project_id") || body.project_id || null;
|
|
118
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
116
119
|
if (!apiKey) {
|
|
117
120
|
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
118
121
|
}
|
|
@@ -127,7 +130,8 @@ export async function handleIntegrationRoutes(
|
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
// Default redirect URL back to our integrations page
|
|
130
|
-
const
|
|
133
|
+
const origin = req.headers.get("origin") || process.env.INSTANCE_URL || `http://localhost:${process.env.PORT || 4280}`;
|
|
134
|
+
const callbackUrl = redirectUrl || `${origin}/mcp?tab=hosted&connected=${appSlug}`;
|
|
131
135
|
|
|
132
136
|
const result = await provider.initiateConnection(apiKey, user.id, appSlug, callbackUrl, credentials);
|
|
133
137
|
return json(result);
|
|
@@ -329,7 +333,8 @@ export async function handleIntegrationRoutes(
|
|
|
329
333
|
: mcpUrl;
|
|
330
334
|
|
|
331
335
|
// Check if already exists (match by config ID in URL)
|
|
332
|
-
|
|
336
|
+
// Use Light variant to skip expensive decryption of env/headers
|
|
337
|
+
const existing = McpServerDB.findAllLight().find(
|
|
333
338
|
s => s.source === "composio" && s.url?.includes(configId)
|
|
334
339
|
);
|
|
335
340
|
if (existing) {
|
|
@@ -527,8 +532,9 @@ export async function handleIntegrationRoutes(
|
|
|
527
532
|
console.log(`[agentdojo:add] fetched server: slug=${server.slug} name=${server.name} url=${server.url?.substring(0, 80)}`);
|
|
528
533
|
|
|
529
534
|
// Check if already exists — match by slug in URL, scoped to same project
|
|
535
|
+
// Use Light variant to skip expensive decryption of env/headers
|
|
530
536
|
const effectiveProjectId = projectId && projectId !== "unassigned" ? projectId : null;
|
|
531
|
-
const allServers = McpServerDB.
|
|
537
|
+
const allServers = McpServerDB.findAllLight();
|
|
532
538
|
const agentdojoServers = allServers.filter(s => s.source === "agentdojo");
|
|
533
539
|
console.log(`[agentdojo:add] total servers=${allServers.length} agentdojo servers=${agentdojoServers.length}`);
|
|
534
540
|
const existing = agentdojoServers.find(
|
package/src/routes/api/mcp.ts
CHANGED
|
@@ -28,16 +28,17 @@ export async function handleMcpRoutes(
|
|
|
28
28
|
let queryMode: string;
|
|
29
29
|
if (forAgent !== null) {
|
|
30
30
|
// Get servers available for an agent (global + agent's project)
|
|
31
|
-
|
|
31
|
+
// Use Light variant: skips expensive decryption of env/headers
|
|
32
|
+
servers = McpServerDB.findForAgentLight(forAgent || null);
|
|
32
33
|
queryMode = `forAgent=${forAgent}`;
|
|
33
34
|
} else if (projectFilter === "global") {
|
|
34
|
-
servers = McpServerDB.
|
|
35
|
+
servers = McpServerDB.findGlobalLight();
|
|
35
36
|
queryMode = "global";
|
|
36
37
|
} else if (projectFilter && projectFilter !== "all") {
|
|
37
|
-
servers = McpServerDB.
|
|
38
|
+
servers = McpServerDB.findByProjectLight(projectFilter);
|
|
38
39
|
queryMode = `project=${projectFilter}`;
|
|
39
40
|
} else {
|
|
40
|
-
servers = McpServerDB.
|
|
41
|
+
servers = McpServerDB.findAllLight();
|
|
41
42
|
queryMode = "all";
|
|
42
43
|
}
|
|
43
44
|
const agentdojoCount = servers.filter(s => s.source === "agentdojo").length;
|
|
@@ -56,6 +56,9 @@ WHAT YOU CAN DO:
|
|
|
56
56
|
- **Skills**: List, enable/disable, and assign skills to agents
|
|
57
57
|
- **Providers**: Check which LLM providers have API keys configured
|
|
58
58
|
- **Communication**: Send messages to running agents
|
|
59
|
+
- **Integrations**: Connect third-party apps (GitHub, Slack, etc.) via API key, create MCP servers from them, and add them locally
|
|
60
|
+
- **Tests**: Create and run automated tests for agent workflows
|
|
61
|
+
- **Triggers**: Subscribe agents to external events (webhooks)
|
|
59
62
|
|
|
60
63
|
WORKFLOW FOR CREATING AGENTS:
|
|
61
64
|
1. Use list_providers to check which providers have API keys
|
|
@@ -63,6 +66,14 @@ WORKFLOW FOR CREATING AGENTS:
|
|
|
63
66
|
3. Optionally assign MCP servers (for tools) and skills (for behavior)
|
|
64
67
|
4. Use start_agent to run it
|
|
65
68
|
|
|
69
|
+
WORKFLOW FOR ADDING INTEGRATIONS:
|
|
70
|
+
1. Use list_integration_providers to see available providers (agentdojo, composio)
|
|
71
|
+
2. Use list_integration_apps to browse available apps/toolkits
|
|
72
|
+
3. Use connect_integration_app to authenticate with an API key (for OAuth, direct user to Browse Toolkits UI)
|
|
73
|
+
4. Use create_integration_config to create an MCP server from the connected app
|
|
74
|
+
5. Use add_integration_config_locally to add it as a local MCP server
|
|
75
|
+
6. Use assign_mcp_server_to_agent to give an agent access
|
|
76
|
+
|
|
66
77
|
AGENT FEATURES (enable when creating/updating):
|
|
67
78
|
- **memory**: Persistent memory across conversations (needs OpenAI key for embeddings)
|
|
68
79
|
- **tasks**: Scheduling and task tracking
|
|
@@ -70,6 +81,9 @@ AGENT FEATURES (enable when creating/updating):
|
|
|
70
81
|
- **mcp**: Required if assigning MCP servers — gives the agent tool-use capability
|
|
71
82
|
- **files**: File read/write in agent workspace
|
|
72
83
|
|
|
84
|
+
CRITICAL — PROJECT CONTEXT:
|
|
85
|
+
Your chat context tells you the current project (name and id). **You MUST pass this project_id to EVERY tool call that accepts a project_id parameter.** API keys, integrations, MCP servers, and agents are scoped per project — calls without project_id will fail to find them. Extract the id from your context and always include it.
|
|
86
|
+
|
|
73
87
|
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
88
|
Be concise. Use markdown formatting.`,
|
|
75
89
|
features: {
|
|
@@ -81,6 +95,10 @@ Be concise. Use markdown formatting.`,
|
|
|
81
95
|
realtime: false,
|
|
82
96
|
files: false,
|
|
83
97
|
agents: false,
|
|
98
|
+
builtinTools: {
|
|
99
|
+
webSearch: providerId === "anthropic",
|
|
100
|
+
webFetch: providerId === "anthropic",
|
|
101
|
+
},
|
|
84
102
|
},
|
|
85
103
|
mcp_servers: [],
|
|
86
104
|
skills: [],
|
|
@@ -118,8 +136,24 @@ Be concise. Use markdown formatting.`,
|
|
|
118
136
|
return json({ agent: toApiAgent(metaAgent), message: "Already running" });
|
|
119
137
|
}
|
|
120
138
|
|
|
139
|
+
// Ensure builtinTools are enabled for anthropic provider
|
|
140
|
+
if (metaAgent.provider === "anthropic" && !metaAgent.features?.builtinTools?.webSearch) {
|
|
141
|
+
const updatedFeatures = {
|
|
142
|
+
...metaAgent.features,
|
|
143
|
+
builtinTools: { webSearch: true, webFetch: true },
|
|
144
|
+
};
|
|
145
|
+
AgentDB.update(META_AGENT_ID, { features: updatedFeatures });
|
|
146
|
+
console.log(`[meta-agent] Enabled builtinTools for anthropic provider`);
|
|
147
|
+
}
|
|
148
|
+
// Always re-read to get latest features
|
|
149
|
+
const freshAgent = AgentDB.findById(META_AGENT_ID);
|
|
150
|
+
if (!freshAgent) {
|
|
151
|
+
return json({ error: "Meta agent not found after update" }, 500);
|
|
152
|
+
}
|
|
153
|
+
console.log(`[meta-agent] Starting with builtinTools:`, JSON.stringify(freshAgent.features?.builtinTools));
|
|
154
|
+
|
|
121
155
|
// Start the agent using existing startAgentProcess function
|
|
122
|
-
const result = await startAgentProcess(
|
|
156
|
+
const result = await startAgentProcess(freshAgent, { silent: true });
|
|
123
157
|
if (!result.success) {
|
|
124
158
|
return json({ error: result.error || "Failed to start meta agent" }, 500);
|
|
125
159
|
}
|
|
@@ -88,9 +88,9 @@ export async function handleProjectRoutes(
|
|
|
88
88
|
return json({ error: "Project not found" }, 404);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// Stop any running agents in this project first
|
|
91
|
+
// Stop any running agents in this project first - in parallel
|
|
92
92
|
const projectAgents = AgentDB.findByProject(projectMatch[1]);
|
|
93
|
-
|
|
93
|
+
await Promise.allSettled(projectAgents.map(async (agent) => {
|
|
94
94
|
if (agent.status === "running") {
|
|
95
95
|
const entry = agentProcesses.get(agent.id);
|
|
96
96
|
if (entry) {
|
|
@@ -102,7 +102,7 @@ export async function handleProjectRoutes(
|
|
|
102
102
|
}
|
|
103
103
|
setAgentStatus(agent.id, "stopped", "project_deleted");
|
|
104
104
|
}
|
|
105
|
-
}
|
|
105
|
+
}));
|
|
106
106
|
|
|
107
107
|
ProjectDB.delete(projectMatch[1]);
|
|
108
108
|
return json({ success: true });
|