@elizaos/plugin-research 0.1.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.
Files changed (71) hide show
  1. package/README.md +400 -0
  2. package/dist/index.cjs +9366 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.js +9284 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +80 -0
  7. package/src/__tests__/action-chaining.test.ts +532 -0
  8. package/src/__tests__/actions.test.ts +118 -0
  9. package/src/__tests__/cache-rate-limiter.test.ts +303 -0
  10. package/src/__tests__/content-extractors.test.ts +26 -0
  11. package/src/__tests__/deepresearch-bench-integration.test.ts +520 -0
  12. package/src/__tests__/deepresearch-bench-simplified.e2e.test.ts +290 -0
  13. package/src/__tests__/deepresearch-bench.e2e.test.ts +376 -0
  14. package/src/__tests__/e2e.test.ts +1870 -0
  15. package/src/__tests__/multi-benchmark-runner.ts +427 -0
  16. package/src/__tests__/providers.test.ts +156 -0
  17. package/src/__tests__/real-world.e2e.test.ts +788 -0
  18. package/src/__tests__/research-scenarios.test.ts +755 -0
  19. package/src/__tests__/research.e2e.test.ts +704 -0
  20. package/src/__tests__/research.test.ts +174 -0
  21. package/src/__tests__/search-providers.test.ts +174 -0
  22. package/src/__tests__/single-benchmark-runner.ts +735 -0
  23. package/src/__tests__/test-search-providers.ts +171 -0
  24. package/src/__tests__/verify-apis.test.ts +82 -0
  25. package/src/actions.ts +1677 -0
  26. package/src/benchmark/deepresearch-benchmark.ts +369 -0
  27. package/src/evaluation/research-evaluator.ts +444 -0
  28. package/src/examples/api-integration.md +498 -0
  29. package/src/examples/browserbase-integration.md +132 -0
  30. package/src/examples/debug-research-query.ts +162 -0
  31. package/src/examples/defi-code-scenarios.md +536 -0
  32. package/src/examples/defi-implementation-guide.md +454 -0
  33. package/src/examples/eliza-research-example.ts +142 -0
  34. package/src/examples/fix-renewable-energy-research.ts +209 -0
  35. package/src/examples/research-scenarios.md +408 -0
  36. package/src/examples/run-complete-renewable-research.ts +303 -0
  37. package/src/examples/run-deep-research.ts +352 -0
  38. package/src/examples/run-logged-research.ts +304 -0
  39. package/src/examples/run-real-research.ts +151 -0
  40. package/src/examples/save-research-output.ts +133 -0
  41. package/src/examples/test-file-logging.ts +199 -0
  42. package/src/examples/test-real-research.ts +67 -0
  43. package/src/examples/test-renewable-energy-research.ts +229 -0
  44. package/src/index.ts +28 -0
  45. package/src/integrations/cache.ts +128 -0
  46. package/src/integrations/content-extractors/firecrawl.ts +314 -0
  47. package/src/integrations/content-extractors/pdf-extractor.ts +350 -0
  48. package/src/integrations/content-extractors/playwright.ts +420 -0
  49. package/src/integrations/factory.ts +419 -0
  50. package/src/integrations/index.ts +18 -0
  51. package/src/integrations/rate-limiter.ts +181 -0
  52. package/src/integrations/search-providers/academic.ts +290 -0
  53. package/src/integrations/search-providers/exa.ts +205 -0
  54. package/src/integrations/search-providers/npm.ts +330 -0
  55. package/src/integrations/search-providers/pypi.ts +211 -0
  56. package/src/integrations/search-providers/serpapi.ts +277 -0
  57. package/src/integrations/search-providers/serper.ts +358 -0
  58. package/src/integrations/search-providers/stagehand-google.ts +87 -0
  59. package/src/integrations/search-providers/tavily.ts +187 -0
  60. package/src/processing/relevance-analyzer.ts +353 -0
  61. package/src/processing/research-logger.ts +450 -0
  62. package/src/processing/result-processor.ts +372 -0
  63. package/src/prompts/research-prompts.ts +419 -0
  64. package/src/providers/cacheProvider.ts +164 -0
  65. package/src/providers.ts +173 -0
  66. package/src/service.ts +2588 -0
  67. package/src/services/swe-bench.ts +286 -0
  68. package/src/strategies/research-strategies.ts +790 -0
  69. package/src/types/pdf-parse.d.ts +34 -0
  70. package/src/types.ts +551 -0
  71. package/src/verification/claim-verifier.ts +443 -0
