@agentforge-ai/cli 0.5.4 → 0.6.0

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.
@@ -1,24 +1,38 @@
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 can use Mastra
9
- * to execute agents and manage workflows.
3
+ *
4
+ * These actions run in the Convex Node.js runtime and execute LLM calls
5
+ * using Mastra-native model routing 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 Mastra Agent with "provider/model-name" format
10
11
  */
12
+ import { action } from "./_generated/server";
13
+ import { v } from "convex/values";
14
+ import { api } from "./_generated/api";
15
+ import { Agent } from "@mastra/core/agent";
11
16
 
12
- // Return type for executeAgent to break circular type inference
17
+ // Return type for executeAgent
13
18
  type ExecuteAgentResult = {
14
19
  success: boolean;
15
20
  threadId: string;
16
21
  sessionId: string;
17
22
  response: string;
18
- usage?: Record<string, unknown>;
23
+ usage?: {
24
+ promptTokens: number;
25
+ completionTokens: number;
26
+ totalTokens: number;
27
+ };
19
28
  };
20
29
 
21
- // Action: Execute agent with Mastra
30
+ /**
31
+ * Execute an agent with a prompt and return the response.
32
+ *
33
+ * This is the programmatic API for agent execution. For chat UI,
34
+ * prefer `chat.sendMessage` which handles thread management automatically.
35
+ */
22
36
  export const executeAgent = action({
23
37
  args: {
24
38
  agentId: v.string(),
@@ -30,7 +44,7 @@ export const executeAgent = action({
30
44
  handler: async (ctx, args): Promise<ExecuteAgentResult> => {
31
45
  // Get agent configuration from database
32
46
  const agent = await ctx.runQuery(api.agents.get, { id: args.agentId });
33
-
47
+
34
48
  if (!agent) {
35
49
  throw new Error(`Agent ${args.agentId} not found`);
36
50
  }
@@ -58,50 +72,34 @@ export const executeAgent = action({
58
72
  threadId,
59
73
  agentId: args.agentId,
60
74
  userId: args.userId,
61
- channel: "dashboard",
75
+ channel: "api",
62
76
  });
63
77
 
64
78
  try {
65
- // Import Mastra dynamically (Node.js runtime)
66
- // @ts-expect-error - Mastra is installed at runtime in the user's project
67
- const { Agent } = await import("@mastra/core/agent");
68
-
69
- // Format model string for Mastra
70
- const modelString = agent.model.includes("/")
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
- });
79
+ // Resolve the model
80
+ const provider = agent.provider || "openrouter";
81
+ const modelId = agent.model || "openai/gpt-4o-mini";
82
+ const modelKey = `${provider}/${modelId}`;
85
83
 
86
84
  // Get conversation history for context
87
- const messages = await ctx.runQuery(api.messages.list, { threadId }) as Array<{ role: string; content: string }>;
88
-
89
- // Build context from message history
90
- const context = messages
91
- .slice(-10) // Last 10 messages for context
92
- .map((m) => `${m.role}: ${m.content}`)
93
- .join("\n");
94
-
95
- // Execute agent
96
- const result: any = await mastraAgent.generate(args.prompt, {
97
- ...(args.stream && { stream: args.stream }),
98
- context: context || undefined,
85
+ const messages = await ctx.runQuery(api.messages.list, { threadId });
86
+ const conversationMessages = (messages as Array<{ role: string; content: string }>)
87
+ .slice(-20)
88
+ .map((m) => ({
89
+ role: m.role as "user" | "assistant" | "system",
90
+ content: m.content,
91
+ }));
92
+
93
+ // Execute via Mastra Agent
94
+ const mastraAgent = new Agent({
95
+ name: "agentforge-executor",
96
+ instructions: agent.instructions || "You are a helpful AI assistant.",
97
+ model: modelKey,
99
98
  });
100
99
 
101
- // Extract response content
102
- const responseContent: string = typeof result === "string"
103
- ? result
104
- : result.text || result.content || JSON.stringify(result);
100
+ const result = await mastraAgent.generate(conversationMessages);
101
+
102
+ const responseContent = result.text;
105
103
 
106
104
  // Add assistant message to thread
107
105
  await ctx.runMutation(api.messages.add, {
@@ -116,17 +114,27 @@ export const executeAgent = action({
116
114
  status: "completed",
117
115
  });
118
116
 
119
- // Record usage (if available in result)
120
- if (result.usage) {
117
+ // Build usage data
118
+ const usage = result.usage
119
+ ? {
120
+ promptTokens: result.usage.promptTokens || 0,
121
+ completionTokens: result.usage.completionTokens || 0,
122
+ totalTokens:
123
+ (result.usage.promptTokens || 0) +
124
+ (result.usage.completionTokens || 0),
125
+ }
126
+ : undefined;
127
+
128
+ // Record usage
129
+ if (usage) {
121
130
  await ctx.runMutation(api.usage.record, {
122
131
  agentId: args.agentId,
123
132
  sessionId,
124
- provider: agent.provider,
125
- model: agent.model,
126
- promptTokens: result.usage.promptTokens || 0,
127
- completionTokens: result.usage.completionTokens || 0,
128
- totalTokens: result.usage.totalTokens || 0,
129
- cost: result.usage.cost,
133
+ provider: agent.provider || "openrouter",
134
+ model: agent.model || "unknown",
135
+ promptTokens: usage.promptTokens,
136
+ completionTokens: usage.completionTokens,
137
+ totalTokens: usage.totalTokens,
130
138
  userId: args.userId,
131
139
  });
132
140
  }
@@ -136,10 +144,11 @@ export const executeAgent = action({
136
144
  threadId: threadId as string,
137
145
  sessionId,
138
146
  response: responseContent,
139
- usage: result.usage,
147
+ usage,
140
148
  };
141
149
  } catch (error: unknown) {
142
- const errorMessage = error instanceof Error ? error.message : String(error);
150
+ const errorMessage =
151
+ error instanceof Error ? error.message : String(error);
143
152
 
144
153
  // Update session status to error
145
154
  await ctx.runMutation(api.sessions.updateStatus, {
@@ -147,19 +156,37 @@ export const executeAgent = action({
147
156
  status: "error",
148
157
  });
149
158
 
150
- // Add error message
159
+ // Add error message to thread
151
160
  await ctx.runMutation(api.messages.add, {
152
161
  threadId,
153
162
  role: "assistant",
154
163
  content: `Error: ${errorMessage}`,
155
164
  });
156
165
 
166
+ // Log the error
167
+ await ctx.runMutation(api.logs.add, {
168
+ level: "error",
169
+ source: "mastraIntegration",
170
+ message: `Agent execution failed: ${errorMessage}`,
171
+ metadata: {
172
+ agentId: args.agentId,
173
+ threadId,
174
+ sessionId,
175
+ },
176
+ userId: args.userId,
177
+ });
178
+
157
179
  throw error;
158
180
  }
159
181
  },
160
182
  });
161
183
 
162
- // Action: Stream agent response
184
+ /**
185
+ * Stream agent response (placeholder — streaming requires SSE/WebSocket).
186
+ *
187
+ * For now, this falls back to non-streaming execution.
188
+ * Full streaming support will be added via Convex HTTP actions + SSE.
189
+ */
163
190
  export const streamAgent = action({
164
191
  args: {
165
192
  agentId: v.string(),
@@ -168,26 +195,31 @@ export const streamAgent = action({
168
195
  userId: v.optional(v.string()),
169
196
  },
170
197
  handler: async (ctx, args): Promise<{ success: boolean; message: string }> => {
171
- // Similar to executeAgent but with streaming support
172
- // This would require WebSocket or SSE implementation
173
- // For now, return a placeholder
198
+ // Fall back to non-streaming execution
199
+ const result = await ctx.runAction(api.mastraIntegration.executeAgent, {
200
+ agentId: args.agentId,
201
+ prompt: args.prompt,
202
+ threadId: args.threadId,
203
+ userId: args.userId,
204
+ });
205
+
174
206
  return {
175
- success: true,
176
- message: "Streaming support coming soon",
207
+ success: result.success,
208
+ message: result.response,
177
209
  };
178
210
  },
179
211
  });
180
212
 
181
- // Action: Execute workflow with multiple agents
213
+ /**
214
+ * Execute workflow with multiple agents (placeholder).
215
+ */
182
216
  export const executeWorkflow = action({
183
217
  args: {
184
218
  workflowId: v.string(),
185
219
  input: v.any(),
186
220
  userId: v.optional(v.string()),
187
221
  },
188
- handler: async (ctx, args): Promise<{ success: boolean; message: string }> => {
189
- // Placeholder for workflow execution
190
- // This would orchestrate multiple agents in sequence or parallel
222
+ handler: async (_ctx, _args): Promise<{ success: boolean; message: string }> => {
191
223
  return {
192
224
  success: true,
193
225
  message: "Workflow execution coming soon",