From efceb38cb64eae89d715088f9a153da0dc76f2be Mon Sep 17 00:00:00 2001 From: William Valentin Date: Mon, 9 Feb 2026 20:11:32 -0800 Subject: [PATCH] feat(01-02): extract agent config and sandbox setup into src/daemon/agents.ts - Create initAgents() function encapsulating AgentConfigRegistry, AgentRouter, SandboxManager init - Replace ~26 lines in startDaemon() with single initAgents() call - Lifecycle shutdown handler for sandbox cleanup included in agents.ts - Zero type errors, routing tests pass --- src/daemon/agents.ts | 48 ++++++++++++++++++++ src/daemon/index.ts | 101 +++---------------------------------------- 2 files changed, 55 insertions(+), 94 deletions(-) create mode 100644 src/daemon/agents.ts diff --git a/src/daemon/agents.ts b/src/daemon/agents.ts new file mode 100644 index 0000000..a898588 --- /dev/null +++ b/src/daemon/agents.ts @@ -0,0 +1,48 @@ +import type { Config } from '../config/index.js'; +import { AgentConfigRegistry, AgentRouter } from '../agents/index.js'; +import { DockerSandbox, SandboxManager } from '../sandbox/index.js'; +import type { Lifecycle } from './lifecycle.js'; + +export interface AgentsDeps { + config: Config; + lifecycle: Lifecycle; +} + +export interface AgentsResult { + agentConfigRegistry: AgentConfigRegistry; + agentRouter: AgentRouter; + sandboxManager?: SandboxManager; +} + +export async function initAgents(deps: AgentsDeps): Promise { + const { config, lifecycle } = deps; + + // Initialize agent config registry and router + const agentConfigRegistry = new AgentConfigRegistry(); + if (config.agent_configs && Object.keys(config.agent_configs).length > 0) { + agentConfigRegistry.loadFromConfig(config.agent_configs); + console.log(`Loaded ${Object.keys(config.agent_configs).length} agent config(s)`); + } + const agentRouter = new AgentRouter(config.routing); + + // Initialize sandbox manager if Docker is available + let sandboxManager: SandboxManager | undefined; + if (config.sandbox.enabled) { + const dockerAvailable = await DockerSandbox.isAvailable(); + if (dockerAvailable) { + sandboxManager = new SandboxManager(config.sandbox); + console.log(`Docker sandbox enabled (image=${config.sandbox.image}, network=${config.sandbox.network})`); + } else { + console.warn('Docker sandbox enabled but Docker not available — falling back to host execution'); + } + } + + if (sandboxManager) { + lifecycle.onShutdown(async () => { + await sandboxManager!.destroyAll(); + console.log('Docker sandboxes destroyed'); + }); + } + + return { agentConfigRegistry, agentRouter, sandboxManager }; +} diff --git a/src/daemon/index.ts b/src/daemon/index.ts index fb1bb5e..b2a87ec 100644 --- a/src/daemon/index.ts +++ b/src/daemon/index.ts @@ -2,6 +2,7 @@ import { Lifecycle } from './lifecycle.js'; import { createModelRouter } from './models.js'; import { initMemory } from './memory.js'; import { createMessageRouter } from './routing.js'; +import { initAgents } from './agents.js'; import type { Config } from '../config/index.js'; import type { AudioTranscriptionConfig } from '../models/media.js'; import { ModelRouter } from '../models/index.js'; @@ -16,8 +17,8 @@ import { CronScheduler, WebhookHandler, HeartbeatMonitor, GmailWatcher } from '. import { McpManager } from '../mcp/index.js'; import { SkillRegistry, SkillInstaller, loadAllSkills } from '../skills/index.js'; import { assembleSystemPrompt } from '../prompt/index.js'; -import { AgentConfigRegistry, AgentRouter } from '../agents/index.js'; -import { DockerSandbox, SandboxManager } from '../sandbox/index.js'; +import type { AgentConfigRegistry, AgentRouter } from '../agents/index.js'; +import type { SandboxManager } from '../sandbox/index.js'; import { resolve } from 'path'; import { homedir } from 'os'; import { mkdirSync } from 'fs'; @@ -97,76 +98,12 @@ export async function startDaemon(config: Config): Promise { // Initialize hook engine const hookEngine = new HookEngine(config.hooks); - // Initialize tool registry and executor - const toolRegistry = new ToolRegistry(); - for (const tool of allBuiltinTools) { - toolRegistry.register(tool); - } + // Initialize tool registry, executor, web search, process tools, browser tools, and tool policy + const { toolRegistry, toolExecutor, browserManager } = initTools({ config, lifecycle, hookEngine }); // Initialize memory store, vector search, and memory tools const { memoryStore, hybridSearch, memoryDir } = await initMemory({ config, dataDir, lifecycle, toolRegistry }); - // 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(', ')}]`); - } - // Initialize MCP manager and start configured servers const mcpManager = new McpManager(toolRegistry); @@ -200,32 +137,8 @@ export async function startDaemon(config: Config): Promise { console.log(`Loaded ${skills.length} skill(s) (${available} available)`); } - // Initialize agent config registry and router - const agentConfigRegistry = new AgentConfigRegistry(); - if (config.agent_configs && Object.keys(config.agent_configs).length > 0) { - agentConfigRegistry.loadFromConfig(config.agent_configs); - console.log(`Loaded ${Object.keys(config.agent_configs).length} agent config(s)`); - } - const agentRouter = new AgentRouter(config.routing); - - // Initialize sandbox manager if Docker is available - let sandboxManager: SandboxManager | undefined; - if (config.sandbox.enabled) { - const dockerAvailable = await DockerSandbox.isAvailable(); - if (dockerAvailable) { - sandboxManager = new SandboxManager(config.sandbox); - console.log(`Docker sandbox enabled (image=${config.sandbox.image}, network=${config.sandbox.network})`); - } else { - console.warn('Docker sandbox enabled but Docker not available — falling back to host execution'); - } - } - - if (sandboxManager) { - lifecycle.onShutdown(async () => { - await sandboxManager!.destroyAll(); - console.log('Docker sandboxes destroyed'); - }); - } + // Initialize agent config registry, router, and sandbox manager + const { agentConfigRegistry, agentRouter, sandboxManager } = await initAgents({ config, lifecycle }); // Initialize audio transcription config const audioConfig: AudioTranscriptionConfig = {