/** * Tool Registry * Central registry for all agent tools. * Tools self-register on import via side-effect in index.ts. */ import { tool } from 'ai' import { z } from 'zod' export interface ToolContext { userId: string agentId?: string actionId?: string conversationId?: string notebookId?: string webSearch?: boolean config: Record } export interface RegisteredTool { name: string description: string buildTool: (ctx: ToolContext) => any // Returns an AI SDK tool() synchronously isInternal: boolean // true = no API key needed } class ToolRegistry { private tools: Map = new Map() register(tool: RegisteredTool): void { this.tools.set(tool.name, tool) } get(name: string): RegisteredTool | undefined { return this.tools.get(name) } buildToolsForAgent(toolNames: string[], ctx: ToolContext): Record { const built: Record = {} for (const name of toolNames) { const registered = this.tools.get(name) if (registered) { built[name] = registered.buildTool(ctx) } } return built } /** * Build tools for the chat endpoint. * Includes internal tools (note_search, note_read) and web tools when configured. */ buildToolsForChat(ctx: ToolContext): Record { const toolNames: string[] = ['note_search', 'note_read'] // Add web tools only when user toggled web search AND config is present if (ctx.webSearch) { const hasWebSearch = ctx.config.WEB_SEARCH_PROVIDER || ctx.config.BRAVE_SEARCH_API_KEY || ctx.config.SEARXNG_URL if (hasWebSearch) { toolNames.push('web_search') } const hasWebScrape = ctx.config.JINA_API_KEY if (hasWebScrape) { toolNames.push('web_scrape') } } return this.buildToolsForAgent(toolNames, ctx) } getAvailableTools(): Array<{ name: string; description: string; isInternal: boolean }> { return Array.from(this.tools.values()).map(t => ({ name: t.name, description: t.description, isInternal: t.isInternal, })) } } // Singleton export const toolRegistry = new ToolRegistry()