apteva 0.4.32 → 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.
Files changed (113) hide show
  1. package/dist/ActivityPage.c48n83h2.js +3 -0
  2. package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
  3. package/dist/App.09yb8t0b.js +1 -0
  4. package/dist/App.152mbs1r.js +4 -0
  5. package/dist/App.3a67nx9w.js +4 -0
  6. package/dist/App.9epx6785.js +4 -0
  7. package/dist/App.d8955awp.js +4 -0
  8. package/dist/App.drwb57jq.js +4 -0
  9. package/dist/App.gssbmajb.js +4 -0
  10. package/dist/App.qw70pc29.js +53 -0
  11. package/dist/App.qzbx5wtj.js +4 -0
  12. package/dist/App.r5serxkt.js +8 -0
  13. package/dist/App.tpmp9020.js +20 -0
  14. package/dist/App.v2wb4d7d.js +61 -0
  15. package/dist/App.vxmaaj0m.js +13 -0
  16. package/dist/App.w4p2tda9.js +4 -0
  17. package/dist/App.wv2ng55q.js +221 -0
  18. package/dist/App.yncnrn0f.js +4 -0
  19. package/dist/ConnectionsPage.k6cspyqq.js +3 -0
  20. package/dist/McpPage.cdxm48xj.js +3 -0
  21. package/dist/SettingsPage.evpv7c2y.js +3 -0
  22. package/dist/SkillsPage.pvzp6c1a.js +3 -0
  23. package/dist/TasksPage.6jnvbpsy.js +3 -0
  24. package/dist/TelemetryPage.t7vk24zc.js +3 -0
  25. package/dist/TestsPage.5x6658aa.js +3 -0
  26. package/dist/ThreadsPage.3fvhtevh.js +3 -0
  27. package/dist/apteva-kit.css +1 -1
  28. package/dist/index.html +1 -1
  29. package/dist/styles.css +1 -1
  30. package/package.json +10 -9
  31. package/src/crypto.ts +4 -3
  32. package/src/db.ts +171 -36
  33. package/src/integrations/agentdojo.ts +95 -12
  34. package/src/integrations/index.ts +7 -0
  35. package/src/mcp-platform.ts +870 -142
  36. package/src/openapi.ts +96 -0
  37. package/src/providers.ts +60 -34
  38. package/src/routes/api/agent-utils.ts +59 -47
  39. package/src/routes/api/agents.ts +71 -2
  40. package/src/routes/api/integrations.ts +11 -5
  41. package/src/routes/api/mcp.ts +5 -4
  42. package/src/routes/api/meta-agent.ts +37 -1
  43. package/src/routes/api/projects.ts +3 -3
  44. package/src/routes/api/providers.ts +121 -30
  45. package/src/routes/api/skills.ts +2 -3
  46. package/src/routes/api/system.ts +98 -14
  47. package/src/routes/api/telemetry.ts +19 -1
  48. package/src/routes/share.ts +85 -0
  49. package/src/server.ts +43 -32
  50. package/src/triggers/agentdojo.ts +2 -2
  51. package/src/web/App.tsx +107 -21
  52. package/src/web/components/activity/ActivityPage.tsx +242 -389
  53. package/src/web/components/agents/AgentCard.tsx +19 -27
  54. package/src/web/components/agents/AgentPanel.tsx +358 -198
  55. package/src/web/components/agents/AgentsView.tsx +4 -4
  56. package/src/web/components/agents/CreateAgentModal.tsx +21 -79
  57. package/src/web/components/api/ApiDocsPage.tsx +66 -66
  58. package/src/web/components/auth/CreateAccountStep.tsx +16 -16
  59. package/src/web/components/auth/LoginPage.tsx +10 -10
  60. package/src/web/components/common/Icons.tsx +8 -0
  61. package/src/web/components/common/LoadingSpinner.tsx +2 -2
  62. package/src/web/components/common/Modal.tsx +8 -8
  63. package/src/web/components/common/Select.tsx +11 -10
  64. package/src/web/components/connections/ConnectionsPage.tsx +4 -4
  65. package/src/web/components/connections/IntegrationsTab.tsx +18 -18
  66. package/src/web/components/connections/OverviewTab.tsx +13 -13
  67. package/src/web/components/connections/TriggersTab.tsx +99 -99
  68. package/src/web/components/dashboard/Dashboard.tsx +177 -52
  69. package/src/web/components/index.ts +1 -1
  70. package/src/web/components/layout/Header.tsx +50 -34
  71. package/src/web/components/layout/Sidebar.tsx +41 -16
  72. package/src/web/components/mcp/IntegrationsPanel.tsx +160 -69
  73. package/src/web/components/mcp/McpPage.tsx +218 -209
  74. package/src/web/components/meta-agent/MetaAgent.tsx +15 -11
  75. package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
  76. package/src/web/components/settings/SettingsPage.tsx +389 -221
  77. package/src/web/components/skills/SkillsPage.tsx +88 -88
  78. package/src/web/components/tasks/TasksPage.tsx +385 -68
  79. package/src/web/components/telemetry/TelemetryPage.tsx +294 -39
  80. package/src/web/components/tests/TestsPage.tsx +50 -50
  81. package/src/web/components/threads/ThreadsPage.tsx +315 -0
  82. package/src/web/context/AuthContext.tsx +3 -3
  83. package/src/web/context/ProjectContext.tsx +8 -3
  84. package/src/web/context/TelemetryContext.tsx +24 -6
  85. package/src/web/context/ThemeContext.tsx +69 -0
  86. package/src/web/context/index.ts +3 -1
  87. package/src/web/styles.css +25 -7
  88. package/src/web/themes.ts +99 -0
  89. package/src/web/types.ts +4 -7
  90. package/dist/ActivityPage.41nbye4r.js +0 -3
  91. package/dist/ApiDocsPage.4smnt8m3.js +0 -4
  92. package/dist/App.0sbax9et.js +0 -4
  93. package/dist/App.0ws427h8.js +0 -4
  94. package/dist/App.6q6bar8b.js +0 -4
  95. package/dist/App.80301vdb.js +0 -4
  96. package/dist/App.af2wg84v.js +0 -267
  97. package/dist/App.ca1rz1ph.js +0 -4
  98. package/dist/App.ensa6z0r.js +0 -4
  99. package/dist/App.f8g7tych.js +0 -13
  100. package/dist/App.mvtqv6qc.js +0 -20
  101. package/dist/App.ncgc9cxy.js +0 -4
  102. package/dist/App.p02f4ret.js +0 -1
  103. package/dist/App.p0fb1pds.js +0 -4
  104. package/dist/App.pmaq48sj.js +0 -4
  105. package/dist/App.yv87t9m5.js +0 -4
  106. package/dist/App.zjmfm8p6.js +0 -4
  107. package/dist/ConnectionsPage.anb3rv9a.js +0 -3
  108. package/dist/McpPage.y396h6fy.js +0 -3
  109. package/dist/SettingsPage.p1hc60gk.js +0 -3
  110. package/dist/SkillsPage.yj3xdsay.js +0 -3
  111. package/dist/TasksPage.sjv0khtv.js +0 -3
  112. package/dist/TelemetryPage.2qm4w16r.js +0 -3
  113. package/dist/TestsPage.zzs4qfj8.js +0 -3
