import * as plugins from './plugins.js'; export interface IResearchOptions { apiKey: string; query: string; searchDepth?: 'basic' | 'advanced' | 'deep'; maxSources?: number; allowedDomains?: string[]; blockedDomains?: string[]; } export interface IResearchResponse { answer: string; sources: Array<{ url: string; title: string; snippet: string }>; searchQueries?: string[]; metadata?: Record; } export async function research(options: IResearchOptions): Promise { const client = new plugins.Anthropic({ apiKey: options.apiKey }); const systemMessage = `You are a research assistant with web search capabilities. Provide comprehensive, well-researched answers with citations and sources. When searching the web, be thorough and cite your sources accurately.`; // Build web search tool config const webSearchTool: any = { type: 'web_search_20250305', name: 'web_search', }; if (options.maxSources) { webSearchTool.max_uses = options.maxSources; } if (options.allowedDomains?.length) { webSearchTool.allowed_domains = options.allowedDomains; } else if (options.blockedDomains?.length) { webSearchTool.blocked_domains = options.blockedDomains; } const result = await client.messages.create({ model: 'claude-sonnet-4-5-20250929', system: systemMessage, messages: [ { role: 'user' as const, content: options.query }, ], max_tokens: 20000, temperature: 0.7, tools: [webSearchTool], }); // Extract answer, sources, and search queries let answer = ''; const sources: Array<{ url: string; title: string; snippet: string }> = []; const searchQueries: string[] = []; for (const block of result.content) { const b: any = block; if ('text' in b) { answer += b.text; // Extract citations if present if (b.citations && Array.isArray(b.citations)) { for (const citation of b.citations) { if (citation.type === 'web_search_result_location') { sources.push({ title: citation.title || '', url: citation.url || '', snippet: citation.cited_text || '', }); } } } } else if (b.type === 'server_tool_use') { if (b.name === 'web_search' && b.input?.query) { searchQueries.push(b.input.query); } } else if (b.type === 'web_search_tool_result') { if (Array.isArray(b.content)) { for (const item of b.content) { if (item.type === 'web_search_result') { if (!sources.some(s => s.url === item.url)) { sources.push({ title: item.title || '', url: item.url || '', snippet: '', }); } } } } } } // Fallback: parse markdown links if no citations found if (sources.length === 0) { const urlRegex = /\[([^\]]+)\]\(([^)]+)\)/g; let match: RegExpExecArray | null; while ((match = urlRegex.exec(answer)) !== null) { sources.push({ title: match[1], url: match[2], snippet: '', }); } } const usage: any = result.usage; return { answer, sources, searchQueries: searchQueries.length > 0 ? searchQueries : undefined, metadata: { model: 'claude-sonnet-4-5-20250929', searchDepth: options.searchDepth || 'basic', tokensUsed: usage?.output_tokens, webSearchesPerformed: usage?.server_tool_use?.web_search_requests ?? 0, }, }; }