@mastra/agent-builder 0.0.0-experimental-agent-builder-20250815195917 → 0.0.1-alpha.2
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/CHANGELOG.md +22 -16
- package/README.md +4 -17
- package/dist/agent/index.d.ts +5885 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/defaults.d.ts +6529 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2943 -591
- package/dist/index.js.map +1 -0
- package/dist/processors/tool-summary.d.ts +29 -0
- package/dist/processors/tool-summary.d.ts.map +1 -0
- package/dist/processors/write-file.d.ts +10 -0
- package/dist/processors/write-file.d.ts.map +1 -0
- package/dist/types.d.ts +1121 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +63 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/workflows/index.d.ts +5 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/shared/schema.d.ts +139 -0
- package/dist/workflows/shared/schema.d.ts.map +1 -0
- package/dist/workflows/task-planning/prompts.d.ts +37 -0
- package/dist/workflows/task-planning/prompts.d.ts.map +1 -0
- package/dist/workflows/task-planning/schema.d.ts +548 -0
- package/dist/workflows/task-planning/schema.d.ts.map +1 -0
- package/dist/workflows/task-planning/task-planning.d.ts +992 -0
- package/dist/workflows/task-planning/task-planning.d.ts.map +1 -0
- package/dist/workflows/template-builder/template-builder.d.ts +1910 -0
- package/dist/workflows/template-builder/template-builder.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/prompts.d.ts +44 -0
- package/dist/workflows/workflow-builder/prompts.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/schema.d.ts +1170 -0
- package/dist/workflows/workflow-builder/schema.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/tools.d.ts +309 -0
- package/dist/workflows/workflow-builder/tools.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/workflow-builder.d.ts +2714 -0
- package/dist/workflows/workflow-builder/workflow-builder.d.ts.map +1 -0
- package/dist/workflows/workflow-map.d.ts +3735 -0
- package/dist/workflows/workflow-map.d.ts.map +1 -0
- package/package.json +21 -9
- package/dist/_tsup-dts-rollup.d.cts +0 -13109
- package/dist/_tsup-dts-rollup.d.ts +0 -13109
- package/dist/index.cjs +0 -3772
- package/dist/index.d.cts +0 -1
- package/eslint.config.js +0 -11
- package/integration-tests/CHANGELOG.md +0 -20
- package/integration-tests/README.md +0 -154
- package/integration-tests/docker-compose.yml +0 -39
- package/integration-tests/package.json +0 -38
- package/integration-tests/src/agent-template-behavior.test.ts +0 -103
- package/integration-tests/src/fixtures/minimal-mastra-project/env.example +0 -6
- package/integration-tests/src/fixtures/minimal-mastra-project/package.json +0 -17
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/agents/weather.ts +0 -34
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/index.ts +0 -15
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/mcp/index.ts +0 -46
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/tools/weather.ts +0 -13
- package/integration-tests/src/fixtures/minimal-mastra-project/tsconfig.json +0 -17
- package/integration-tests/src/template-integration.test.ts +0 -312
- package/integration-tests/tsconfig.json +0 -13
- package/integration-tests/vitest.config.ts +0 -17
- package/src/agent-builder.test.ts +0 -291
- package/src/defaults.ts +0 -2728
- package/src/index.ts +0 -187
- package/src/processors/tool-summary.ts +0 -136
- package/src/processors/write-file.ts +0 -17
- package/src/types.ts +0 -120
- package/src/utils.ts +0 -133
- package/src/workflows/index.ts +0 -1541
- package/tsconfig.json +0 -5
- package/vitest.config.ts +0 -11
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from 'node:child_process';
|
|
2
|
-
import { spawn, execSync } from 'node:child_process';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { mkdtempSync, mkdirSync, rmSync, cpSync, existsSync, readFileSync } from 'node:fs';
|
|
5
|
-
import { join, resolve } from 'node:path';
|
|
6
|
-
import { Mastra } from '@mastra/core';
|
|
7
|
-
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
|
|
8
|
-
import { fetchMastraTemplates } from '../../src/utils';
|
|
9
|
-
import { mergeTemplateWorkflow } from '../../src/workflows';
|
|
10
|
-
|
|
11
|
-
function exec(cmd: string, cwd?: string): string {
|
|
12
|
-
return execSync(cmd, { stdio: 'pipe', cwd, encoding: 'utf-8' });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function initGitRepo(repoDir: string) {
|
|
16
|
-
exec('git init -q', repoDir);
|
|
17
|
-
exec('git config user.email "test@example.com"', repoDir);
|
|
18
|
-
exec('git config user.name "Test User"', repoDir);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function commitAll(repoDir: string, message: string) {
|
|
22
|
-
exec('git add .', repoDir);
|
|
23
|
-
exec(`git commit -m "${message}" -q`, repoDir);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe('Template Workflow Integration Tests', () => {
|
|
27
|
-
const integrationProjectsDir = resolve(__dirname, '../integration-projects');
|
|
28
|
-
mkdirSync(integrationProjectsDir, { recursive: true });
|
|
29
|
-
const tempRoot = mkdtempSync(join(integrationProjectsDir, 'template-workflow-test-'));
|
|
30
|
-
const fixtureProjectPath = resolve(__dirname, 'fixtures/minimal-mastra-project');
|
|
31
|
-
const targetRepo = join(tempRoot, 'test-project');
|
|
32
|
-
let mastraServer: ChildProcess;
|
|
33
|
-
let port = 4199;
|
|
34
|
-
let mastraInstance: Mastra;
|
|
35
|
-
|
|
36
|
-
beforeAll(async () => {
|
|
37
|
-
mastraInstance = new Mastra({
|
|
38
|
-
workflows: {
|
|
39
|
-
mergeTemplateWorkflow,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Copy the fixture mastra project into temp directory
|
|
44
|
-
mkdirSync(targetRepo, { recursive: true });
|
|
45
|
-
cpSync(fixtureProjectPath, targetRepo, { recursive: true });
|
|
46
|
-
|
|
47
|
-
// Initialize git in target
|
|
48
|
-
initGitRepo(targetRepo);
|
|
49
|
-
|
|
50
|
-
// Verify .gitignore was copied
|
|
51
|
-
const gitignorePath = join(targetRepo, '.gitignore');
|
|
52
|
-
expect(existsSync(gitignorePath)).toBe(true);
|
|
53
|
-
|
|
54
|
-
commitAll(targetRepo, 'chore: initial mastra project');
|
|
55
|
-
|
|
56
|
-
// Install dependencies in the test project
|
|
57
|
-
console.log('Installing dependencies in test project...');
|
|
58
|
-
exec('pnpm install', targetRepo);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
afterAll(async () => {
|
|
62
|
-
// Kill the Mastra server if it's running
|
|
63
|
-
if (mastraServer?.pid) {
|
|
64
|
-
try {
|
|
65
|
-
process.kill(-mastraServer.pid, 'SIGTERM');
|
|
66
|
-
// Wait a bit for graceful shutdown
|
|
67
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.warn('Failed to kill Mastra server:', e);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Cleanup temp directory
|
|
74
|
-
try {
|
|
75
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
76
|
-
} catch {
|
|
77
|
-
// Ignore cleanup errors
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should merge csv-to-questions template and validate functionality', async () => {
|
|
82
|
-
// Skip test if no OPENAI_API_KEY available
|
|
83
|
-
if (!process.env.OPENAI_API_KEY) {
|
|
84
|
-
console.log('Skipping test: OPENAI_API_KEY not set');
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Get the csv-to-questions template info
|
|
89
|
-
const templates = await fetchMastraTemplates();
|
|
90
|
-
const csvTemplate = templates.find(t => t.slug === 'csv-to-questions');
|
|
91
|
-
expect(csvTemplate).toBeDefined();
|
|
92
|
-
|
|
93
|
-
console.log(`Starting template merge workflow in ${targetRepo}`);
|
|
94
|
-
|
|
95
|
-
const templateWorkflow = mastraInstance.getWorkflow(`mergeTemplateWorkflow`);
|
|
96
|
-
|
|
97
|
-
// Run the merge template workflow
|
|
98
|
-
const workflowRun = await templateWorkflow.createRunAsync();
|
|
99
|
-
const result = await workflowRun.start({
|
|
100
|
-
inputData: {
|
|
101
|
-
repo: csvTemplate!.githubUrl,
|
|
102
|
-
slug: 'csv-to-questions',
|
|
103
|
-
targetPath: targetRepo,
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
console.log('Workflow result:', JSON.stringify(result, null, 2));
|
|
108
|
-
|
|
109
|
-
// Verify the workflow succeeded
|
|
110
|
-
expect(result).toBeDefined();
|
|
111
|
-
expect(result.status).toBe('success');
|
|
112
|
-
expect(result.result?.success).toBe(true);
|
|
113
|
-
expect(result.result?.applied).toBe(true);
|
|
114
|
-
expect(result.result?.branchName).toBe('feat/install-template-csv-to-questions');
|
|
115
|
-
|
|
116
|
-
// Verify the template branch was created
|
|
117
|
-
const branches = exec('git branch', targetRepo);
|
|
118
|
-
expect(branches).toContain('feat/install-template-csv-to-questions');
|
|
119
|
-
|
|
120
|
-
// Verify expected template files were created
|
|
121
|
-
const expectedPaths = [
|
|
122
|
-
'src/mastra/agents/csvQuestionAgent.ts',
|
|
123
|
-
'src/mastra/tools/csvFetcherTool.ts',
|
|
124
|
-
'src/mastra/workflows/csvToQuestionsWorkflow.ts',
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
for (const expectedPath of expectedPaths) {
|
|
128
|
-
const fullPath = join(targetRepo, expectedPath);
|
|
129
|
-
expect(existsSync(fullPath), `Expected ${expectedPath} to exist`).toBe(true);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Verify package.json was updated
|
|
133
|
-
const packageJsonPath = join(targetRepo, 'package.json');
|
|
134
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
135
|
-
expect(packageJson.scripts).toBeDefined();
|
|
136
|
-
|
|
137
|
-
// Check for template-specific scripts or dependencies
|
|
138
|
-
const hasTemplateScript = Object.keys(packageJson.scripts || {}).some(
|
|
139
|
-
key => key.includes('csv-to-questions') || key.includes('template'),
|
|
140
|
-
);
|
|
141
|
-
expect(hasTemplateScript).toBe(true);
|
|
142
|
-
|
|
143
|
-
console.log('Template merge completed successfully');
|
|
144
|
-
}, 300000); // 5 minute timeout for full workflow
|
|
145
|
-
|
|
146
|
-
it('should start Mastra server and validate both original and new agents work', async () => {
|
|
147
|
-
// Skip test if no OPENAI_API_KEY available
|
|
148
|
-
if (!process.env.OPENAI_API_KEY) {
|
|
149
|
-
console.log('Skipping test: OPENAI_API_KEY not set');
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
console.log('Starting Mastra server...');
|
|
154
|
-
|
|
155
|
-
// Start the Mastra server
|
|
156
|
-
mastraServer = spawn('pnpm', ['dev'], {
|
|
157
|
-
stdio: 'pipe',
|
|
158
|
-
cwd: targetRepo,
|
|
159
|
-
detached: true,
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Wait for server to be ready
|
|
163
|
-
await new Promise<void>((resolve, reject) => {
|
|
164
|
-
let output = '';
|
|
165
|
-
const timeout = setTimeout(() => {
|
|
166
|
-
reject(new Error('Mastra server failed to start within timeout'));
|
|
167
|
-
}, 300000);
|
|
168
|
-
|
|
169
|
-
mastraServer.stdout?.on('data', data => {
|
|
170
|
-
output += data.toString();
|
|
171
|
-
console.log('Server output:', data.toString());
|
|
172
|
-
if (output.includes('http://localhost:') || output.includes(`localhost:${port}`)) {
|
|
173
|
-
clearTimeout(timeout);
|
|
174
|
-
resolve();
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
mastraServer.stderr?.on('data', data => {
|
|
179
|
-
const errorStr = data.toString();
|
|
180
|
-
console.error('Mastra server error:', errorStr);
|
|
181
|
-
// Don't reject on warnings, only on actual errors
|
|
182
|
-
if (errorStr.toLowerCase().includes('error') && !errorStr.toLowerCase().includes('warning')) {
|
|
183
|
-
clearTimeout(timeout);
|
|
184
|
-
reject(new Error(`Mastra server error: ${errorStr}`));
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
mastraServer.on('exit', code => {
|
|
189
|
-
clearTimeout(timeout);
|
|
190
|
-
if (code !== 0) {
|
|
191
|
-
reject(new Error(`Mastra server exited with code ${code}`));
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
console.log(`Mastra server started on port ${port}`);
|
|
197
|
-
|
|
198
|
-
// Test the original weather agent (from fixture)
|
|
199
|
-
console.log('Testing original weather agent...');
|
|
200
|
-
const weatherResponse = await fetch(`http://localhost:${port}/api/agents/weatherAgent/generate`, {
|
|
201
|
-
method: 'POST',
|
|
202
|
-
headers: { 'Content-Type': 'application/json' },
|
|
203
|
-
body: JSON.stringify({
|
|
204
|
-
messages: [{ role: 'user', content: 'What is the weather in San Francisco?' }],
|
|
205
|
-
threadId: randomUUID(),
|
|
206
|
-
resourceId: 'test-resource',
|
|
207
|
-
}),
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
expect(weatherResponse.ok).toBe(true);
|
|
211
|
-
const weatherResult = await weatherResponse.json();
|
|
212
|
-
expect(weatherResult).toBeDefined();
|
|
213
|
-
expect(weatherResult.text || weatherResult.content).toContain('weather');
|
|
214
|
-
|
|
215
|
-
// Test the new CSV agent (from template)
|
|
216
|
-
console.log('Testing new CSV agent...');
|
|
217
|
-
const csvResponse = await fetch(`http://localhost:${port}/api/agents/csvQuestionAgent/generate`, {
|
|
218
|
-
method: 'POST',
|
|
219
|
-
headers: { 'Content-Type': 'application/json' },
|
|
220
|
-
body: JSON.stringify({
|
|
221
|
-
messages: [
|
|
222
|
-
{
|
|
223
|
-
role: 'user',
|
|
224
|
-
content: 'I want to analyze a CSV file with sales data. Can you help me?',
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
threadId: randomUUID(),
|
|
228
|
-
resourceId: 'test-resource',
|
|
229
|
-
}),
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
expect(csvResponse.ok).toBe(true);
|
|
233
|
-
const csvResult = await csvResponse.json();
|
|
234
|
-
expect(csvResult).toBeDefined();
|
|
235
|
-
expect(csvResult.text || csvResult.content).toMatch(/csv|data|analyze/i);
|
|
236
|
-
|
|
237
|
-
// Test workflows endpoint to ensure new workflow is registered
|
|
238
|
-
console.log('Testing workflows endpoint...');
|
|
239
|
-
const workflowsResponse = await fetch(`http://localhost:${port}/api/workflows`);
|
|
240
|
-
expect(workflowsResponse.ok).toBe(true);
|
|
241
|
-
const workflows = await workflowsResponse.json();
|
|
242
|
-
expect(workflows).toBeDefined();
|
|
243
|
-
|
|
244
|
-
// Check if the CSV workflow is registered (workflows is an object, not array)
|
|
245
|
-
const hasCSVWorkflow =
|
|
246
|
-
workflows &&
|
|
247
|
-
('csvToQuestionsWorkflow' in workflows || Object.values(workflows).some((w: any) => w.name?.includes('csv')));
|
|
248
|
-
expect(hasCSVWorkflow).toBe(true);
|
|
249
|
-
|
|
250
|
-
console.log('All agent and workflow tests passed!');
|
|
251
|
-
}, 300000); // 5 minute timeout for server startup and testing
|
|
252
|
-
|
|
253
|
-
it('should validate git history shows proper template integration', async () => {
|
|
254
|
-
// Check git log for template commits
|
|
255
|
-
const gitLog = exec('git log --oneline', targetRepo);
|
|
256
|
-
expect(gitLog).toContain('feat(template): register components from csv-to-questions@');
|
|
257
|
-
expect(gitLog).toContain('feat(template): copy 6 files from csv-to-questions@');
|
|
258
|
-
expect(gitLog).toContain('fix(template): resolve validation errors for csv-to-questions@');
|
|
259
|
-
|
|
260
|
-
// Verify we're on the template branch
|
|
261
|
-
const currentBranch = exec('git branch --show-current', targetRepo);
|
|
262
|
-
expect(currentBranch.trim()).toBe('feat/install-template-csv-to-questions');
|
|
263
|
-
|
|
264
|
-
// Verify the original default branch still exists
|
|
265
|
-
const allBranches = exec('git branch', targetRepo);
|
|
266
|
-
expect(allBranches).toMatch(/\b(main|master)\b/);
|
|
267
|
-
|
|
268
|
-
console.log('Git history validation completed');
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('should handle merge conflicts gracefully when running workflow twice', async () => {
|
|
272
|
-
// Skip test if no OPENAI_API_KEY available
|
|
273
|
-
if (!process.env.OPENAI_API_KEY) {
|
|
274
|
-
console.log('Skipping test: OPENAI_API_KEY not set');
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Switch back to default branch
|
|
279
|
-
const defaultBranch = exec('git branch', targetRepo).includes('main') ? 'main' : 'master';
|
|
280
|
-
exec(`git checkout ${defaultBranch}`, targetRepo);
|
|
281
|
-
|
|
282
|
-
// Try to merge the same template again (should handle gracefully)
|
|
283
|
-
const templates = await fetchMastraTemplates();
|
|
284
|
-
const csvTemplate = templates.find(t => t.slug === 'csv-to-questions');
|
|
285
|
-
|
|
286
|
-
console.log('Testing duplicate template merge...');
|
|
287
|
-
|
|
288
|
-
const templateWorkflow = mastraInstance.getWorkflow(`mergeTemplateWorkflow`);
|
|
289
|
-
const workflowRun = await templateWorkflow.createRunAsync();
|
|
290
|
-
const result = await workflowRun.start({
|
|
291
|
-
inputData: {
|
|
292
|
-
repo: csvTemplate!.githubUrl,
|
|
293
|
-
slug: 'csv-to-questions',
|
|
294
|
-
targetPath: targetRepo,
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// The workflow should still succeed but handle the existing files intelligently
|
|
299
|
-
expect(result.status).toBe('success');
|
|
300
|
-
|
|
301
|
-
console.log(JSON.stringify(result, null, 2));
|
|
302
|
-
|
|
303
|
-
if (result.status === 'success') {
|
|
304
|
-
expect(result.result.success).toBe(true);
|
|
305
|
-
expect(result.result.applied).toBe(true);
|
|
306
|
-
// Should create a new branch with a different name or handle existing branch
|
|
307
|
-
expect(result.result.branchName).toMatch(/feat\/install-template-csv-to-questions/);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
console.log('Duplicate merge test completed');
|
|
311
|
-
}, 300000);
|
|
312
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"forceConsistentCasingInFileNames": true
|
|
10
|
-
},
|
|
11
|
-
"include": ["src/**/*"],
|
|
12
|
-
"exclude": ["node_modules"]
|
|
13
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
pool: 'forks',
|
|
6
|
-
globals: true,
|
|
7
|
-
environment: 'node',
|
|
8
|
-
testTimeout: 120000,
|
|
9
|
-
hookTimeout: 60000,
|
|
10
|
-
coverage: {
|
|
11
|
-
provider: 'v8',
|
|
12
|
-
reporter: ['text', 'json'],
|
|
13
|
-
},
|
|
14
|
-
reporters: 'dot',
|
|
15
|
-
bail: 1,
|
|
16
|
-
},
|
|
17
|
-
});
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import type { MastraLanguageModel } from '@mastra/core/agent';
|
|
2
|
-
import { RuntimeContext } from '@mastra/core/runtime-context';
|
|
3
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
-
import { AgentBuilderDefaults } from './defaults';
|
|
5
|
-
import { ToolSummaryProcessor } from './processors/tool-summary';
|
|
6
|
-
import { AgentBuilder } from './index';
|
|
7
|
-
|
|
8
|
-
// Mock the openai model for testing
|
|
9
|
-
const mockModel = {
|
|
10
|
-
modelId: 'gpt-4',
|
|
11
|
-
provider: 'openai',
|
|
12
|
-
} as MastraLanguageModel;
|
|
13
|
-
|
|
14
|
-
// Mock config with required properties
|
|
15
|
-
const mockConfig = {
|
|
16
|
-
model: mockModel,
|
|
17
|
-
projectPath: '/test/project',
|
|
18
|
-
summaryModel: mockModel,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
describe('AgentBuilder', () => {
|
|
22
|
-
describe('AgentBuilderDefaults', () => {
|
|
23
|
-
it('should have default instructions', () => {
|
|
24
|
-
expect(AgentBuilderDefaults.DEFAULT_INSTRUCTIONS).toContain('expert Mastra Agent Builder');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should have default memory config', () => {
|
|
28
|
-
expect(AgentBuilderDefaults.DEFAULT_MEMORY_CONFIG).toEqual({
|
|
29
|
-
maxMessages: 20,
|
|
30
|
-
tokenLimit: 10000,
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should have default tools', () => {
|
|
35
|
-
expect(AgentBuilderDefaults.DEFAULT_TOOLS).toHaveProperty('manageProject');
|
|
36
|
-
expect(AgentBuilderDefaults.DEFAULT_TOOLS).toHaveProperty('searchReplace');
|
|
37
|
-
expect(AgentBuilderDefaults.DEFAULT_TOOLS).toHaveProperty('validateCode');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('AgentBuilder class', () => {
|
|
42
|
-
it('should create an instance with basic config', () => {
|
|
43
|
-
const builder = new AgentBuilder({
|
|
44
|
-
model: mockModel,
|
|
45
|
-
projectPath: '/test/project',
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
expect(builder).toBeInstanceOf(AgentBuilder);
|
|
49
|
-
expect(builder.id).toBe('agent-builder');
|
|
50
|
-
expect(builder.name).toBe('agent-builder');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should have access to default tools', () => {
|
|
54
|
-
const builder = new AgentBuilder({
|
|
55
|
-
model: mockModel,
|
|
56
|
-
projectPath: '/test/project',
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const tools = builder.getTools({ runtimeContext: new RuntimeContext() });
|
|
60
|
-
expect(tools).toBeDefined();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should combine custom instructions with default instructions', () => {
|
|
64
|
-
const customInstructions = 'Custom instructions for testing';
|
|
65
|
-
const builder = new AgentBuilder({
|
|
66
|
-
model: mockModel,
|
|
67
|
-
instructions: customInstructions,
|
|
68
|
-
projectPath: '/test/project',
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Since instructions are private, we can't directly test them,
|
|
72
|
-
// but we can verify the builder was created successfully
|
|
73
|
-
expect(builder).toBeInstanceOf(AgentBuilder);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should merge custom tools with default tools', () => {
|
|
77
|
-
const customTool = {
|
|
78
|
-
id: 'custom-tool',
|
|
79
|
-
description: 'A custom tool for testing',
|
|
80
|
-
execute: vi.fn().mockResolvedValue({ success: true }),
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const builder = new AgentBuilder({
|
|
84
|
-
model: mockModel,
|
|
85
|
-
tools: {
|
|
86
|
-
customTool,
|
|
87
|
-
},
|
|
88
|
-
projectPath: '/test/project',
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
expect(builder).toBeInstanceOf(AgentBuilder);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe('AgentBuilder.defaultConfig', () => {
|
|
96
|
-
it('should return default configuration', () => {
|
|
97
|
-
const defaultConfig = AgentBuilder.defaultConfig('/test/project');
|
|
98
|
-
|
|
99
|
-
expect(defaultConfig).toHaveProperty('instructions');
|
|
100
|
-
expect(defaultConfig).toHaveProperty('memoryConfig');
|
|
101
|
-
expect(defaultConfig).toHaveProperty('tools');
|
|
102
|
-
expect(defaultConfig.memoryConfig).toEqual(AgentBuilderDefaults.DEFAULT_MEMORY_CONFIG);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe('AgentBuilder.createWithDefaults', () => {
|
|
107
|
-
it('should create an instance with merged default settings', () => {
|
|
108
|
-
const builder = new AgentBuilder({
|
|
109
|
-
model: mockModel,
|
|
110
|
-
memoryConfig: {
|
|
111
|
-
maxMessages: 50, // Override default
|
|
112
|
-
},
|
|
113
|
-
projectPath: '/test/project',
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
expect(builder).toBeInstanceOf(AgentBuilder);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe('generateAgent method', () => {
|
|
121
|
-
it('should be defined and callable', async () => {
|
|
122
|
-
const builder = new AgentBuilder(mockConfig);
|
|
123
|
-
|
|
124
|
-
expect(builder.generateAgent).toBeDefined();
|
|
125
|
-
expect(typeof builder.generateAgent).toBe('function');
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('ToolSummaryProcessor', () => {
|
|
130
|
-
it('should cache tool call summaries', async () => {
|
|
131
|
-
const processor = new ToolSummaryProcessor({ summaryModel: mockModel });
|
|
132
|
-
|
|
133
|
-
// Check initial cache is empty
|
|
134
|
-
const initialStats = processor.getCacheStats();
|
|
135
|
-
expect(initialStats.size).toBe(0);
|
|
136
|
-
|
|
137
|
-
// Create a mock tool call
|
|
138
|
-
const mockToolCall = {
|
|
139
|
-
toolName: 'testTool',
|
|
140
|
-
args: { param1: 'value1', param2: 'value2' },
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
// Test cache key generation
|
|
144
|
-
const cacheKey = processor.createCacheKey(mockToolCall);
|
|
145
|
-
expect(cacheKey).toBe('testTool:{"param1":"value1","param2":"value2"}');
|
|
146
|
-
|
|
147
|
-
// Test cache clearing
|
|
148
|
-
processor.clearCache();
|
|
149
|
-
const clearedStats = processor.getCacheStats();
|
|
150
|
-
expect(clearedStats.size).toBe(0);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should create consistent cache keys for same arguments', () => {
|
|
154
|
-
const processor = new ToolSummaryProcessor({ summaryModel: mockModel });
|
|
155
|
-
|
|
156
|
-
const toolCall1 = {
|
|
157
|
-
toolName: 'myTool',
|
|
158
|
-
args: { a: 1, b: 2 },
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const toolCall2 = {
|
|
162
|
-
toolName: 'myTool',
|
|
163
|
-
args: { b: 2, a: 1 }, // Different order
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const key1 = processor.createCacheKey(toolCall1);
|
|
167
|
-
const key2 = processor.createCacheKey(toolCall2);
|
|
168
|
-
|
|
169
|
-
// Should be same key despite different argument order
|
|
170
|
-
expect(key1).toBe(key2);
|
|
171
|
-
expect(key1).toBe('myTool:{"a":1,"b":2}');
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe('Server Management Tools', () => {
|
|
176
|
-
it('should have manageServer and httpRequest tools available', async () => {
|
|
177
|
-
const builder = new AgentBuilder(mockConfig);
|
|
178
|
-
const tools = await builder.getTools({ runtimeContext: new RuntimeContext() });
|
|
179
|
-
|
|
180
|
-
expect(tools.manageServer).toBeDefined();
|
|
181
|
-
expect(tools.httpRequest).toBeDefined();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should validate manageServer tool schema', () => {
|
|
185
|
-
expect(AgentBuilderDefaults.checkMastraServerStatus).toBeDefined();
|
|
186
|
-
expect(AgentBuilderDefaults.startMastraServer).toBeDefined();
|
|
187
|
-
expect(AgentBuilderDefaults.stopMastraServer).toBeDefined();
|
|
188
|
-
expect(AgentBuilderDefaults.makeHttpRequest).toBeDefined();
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('should handle server stop with no running process', async () => {
|
|
192
|
-
// Mock exec to return "No process found"
|
|
193
|
-
const mockExec = vi.fn().mockResolvedValue({ stdout: 'No process found' });
|
|
194
|
-
const originalExec = require('child_process').exec;
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
require('child_process').exec = mockExec;
|
|
198
|
-
|
|
199
|
-
const result = await AgentBuilderDefaults.stopMastraServer({ port: 9999 });
|
|
200
|
-
|
|
201
|
-
expect(result.success).toBe(true);
|
|
202
|
-
expect(result.status).toBe('stopped');
|
|
203
|
-
expect(result.message).toContain('No Mastra server found running on port 9999');
|
|
204
|
-
} finally {
|
|
205
|
-
require('child_process').exec = originalExec;
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('Code Validation', () => {
|
|
211
|
-
it('should have validateCode method', () => {
|
|
212
|
-
expect(AgentBuilderDefaults.validateCode).toBeDefined();
|
|
213
|
-
expect(AgentBuilderDefaults.parseESLintErrors).toBeDefined();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should include full TypeScript output in validation errors', () => {
|
|
217
|
-
// Since we're now passing through raw TypeScript output,
|
|
218
|
-
// we just need to verify the validateCode method includes it
|
|
219
|
-
expect(AgentBuilderDefaults.validateCode).toBeDefined();
|
|
220
|
-
|
|
221
|
-
// The actual validation testing would require a real TypeScript project,
|
|
222
|
-
// but we can verify the method exists and handles errors properly
|
|
223
|
-
const mockTsOutput = `src/test.ts(10,5): error TS2322: Type 'string' is not assignable to type 'number'.
|
|
224
|
-
src/another.ts(20,15): warning TS2345: Argument of type 'null' is not assignable to parameter of type 'string'.
|
|
225
|
-
Found 2 errors in 2 files.`;
|
|
226
|
-
|
|
227
|
-
// This output would now be included directly in the error message
|
|
228
|
-
// for the agent to interpret, rather than being parsed into structured errors
|
|
229
|
-
expect(mockTsOutput).toContain('error TS2322');
|
|
230
|
-
expect(mockTsOutput).toContain('Found 2 errors');
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('should parse ESLint errors correctly', () => {
|
|
234
|
-
const eslintResults = [
|
|
235
|
-
{
|
|
236
|
-
filePath: '/path/to/file.ts',
|
|
237
|
-
messages: [
|
|
238
|
-
{
|
|
239
|
-
ruleId: 'no-unused-vars',
|
|
240
|
-
severity: 2,
|
|
241
|
-
message: "'unusedVar' is defined but never used.",
|
|
242
|
-
line: 5,
|
|
243
|
-
column: 10,
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
ruleId: 'prefer-const',
|
|
247
|
-
severity: 1,
|
|
248
|
-
message: "'data' is never reassigned. Use 'const' instead of 'let'.",
|
|
249
|
-
line: 8,
|
|
250
|
-
column: 3,
|
|
251
|
-
},
|
|
252
|
-
],
|
|
253
|
-
},
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
const errors = AgentBuilderDefaults.parseESLintErrors(eslintResults);
|
|
257
|
-
|
|
258
|
-
expect(errors).toHaveLength(2);
|
|
259
|
-
expect(errors[0]).toEqual({
|
|
260
|
-
type: 'eslint',
|
|
261
|
-
severity: 'error',
|
|
262
|
-
message: "'unusedVar' is defined but never used.",
|
|
263
|
-
file: '/path/to/file.ts',
|
|
264
|
-
line: 5,
|
|
265
|
-
column: 10,
|
|
266
|
-
code: 'no-unused-vars',
|
|
267
|
-
});
|
|
268
|
-
expect(errors[1]).toEqual({
|
|
269
|
-
type: 'eslint',
|
|
270
|
-
severity: 'warning',
|
|
271
|
-
message: "'data' is never reassigned. Use 'const' instead of 'let'.",
|
|
272
|
-
file: '/path/to/file.ts',
|
|
273
|
-
line: 8,
|
|
274
|
-
column: 3,
|
|
275
|
-
code: 'prefer-const',
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it('should include validation workflow in instructions', () => {
|
|
280
|
-
const instructions = AgentBuilderDefaults.DEFAULT_INSTRUCTIONS('/test/path');
|
|
281
|
-
|
|
282
|
-
expect(instructions).toContain('VALIDATE CODE AFTER CHANGES');
|
|
283
|
-
expect(instructions).toContain('validateCode');
|
|
284
|
-
expect(instructions).toContain("['types', 'lint']");
|
|
285
|
-
expect(instructions).toContain('Re-validate until clean');
|
|
286
|
-
expect(instructions).toContain(
|
|
287
|
-
'Documentation → Web Research → Clarification → Project Exploration → Implementation → Validation',
|
|
288
|
-
);
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
});
|