package/src/openapi.ts CHANGED
@@ -1006,6 +1006,40 @@ while (true) {
1006
1006
  },
1007
1007
  },
1008
1008
  },
1009
+ "/tasks/{agentId}": {
1010
+ post: {
1011
+ tags: ["Tasks"],
1012
+ summary: "Create a task on an agent",
1013
+ description: "Create a new task on a running agent. The agent must have the tasks feature enabled.",
1014
+ parameters: [
1015
+ { name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID to create the task on" },
1016
+ ],
1017
+ requestBody: {
1018
+ required: true,
1019
+ content: {
1020
+ "application/json": {
1021
+ schema: {
1022
+ type: "object",
1023
+ properties: {
1024
+ title: { type: "string", description: "Task title" },
1025
+ description: { type: "string", description: "Task description" },
1026
+ type: { type: "string", enum: ["once", "recurring"], default: "once" },
1027
+ priority: { type: "integer", minimum: 1, maximum: 10, default: 5 },
1028
+ execute_at: { type: "string", format: "date-time", description: "Scheduled execution time (for one-time tasks)" },
1029
+ recurrence: { type: "string", description: "Cron expression (for recurring tasks)" },
1030
+ },
1031
+ required: ["title"],
1032
+ },
1033
+ },
1034
+ },
1035
+ },
1036
+ responses: {
1037
+ "201": { description: "Task created successfully" },
1038
+ "400": { description: "Agent is not running or invalid input" },
1039
+ "404": { description: "Agent not found" },
1040
+ },
1041
+ },
1042
+ },
1009
1043
  "/tasks/{agentId}/{taskId}": {
1010
1044
  get: {
1011
1045
  tags: ["Tasks"],
@@ -1033,6 +1067,68 @@ while (true) {
1033
1067
  "404": { description: "Agent not found" },
1034
1068
  },
1035
1069
  },
1070
+ put: {
1071
+ tags: ["Tasks"],
1072
+ summary: "Update a task on an agent",
1073
+ description: "Update an existing task on a running agent.",
1074
+ parameters: [
1075
+ { name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID" },
1076
+ { name: "taskId", in: "path", required: true, schema: { type: "string" }, description: "Task ID to update" },
1077
+ ],
1078
+ requestBody: {
1079
+ required: true,
1080
+ content: {
1081
+ "application/json": {
1082
+ schema: {
1083
+ type: "object",
1084
+ properties: {
1085
+ title: { type: "string" },
1086
+ description: { type: "string" },
1087
+ type: { type: "string", enum: ["once", "recurring"] },
1088
+ priority: { type: "integer", minimum: 1, maximum: 10 },
1089
+ execute_at: { type: "string", format: "date-time" },
1090
+ recurrence: { type: "string" },
1091
+ },
1092
+ },
1093
+ },
1094
+ },
1095
+ },
1096
+ responses: {
1097
+ "200": { description: "Task updated" },
1098
+ "400": { description: "Agent is not running or invalid input" },
1099
+ "404": { description: "Agent or task not found" },
1100
+ },
1101
+ },
1102
+ delete: {
1103
+ tags: ["Tasks"],
1104
+ summary: "Delete a task on an agent",
1105
+ description: "Delete a task from a running agent.",
1106
+ parameters: [
1107
+ { name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID" },
1108
+ { name: "taskId", in: "path", required: true, schema: { type: "string" }, description: "Task ID to delete" },
1109
+ ],
1110
+ responses: {
1111
+ "200": { description: "Task deleted" },
1112
+ "400": { description: "Agent is not running" },
1113
+ "404": { description: "Agent or task not found" },
1114
+ },
1115
+ },
1116
+ },
1117
+ "/tasks/{agentId}/{taskId}/execute": {
1118
+ post: {
1119
+ tags: ["Tasks"],
1120
+ summary: "Execute a task immediately",
1121
+ description: "Immediately execute a task on a running agent, regardless of its schedule.",
1122
+ parameters: [
1123
+ { name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID" },
1124
+ { name: "taskId", in: "path", required: true, schema: { type: "string" }, description: "Task ID to execute" },
1125
+ ],
1126
+ responses: {
1127
+ "200": { description: "Task execution started" },
1128
+ "400": { description: "Agent is not running" },
1129
+ "404": { description: "Agent or task not found" },
1130
+ },
1131
+ },
1036
1132
  },
1037
1133
  "/mcp/servers": {
1038
1134
  get: {
package/src/providers.ts CHANGED
@@ -12,8 +12,9 @@ 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-5", label: "Claude Sonnet 4.5", recommended: true },
16
- { value: "claude-haiku-4-5", label: "Claude Haiku 4.5 (Fast)" },
15
+ { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", recommended: true, input_cost: 3, output_cost: 15 },
16
+ { value: "claude-sonnet-4-5", label: "Claude Sonnet 4.5", input_cost: 3, output_cost: 15 },
17
+ { value: "claude-haiku-4-5", label: "Claude Haiku 4.5 (Fast)", input_cost: 0.8, output_cost: 4 },
17
18
  ],
18
19
  },
19
20
  openai: {
@@ -25,8 +26,8 @@ export const PROVIDERS = {
25
26
  docsUrl: "https://platform.openai.com/api-keys",
26
27
  testEndpoint: "https://api.openai.com/v1/models",
27
28
  models: [
28
- { value: "gpt-4o", label: "GPT-4o", recommended: true },
29
- { value: "gpt-4o-mini", label: "GPT-4o Mini (Fast)" },
29
+ { value: "gpt-4o", label: "GPT-4o", recommended: true, input_cost: 2.5, output_cost: 10 },
30
+ { value: "gpt-4o-mini", label: "GPT-4o Mini (Fast)", input_cost: 0.15, output_cost: 0.6 },
30
31
  ],
31
32
  },
32
33
  groq: {
@@ -38,8 +39,8 @@ export const PROVIDERS = {
38
39
  docsUrl: "https://console.groq.com/keys",
39
40
  testEndpoint: "https://api.groq.com/openai/v1/models",
40
41
  models: [
41
- { value: "llama-3.3-70b-versatile", label: "Llama 3.3 70B", recommended: true },
42
- { value: "llama-3.1-8b-instant", label: "Llama 3.1 8B (Fast)" },
42
+ { value: "llama-3.3-70b-versatile", label: "Llama 3.3 70B", recommended: true, input_cost: 0, output_cost: 0 },
43
+ { value: "llama-3.1-8b-instant", label: "Llama 3.1 8B (Fast)", input_cost: 0, output_cost: 0 },
43
44
  ],
44
45
  },
45
46
  gemini: {
@@ -51,8 +52,8 @@ export const PROVIDERS = {
51
52
  docsUrl: "https://aistudio.google.com/app/apikey",
52
53
  testEndpoint: "https://generativelanguage.googleapis.com/v1/models",
53
54
  models: [
54
- { value: "gemini-3-pro-preview", label: "Gemini 3 Pro Preview (Latest)", recommended: true },
55
- { value: "gemini-3-flash-preview", label: "Gemini 3 Flash Preview (Fast)" },
55
+ { value: "gemini-3-pro-preview", label: "Gemini 3 Pro Preview (Latest)", recommended: true, input_cost: 2, output_cost: 12 },
56
+ { value: "gemini-3-flash-preview", label: "Gemini 3 Flash Preview (Fast)", input_cost: 0.5, output_cost: 3 },
56
57
  ],
57
58
  },
58
59
  xai: {
@@ -64,8 +65,8 @@ export const PROVIDERS = {
64
65
  docsUrl: "https://console.x.ai/",
65
66
  testEndpoint: "https://api.x.ai/v1/models",
66
67
  models: [
67
- { value: "grok-2", label: "Grok 2", recommended: true },
68
- { value: "grok-2-mini", label: "Grok 2 Mini (Fast)" },
68
+ { value: "grok-2", label: "Grok 2", recommended: true, input_cost: 2, output_cost: 10 },
69
+ { value: "grok-2-mini", label: "Grok 2 Mini (Fast)", input_cost: 0.3, output_cost: 1 },
69
70
  ],
70
71
  },
71
72
  together: {
@@ -77,8 +78,8 @@ export const PROVIDERS = {
77
78
  docsUrl: "https://api.together.xyz/settings/api-keys",
78
79
  testEndpoint: "https://api.together.xyz/v1/models",
79
80
  models: [
80
- { value: "moonshotai/Kimi-K2.5", label: "Kimi K2.5", recommended: true },
81
- { value: "moonshotai/Kimi-K2-Thinking", label: "Kimi K2 Thinking (Reasoning)" },
81
+ { value: "moonshotai/Kimi-K2.5", label: "Kimi K2.5", recommended: true, input_cost: 1, output_cost: 4 },
82
+ { value: "moonshotai/Kimi-K2-Thinking", label: "Kimi K2 Thinking (Reasoning)", input_cost: 1, output_cost: 4 },
82
83
  ],
83
84
  },
84
85
  fireworks: {
@@ -90,10 +91,10 @@ export const PROVIDERS = {
90
91
  docsUrl: "https://fireworks.ai/api-keys",
91
92
  testEndpoint: "https://api.fireworks.ai/inference/v1/models",
92
93
  models: [
93
- { value: "accounts/fireworks/models/kimi-k2p5", label: "Kimi K2.5", recommended: true },
94
- { value: "accounts/fireworks/models/kimi-k2-thinking", label: "Kimi K2 Thinking (Reasoning)" },
95
- { value: "accounts/fireworks/models/minimax-m2p5", label: "MiniMax M2.5" },
96
- { value: "accounts/fireworks/models/glm-5", label: "GLM 5" },
94
+ { value: "accounts/fireworks/models/kimi-k2p5", label: "Kimi K2.5", recommended: true, input_cost: 1, output_cost: 4 },
95
+ { value: "accounts/fireworks/models/kimi-k2-thinking", label: "Kimi K2 Thinking (Reasoning)", input_cost: 1, output_cost: 4 },
96
+ { value: "accounts/fireworks/models/minimax-m2p5", label: "MiniMax M2.5", input_cost: 1, output_cost: 4 },
97
+ { value: "accounts/fireworks/models/glm-5", label: "GLM 5", input_cost: 1, output_cost: 4 },
97
98
  ],
98
99
  },
99
100
  moonshot: {
@@ -105,8 +106,24 @@ export const PROVIDERS = {
105
106
  docsUrl: "https://platform.moonshot.cn/console/api-keys",
106
107
  testEndpoint: "https://api.moonshot.cn/v1/models",
107
108
  models: [
108
- { value: "moonshot-v1-128k", label: "Kimi 128K", recommended: true },
109
- { value: "moonshot-v1-32k", label: "Kimi 32K (Fast)" },
109
+ { value: "moonshot-v1-128k", label: "Kimi 128K", recommended: true, input_cost: 1, output_cost: 4 },
110
+ { value: "moonshot-v1-32k", label: "Kimi 32K (Fast)", input_cost: 0.5, output_cost: 2 },
111
+ ],
112
+ },
113
+ venice: {
114
+ id: "venice",
115
+ name: "Venice",
116
+ displayName: "Venice AI",
117
+ type: "llm" as const,
118
+ envVar: "VENICE_API_KEY",
119
+ docsUrl: "https://docs.venice.ai/overview/pricing",
120
+ testEndpoint: "https://api.venice.ai/api/v1/models",
121
+ models: [
122
+ { value: "llama-3.3-70b", label: "Llama 3.3 70B", recommended: true, input_cost: 0.7, output_cost: 2.8 },
123
+ { value: "olafangensan-glm-4.7-flash-heretic", label: "GLM 4.7 Flash Heretic", input_cost: 0.14, output_cost: 0.8 },
124
+ { value: "qwen3-235b-a22b-instruct-2507", label: "Qwen 3 235B Instruct", input_cost: 0.15, output_cost: 0.75 },
125
+ { value: "deepseek-v3.2", label: "DeepSeek V3.2", input_cost: 0.4, output_cost: 1 },
126
+ { value: "venice-uncensored", label: "Venice Uncensored 1.1", input_cost: 0.2, output_cost: 0.9 },
110
127
  ],
111
128
  },
112
129
  ollama: {
@@ -121,11 +138,11 @@ export const PROVIDERS = {
121
138
  defaultBaseUrl: "http://localhost:11434",
122
139
  models: [
123
140
  // Default models - actual list fetched dynamically from Ollama
124
- { value: "llama3.3", label: "Llama 3.3 (70B)", recommended: true },
125
- { value: "llama3.2", label: "Llama 3.2 (3B)" },
126
- { value: "qwen2.5", label: "Qwen 2.5" },
127
- { value: "mistral", label: "Mistral" },
128
- { value: "deepseek-r1", label: "DeepSeek R1" },
141
+ { value: "llama3.3", label: "Llama 3.3 (70B)", recommended: true, input_cost: 0, output_cost: 0 },
142
+ { value: "llama3.2", label: "Llama 3.2 (3B)", input_cost: 0, output_cost: 0 },
143
+ { value: "qwen2.5", label: "Qwen 2.5", input_cost: 0, output_cost: 0 },
144
+ { value: "mistral", label: "Mistral", input_cost: 0, output_cost: 0 },
145
+ { value: "deepseek-r1", label: "DeepSeek R1", input_cost: 0, output_cost: 0 },
129
146
  ],
130
147
  },
131
148
  // Browser Providers
@@ -152,22 +169,21 @@ export const PROVIDERS = {
152
169
  browserengine: {
153
170
  id: "browserengine",
154
171
  name: "BrowserEngine",
155
- displayName: "BrowserEngine (Self-hosted)",
172
+ displayName: "BrowserEngine",
156
173
  type: "browser" as const,
157
- envVar: "BROWSER_ENGINE_URL",
158
- docsUrl: "",
159
- description: "Your own virtual browser service",
160
- isLocal: true,
174
+ envVar: "BROWSERENGINE_API_KEY",
175
+ docsUrl: "https://browserengine.co",
176
+ description: "Cloud browser automation with stealth browsing and proxies",
161
177
  models: [],
162
178
  },
163
- chrome: {
164
- id: "chrome",
165
- name: "Chrome",
166
- displayName: "Chrome DevTools",
179
+ cdp: {
180
+ id: "cdp",
181
+ name: "CDP",
182
+ displayName: "Direct CDP",
167
183
  type: "browser" as const,
168
- envVar: "CHROME_DEBUG_URL",
184
+ envVar: "CDP_URL",
169
185
  docsUrl: "",
170
- description: "Direct CDP connection to Chrome",
186
+ description: "Connect directly to any browser via Chrome DevTools Protocol",
171
187
  isLocal: true,
172
188
  models: [],
173
189
  },
@@ -217,6 +233,16 @@ export const PROVIDERS = {
217
233
 
218
234
  export type ProviderId = keyof typeof PROVIDERS;
219
235
 
236
+ /** Get cost per 1M tokens for a given provider + model. Returns { input_cost, output_cost } in USD. */
237
+ export function getModelCost(provider: string, model: string): { input_cost: number; output_cost: number } {
238
+ const providerDef = PROVIDERS[provider as ProviderId];
239
+ if (!providerDef || !("models" in providerDef)) return { input_cost: 0, output_cost: 0 };
240
+ const modelDef = (providerDef.models as ReadonlyArray<{ value: string; input_cost?: number; output_cost?: number }>)
241
+ .find(m => m.value === model);
242
+ if (!modelDef) return { input_cost: 0, output_cost: 0 };
243
+ return { input_cost: modelDef.input_cost ?? 0, output_cost: modelDef.output_cost ?? 0 };
244
+ }
245
+
220
246
  // Provider Keys Management
221
247
  export const ProviderKeys = {
222
248
  // Save an API key (encrypts before storing)
@@ -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 === "chrome") {
137
- const debugUrl = ProviderKeys.getDecryptedForProject("chrome", projectId);
138
- operatorResult.browser_provider = "chrome";
139
- operatorResult.virtual_browser = "http://localhost:8098"; // fallback
140
- if (debugUrl) {
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: auto-select first configured browser provider, or fall back to self
149
- operatorResult.browser_provider = "self";
150
- operatorResult.virtual_browser = ProviderKeys.getDecryptedForProject("browserengine", projectId) || "http://localhost:8098";
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 = SkillDB.findById(skillId);
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 = McpServerDB.findById(id);
189
+ const server = mcpMap.get(id);
194
190
  if (!server) continue;
195
191
 
196
192
  if (server.type === "local" && server.status === "running") {
@@ -341,7 +337,6 @@ export function buildAgentConfig(agent: Agent, providerKey: string) {
341
337
  const baseUrl = process.env.PUBLIC_URL || `http://localhost:${process.env.PORT || 4280}`;
342
338
  return {
343
339
  enabled: multiAgentConfig.enabled,
344
- mode: multiAgentConfig.mode || "worker",
345
340
  group: multiAgentConfig.group || agent.project_id || undefined,
346
341
  // This agent's reachable URL for peer communication
347
342
  url: `http://localhost:${agent.port}`,
@@ -387,9 +382,8 @@ export async function pushSkillsToAgent(agentId: string, port: number, skills: A
387
382
  }
388
383
 
389
384
  try {
390
- // Push each skill - try PUT first (update), then POST (create) if not found
391
- for (const skill of skills) {
392
- // First try PUT to update existing skill
385
+ // Push all skills in parallel - try PUT first (update), then POST (create) if not found
386
+ await Promise.allSettled(skills.map(async (skill) => {
393
387
  let res = await agentFetch(agentId, port, "/skills", {
394
388
  method: "PUT",
395
389
  headers: { "Content-Type": "application/json" },
@@ -397,7 +391,6 @@ export async function pushSkillsToAgent(agentId: string, port: number, skills: A
397
391
  signal: AbortSignal.timeout(5000),
398
392
  });
399
393
 
400
- // If skill doesn't exist (404), create it with POST
401
394
  if (res.status === 404) {
402
395
  res = await agentFetch(agentId, port, "/skills", {
403
396
  method: "POST",
@@ -411,7 +404,7 @@ export async function pushSkillsToAgent(agentId: string, port: number, skills: A
411
404
  const data = await res.json().catch(() => ({}));
412
405
  console.error(`[pushSkillsToAgent] Failed to push skill ${skill.name}:`, data.error || res.status);
413
406
  }
414
- }
407
+ }));
415
408
 
416
409
  // Enable skills globally via POST /skills/status
417
410
  const statusRes = await agentFetch(agentId, port, "/skills/status", {
@@ -611,6 +604,8 @@ export async function startAgentProcess(
611
604
  console.log(` Pushing configuration...`);
612
605
  }
613
606
  const config = buildAgentConfig(agent, providerKey);
607
+ console.log(`[DEBUG] operator config being pushed:`, JSON.stringify(config.operator, null, 2));
608
+ console.log(`[DEBUG] builtin_tools:`, JSON.stringify(config.operator?.builtin_tools));
614
609
  const configResult = await pushConfigToAgent(agent.id, port, config);
615
610
  if (!configResult.success) {
616
611
  if (!silent) {
@@ -650,24 +645,27 @@ export async function startAgentProcess(
650
645
  }
651
646
 
652
647
  // Transform DB agent to API response format (camelCase for frontend compatibility)
648
+ // Uses batch queries + light MCP loading (no decryption) for performance
653
649
  export function toApiAgent(agent: Agent) {
654
- // Look up MCP server details
650
+ // Batch load MCP servers (light = no decryption) and skills in 2 queries
651
+ const mcpMap = McpServerDB.findByIdsLight(agent.mcp_servers || []);
652
+ const skillMap = SkillDB.findByIds(agent.skills || []);
653
+
655
654
  const mcpServerDetails = (agent.mcp_servers || [])
656
- .map(id => McpServerDB.findById(id))
657
- .filter((s): s is NonNullable<typeof s> => s !== null)
655
+ .map(id => mcpMap.get(id))
656
+ .filter((s): s is NonNullable<typeof s> => !!s)
658
657
  .map(s => ({
659
658
  id: s.id,
660
659
  name: s.name,
661
660
  type: s.type,
662
661
  status: s.status,
663
662
  port: s.port,
664
- url: s.url, // Include URL for HTTP servers
663
+ url: s.url,
665
664
  }));
666
665
 
667
- // Look up skill details
668
666
  const skillDetails = (agent.skills || [])
669
- .map(id => SkillDB.findById(id))
670
- .filter((s): s is NonNullable<typeof s> => s !== null)
667
+ .map(id => skillMap.get(id))
668
+ .filter((s): s is NonNullable<typeof s> => !!s)
671
669
  .map(s => ({
672
670
  id: s.id,
673
671
  name: s.name,
@@ -676,6 +674,11 @@ export function toApiAgent(agent: Agent) {
676
674
  enabled: s.enabled,
677
675
  }));
678
676
 
677
+ // Look up subscriptions
678
+ const subscriptions = SubscriptionDB.findByAgentId(agent.id).map(s => ({
679
+ id: s.id, trigger_slug: s.trigger_slug, enabled: s.enabled,
680
+ }));
681
+
679
682
  return {
680
683
  id: agent.id,
681
684
  name: agent.name,
@@ -685,29 +688,33 @@ export function toApiAgent(agent: Agent) {
685
688
  status: agent.status,
686
689
  port: agent.port,
687
690
  features: agent.features,
688
- mcpServers: agent.mcp_servers, // Keep IDs for backwards compatibility
689
- mcpServerDetails, // Include full details
690
- skills: agent.skills, // Skill IDs
691
- skillDetails, // Include full details
691
+ mcpServers: agent.mcp_servers,
692
+ mcpServerDetails,
693
+ skills: agent.skills,
694
+ skillDetails,
695
+ subscriptions,
692
696
  projectId: agent.project_id,
693
697
  createdAt: agent.created_at,
694
698
  updatedAt: agent.updated_at,
695
699
  };
696
700
  }
697
701
 
698
- // Batch transform: fetch all MCP servers + skills in 2 queries instead of N per agent
702
+ // Batch transform: fetch all MCP servers + skills + subscriptions in 3 queries instead of N per agent
699
703
  export function toApiAgentsBatch(agents: Agent[]) {
700
704
  // Collect all unique IDs
701
705
  const allMcpIds = new Set<string>();
702
706
  const allSkillIds = new Set<string>();
707
+ const allAgentIds: string[] = [];
703
708
  for (const agent of agents) {
709
+ allAgentIds.push(agent.id);
704
710
  for (const id of agent.mcp_servers || []) allMcpIds.add(id);
705
711
  for (const id of agent.skills || []) allSkillIds.add(id);
706
712
  }
707
713
 
708
- // Batch load in 2 queries
709
- const mcpMap = McpServerDB.findByIds([...allMcpIds]);
714
+ // Batch load in 3 queries (Light = no decryption for MCP servers)
715
+ const mcpMap = McpServerDB.findByIdsLight([...allMcpIds]);
710
716
  const skillMap = SkillDB.findByIds([...allSkillIds]);
717
+ const subsMap = SubscriptionDB.findByAgentIds(allAgentIds);
711
718
 
712
719
  return agents.map(agent => {
713
720
  const mcpServerDetails = (agent.mcp_servers || [])
@@ -720,11 +727,15 @@ export function toApiAgentsBatch(agents: Agent[]) {
720
727
  .filter((s): s is NonNullable<typeof s> => !!s)
721
728
  .map(s => ({ id: s.id, name: s.name, description: s.description, version: s.version, enabled: s.enabled }));
722
729
 
730
+ const subscriptions = (subsMap.get(agent.id) || []).map(s => ({
731
+ id: s.id, trigger_slug: s.trigger_slug, enabled: s.enabled,
732
+ }));
733
+
723
734
  return {
724
735
  id: agent.id, name: agent.name, model: agent.model, provider: agent.provider,
725
736
  systemPrompt: agent.system_prompt, status: agent.status, port: agent.port,
726
737
  features: agent.features, mcpServers: agent.mcp_servers, mcpServerDetails,
727
- skills: agent.skills, skillDetails, projectId: agent.project_id,
738
+ skills: agent.skills, skillDetails, subscriptions, projectId: agent.project_id,
728
739
  createdAt: agent.created_at, updatedAt: agent.updated_at,
729
740
  };
730
741
  });
@@ -742,11 +753,12 @@ export function toApiProject(project: Project) {
742
753
  };
743
754
  }
744
755
 
745
- // Helper to fetch from a running agent (with authentication)
746
- export async function fetchFromAgent(agentId: string, port: number, endpoint: string): Promise<any> {
756
+ // Helper to fetch from a running agent (with authentication + timeout)
757
+ export async function fetchFromAgent(agentId: string, port: number, endpoint: string, timeoutMs = 3000): Promise<any> {
747
758
  try {
748
759
  const response = await agentFetch(agentId, port, endpoint, {
749
760
  headers: { "Accept": "application/json" },
761
+ signal: AbortSignal.timeout(timeoutMs),
750
762
  });
751
763
  if (response.ok) {
752
764
  return await response.json();
@@ -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-5",
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,
@@ -270,6 +270,25 @@ export async function handleAgentRoutes(
270
270
  });
271
271
  }
272
272
 
273
+ // ==================== SHARE LINK ====================
274
+
275
+ // GET /api/agents/:id/share-token - Get the share token for this agent
276
+ const shareTokenMatch = path.match(/^\/api\/agents\/([^/]+)\/share-token$/);
277
+ if (shareTokenMatch && method === "GET") {
278
+ const agent = AgentDB.findById(shareTokenMatch[1]);
279
+ if (!agent) {
280
+ return json({ error: "Agent not found" }, 404);
281
+ }
282
+
283
+ const { getShareToken } = await import("../share");
284
+ const token = getShareToken(agent.id);
285
+ if (!token) {
286
+ return json({ error: "Could not generate share token" }, 500);
287
+ }
288
+
289
+ return json({ token });
290
+ }
291
+
273
292
  // ==================== AGENT LIFECYCLE ====================
274
293
 
275
294
  // POST /api/agents/:id/start - Start an agent
@@ -358,6 +377,7 @@ export async function handleAgentRoutes(
358
377
  try {
359
378
  const body = await req.json();
360
379
 
380
+
361
381
  // Proxy to the agent's /chat endpoint with authentication
362
382
  const response = await agentFetch(agent.id, agent.port, "/chat", {
363
383
  method: "POST",
@@ -451,6 +471,56 @@ export async function handleAgentRoutes(
451
471
 
452
472
  // ==================== THREAD & MESSAGE PROXY ====================
453
473
 
474
+ // GET /api/threads - Consolidated threads from all running agents
475
+ if (path === "/api/threads" && method === "GET") {
476
+ const url = new URL(req.url);
477
+ const projectId = url.searchParams.get("project_id");
478
+
479
+ let agents;
480
+ if (projectId === "unassigned") {
481
+ agents = AgentDB.findByProject(null);
482
+ } else if (projectId) {
483
+ agents = AgentDB.findByProject(projectId);
484
+ } else {
485
+ agents = AgentDB.findAll();
486
+ }
487
+
488
+ // Only query running agents (excluding meta agent)
489
+ const runningAgents = agents.filter(a => a.id !== META_AGENT_ID && a.status === "running" && a.port);
490
+
491
+ const results = await Promise.allSettled(
492
+ runningAgents.map(async (agent) => {
493
+ const response = await agentFetch(agent.id, agent.port!, "/threads", {
494
+ method: "GET",
495
+ headers: { "Accept": "application/json" },
496
+ signal: AbortSignal.timeout(3000),
497
+ });
498
+ if (!response.ok) return [];
499
+ const data = await response.json() as any;
500
+ const threads = Array.isArray(data) ? data : (data.threads ?? []);
501
+ return threads.map((t: any) => ({
502
+ ...t,
503
+ agent_id: agent.id,
504
+ agent_name: agent.name,
505
+ }));
506
+ })
507
+ );
508
+
509
+ const allThreads = results
510
+ .filter((r): r is PromiseFulfilledResult<any[]> => r.status === "fulfilled")
511
+ .flatMap(r => r.value)
512
+ .filter((t: any) => !t.parent_id);
513
+
514
+ // Sort by most recent first
515
+ allThreads.sort((a, b) => {
516
+ const ta = a.updated_at || a.created_at || "";
517
+ const tb = b.updated_at || b.created_at || "";
518
+ return tb.localeCompare(ta);
519
+ });
520
+
521
+ return json({ threads: allThreads });
522
+ }
523
+
454
524
  // GET/POST /api/agents/:id/threads
455
525
  const threadsListMatch = path.match(/^\/api\/agents\/([^/]+)\/threads$/);
456
526
  if (threadsListMatch && method === "GET") {
@@ -789,7 +859,6 @@ export async function handleAgentRoutes(
789
859
  id: a.id,
790
860
  name: a.name,
791
861
  url: `http://localhost:${a.port}`,
792
- mode: agentConfig.mode || "worker",
793
862
  group: agentConfig.group || a.project_id,
794
863
  };
795
864
  });