@@ -0,0 +1,1870 @@
1
+ import { IAgentRuntime, Memory, elizaLogger } from '@elizaos/core';
2
+ import { ResearchService } from '../service';
3
+ import { ResearchProject, ResearchStatus, ResearchPhase } from '../types';
4
+
5
+ // #region: Test Helpers
6
+
7
+ // Helper to create a realistic test memory object
8
+ function createTestMemory(text: string): Memory {
9
+ return {
10
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` as `${string}-${string}-${string}-${string}-${string}`,
11
+ entityId: '00000000-0000-0000-0000-000000000001' as `${string}-${string}-${string}-${string}-${string}`,
12
+ roomId: '00000000-0000-0000-0000-000000000002' as `${string}-${string}-${string}-${string}-${string}`,
13
+ content: { text },
14
+ createdAt: Date.now(),
15
+ };
16
+ }
17
+
18
+ // Helper to wait and monitor research progress
19
+ async function monitorResearch(
20
+ service: ResearchService,
21
+ projectId: string,
22
+ options: {
23
+ timeout?: number;
24
+ checkInterval?: number;
25
+ onProgress?: (project: ResearchProject) => void;
26
+ } = {}
27
+ ): Promise<ResearchProject | null> {
28
+ const {
29
+ timeout = 180000, // 3 minutes default
30
+ checkInterval = 5000,
31
+ onProgress,
32
+ } = options;
33
+
34
+ const startTime = Date.now();
35
+ let lastPhase: ResearchPhase | null = null;
36
+
37
+ while (Date.now() - startTime < timeout) {
38
+ const project = await service.getProject(projectId);
39
+ if (!project) return null;
40
+
41
+ // Log phase changes
42
+ if (project.phase !== lastPhase) {
43
+ elizaLogger.info(`Research phase: ${lastPhase || 'START'} → ${project.phase}`);
44
+ lastPhase = project.phase;
45
+ }
46
+
47
+ // Call progress callback
48
+ if (onProgress) {
49
+ onProgress(project);
50
+ }
51
+
52
+ // Check completion
53
+ if (
54
+ project.status === ResearchStatus.COMPLETED ||
55
+ project.status === ResearchStatus.FAILED
56
+ ) {
57
+ return project;
58
+ }
59
+
60
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
61
+ }
62
+
63
+ // Return whatever we have after timeout
64
+ const finalProject = await service.getProject(projectId);
65
+ return finalProject || null;
66
+ }
67
+
68
+ // Test helper to wait for research completion with longer timeout for real operations
69
+ async function waitForResearchCompletion(
70
+ service: ResearchService,
71
+ projectId: string,
72
+ maxWaitTime: number = 300000 // 5 minutes for real web operations
73
+ ): Promise<boolean> {
74
+ const startTime = Date.now();
75
+
76
+ while (Date.now() - startTime < maxWaitTime) {
77
+ const project = await service.getProject(projectId);
78
+ if (!project) return false;
79
+
80
+ if (
81
+ project.status === ResearchStatus.COMPLETED ||
82
+ project.status === ResearchStatus.FAILED
83
+ ) {
84
+ return project.status === ResearchStatus.COMPLETED;
85
+ }
86
+
87
+ // Wait 5 seconds before checking again (longer for real operations)
88
+ await new Promise((resolve) => setTimeout(resolve, 5000));
89
+ }
90
+
91
+ return false;
92
+ }
93
+
94
+ // Helper to validate research quality
95
+ function validateResearchQuality(project: ResearchProject): {
96
+ isValid: boolean;
97
+ issues: string[];
98
+ } {
99
+ const issues: string[] = [];
100
+
101
+ if (project.sources.length === 0) {
102
+ issues.push('No sources found');
103
+ }
104
+
105
+ if (project.findings.length === 0) {
106
+ issues.push('No findings collected');
107
+ }
108
+
109
+ // Check for diverse sources
110
+ const uniqueDomains = new Set(
111
+ project.sources.map((s) => {
112
+ try {
113
+ return new URL(s.url).hostname;
114
+ } catch {
115
+ return 'unknown';
116
+ }
117
+ })
118
+ );
119
+
120
+ if (uniqueDomains.size < 2 && project.sources.length > 2) {
121
+ issues.push('Sources lack diversity (all from same domain)');
122
+ }
123
+
124
+ // Check findings have reasonable content
125
+ const avgFindingLength =
126
+ project.findings.reduce((sum, f) => sum + f.content.length, 0) /
127
+ (project.findings.length || 1);
128
+ if (avgFindingLength < 100) {
129
+ issues.push('Findings are too short (average < 100 chars)');
130
+ }
131
+
132
+ // Check relevance scores
133
+ const avgRelevance =
134
+ project.findings.reduce((sum, f) => sum + f.relevance, 0) /
135
+ (project.findings.length || 1);
136
+ if (avgRelevance < 0.3) {
137
+ issues.push('Low average relevance score');
138
+ }
139
+
140
+ return {
141
+ isValid: issues.length === 0,
142
+ issues,
143
+ };
144
+ }
145
+
146
+ // #endregion
147
+
148
+ // #region: Real-World Persona Scenarios
149
+
150
+ // Test 1: Research for Building a New Feature (Real Developer Workflow)
151
+ export async function testFeatureDevelopmentResearch(
152
+ runtime: IAgentRuntime
153
+ ): Promise<void> {
154
+ elizaLogger.info('🔨 Starting Real-World Test: Feature Development Research');
155
+
156
+ const service = runtime.getService<ResearchService>('research');
157
+ if (!service) throw new Error('Research service not available');
158
+
159
+ // Scenario: Developer needs to implement WebSocket real-time features
160
+ const queries = [
161
+ 'WebSocket implementation Node.js TypeScript scaling best practices 2024',
162
+ 'Socket.io vs native WebSocket performance comparison production',
163
+ 'WebSocket authentication JWT security implementation examples',
164
+ ];
165
+
166
+ elizaLogger.info(
167
+ 'Researching WebSocket implementation across multiple aspects...'
168
+ );
169
+
170
+ const projects = await Promise.all(
171
+ queries.map((query, index) =>
172
+ service.createResearchProject(query, {
173
+ maxSearchResults: 3,
174
+ metadata: {
175
+ aspect: ['implementation', 'comparison', 'security'][index],
176
+ featureType: 'websocket',
177
+ },
178
+ })
179
+ )
180
+ );
181
+
182
+ // Monitor all projects
183
+ const results = await Promise.all(
184
+ projects.map((project) =>
185
+ monitorResearch(service, project.id, {
186
+ timeout: 120000,
187
+ onProgress: (p) => {
188
+ if (p.findings.length > 0 && p.findings.length % 3 === 0) {
189
+ elizaLogger.info(
190
+ `Project ${project.query.substring(
191
+ 0,
192
+ 30
193
+ )}... has ${p.findings.length} findings`
194
+ );
195
+ }
196
+ },
197
+ })
198
+ )
199
+ );
200
+
201
+ // Analyze combined results
202
+ const allFindings = results.flatMap((r) => r?.findings || []);
203
+ const allSources = results.flatMap((r) => r?.sources || []);
204
+
205
+ // Check for implementation details
206
+ const hasImplementationDetails = allFindings.some((f) => {
207
+ const content = f.content.toLowerCase();
208
+ return (
209
+ content.includes('const') ||
210
+ content.includes('server') ||
211
+ content.includes('client') ||
212
+ content.includes('connection')
213
+ );
214
+ });
215
+
216
+ // Check for security considerations
217
+ const hasSecurityInfo = allFindings.some((f) => {
218
+ const content = f.content.toLowerCase();
219
+ return (
220
+ content.includes('auth') ||
221
+ content.includes('security') ||
222
+ content.includes('jwt') ||
223
+ content.includes('cors')
224
+ );
225
+ });
226
+
227
+ // Check for performance insights
228
+ const hasPerformanceInfo = allFindings.some((f) => {
229
+ const content = f.content.toLowerCase();
230
+ return (
231
+ content.includes('performance') ||
232
+ content.includes('scaling') ||
233
+ content.includes('benchmark') ||
234
+ content.includes('latency')
235
+ );
236
+ });
237
+
238
+ // Find Stack Overflow or GitHub sources (developer favorites)
239
+ const devSources = allSources.filter(
240
+ (s) =>
241
+ s.url.includes('stackoverflow.com') ||
242
+ s.url.includes('github.com') ||
243
+ s.url.includes('dev.to') ||
244
+ s.url.includes('medium.com')
245
+ );
246
+
247
+ elizaLogger.info('📊 Feature Development Research Results:');
248
+ elizaLogger.info(`- Total findings across aspects: ${allFindings.length}`);
249
+ elizaLogger.info(
250
+ `- Implementation details found: ${hasImplementationDetails}`
251
+ );
252
+ elizaLogger.info(`- Security considerations found: ${hasSecurityInfo}`);
253
+ elizaLogger.info(`- Performance insights found: ${hasPerformanceInfo}`);
254
+ elizaLogger.info(
255
+ `- Developer-focused sources: ${devSources.length}/${allSources.length}`
256
+ );
257
+
258
+ if (devSources.length > 0) {
259
+ elizaLogger.info(`- Sample dev source: ${devSources[0].title}`);
260
+ }
261
+
262
+ // Simulate decision-making based on research
263
+ if (hasImplementationDetails && hasSecurityInfo && hasPerformanceInfo) {
264
+ elizaLogger.success(
265
+ '✅ Research provides comprehensive information for feature implementation'
266
+ );
267
+ } else {
268
+ elizaLogger.warn('⚠️ Some aspects missing - may need additional research');
269
+ }
270
+
271
+ elizaLogger.success(
272
+ '✅ Real-World Test Passed: Feature Development Research'
273
+ );
274
+ }
275
+
276
+ // Test 2: Research a Person for Hiring/Partnership (Real HR/Business Workflow)
277
+ export async function testPersonBackgroundResearch(
278
+ runtime: IAgentRuntime
279
+ ): Promise<void> {
280
+ elizaLogger.info('👤 Starting Real-World Test: Person Background Research');
281
+
282
+ const service = runtime.getService<ResearchService>('research');
283
+ if (!service) throw new Error('Research service not available');
284
+
285
+ // Scenario: Researching a potential technical advisor or hire
286
+ const personQuery =
287
+ 'Andrej Karpathy AI research contributions Tesla OpenAI recent projects 2024';
288
+
289
+ const project = await service.createResearchProject(personQuery, {
290
+ maxSearchResults: 5,
291
+ metadata: {
292
+ researchType: 'person_background',
293
+ purpose: 'professional_evaluation',
294
+ },
295
+ });
296
+
297
+ elizaLogger.info('Researching professional background...');
298
+
299
+ const result = await monitorResearch(service, project.id, {
300
+ timeout: 150000,
301
+ onProgress: (p) => {
302
+ // Track what types of sources we're finding
303
+ const sources = p.sources.map((s) => new URL(s.url).hostname);
304
+ const hasLinkedIn = sources.some((s) => s.includes('linkedin'));
305
+ const hasTwitter = sources.some(
306
+ (s) => s.includes('twitter') || s.includes('x.com')
307
+ );
308
+ const hasGitHub = sources.some((s) => s.includes('github'));
309
+
310
+ if (
311
+ (hasLinkedIn || hasTwitter || hasGitHub) &&
312
+ p.phase === ResearchPhase.SEARCHING
313
+ ) {
314
+ elizaLogger.info(
315
+ `Found professional profiles: LinkedIn=${hasLinkedIn}, Twitter=${hasTwitter}, GitHub=${hasGitHub}`
316
+ );
317
+ }
318
+ },
319
+ });
320
+
321
+ if (!result) throw new Error('Person research failed to complete');
322
+
323
+ // Analyze findings for key information
324
+ const findings = result.findings;
325
+
326
+ // Professional history
327
+ const hasTeslaInfo = findings.some((f) =>
328
+ f.content.toLowerCase().includes('tesla')
329
+ );
330
+ const hasOpenAIInfo = findings.some((f) =>
331
+ f.content.toLowerCase().includes('openai')
332
+ );
333
+ const hasEducation = findings.some(
334
+ (f) =>
335
+ f.content.toLowerCase().includes('stanford') ||
336
+ f.content.toLowerCase().includes('phd') ||
337
+ f.content.toLowerCase().includes('university')
338
+ );
339
+
340
+ // Recent activities
341
+ const hasRecentActivity = findings.some((f) => {
342
+ const content = f.content;
343
+ return (
344
+ content.includes('2024') ||
345
+ content.includes('2023') ||
346
+ content.toLowerCase().includes('recent') ||
347
+ content.toLowerCase().includes('latest')
348
+ );
349
+ });
350
+
351
+ // Technical contributions
352
+ const hasTechnicalWork = findings.some((f) => {
353
+ const content = f.content.toLowerCase();
354
+ return (
355
+ content.includes('paper') ||
356
+ content.includes('research') ||
357
+ content.includes('model') ||
358
+ content.includes('algorithm') ||
359
+ content.includes('course')
360
+ );
361
+ });
362
+
363
+ // Extract key achievements
364
+ const achievements = findings.filter((f) => {
365
+ const content = f.content.toLowerCase();
366
+ return (
367
+ content.includes('founded') ||
368
+ content.includes('created') ||
369
+ content.includes('developed') ||
370
+ content.includes('led') ||
371
+ content.includes('published')
372
+ );
373
+ });
374
+
375
+ elizaLogger.info('📋 Person Background Research Results:');
376
+ elizaLogger.info(
377
+ `- Professional history coverage: Tesla=${hasTeslaInfo}, OpenAI=${hasOpenAIInfo}`
378
+ );
379
+ elizaLogger.info(`- Education info found: ${hasEducation}`);
380
+ elizaLogger.info(`- Recent activity (2023-2024): ${hasRecentActivity}`);
381
+ elizaLogger.info(`- Technical contributions: ${hasTechnicalWork}`);
382
+ elizaLogger.info(`- Key achievements identified: ${achievements.length}`);
383
+
384
+ if (achievements.length > 0) {
385
+ const sample = achievements[0].content.substring(0, 150);
386
+ elizaLogger.info(`- Sample achievement: "${sample}..."`);
387
+ }
388
+
389
+ // Professional assessment
390
+ const professionalScore = [
391
+ hasTeslaInfo,
392
+ hasOpenAIInfo,
393
+ hasEducation,
394
+ hasRecentActivity,
395
+ hasTechnicalWork,
396
+ ].filter(Boolean).length;
397
+
398
+ if (professionalScore >= 4) {
399
+ elizaLogger.success('✅ Comprehensive professional profile assembled');
400
+ } else if (professionalScore >= 2) {
401
+ elizaLogger.info(
402
+ 'ℹ️ Partial professional profile - may need additional sources'
403
+ );
404
+ } else {
405
+ elizaLogger.warn('⚠️ Limited professional information found');
406
+ }
407
+
408
+ elizaLogger.success('✅ Real-World Test Passed: Person Background Research');
409
+ }
410
+
411
+ // Test 3: Breaking News Research (Real Journalist/Analyst Workflow)
412
+ export async function testBreakingNewsResearch(
413
+ runtime: IAgentRuntime
414
+ ): Promise<void> {
415
+ elizaLogger.info('📰 Starting Real-World Test: Breaking News Research');
416
+
417
+ const service = runtime.getService<ResearchService>('research');
418
+ if (!service) throw new Error('Research service not available');
419
+
420
+ // Scenario: Researching breaking AI news
421
+ const newsQuery =
422
+ 'AI artificial intelligence news today latest announcements breakthroughs December 2024';
423
+
424
+ const project = await service.createResearchProject(newsQuery, {
425
+ maxSearchResults: 6,
426
+ metadata: {
427
+ researchType: 'breaking_news',
428
+ timeframe: 'current',
429
+ industry: 'AI/ML',
430
+ },
431
+ });
432
+
433
+ elizaLogger.info('Scanning for breaking AI news...');
434
+
435
+ const result = await monitorResearch(service, project.id, {
436
+ timeout: 150000,
437
+ checkInterval: 3000,
438
+ onProgress: (p) => {
439
+ // Track news sources as they're found
440
+ if (p.phase === ResearchPhase.SEARCHING && p.sources.length > 0) {
441
+ const newsSources = p.sources.filter((s) => {
442
+ const url = s.url.toLowerCase();
443
+ return (
444
+ url.includes('news') ||
445
+ url.includes('article') ||
446
+ url.includes('press') ||
447
+ url.includes('announcement')
448
+ );
449
+ });
450
+
451
+ if (newsSources.length > 0) {
452
+ elizaLogger.info(`Found ${newsSources.length} news sources`);
453
+ }
454
+ }
455
+ },
456
+ });
457
+
458
+ if (!result) throw new Error('News research failed to complete');
459
+
460
+ // Analyze news findings
461
+ const findings = result.findings;
462
+ const sources = result.sources;
463
+
464
+ // Identify news sources by domain
465
+ const newsSourceTypes = {
466
+ mainstream: sources.filter(
467
+ (s) =>
468
+ s.url.includes('reuters.com') ||
469
+ s.url.includes('bloomberg.com') ||
470
+ s.url.includes('wsj.com') ||
471
+ s.url.includes('nytimes.com')
472
+ ),
473
+ tech: sources.filter(
474
+ (s) =>
475
+ s.url.includes('techcrunch.com') ||
476
+ s.url.includes('theverge.com') ||
477
+ s.url.includes('arstechnica.com') ||
478
+ s.url.includes('wired.com')
479
+ ),
480
+ ai_specific: sources.filter(
481
+ (s) =>
482
+ s.url.includes('openai.com') ||
483
+ s.url.includes('anthropic.com') ||
484
+ s.url.includes('deepmind.com') ||
485
+ s.url.includes('ai.')
486
+ ),
487
+ social: sources.filter(
488
+ (s) =>
489
+ s.url.includes('twitter.com') ||
490
+ s.url.includes('x.com') ||
491
+ s.url.includes('reddit.com')
492
+ ),
493
+ };
494
+
495
+ // Check for time-sensitive content
496
+ const currentMonth = new Date().toLocaleDateString('en-US', {
497
+ month: 'long',
498
+ });
499
+ const currentYear = new Date().getFullYear();
500
+
501
+ const hasCurrentMonth = findings.some((f) => f.content.includes(currentMonth));
502
+ const hasCurrentYear = findings.some((f) =>
503
+ f.content.includes(currentYear.toString())
504
+ );
505
+ const hasTimeWords = findings.some((f) => {
506
+ const content = f.content.toLowerCase();
507
+ return (
508
+ content.includes('today') ||
509
+ content.includes('yesterday') ||
510
+ content.includes('this week') ||
511
+ content.includes('announced') ||
512
+ content.includes('just')
513
+ );
514
+ });
515
+
516
+ // Identify major announcements
517
+ const announcements = findings.filter((f) => {
518
+ const content = f.content.toLowerCase();
519
+ return (
520
+ content.includes('announc') ||
521
+ content.includes('launch') ||
522
+ content.includes('releas') ||
523
+ content.includes('unveil') ||
524
+ content.includes('introduc')
525
+ );
526
+ });
527
+
528
+ // Extract companies/organizations mentioned
529
+ const companies = [
530
+ 'OpenAI',
531
+ 'Google',
532
+ 'Microsoft',
533
+ 'Anthropic',
534
+ 'Meta',
535
+ 'Amazon',
536
+ 'Apple',
537
+ ];
538
+ const companyMentions: Record<string, number> = {};
539
+
540
+ companies.forEach((company) => {
541
+ companyMentions[company] = findings.filter((f) =>
542
+ f.content.includes(company)
543
+ ).length;
544
+ });
545
+
546
+ elizaLogger.info('📊 Breaking News Research Results:');
547
+ elizaLogger.info(`- Source distribution:`);
548
+ elizaLogger.info(
549
+ ` * Mainstream media: ${newsSourceTypes.mainstream.length}`
550
+ );
551
+ elizaLogger.info(` * Tech media: ${newsSourceTypes.tech.length}`);
552
+ elizaLogger.info(` * AI companies: ${newsSourceTypes.ai_specific.length}`);
553
+ elizaLogger.info(` * Social media: ${newsSourceTypes.social.length}`);
554
+ elizaLogger.info(`- Timeliness indicators:`);
555
+ elizaLogger.info(` * Current month mentioned: ${hasCurrentMonth}`);
556
+ elizaLogger.info(` * Current year mentioned: ${hasCurrentYear}`);
557
+ elizaLogger.info(` * Time-sensitive words: ${hasTimeWords}`);
558
+ elizaLogger.info(`- Announcements found: ${announcements.length}`);
559
+ elizaLogger.info(
560
+ `- Company mentions: ${Object.entries(companyMentions)
561
+ .filter(([_, count]) => count > 0)
562
+ .map(([company, count]) => `${company}=${count}`)
563
+ .join(', ')}`
564
+ );
565
+
566
+ if (announcements.length > 0) {
567
+ const latestAnnouncement = announcements[0].content.substring(0, 200);
568
+ elizaLogger.info(`- Latest announcement: "${latestAnnouncement}..."`);
569
+ }
570
+
571
+ // News quality assessment
572
+ const hasRecentNews = hasCurrentMonth || hasTimeWords;
573
+ const hasDiverseSources =
574
+ Object.values(newsSourceTypes).filter((arr) => arr.length > 0).length >= 2;
575
+ const hasAnnouncements = announcements.length > 0;
576
+
577
+ if (hasRecentNews && hasDiverseSources && hasAnnouncements) {
578
+ elizaLogger.success('✅ High-quality breaking news coverage achieved');
579
+ } else {
580
+ elizaLogger.info(
581
+ `ℹ️ News coverage: Recent=${hasRecentNews}, Diverse=${hasDiverseSources}, Announcements=${hasAnnouncements}`
582
+ );
583
+ }
584
+
585
+ elizaLogger.success('✅ Real-World Test Passed: Breaking News Research');
586
+ }
587
+
588
+ // Test 4: Market/Competitive Intelligence (Real Business Strategy Workflow)
589
+ export async function testMarketIntelligenceResearch(
590
+ runtime: IAgentRuntime
591
+ ): Promise<void> {
592
+ elizaLogger.info('📈 Starting Real-World Test: Market Intelligence Research');
593
+
594
+ const service = runtime.getService<ResearchService>('research');
595
+ if (!service) throw new Error('Research service not available');
596
+
597
+ // Scenario: Analyzing the AI agent framework market
598
+ const marketQuery =
599
+ 'AI agent frameworks market analysis 2024 LangChain AutoGPT CrewAI pricing features comparison adoption';
600
+
601
+ const project = await service.createResearchProject(marketQuery, {
602
+ maxSearchResults: 5,
603
+ metadata: {
604
+ researchType: 'market_intelligence',
605
+ competitors: ['LangChain', 'AutoGPT', 'CrewAI'],
606
+ analysisType: 'competitive',
607
+ },
608
+ });
609
+
610
+ elizaLogger.info('Conducting market intelligence analysis...');
611
+
612
+ const result = await monitorResearch(service, project.id, {
613
+ timeout: 180000,
614
+ onProgress: (p) => {
615
+ if (p.phase === ResearchPhase.ANALYZING && p.findings.length > 5) {
616
+ elizaLogger.info(
617
+ `Analyzing ${p.findings.length} market data points...`
618
+ );
619
+ }
620
+ },
621
+ });
622
+
623
+ if (!result) throw new Error('Market research failed to complete');
624
+
625
+ // Market analysis
626
+ const findings = result.findings;
627
+ const competitors = ['LangChain', 'AutoGPT', 'CrewAI', 'BabyAGI', 'AgentGPT'];
628
+
629
+ // Competitor analysis
630
+ const competitorData: Record<
631
+ string,
632
+ {
633
+ mentions: number;
634
+ features: string[];
635
+ pricing: boolean;
636
+ adoption: boolean;
637
+ }
638
+ > = {};
639
+
640
+ competitors.forEach((competitor) => {
641
+ const competitorFindings = findings.filter((f) =>
642
+ f.content.toLowerCase().includes(competitor.toLowerCase())
643
+ );
644
+
645
+ const features: string[] = [];
646
+ let hasPricing = false;
647
+ let hasAdoption = false;
648
+
649
+ competitorFindings.forEach((f) => {
650
+ const content = f.content.toLowerCase();
651
+
652
+ // Extract features
653
+ if (content.includes('feature') || content.includes('capability')) {
654
+ if (content.includes('memory')) features.push('memory');
655
+ if (content.includes('tool') || content.includes('function'))
656
+ features.push('tools');
657
+ if (content.includes('chain') || content.includes('workflow'))
658
+ features.push('workflow');
659
+ if (content.includes('llm') || content.includes('model'))
660
+ features.push('multi-llm');
661
+ }
662
+
663
+ // Check for pricing info
664
+ if (
665
+ content.includes('price') ||
666
+ content.includes('cost') ||
667
+ content.includes('free') ||
668
+ content.includes('$')
669
+ ) {
670
+ hasPricing = true;
671
+ }
672
+
673
+ // Check for adoption metrics
674
+ if (
675
+ content.includes('user') ||
676
+ content.includes('download') ||
677
+ content.includes('star') ||
678
+ content.includes('popular')
679
+ ) {
680
+ hasAdoption = true;
681
+ }
682
+ });
683
+
684
+ competitorData[competitor] = {
685
+ mentions: competitorFindings.length,
686
+ features: [...new Set(features)],
687
+ pricing: hasPricing,
688
+ adoption: hasAdoption,
689
+ };
690
+ });
691
+
692
+ // Market trends
693
+ const trendKeywords = [
694
+ 'growth',
695
+ 'trend',
696
+ 'future',
697
+ 'emerging',
698
+ 'adoption',
699
+ 'market size',
700
+ ];
701
+ const trendsFound = trendKeywords.filter((keyword) =>
702
+ findings.some((f) => f.content.toLowerCase().includes(keyword))
703
+ );
704
+
705
+ // Technical comparisons
706
+ const hasComparisons = findings.some((f) => {
707
+ const content = f.content.toLowerCase();
708
+ return (
709
+ content.includes('compar') ||
710
+ content.includes('versus') ||
711
+ content.includes('vs') ||
712
+ content.includes('better') ||
713
+ content.includes('advantage')
714
+ );
715
+ });
716
+
717
+ // Use cases and applications
718
+ const useCases = findings.filter((f) => {
719
+ const content = f.content.toLowerCase();
720
+ return (
721
+ content.includes('use case') ||
722
+ content.includes('application') ||
723
+ content.includes('example') ||
724
+ content.includes('implementation')
725
+ );
726
+ });
727
+
728
+ elizaLogger.info('📊 Market Intelligence Results:');
729
+ elizaLogger.info('- Competitor Analysis:');
730
+ Object.entries(competitorData).forEach(([competitor, data]) => {
731
+ if (data.mentions > 0) {
732
+ elizaLogger.info(
733
+ ` * ${competitor}: ${
734
+ data.mentions
735
+ } mentions, features=[${data.features.join(
736
+ ','
737
+ )}], pricing=${data.pricing}, adoption=${data.adoption}`
738
+ );
739
+ }
740
+ });
741
+ elizaLogger.info(`- Market trends identified: ${trendsFound.join(', ')}`);
742
+ elizaLogger.info(`- Comparative analysis found: ${hasComparisons}`);
743
+ elizaLogger.info(`- Use cases documented: ${useCases.length}`);
744
+
745
+ // Strategic insights
746
+ const wellCoveredCompetitors = Object.entries(competitorData)
747
+ .filter(([_, data]) => data.mentions >= 2)
748
+ .map(([name, _]) => name);
749
+
750
+ const hasComprehensiveData =
751
+ wellCoveredCompetitors.length >= 2 && hasComparisons && useCases.length > 0;
752
+
753
+ if (hasComprehensiveData) {
754
+ elizaLogger.success('✅ Comprehensive market intelligence gathered');
755
+ elizaLogger.info(
756
+ `Key competitors analyzed: ${wellCoveredCompetitors.join(', ')}`
757
+ );
758
+ } else {
759
+ elizaLogger.info(
760
+ 'ℹ️ Partial market intelligence - consider additional research'
761
+ );
762
+ }
763
+
764
+ elizaLogger.success('✅ Real-World Test Passed: Market Intelligence Research');
765
+ }
766
+
767
+ // Test 5: Technical Problem Solving Research (Real Developer Debug Workflow)
768
+ export async function testProblemSolvingResearch(
769
+ runtime: IAgentRuntime
770
+ ): Promise<void> {
771
+ elizaLogger.info(
772
+ '🔧 Starting Real-World Test: Technical Problem Solving Research'
773
+ );
774
+
775
+ const service = runtime.getService<ResearchService>('research');
776
+ if (!service) throw new Error('Research service not available');
777
+
778
+ // Scenario: Debugging a complex technical issue
779
+ const problemQuery =
780
+ 'TypeError cannot read property undefined JavaScript async await Promise debugging stack trace fix';
781
+
782
+ const project = await service.createResearchProject(problemQuery, {
783
+ maxSearchResults: 4,
784
+ metadata: {
785
+ researchType: 'debugging',
786
+ problemType: 'runtime_error',
787
+ technology: 'JavaScript',
788
+ },
789
+ });
790
+
791
+ elizaLogger.info('Researching technical problem solutions...');
792
+
793
+ const result = await monitorResearch(service, project.id, {
794
+ timeout: 120000,
795
+ onProgress: (p) => {
796
+ // Look for Stack Overflow as it appears
797
+ const hasStackOverflow = p.sources.some((s) =>
798
+ s.url.includes('stackoverflow.com')
799
+ );
800
+ if (hasStackOverflow && p.sources.length === 1) {
801
+ elizaLogger.info('Found Stack Overflow - good sign for debugging!');
802
+ }
803
+ },
804
+ });
805
+
806
+ if (!result) throw new Error('Problem solving research failed to complete');
807
+
808
+ // Analyze debugging findings
809
+ const findings = result.findings;
810
+ const sources = result.sources;
811
+
812
+ // Categorize sources
813
+ const debuggingSources = {
814
+ stackoverflow: sources.filter((s) => s.url.includes('stackoverflow.com')),
815
+ github: sources.filter((s) => s.url.includes('github.com')),
816
+ documentation: sources.filter(
817
+ (s) =>
818
+ s.url.includes('developer.mozilla.org') ||
819
+ s.url.includes('javascript.info') ||
820
+ s.url.includes('docs.')
821
+ ),
822
+ blogs: sources.filter(
823
+ (s) =>
824
+ s.url.includes('blog') ||
825
+ s.url.includes('medium.com') ||
826
+ s.url.includes('dev.to')
827
+ ),
828
+ };
829
+
830
+ // Look for solutions
831
+ const hasSolutions = findings.filter((f) => {
832
+ const content = f.content.toLowerCase();
833
+ return (
834
+ content.includes('solution') ||
835
+ content.includes('fix') ||
836
+ content.includes('resolve') ||
837
+ content.includes('solved') ||
838
+ content.includes('work')
839
+ );
840
+ });
841
+
842
+ // Look for code examples
843
+ const hasCodeExamples = findings.filter((f) => {
844
+ const content = f.content;
845
+ return (
846
+ content.includes('```') ||
847
+ content.includes('const ') ||
848
+ content.includes('let ') ||
849
+ content.includes('function') ||
850
+ content.includes('async ') ||
851
+ content.includes('await ') ||
852
+ content.includes('try') ||
853
+ content.includes('catch')
854
+ );
855
+ });
856
+
857
+ // Look for explanations
858
+ const hasExplanations = findings.filter((f) => {
859
+ const content = f.content.toLowerCase();
860
+ return (
861
+ content.includes('because') ||
862
+ content.includes('reason') ||
863
+ content.includes('cause') ||
864
+ content.includes('happen') ||
865
+ content.includes('occur')
866
+ );
867
+ });
868
+
869
+ // Check for similar issues
870
+ const similarIssues = findings.filter((f) => {
871
+ const content = f.content.toLowerCase();
872
+ return (
873
+ content.includes('similar') ||
874
+ content.includes('same error') ||
875
+ content.includes('same issue') ||
876
+ content.includes('also')
877
+ );
878
+ });
879
+
880
+ elizaLogger.info('🔍 Problem Solving Research Results:');
881
+ elizaLogger.info('- Source distribution:');
882
+ elizaLogger.info(
883
+ ` * Stack Overflow: ${debuggingSources.stackoverflow.length}`
884
+ );
885
+ elizaLogger.info(` * GitHub Issues: ${debuggingSources.github.length}`);
886
+ elizaLogger.info(
887
+ ` * Documentation: ${debuggingSources.documentation.length}`
888
+ );
889
+ elizaLogger.info(` * Technical Blogs: ${debuggingSources.blogs.length}`);
890
+ elizaLogger.info(`- Solutions found: ${hasSolutions.length}`);
891
+ elizaLogger.info(`- Code examples: ${hasCodeExamples.length}`);
892
+ elizaLogger.info(`- Explanations: ${hasExplanations.length}`);
893
+ elizaLogger.info(`- Similar issues: ${similarIssues.length}`);
894
+
895
+ // Extract a solution if found
896
+ if (hasSolutions.length > 0 && hasCodeExamples.length > 0) {
897
+ elizaLogger.success('✅ Found solutions with code examples!');
898
+
899
+ // Find the most relevant solution
900
+ const bestSolution = hasSolutions.sort((a, b) => b.relevance - a.relevance)[0];
901
+ const preview = bestSolution.content.substring(0, 250);
902
+ elizaLogger.info(`Top solution preview: "${preview}..."`);
903
+ }
904
+
905
+ // Problem solving quality
906
+ const hasGoodSources =
907
+ debuggingSources.stackoverflow.length > 0 ||
908
+ debuggingSources.documentation.length > 0;
909
+ const hasGoodContent =
910
+ hasSolutions.length > 0 && hasCodeExamples.length > 0;
911
+ const hasContext = hasExplanations.length > 0;
912
+
913
+ if (hasGoodSources && hasGoodContent && hasContext) {
914
+ elizaLogger.success('✅ Comprehensive debugging information found');
915
+ } else {
916
+ elizaLogger.info(
917
+ `ℹ️ Debugging info: Sources=${hasGoodSources}, Solutions=${hasGoodContent}, Context=${hasContext}`
918
+ );
919
+ }
920
+
921
+ elizaLogger.success(
922
+ '✅ Real-World Test Passed: Technical Problem Solving Research'
923
+ );
924
+ }
925
+
926
+ // Test 6: Academic/Learning Research (Real Student/Researcher Workflow)
927
+ export async function testAcademicResearch(runtime: IAgentRuntime): Promise<void> {
928
+ elizaLogger.info('🎓 Starting Real-World Test: Academic/Learning Research');
929
+
930
+ const service = runtime.getService<ResearchService>('research');
931
+ if (!service) throw new Error('Research service not available');
932
+
933
+ // Scenario: Researching for learning/academic purposes
934
+ const academicQuery =
935
+ 'transformer architecture attention mechanism self-attention tutorial papers implementation from scratch';
936
+
937
+ const project = await service.createResearchProject(academicQuery, {
938
+ maxSearchResults: 5,
939
+ metadata: {
940
+ researchType: 'academic',
941
+ subject: 'machine_learning',
942
+ level: 'advanced',
943
+ },
944
+ });
945
+
946
+ elizaLogger.info(
947
+ 'Conducting academic research on transformer architecture...'
948
+ );
949
+
950
+ const result = await monitorResearch(service, project.id, {
951
+ timeout: 150000,
952
+ });
953
+
954
+ if (!result) throw new Error('Academic research failed to complete');
955
+
956
+ // Analyze academic findings
957
+ const findings = result.findings;
958
+ const sources = result.sources;
959
+
960
+ // Categorize academic sources
961
+ const academicSources = {
962
+ papers: sources.filter(
963
+ (s) =>
964
+ s.url.includes('arxiv.org') ||
965
+ s.url.includes('paper') ||
966
+ s.url.includes('pdf') ||
967
+ s.title.toLowerCase().includes('paper')
968
+ ),
969
+ tutorials: sources.filter(
970
+ (s) =>
971
+ s.url.includes('tutorial') ||
972
+ s.url.includes('guide') ||
973
+ s.url.includes('explained') ||
974
+ s.title.toLowerCase().includes('tutorial')
975
+ ),
976
+ educational: sources.filter(
977
+ (s) =>
978
+ s.url.includes('edu') ||
979
+ s.url.includes('course') ||
980
+ s.url.includes('stanford') ||
981
+ s.url.includes('mit')
982
+ ),
983
+ implementation: sources.filter(
984
+ (s) =>
985
+ s.url.includes('github.com') ||
986
+ s.url.includes('colab') ||
987
+ s.url.includes('kaggle')
988
+ ),
989
+ };
990
+
991
+ // Look for key concepts
992
+ const concepts = [
993
+ 'attention',
994
+ 'self-attention',
995
+ 'multi-head',
996
+ 'query',
997
+ 'key',
998
+ 'value',
999
+ 'transformer',
1000
+ ];
1001
+ const conceptCoverage: Record<string, number> = {};
1002
+
1003
+ concepts.forEach((concept) => {
1004
+ conceptCoverage[concept] = findings.filter((f) =>
1005
+ f.content.toLowerCase().includes(concept)
1006
+ ).length;
1007
+ });
1008
+
1009
+ // Check for mathematical content
1010
+ const hasMath = findings.some((f) => {
1011
+ const content = f.content;
1012
+ return (
1013
+ content.includes('equation') ||
1014
+ content.includes('formula') ||
1015
+ content.includes('Σ') ||
1016
+ content.includes('matrix') ||
1017
+ content.includes('dimension')
1018
+ );
1019
+ });
1020
+
1021
+ // Check for visual explanations
1022
+ const hasVisuals = findings.some((f) => {
1023
+ const content = f.content.toLowerCase();
1024
+ return (
1025
+ content.includes('diagram') ||
1026
+ content.includes('figure') ||
1027
+ content.includes('image') ||
1028
+ content.includes('illustration') ||
1029
+ content.includes('visualization')
1030
+ );
1031
+ });
1032
+
1033
+ // Check for code implementations
1034
+ const hasImplementations = findings.filter((f) => {
1035
+ const content = f.content;
1036
+ return (
1037
+ content.includes('class Transformer') ||
1038
+ content.includes('class Attention') ||
1039
+ content.includes('def attention') ||
1040
+ content.includes('import torch') ||
1041
+ content.includes('import tensorflow')
1042
+ );
1043
+ });
1044
+
1045
+ elizaLogger.info('📚 Academic Research Results:');
1046
+ elizaLogger.info('- Source types:');
1047
+ elizaLogger.info(` * Academic papers: ${academicSources.papers.length}`);
1048
+ elizaLogger.info(
1049
+ ` * Tutorials/Guides: ${academicSources.tutorials.length}`
1050
+ );
1051
+ elizaLogger.info(
1052
+ ` * Educational institutions: ${academicSources.educational.length}`
1053
+ );
1054
+ elizaLogger.info(
1055
+ ` * Code implementations: ${academicSources.implementation.length}`
1056
+ );
1057
+ elizaLogger.info('- Concept coverage:');
1058
+ Object.entries(conceptCoverage).forEach(([concept, count]) => {
1059
+ if (count > 0) elizaLogger.info(` * ${concept}: ${count} mentions`);
1060
+ });
1061
+ elizaLogger.info(`- Mathematical content: ${hasMath}`);
1062
+ elizaLogger.info(`- Visual explanations: ${hasVisuals}`);
1063
+ elizaLogger.info(
1064
+ `- Code implementations found: ${hasImplementations.length}`
1065
+ );
1066
+
1067
+ // Learning quality assessment
1068
+ const hasTheory =
1069
+ academicSources.papers.length > 0 ||
1070
+ Object.values(conceptCoverage).some((c) => c > 2);
1071
+ const hasPractice =
1072
+ academicSources.tutorials.length > 0 || hasImplementations.length > 0;
1073
+ const conceptsCovered = Object.values(conceptCoverage).filter(
1074
+ (c) => c > 0
1075
+ ).length;
1076
+
1077
+ if (hasTheory && hasPractice && conceptsCovered >= 4) {
1078
+ elizaLogger.success(
1079
+ '✅ Excellent academic research - covers theory and practice'
1080
+ );
1081
+ } else {
1082
+ elizaLogger.info(
1083
+ `ℹ️ Academic coverage: Theory=${hasTheory}, Practice=${hasPractice}, Concepts=${conceptsCovered}/7`
1084
+ );
1085
+ }
1086
+
1087
+ elizaLogger.success('✅ Real-World Test Passed: Academic/Learning Research');
1088
+ }
1089
+
1090
+ // #endregion
1091
+
1092
+ // #region: Core Functionality Scenarios
1093
+
1094
+ // Test 1: Code/Feature Research - Simulating research before building a feature
1095
+ export async function testCodeFeatureResearch(
1096
+ runtime: IAgentRuntime
1097
+ ): Promise<void> {
1098
+ elizaLogger.info('Starting E2E Test: Code/Feature Research');
1099
+
1100
+ const service = runtime.getService<ResearchService>('research');
1101
+ if (!service) {
1102
+ throw new Error('Research service not available');
1103
+ }
1104
+
1105
+ // Research query that a developer would make before implementing a feature
1106
+ const featureQuery =
1107
+ 'How to implement OAuth2 authentication in Node.js with TypeScript best practices security 2024';
1108
+
1109
+ const project = await service.createResearchProject(featureQuery, {
1110
+ maxSearchResults: 5,
1111
+ language: 'en',
1112
+ metadata: {
1113
+ researchType: 'technical',
1114
+ purpose: 'feature_implementation',
1115
+ },
1116
+ });
1117
+
1118
+ elizaLogger.info(`Created code research project: ${project.id}`);
1119
+
1120
+ // Monitor progress
1121
+ let lastUpdate = Date.now();
1122
+ let phaseProgress: Record<string, number> = {};
1123
+
1124
+ const checkProgress = setInterval(async () => {
1125
+ const current = await service.getProject(project.id);
1126
+ if (!current) return;
1127
+
1128
+ if (!phaseProgress[current.phase]) {
1129
+ phaseProgress[current.phase] = Date.now() - lastUpdate;
1130
+ elizaLogger.info(
1131
+ `Phase ${current.phase} started after ${phaseProgress[current.phase]}ms`
1132
+ );
1133
+ lastUpdate = Date.now();
1134
+ }
1135
+
1136
+ if (
1137
+ current.status === ResearchStatus.COMPLETED ||
1138
+ current.status === ResearchStatus.FAILED
1139
+ ) {
1140
+ clearInterval(checkProgress);
1141
+ }
1142
+ }, 3000);
1143
+
1144
+ // Wait for completion
1145
+ const completed = await waitForResearchCompletion(service, project.id, 180000);
1146
+ clearInterval(checkProgress);
1147
+
1148
+ if (!completed) {
1149
+ throw new Error('Code research did not complete within timeout');
1150
+ }
1151
+
1152
+ const finalProject = await service.getProject(project.id);
1153
+ if (!finalProject) {
1154
+ throw new Error('Could not retrieve completed project');
1155
+ }
1156
+
1157
+ // Validate research quality
1158
+ const quality = validateResearchQuality(finalProject);
1159
+ if (!quality.isValid) {
1160
+ elizaLogger.warn(`Research quality issues: ${quality.issues.join(', ')}`);
1161
+ }
1162
+
1163
+ // Verify we found technical content
1164
+ const hasCodeExamples = finalProject.findings.some(
1165
+ (f) =>
1166
+ f.content.includes('npm') ||
1167
+ f.content.includes('const') ||
1168
+ f.content.includes('import') ||
1169
+ f.content.includes('OAuth')
1170
+ );
1171
+
1172
+ if (!hasCodeExamples) {
1173
+ elizaLogger.warn('No code examples found in technical research');
1174
+ }
1175
+
1176
+ // Check for security considerations (important for auth research)
1177
+ const hasSecurityInfo = finalProject.findings.some(
1178
+ (f) =>
1179
+ f.content.toLowerCase().includes('security') ||
1180
+ f.content.toLowerCase().includes('csrf') ||
1181
+ f.content.toLowerCase().includes('token')
1182
+ );
1183
+
1184
+ if (!hasSecurityInfo) {
1185
+ elizaLogger.warn('No security information found in OAuth research');
1186
+ }
1187
+
1188
+ elizaLogger.info(
1189
+ `Code research completed with ${finalProject.sources.length} sources and ${finalProject.findings.length} findings`
1190
+ );
1191
+ elizaLogger.info(
1192
+ `Found code examples: ${hasCodeExamples}, Security info: ${hasSecurityInfo}`
1193
+ );
1194
+
1195
+ if (finalProject.report) {
1196
+ elizaLogger.info(
1197
+ `Report generated with ${finalProject.report.sections.length} sections`
1198
+ );
1199
+ }
1200
+
1201
+ elizaLogger.success('E2E Test Passed: Code/Feature Research');
1202
+ }
1203
+
1204
+ // Test 2: Person Research - Researching a public figure
1205
+ export async function testPersonResearch(runtime: IAgentRuntime): Promise<void> {
1206
+ elizaLogger.info('Starting E2E Test: Person Research');
1207
+
1208
+ const service = runtime.getService<ResearchService>('research');
1209
+ if (!service) {
1210
+ throw new Error('Research service not available');
1211
+ }
1212
+
1213
+ // Research a well-known tech figure
1214
+ const personQuery =
1215
+ 'Vitalik Buterin Ethereum founder recent projects writings 2024';
1216
+
1217
+ const project = await service.createResearchProject(personQuery, {
1218
+ maxSearchResults: 4,
1219
+ metadata: {
1220
+ researchType: 'person',
1221
+ purpose: 'background_research',
1222
+ },
1223
+ });
1224
+
1225
+ elizaLogger.info(`Created person research project: ${project.id}`);
1226
+
1227
+ // Wait for completion
1228
+ const completed = await waitForResearchCompletion(service, project.id, 150000);
1229
+
1230
+ if (!completed) {
1231
+ const partial = await service.getProject(project.id);
1232
+ if (partial && partial.findings.length > 0) {
1233
+ elizaLogger.warn(
1234
+ `Person research incomplete but found ${partial.findings.length} findings`
1235
+ );
1236
+ } else {
1237
+ throw new Error('Person research did not complete and no findings collected');
1238
+ }
1239
+ }
1240
+
1241
+ const finalProject = await service.getProject(project.id);
1242
+ if (!finalProject) {
1243
+ throw new Error('Could not retrieve completed project');
1244
+ }
1245
+
1246
+ // Validate we found relevant information about the person
1247
+ const relevantFindings = finalProject.findings.filter(
1248
+ (f) =>
1249
+ f.content.toLowerCase().includes('vitalik') ||
1250
+ f.content.toLowerCase().includes('ethereum') ||
1251
+ f.content.toLowerCase().includes('buterin')
1252
+ );
1253
+
1254
+ if (relevantFindings.length === 0) {
1255
+ throw new Error('No relevant findings about the person');
1256
+ }
1257
+
1258
+ // Check for recent information
1259
+ const hasRecentInfo = finalProject.findings.some(
1260
+ (f) =>
1261
+ f.content.includes('2024') ||
1262
+ f.content.includes('2023') ||
1263
+ f.content.toLowerCase().includes('recent')
1264
+ );
1265
+
1266
+ elizaLogger.info(
1267
+ `Person research completed: ${relevantFindings.length}/${finalProject.findings.length} relevant findings`
1268
+ );
1269
+ elizaLogger.info(`Found recent information: ${hasRecentInfo}`);
1270
+
1271
+ // Sample a finding
1272
+ if (relevantFindings.length > 0) {
1273
+ const sample = relevantFindings[0].content.substring(0, 200);
1274
+ elizaLogger.info(`Sample finding: "${sample}..."`);
1275
+ }
1276
+
1277
+ elizaLogger.success('E2E Test Passed: Person Research');
1278
+ }
1279
+
1280
+ // Test 3: News/Current Events Research
1281
+ export async function testNewsResearch(runtime: IAgentRuntime): Promise<void> {
1282
+ elizaLogger.info('Starting E2E Test: News/Current Events Research');
1283
+
1284
+ const service = runtime.getService<ResearchService>('research');
1285
+ if (!service) {
1286
+ throw new Error('Research service not available');
1287
+ }
1288
+
1289
+ // Research current AI developments
1290
+ const newsQuery =
1291
+ 'latest artificial intelligence breakthroughs news December 2024 ChatGPT Claude Gemini';
1292
+
1293
+ const project = await service.createResearchProject(newsQuery, {
1294
+ maxSearchResults: 5,
1295
+ metadata: {
1296
+ researchType: 'news',
1297
+ timeframe: 'current',
1298
+ purpose: 'market_intelligence',
1299
+ },
1300
+ });
1301
+
1302
+ elizaLogger.info(`Created news research project: ${project.id}`);
1303
+
1304
+ // Monitor for news-specific content
1305
+ let checkCount = 0;
1306
+ const newsCheckInterval = setInterval(async () => {
1307
+ checkCount++;
1308
+ const current = await service.getProject(project.id);
1309
+ if (!current) return;
1310
+
1311
+ // Check if we're finding news sources
1312
+ const newsSource = current.sources.find(
1313
+ (s) =>
1314
+ s.url.includes('news') ||
1315
+ s.url.includes('article') ||
1316
+ s.url.includes('blog') ||
1317
+ s.title.toLowerCase().includes('2024')
1318
+ );
1319
+
1320
+ if (newsSource && checkCount === 1) {
1321
+ elizaLogger.info(`Found news source: ${newsSource.title}`);
1322
+ }
1323
+
1324
+ if (
1325
+ current.status === ResearchStatus.COMPLETED ||
1326
+ current.status === ResearchStatus.FAILED ||
1327
+ checkCount > 30
1328
+ ) {
1329
+ clearInterval(newsCheckInterval);
1330
+ }
1331
+ }, 5000);
1332
+
1333
+ // Wait for completion
1334
+ const completed = await waitForResearchCompletion(service, project.id, 180000);
1335
+ clearInterval(newsCheckInterval);
1336
+
1337
+ const finalProject = await service.getProject(project.id);
1338
+ if (!finalProject) {
1339
+ throw new Error('Could not retrieve completed project');
1340
+ }
1341
+
1342
+ // Validate news research quality
1343
+ const quality = validateResearchQuality(finalProject);
1344
+
1345
+ // Check for AI-related content
1346
+ const aiFindings = finalProject.findings.filter((f) => {
1347
+ const content = f.content.toLowerCase();
1348
+ return (
1349
+ content.includes('ai') ||
1350
+ content.includes('artificial intelligence') ||
1351
+ content.includes('chatgpt') ||
1352
+ content.includes('claude') ||
1353
+ content.includes('gemini') ||
1354
+ content.includes('machine learning')
1355
+ );
1356
+ });
1357
+
1358
+ if (aiFindings.length === 0) {
1359
+ elizaLogger.warn('No AI-related findings in news research');
1360
+ }
1361
+
1362
+ // Check for recent dates
1363
+ const hasRecentDates = finalProject.findings.some((f) => {
1364
+ const content = f.content;
1365
+ return (
1366
+ content.includes('2024') ||
1367
+ content.includes('December') ||
1368
+ content.includes('November') ||
1369
+ content.includes('recent')
1370
+ );
1371
+ });
1372
+
1373
+ elizaLogger.info(
1374
+ `News research completed: ${aiFindings.length} AI-related findings out of ${finalProject.findings.length} total`
1375
+ );
1376
+ elizaLogger.info(`Contains recent dates: ${hasRecentDates}`);
1377
+ elizaLogger.info(
1378
+ `Research quality: ${
1379
+ quality.isValid ? 'Good' : 'Issues: ' + quality.issues.join(', ')
1380
+ }`
1381
+ );
1382
+
1383
+ elizaLogger.success('E2E Test Passed: News/Current Events Research');
1384
+ }
1385
+
1386
+ // Test 4: Technical Documentation Research
1387
+ export async function testDocumentationResearch(
1388
+ runtime: IAgentRuntime
1389
+ ): Promise<void> {
1390
+ elizaLogger.info('Starting E2E Test: Technical Documentation Research');
1391
+
1392
+ const service = runtime.getService<ResearchService>('research');
1393
+ if (!service) {
1394
+ throw new Error('Research service not available');
1395
+ }
1396
+
1397
+ // Research technical documentation
1398
+ const docQuery =
1399
+ 'React Server Components documentation examples best practices performance optimization';
1400
+
1401
+ const project = await service.createResearchProject(docQuery, {
1402
+ maxSearchResults: 4,
1403
+ metadata: {
1404
+ researchType: 'documentation',
1405
+ purpose: 'learning',
1406
+ },
1407
+ });
1408
+
1409
+ elizaLogger.info(`Created documentation research project: ${project.id}`);
1410
+
1411
+ // Wait for completion
1412
+ const completed = await waitForResearchCompletion(service, project.id, 150000);
1413
+
1414
+ const finalProject = await service.getProject(project.id);
1415
+ if (!finalProject) {
1416
+ throw new Error('Could not retrieve completed project');
1417
+ }
1418
+
1419
+ // Check for documentation-specific content
1420
+ const hasDocSources = finalProject.sources.some(
1421
+ (s) =>
1422
+ s.url.includes('react.dev') ||
1423
+ s.url.includes('docs') ||
1424
+ s.url.includes('documentation') ||
1425
+ s.url.includes('github.com')
1426
+ );
1427
+
1428
+ const hasCodeExamples = finalProject.findings.some(
1429
+ (f) =>
1430
+ f.content.includes('```') ||
1431
+ f.content.includes('<') ||
1432
+ f.content.includes('/>') ||
1433
+ f.content.includes('function') ||
1434
+ f.content.includes('const')
1435
+ );
1436
+
1437
+ const hasBestPractices = finalProject.findings.some(
1438
+ (f) =>
1439
+ f.content.toLowerCase().includes('best practice') ||
1440
+ f.content.toLowerCase().includes('recommendation') ||
1441
+ f.content.toLowerCase().includes('should') ||
1442
+ f.content.toLowerCase().includes('avoid')
1443
+ );
1444
+
1445
+ elizaLogger.info(`Documentation research completed:`);
1446
+ elizaLogger.info(`- Found documentation sources: ${hasDocSources}`);
1447
+ elizaLogger.info(`- Contains code examples: ${hasCodeExamples}`);
1448
+ elizaLogger.info(`- Includes best practices: ${hasBestPractices}`);
1449
+ elizaLogger.info(`- Total findings: ${finalProject.findings.length}`);
1450
+
1451
+ elizaLogger.success('E2E Test Passed: Technical Documentation Research');
1452
+ }
1453
+
1454
+ // Test 5: Competitive Analysis Research
1455
+ export async function testCompetitiveAnalysis(
1456
+ runtime: IAgentRuntime
1457
+ ): Promise<void> {
1458
+ elizaLogger.info('Starting E2E Test: Competitive Analysis Research');
1459
+
1460
+ const service = runtime.getService<ResearchService>('research');
1461
+ if (!service) {
1462
+ throw new Error('Research service not available');
1463
+ }
1464
+
1465
+ // Research competitors in the AI agent space
1466
+ const competitorQuery =
1467
+ 'AI agent frameworks comparison AutoGPT BabyAGI LangChain CrewAI features pricing';
1468
+
1469
+ const project = await service.createResearchProject(competitorQuery, {
1470
+ maxSearchResults: 5,
1471
+ metadata: {
1472
+ researchType: 'competitive_analysis',
1473
+ purpose: 'market_research',
1474
+ },
1475
+ });
1476
+
1477
+ elizaLogger.info(`Created competitive analysis project: ${project.id}`);
1478
+
1479
+ // Wait for completion
1480
+ const completed = await waitForResearchCompletion(service, project.id, 180000);
1481
+
1482
+ const finalProject = await service.getProject(project.id);
1483
+ if (!finalProject) {
1484
+ throw new Error('Could not retrieve completed project');
1485
+ }
1486
+
1487
+ // Check for competitor mentions
1488
+ const competitors = ['AutoGPT', 'BabyAGI', 'LangChain', 'CrewAI'];
1489
+ const competitorMentions: Record<string, number> = {};
1490
+
1491
+ competitors.forEach((competitor) => {
1492
+ competitorMentions[competitor] = finalProject.findings.filter((f) =>
1493
+ f.content.toLowerCase().includes(competitor.toLowerCase())
1494
+ ).length;
1495
+ });
1496
+
1497
+ const totalMentions = Object.values(competitorMentions).reduce(
1498
+ (a, b) => a + b,
1499
+ 0
1500
+ );
1501
+
1502
+ // Check for comparison content
1503
+ const hasComparison = finalProject.findings.some((f) => {
1504
+ const content = f.content.toLowerCase();
1505
+ return (
1506
+ content.includes('compar') ||
1507
+ content.includes('versus') ||
1508
+ content.includes('vs') ||
1509
+ content.includes('better') ||
1510
+ content.includes('advantage')
1511
+ );
1512
+ });
1513
+
1514
+ // Check for feature analysis
1515
+ const hasFeatures = finalProject.findings.some((f) => {
1516
+ const content = f.content.toLowerCase();
1517
+ return (
1518
+ content.includes('feature') ||
1519
+ content.includes('capability') ||
1520
+ content.includes('functionality') ||
1521
+ content.includes('support')
1522
+ );
1523
+ });
1524
+
1525
+ elizaLogger.info(`Competitive analysis completed:`);
1526
+ elizaLogger.info(
1527
+ `- Competitor mentions: ${JSON.stringify(competitorMentions)}`
1528
+ );
1529
+ elizaLogger.info(`- Total competitor mentions: ${totalMentions}`);
1530
+ elizaLogger.info(`- Contains comparisons: ${hasComparison}`);
1531
+ elizaLogger.info(`- Analyzes features: ${hasFeatures}`);
1532
+
1533
+ if (totalMentions < 2) {
1534
+ elizaLogger.warn('Limited competitor information found');
1535
+ }
1536
+
1537
+ elizaLogger.success('E2E Test Passed: Competitive Analysis Research');
1538
+ }
1539
+
1540
+ // Test 6: DeFi Research Integration
1541
+ export async function testDeFiResearch(runtime: IAgentRuntime): Promise<void> {
1542
+ elizaLogger.info('Starting E2E Test: DeFi Research Integration');
1543
+
1544
+ const service = runtime.getService<ResearchService>('research');
1545
+ if (!service) {
1546
+ throw new Error('Research service not available');
1547
+ }
1548
+
1549
+ // Import DeFi action to test
1550
+ const { defiSecurityResearchAction } = await import(
1551
+ '../actions/defi-actions'
1552
+ );
1553
+
1554
+ // Create a mock message for DeFi research
1555
+ const mockMessage: Memory = {
1556
+ id: '00000000-0000-0000-0000-000000000001' as `${string}-${string}-${string}-${string}-${string}`,
1557
+ entityId: '00000000-0000-0000-0000-000000000002' as `${string}-${string}-${string}-${string}-${string}`,
1558
+ roomId: '00000000-0000-0000-0000-000000000003' as `${string}-${string}-${string}-${string}-${string}`,
1559
+ content: {
1560
+ text: 'Research smart contract security vulnerabilities and audit best practices for DeFi protocols',
1561
+ },
1562
+ createdAt: Date.now(),
1563
+ };
1564
+
1565
+ // Execute DeFi security research
1566
+ const result = await defiSecurityResearchAction.handler(
1567
+ runtime,
1568
+ mockMessage,
1569
+ undefined,
1570
+ {}
1571
+ );
1572
+
1573
+ if (!result || typeof result !== 'object') {
1574
+ throw new Error('DeFi research action returned invalid result');
1575
+ }
1576
+
1577
+ // Extract project ID from response
1578
+ const responseText = (result as any).text || '';
1579
+ const projectIdMatch = responseText.match(/([a-f0-9-]{36})/);
1580
+
1581
+ if (!projectIdMatch) {
1582
+ throw new Error('Could not extract project ID from DeFi research response');
1583
+ }
1584
+
1585
+ const projectId = projectIdMatch[1];
1586
+ elizaLogger.info(`DeFi research started with project ID: ${projectId}`);
1587
+
1588
+ // Monitor DeFi-specific findings
1589
+ let defiCheckCount = 0;
1590
+ const defiCheckInterval = setInterval(async () => {
1591
+ defiCheckCount++;
1592
+ const current = await service.getProject(projectId);
1593
+ if (!current) return;
1594
+
1595
+ // Check for DeFi-specific content
1596
+ const defiFindings = current.findings.filter((f) => {
1597
+ const content = f.content.toLowerCase();
1598
+ return (
1599
+ content.includes('smart contract') ||
1600
+ content.includes('defi') ||
1601
+ content.includes('audit') ||
1602
+ content.includes('vulnerability') ||
1603
+ content.includes('security')
1604
+ );
1605
+ });
1606
+
1607
+ if (defiFindings.length > 0 && defiCheckCount === 1) {
1608
+ elizaLogger.info(
1609
+ `Found ${defiFindings.length} DeFi-specific findings`
1610
+ );
1611
+ }
1612
+
1613
+ if (
1614
+ current.status === ResearchStatus.COMPLETED ||
1615
+ current.status === ResearchStatus.FAILED ||
1616
+ defiCheckCount > 20
1617
+ ) {
1618
+ clearInterval(defiCheckInterval);
1619
+ }
1620
+ }, 5000);
1621
+
1622
+ // Wait for completion
1623
+ const completed = await waitForResearchCompletion(service, projectId, 120000);
1624
+ clearInterval(defiCheckInterval);
1625
+
1626
+ const finalProject = await service.getProject(projectId);
1627
+ if (!finalProject) {
1628
+ throw new Error('Could not retrieve DeFi research project');
1629
+ }
1630
+
1631
+ // Validate DeFi research quality
1632
+ const hasSecurityContent = finalProject.findings.some(
1633
+ (f) =>
1634
+ f.content.toLowerCase().includes('security') ||
1635
+ f.content.toLowerCase().includes('vulnerability') ||
1636
+ f.content.toLowerCase().includes('exploit')
1637
+ );
1638
+
1639
+ const hasAuditContent = finalProject.findings.some(
1640
+ (f) =>
1641
+ f.content.toLowerCase().includes('audit') ||
1642
+ f.content.toLowerCase().includes('review') ||
1643
+ f.content.toLowerCase().includes('verification')
1644
+ );
1645
+
1646
+ const hasCodeExamples = finalProject.findings.some(
1647
+ (f) =>
1648
+ f.content.includes('solidity') ||
1649
+ f.content.includes('contract') ||
1650
+ f.content.includes('function') ||
1651
+ f.content.includes('require')
1652
+ );
1653
+
1654
+ elizaLogger.info(`DeFi research completed:`);
1655
+ elizaLogger.info(`- Security content found: ${hasSecurityContent}`);
1656
+ elizaLogger.info(`- Audit content found: ${hasAuditContent}`);
1657
+ elizaLogger.info(`- Code examples found: ${hasCodeExamples}`);
1658
+ elizaLogger.info(`- Total findings: ${finalProject.findings.length}`);
1659
+
1660
+ elizaLogger.success('E2E Test Passed: DeFi Research Integration');
1661
+ }
1662
+
1663
+ // Test 7: Research Quality and Relevance
1664
+ export async function testResearchQualityAssurance(
1665
+ runtime: IAgentRuntime
1666
+ ): Promise<void> {
1667
+ elizaLogger.info('Starting E2E Test: Research Quality Assurance');
1668
+
1669
+ const service = runtime.getService<ResearchService>('research');
1670
+ if (!service) {
1671
+ throw new Error('Research service not available');
1672
+ }
1673
+
1674
+ // Create a very specific research query
1675
+ const specificQuery =
1676
+ 'ElizaOS plugin development tutorial TypeScript examples 2024';
1677
+
1678
+ const project = await service.createResearchProject(specificQuery, {
1679
+ maxSearchResults: 3,
1680
+ metadata: {
1681
+ researchType: 'tutorial',
1682
+ expectedKeywords: ['ElizaOS', 'plugin', 'TypeScript', 'development'],
1683
+ },
1684
+ });
1685
+
1686
+ elizaLogger.info(`Created quality assurance research project: ${project.id}`);
1687
+
1688
+ // Wait for completion
1689
+ const completed = await waitForResearchCompletion(service, project.id, 120000);
1690
+
1691
+ const finalProject = await service.getProject(project.id);
1692
+ if (!finalProject) {
1693
+ throw new Error('Could not retrieve completed project');
1694
+ }
1695
+
1696
+ // Detailed quality checks
1697
+ const expectedKeywords = [
1698
+ 'ElizaOS',
1699
+ 'plugin',
1700
+ 'TypeScript',
1701
+ 'development',
1702
+ 'tutorial',
1703
+ ];
1704
+ const keywordCoverage: Record<string, number> = {};
1705
+
1706
+ expectedKeywords.forEach((keyword) => {
1707
+ keywordCoverage[keyword] = finalProject.findings.filter((f) =>
1708
+ f.content.toLowerCase().includes(keyword.toLowerCase())
1709
+ ).length;
1710
+ });
1711
+
1712
+ // Calculate relevance metrics
1713
+ const totalFindings = finalProject.findings.length;
1714
+ const highRelevanceFindings = finalProject.findings.filter(
1715
+ (f) => f.relevance > 0.7
1716
+ ).length;
1717
+ const mediumRelevanceFindings = finalProject.findings.filter(
1718
+ (f) => f.relevance > 0.4 && f.relevance <= 0.7
1719
+ ).length;
1720
+ const lowRelevanceFindings = finalProject.findings.filter(
1721
+ (f) => f.relevance <= 0.4
1722
+ ).length;
1723
+
1724
+ // Check source diversity
1725
+ const sourceDomains = new Set(
1726
+ finalProject.sources.map((s) => {
1727
+ try {
1728
+ return new URL(s.url).hostname;
1729
+ } catch {
1730
+ return 'unknown';
1731
+ }
1732
+ })
1733
+ );
1734
+
1735
+ // Quality report
1736
+ elizaLogger.info(`Research Quality Analysis:`);
1737
+ elizaLogger.info(`- Keyword coverage: ${JSON.stringify(keywordCoverage)}`);
1738
+ elizaLogger.info(`- Relevance distribution:`);
1739
+ elizaLogger.info(
1740
+ ` * High (>0.7): ${highRelevanceFindings}/${totalFindings}`
1741
+ );
1742
+ elizaLogger.info(
1743
+ ` * Medium (0.4-0.7): ${mediumRelevanceFindings}/${totalFindings}`
1744
+ );
1745
+ elizaLogger.info(` * Low (<0.4): ${lowRelevanceFindings}/${totalFindings}`);
1746
+ elizaLogger.info(`- Source diversity: ${sourceDomains.size} unique domains`);
1747
+ elizaLogger.info(
1748
+ `- Average finding length: ${Math.round(
1749
+ finalProject.findings.reduce((sum, f) => sum + f.content.length, 0) /
1750
+ totalFindings
1751
+ )} chars`
1752
+ );
1753
+
1754
+ // Validate minimum quality standards
1755
+ const keywordsCovered = Object.values(keywordCoverage).filter(
1756
+ (count) => count > 0
1757
+ ).length;
1758
+ if (keywordsCovered < 2) {
1759
+ elizaLogger.warn(
1760
+ `Low keyword coverage: only ${keywordsCovered}/${expectedKeywords.length} keywords found`
1761
+ );
1762
+ }
1763
+
1764
+ if (highRelevanceFindings < totalFindings * 0.3) {
1765
+ elizaLogger.warn(
1766
+ `Low proportion of high-relevance findings: ${highRelevanceFindings}/${totalFindings}`
1767
+ );
1768
+ }
1769
+
1770
+ elizaLogger.success('E2E Test Passed: Research Quality Assurance');
1771
+ }
1772
+
1773
+ // #endregion
1774
+
1775
+ // #region: Test Suite Export
1776
+
1777
+ // Export all tests as a TestSuite for the ElizaOS test runner
1778
+ const realWorldE2ETests = [
1779
+ {
1780
+ name: 'Real-World Research Scenarios',
1781
+ description: 'Comprehensive tests simulating actual research workflows',
1782
+ tests: [
1783
+ {
1784
+ name: 'Feature Development Research',
1785
+ description:
1786
+ 'Simulates a developer researching how to implement a new feature',
1787
+ fn: testFeatureDevelopmentResearch,
1788
+ },
1789
+ {
1790
+ name: 'Person Background Research',
1791
+ description:
1792
+ 'Simulates researching a person for hiring or partnership evaluation',
1793
+ fn: testPersonBackgroundResearch,
1794
+ },
1795
+ {
1796
+ name: 'Breaking News Research',
1797
+ description:
1798
+ 'Simulates a journalist or analyst researching breaking news',
1799
+ fn: testBreakingNewsResearch,
1800
+ },
1801
+ {
1802
+ name: 'Market Intelligence Research',
1803
+ description: 'Simulates competitive analysis and market research',
1804
+ fn: testMarketIntelligenceResearch,
1805
+ },
1806
+ {
1807
+ name: 'Technical Problem Solving',
1808
+ description: 'Simulates debugging and problem-solving research',
1809
+ fn: testProblemSolvingResearch,
1810
+ },
1811
+ {
1812
+ name: 'Academic/Learning Research',
1813
+ description: 'Simulates research for learning and academic purposes',
1814
+ fn: testAcademicResearch,
1815
+ },
1816
+ ],
1817
+ },
1818
+ ];
1819
+
1820
+ const researchE2ETests = [
1821
+ {
1822
+ name: 'Research Plugin E2E Tests - Core Scenarios',
1823
+ description:
1824
+ 'Comprehensive end-to-end tests simulating real-world research use cases',
1825
+ tests: [
1826
+ {
1827
+ name: 'Code/Feature Research',
1828
+ description:
1829
+ 'Simulates researching technical implementation details before building a feature',
1830
+ fn: testCodeFeatureResearch,
1831
+ },
1832
+ {
1833
+ name: 'Person Research',
1834
+ description: 'Tests researching information about a public figure',
1835
+ fn: testPersonResearch,
1836
+ },
1837
+ {
1838
+ name: 'News/Current Events Research',
1839
+ description: 'Tests researching latest news and current developments',
1840
+ fn: testNewsResearch,
1841
+ },
1842
+ {
1843
+ name: 'Technical Documentation Research',
1844
+ description:
1845
+ 'Tests researching technical documentation and best practices',
1846
+ fn: testDocumentationResearch,
1847
+ },
1848
+ {
1849
+ name: 'Competitive Analysis',
1850
+ description: 'Tests researching competitors and market analysis',
1851
+ fn: testCompetitiveAnalysis,
1852
+ },
1853
+ {
1854
+ name: 'DeFi Research Integration',
1855
+ description: 'Tests DeFi-specific research scenarios',
1856
+ fn: testDeFiResearch,
1857
+ },
1858
+ {
1859
+ name: 'Research Quality Assurance',
1860
+ description:
1861
+ 'Tests research quality, relevance, and comprehensive coverage',
1862
+ fn: testResearchQualityAssurance,
1863
+ },
1864
+ ],
1865
+ },
1866
+ ];
1867
+
1868
+ export const e2eTests = [...realWorldE2ETests, ...researchE2ETests];
1869
+
1870
+ // #endregion