import { writeFile, unlink } from 'fs/promises'; import { join } from 'path'; import { tmpdir } from 'os'; import { randomUUID } from 'crypto'; import { fileURLToPath } from 'url'; import { runClaudeTurn } from '../claude_cli.js'; // Absolute path to the MCP stdio server the claude child spawns. const STDIO_PATH = fileURLToPath(new URL('../../mcp/companion-stdio.js', import.meta.url)); /** * Shared agent turn-runner: builds the per-turn MCP config (selecting the tool * registry + injecting agent/space/view), runs one claude turn, cleans up. * SSE/persistence stay in the route. Returns runClaudeTurn's result. */ export async function runAgentTurn({ agent, persona, registryName, toolNames, spaceId = null, view = null, sessionId, resume = false, userText, claudeExe = 'claude', home, onEvent, extraEnv = {} }) { const agentActor = { kind: 'agent', id: agent.id, capabilities: agent.capabilities, scopes: agent.scopes }; const mcpConfigPath = join(tmpdir(), `void-mcp-${randomUUID()}.json`); const mcpConfig = { mcpServers: { void: { command: process.execPath, args: [STDIO_PATH], env: { VOID_TOOL_REGISTRY: registryName || '', VOID_SPACE_ID: spaceId || '', VOID_AGENT_JSON: JSON.stringify(agentActor), VOID_VIEW_JSON: view ? JSON.stringify(view) : '', DATABASE_URL: process.env.DATABASE_URL || '', OLLAMA_URL: process.env.OLLAMA_URL || '', ...extraEnv } } } }; await writeFile(mcpConfigPath, JSON.stringify(mcpConfig)); try { return await runClaudeTurn({ sessionId, resume, systemPrompt: persona, userText, mcpConfigPath, tools: toolNames, allowedTools: toolNames, claudeExe, home, onEvent }); } finally { unlink(mcpConfigPath).catch(() => {}); } }