@agentforge-ai/cli 0.5.4 → 0.5.5
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/LICENSE +197 -0
- package/dist/default/agentforge.config.ts +126 -6
- package/dist/default/convex/agents.ts +15 -21
- package/dist/default/convex/chat.ts +331 -0
- package/dist/default/convex/mastraIntegration.ts +154 -69
- package/dist/default/dashboard/app/routes/chat.tsx +462 -167
- package/dist/default/skills/browser-automation/SKILL.md +137 -0
- package/dist/default/skills/browser-automation/config.json +11 -0
- package/dist/default/skills/browser-automation/index.ts +93 -0
- package/dist/default/skills/skill-creator/SKILL.md +69 -230
- package/dist/index.js +2455 -290
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
- package/templates/default/agentforge.config.ts +126 -6
- package/templates/default/convex/agents.ts +15 -21
- package/templates/default/convex/chat.ts +331 -0
- package/templates/default/convex/mastraIntegration.ts +154 -69
- package/templates/default/dashboard/app/routes/chat.tsx +462 -167
- package/templates/default/skills/browser-automation/SKILL.md +137 -0
- package/templates/default/skills/browser-automation/config.json +11 -0
- package/templates/default/skills/browser-automation/index.ts +93 -0
- package/templates/default/skills/skill-creator/SKILL.md +69 -230
|
@@ -1,24 +1,89 @@
|
|
|
1
|
-
import { action } from "./_generated/server";
|
|
2
|
-
import { v } from "convex/values";
|
|
3
|
-
import { api } from "./_generated/api";
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
2
|
* Mastra Integration Actions for Convex
|
|
7
|
-
*
|
|
8
|
-
* These actions run in the Node.js runtime and
|
|
9
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* These actions run in the Convex Node.js runtime and execute LLM calls
|
|
5
|
+
* using the Vercel AI SDK with OpenRouter as the default provider.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - For chat: use `chat.sendMessage` (preferred entry point)
|
|
9
|
+
* - For programmatic agent execution: use `mastraIntegration.executeAgent`
|
|
10
|
+
* - Model resolution: uses AI SDK providers directly (OpenRouter, OpenAI, etc.)
|
|
11
|
+
*
|
|
12
|
+
* This replaces the previous broken approach of dynamically importing
|
|
13
|
+
* @mastra/core inside Convex actions. The AI SDK is the correct way to
|
|
14
|
+
* call LLMs from Convex Node.js actions.
|
|
10
15
|
*/
|
|
16
|
+
import { action } from "./_generated/server";
|
|
17
|
+
import { v } from "convex/values";
|
|
18
|
+
import { api } from "./_generated/api";
|
|
11
19
|
|
|
12
|
-
// Return type for executeAgent
|
|
20
|
+
// Return type for executeAgent
|
|
13
21
|
type ExecuteAgentResult = {
|
|
14
22
|
success: boolean;
|
|
15
23
|
threadId: string;
|
|
16
24
|
sessionId: string;
|
|
17
25
|
response: string;
|
|
18
|
-
usage?:
|
|
26
|
+
usage?: {
|
|
27
|
+
promptTokens: number;
|
|
28
|
+
completionTokens: number;
|
|
29
|
+
totalTokens: number;
|
|
30
|
+
};
|
|
19
31
|
};
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a model instance from provider + modelId using the AI SDK.
|
|
35
|
+
*
|
|
36
|
+
* Supports: openrouter, openai, anthropic, google, venice, custom.
|
|
37
|
+
* Falls back to OpenRouter for unknown providers (it routes to all models).
|
|
38
|
+
*/
|
|
39
|
+
async function resolveModel(provider: string, modelId: string) {
|
|
40
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
41
|
+
|
|
42
|
+
switch (provider) {
|
|
43
|
+
case "openai": {
|
|
44
|
+
const openai = createOpenAI({
|
|
45
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
46
|
+
});
|
|
47
|
+
return openai(modelId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
case "anthropic": {
|
|
51
|
+
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
52
|
+
const anthropic = createAnthropic({
|
|
53
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
54
|
+
});
|
|
55
|
+
return anthropic(modelId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
case "google": {
|
|
59
|
+
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
|
60
|
+
const google = createGoogleGenerativeAI({
|
|
61
|
+
apiKey: process.env.GEMINI_API_KEY,
|
|
62
|
+
});
|
|
63
|
+
return google(modelId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
case "openrouter":
|
|
67
|
+
default: {
|
|
68
|
+
// OpenRouter is OpenAI-compatible and routes to all providers
|
|
69
|
+
const openrouter = createOpenAI({
|
|
70
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
71
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
|
72
|
+
});
|
|
73
|
+
const routerModelId = modelId.includes("/")
|
|
74
|
+
? modelId
|
|
75
|
+
: `${provider}/${modelId}`;
|
|
76
|
+
return openrouter(routerModelId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Execute an agent with a prompt and return the response.
|
|
83
|
+
*
|
|
84
|
+
* This is the programmatic API for agent execution. For chat UI,
|
|
85
|
+
* prefer `chat.sendMessage` which handles thread management automatically.
|
|
86
|
+
*/
|
|
22
87
|
export const executeAgent = action({
|
|
23
88
|
args: {
|
|
24
89
|
agentId: v.string(),
|
|
@@ -30,7 +95,7 @@ export const executeAgent = action({
|
|
|
30
95
|
handler: async (ctx, args): Promise<ExecuteAgentResult> => {
|
|
31
96
|
// Get agent configuration from database
|
|
32
97
|
const agent = await ctx.runQuery(api.agents.get, { id: args.agentId });
|
|
33
|
-
|
|
98
|
+
|
|
34
99
|
if (!agent) {
|
|
35
100
|
throw new Error(`Agent ${args.agentId} not found`);
|
|
36
101
|
}
|
|
@@ -58,50 +123,36 @@ export const executeAgent = action({
|
|
|
58
123
|
threadId,
|
|
59
124
|
agentId: args.agentId,
|
|
60
125
|
userId: args.userId,
|
|
61
|
-
channel: "
|
|
126
|
+
channel: "api",
|
|
62
127
|
});
|
|
63
128
|
|
|
64
129
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
? agent.model
|
|
72
|
-
: `${agent.provider}/${agent.model}`;
|
|
73
|
-
|
|
74
|
-
// Create Mastra agent
|
|
75
|
-
const mastraAgent = new Agent({
|
|
76
|
-
id: agent.id,
|
|
77
|
-
name: agent.name,
|
|
78
|
-
instructions: agent.instructions,
|
|
79
|
-
model: modelString,
|
|
80
|
-
tools: agent.tools || {},
|
|
81
|
-
...(agent.temperature && { temperature: agent.temperature }),
|
|
82
|
-
...(agent.maxTokens && { maxTokens: agent.maxTokens }),
|
|
83
|
-
...(agent.topP && { topP: agent.topP }),
|
|
84
|
-
});
|
|
130
|
+
const { generateText } = await import("ai");
|
|
131
|
+
|
|
132
|
+
// Resolve the model
|
|
133
|
+
const provider = agent.provider || "openrouter";
|
|
134
|
+
const modelId = agent.model || "openai/gpt-4o-mini";
|
|
135
|
+
const model = await resolveModel(provider, modelId);
|
|
85
136
|
|
|
86
137
|
// Get conversation history for context
|
|
87
|
-
const messages = await ctx.runQuery(api.messages.list, { threadId })
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Execute
|
|
96
|
-
const result
|
|
97
|
-
|
|
98
|
-
|
|
138
|
+
const messages = await ctx.runQuery(api.messages.list, { threadId });
|
|
139
|
+
const conversationMessages = (messages as Array<{ role: string; content: string }>)
|
|
140
|
+
.slice(-20)
|
|
141
|
+
.map((m) => ({
|
|
142
|
+
role: m.role as "user" | "assistant" | "system",
|
|
143
|
+
content: m.content,
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
// Execute the LLM call
|
|
147
|
+
const result = await generateText({
|
|
148
|
+
model,
|
|
149
|
+
system: agent.instructions || "You are a helpful AI assistant.",
|
|
150
|
+
messages: conversationMessages,
|
|
151
|
+
...(agent.temperature != null && { temperature: agent.temperature }),
|
|
152
|
+
...(agent.maxTokens != null && { maxTokens: agent.maxTokens }),
|
|
99
153
|
});
|
|
100
154
|
|
|
101
|
-
|
|
102
|
-
const responseContent: string = typeof result === "string"
|
|
103
|
-
? result
|
|
104
|
-
: result.text || result.content || JSON.stringify(result);
|
|
155
|
+
const responseContent = result.text;
|
|
105
156
|
|
|
106
157
|
// Add assistant message to thread
|
|
107
158
|
await ctx.runMutation(api.messages.add, {
|
|
@@ -116,17 +167,27 @@ export const executeAgent = action({
|
|
|
116
167
|
status: "completed",
|
|
117
168
|
});
|
|
118
169
|
|
|
119
|
-
//
|
|
120
|
-
|
|
170
|
+
// Build usage data
|
|
171
|
+
const usage = result.usage
|
|
172
|
+
? {
|
|
173
|
+
promptTokens: result.usage.promptTokens || 0,
|
|
174
|
+
completionTokens: result.usage.completionTokens || 0,
|
|
175
|
+
totalTokens:
|
|
176
|
+
(result.usage.promptTokens || 0) +
|
|
177
|
+
(result.usage.completionTokens || 0),
|
|
178
|
+
}
|
|
179
|
+
: undefined;
|
|
180
|
+
|
|
181
|
+
// Record usage
|
|
182
|
+
if (usage) {
|
|
121
183
|
await ctx.runMutation(api.usage.record, {
|
|
122
184
|
agentId: args.agentId,
|
|
123
185
|
sessionId,
|
|
124
|
-
provider: agent.provider,
|
|
125
|
-
model: agent.model,
|
|
126
|
-
promptTokens:
|
|
127
|
-
completionTokens:
|
|
128
|
-
totalTokens:
|
|
129
|
-
cost: result.usage.cost,
|
|
186
|
+
provider: agent.provider || "openrouter",
|
|
187
|
+
model: agent.model || "unknown",
|
|
188
|
+
promptTokens: usage.promptTokens,
|
|
189
|
+
completionTokens: usage.completionTokens,
|
|
190
|
+
totalTokens: usage.totalTokens,
|
|
130
191
|
userId: args.userId,
|
|
131
192
|
});
|
|
132
193
|
}
|
|
@@ -136,10 +197,11 @@ export const executeAgent = action({
|
|
|
136
197
|
threadId: threadId as string,
|
|
137
198
|
sessionId,
|
|
138
199
|
response: responseContent,
|
|
139
|
-
usage
|
|
200
|
+
usage,
|
|
140
201
|
};
|
|
141
202
|
} catch (error: unknown) {
|
|
142
|
-
const errorMessage =
|
|
203
|
+
const errorMessage =
|
|
204
|
+
error instanceof Error ? error.message : String(error);
|
|
143
205
|
|
|
144
206
|
// Update session status to error
|
|
145
207
|
await ctx.runMutation(api.sessions.updateStatus, {
|
|
@@ -147,19 +209,37 @@ export const executeAgent = action({
|
|
|
147
209
|
status: "error",
|
|
148
210
|
});
|
|
149
211
|
|
|
150
|
-
// Add error message
|
|
212
|
+
// Add error message to thread
|
|
151
213
|
await ctx.runMutation(api.messages.add, {
|
|
152
214
|
threadId,
|
|
153
215
|
role: "assistant",
|
|
154
216
|
content: `Error: ${errorMessage}`,
|
|
155
217
|
});
|
|
156
218
|
|
|
219
|
+
// Log the error
|
|
220
|
+
await ctx.runMutation(api.logs.add, {
|
|
221
|
+
level: "error",
|
|
222
|
+
source: "mastraIntegration",
|
|
223
|
+
message: `Agent execution failed: ${errorMessage}`,
|
|
224
|
+
metadata: {
|
|
225
|
+
agentId: args.agentId,
|
|
226
|
+
threadId,
|
|
227
|
+
sessionId,
|
|
228
|
+
},
|
|
229
|
+
userId: args.userId,
|
|
230
|
+
});
|
|
231
|
+
|
|
157
232
|
throw error;
|
|
158
233
|
}
|
|
159
234
|
},
|
|
160
235
|
});
|
|
161
236
|
|
|
162
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Stream agent response (placeholder — streaming requires SSE/WebSocket).
|
|
239
|
+
*
|
|
240
|
+
* For now, this falls back to non-streaming execution.
|
|
241
|
+
* Full streaming support will be added via Convex HTTP actions + SSE.
|
|
242
|
+
*/
|
|
163
243
|
export const streamAgent = action({
|
|
164
244
|
args: {
|
|
165
245
|
agentId: v.string(),
|
|
@@ -168,26 +248,31 @@ export const streamAgent = action({
|
|
|
168
248
|
userId: v.optional(v.string()),
|
|
169
249
|
},
|
|
170
250
|
handler: async (ctx, args): Promise<{ success: boolean; message: string }> => {
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
251
|
+
// Fall back to non-streaming execution
|
|
252
|
+
const result = await ctx.runAction(api.mastraIntegration.executeAgent, {
|
|
253
|
+
agentId: args.agentId,
|
|
254
|
+
prompt: args.prompt,
|
|
255
|
+
threadId: args.threadId,
|
|
256
|
+
userId: args.userId,
|
|
257
|
+
});
|
|
258
|
+
|
|
174
259
|
return {
|
|
175
|
-
success:
|
|
176
|
-
message:
|
|
260
|
+
success: result.success,
|
|
261
|
+
message: result.response,
|
|
177
262
|
};
|
|
178
263
|
},
|
|
179
264
|
});
|
|
180
265
|
|
|
181
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Execute workflow with multiple agents (placeholder).
|
|
268
|
+
*/
|
|
182
269
|
export const executeWorkflow = action({
|
|
183
270
|
args: {
|
|
184
271
|
workflowId: v.string(),
|
|
185
272
|
input: v.any(),
|
|
186
273
|
userId: v.optional(v.string()),
|
|
187
274
|
},
|
|
188
|
-
handler: async (
|
|
189
|
-
// Placeholder for workflow execution
|
|
190
|
-
// This would orchestrate multiple agents in sequence or parallel
|
|
275
|
+
handler: async (_ctx, _args): Promise<{ success: boolean; message: string }> => {
|
|
191
276
|
return {
|
|
192
277
|
success: true,
|
|
193
278
|
message: "Workflow execution coming soon",
|