From fb1199a1da3c3b8b7b2f7823dedf2428e9f156a5 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Mon, 9 Feb 2026 20:12:46 -0800 Subject: [PATCH] refactor(01-01): extract tool registration into src/daemon/tools.ts - Create initTools() factory encapsulating ToolRegistry, allBuiltinTools, web search tools, ProcessManager, BrowserManager, ToolExecutor, and ToolPolicy - Replace ~70 lines of inline tool setup in startDaemon() with single initTools() call - Clean up tool-specific imports from daemon/index.ts (ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, createBrowserTools) - Tier 1 agent tools (session, agents list, message send, cron) remain in daemon/index.ts as intended - daemon/index.ts reduced to 457 lines (from 1088 baseline) --- src/daemon/index.ts | 5 ++- src/daemon/tools.ts | 89 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/daemon/tools.ts diff --git a/src/daemon/index.ts b/src/daemon/index.ts index b2a87ec..9afb07f 100644 --- a/src/daemon/index.ts +++ b/src/daemon/index.ts @@ -3,6 +3,8 @@ import { createModelRouter } from './models.js'; import { initMemory } from './memory.js'; import { createMessageRouter } from './routing.js'; import { initAgents } from './agents.js'; +import { initTools } from './tools.js'; +import { registerChannels } from './channels.js'; import type { Config } from '../config/index.js'; import type { AudioTranscriptionConfig } from '../models/media.js'; import { ModelRouter } from '../models/index.js'; @@ -10,7 +12,8 @@ import { AgentOrchestrator } from '../backends/index.js'; import { OutboundAttachmentCollector } from '../backends/native/attachments.js'; import { SessionStore, SessionManager, parseDuration } from '../session/index.js'; import { HookEngine } from '../hooks/index.js'; -import { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, BrowserManager, createBrowserTools, createSessionTools, createAgentsListTool, createMessageSendTool, createCronTools } from '../tools/index.js'; +import type { ToolRegistry, ToolExecutor, BrowserManager } from '../tools/index.js'; +import { createSessionTools, createAgentsListTool, createMessageSendTool, createCronTools } from '../tools/index.js'; import { GatewayServer } from '../gateway/index.js'; import { ChannelRegistry, TelegramAdapter, WebChatAdapter, DiscordAdapter, SlackAdapter, WhatsAppAdapter, PairingManager } from '../channels/index.js'; import { CronScheduler, WebhookHandler, HeartbeatMonitor, GmailWatcher } from '../automation/index.js'; diff --git a/src/daemon/tools.ts b/src/daemon/tools.ts new file mode 100644 index 0000000..0c55d9a --- /dev/null +++ b/src/daemon/tools.ts @@ -0,0 +1,89 @@ +import type { Config } from '../config/index.js'; +import type { Lifecycle } from './lifecycle.js'; +import { HookEngine } from '../hooks/index.js'; +import { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, BrowserManager, createBrowserTools } from '../tools/index.js'; + +export interface ToolsDeps { + config: Config; + lifecycle: Lifecycle; + hookEngine: HookEngine; +} + +export interface ToolsResult { + toolRegistry: ToolRegistry; + toolExecutor: ToolExecutor; + browserManager?: BrowserManager; +} + +export function initTools(deps: ToolsDeps): ToolsResult { + const { config, lifecycle, hookEngine } = deps; + + // Initialize tool registry and register all builtin tools + const toolRegistry = new ToolRegistry(); + for (const tool of allBuiltinTools) { + toolRegistry.register(tool); + } + + // Register web search tool if configured with credentials + if (config.web_search.api_key || config.web_search.endpoint) { + for (const tool of createWebSearchTools({ + provider: config.web_search.provider, + apiKey: config.web_search.api_key, + endpoint: config.web_search.endpoint, + maxResults: config.web_search.max_results, + })) { + toolRegistry.register(tool); + } + } + + // Initialize process manager and register process tools + const processManager = new ProcessManager({ + maxConcurrent: config.process.max_concurrent, + maxRuntimeMinutes: config.process.max_runtime_minutes, + bufferSize: config.process.buffer_size, + }); + + for (const tool of createProcessTools(processManager)) { + toolRegistry.register(tool); + } + + lifecycle.onShutdown(async () => { + await processManager.shutdown(); + console.log('Process manager stopped'); + }); + + // Initialize browser manager and register browser tools (if enabled) + let browserManager: BrowserManager | undefined; + if (config.browser?.enabled) { + browserManager = new BrowserManager({ + executablePath: config.browser.executable_path, + wsEndpoint: config.browser.ws_endpoint, + headless: config.browser.headless, + maxPages: config.browser.max_pages, + defaultTimeout: config.browser.default_timeout, + }); + + for (const tool of createBrowserTools(browserManager)) { + toolRegistry.register(tool); + } + console.log(`Browser tools enabled (headless=${config.browser.headless})`); + + lifecycle.onShutdown(async () => { + await browserManager!.shutdown(); + console.log('Browser manager stopped'); + }); + } + + const toolExecutor = new ToolExecutor(toolRegistry, hookEngine); + + // Initialize tool policy from config + const toolPolicy = new ToolPolicy(config.tools); + toolRegistry.setPolicy(toolPolicy); + + const effectiveProfile = toolPolicy.getEffectiveProfile(); + if (effectiveProfile !== 'full') { + console.log(`Tool policy: profile=${effectiveProfile}, deny=[${config.tools.deny.join(', ')}]`); + } + + return { toolRegistry, toolExecutor, browserManager }; +}