/**
 * Agent Serve Tool
 *
 * Make current Clawx instance a networked agent.
 */
import { log } from '../utils/logger.js';
import { startAgentServer } from '../core/agent-server.js';
import { AgentRegistryManager } from '../core/agent-registry.js';
import { agentMaster } from '../core/agent-master.js';
import { v4 as uuidv4 } from 'uuid';
import { join } from 'path';
import { homedir } from 'os';
import { existsSync, mkdirSync, writeFileSync, appendFileSync } from 'fs';
export const agentServeTool = {
    name: 'agent_serve',
    label: 'Serve as Agent',
    description: 'Make current Clawx instance a networked agent',
    parameters: {
        type: 'object',
        properties: {
            name: {
                type: 'string',
                description: 'Agent name (default: "master")',
                default: 'master',
            },
            port: {
                type: 'number',
                description: 'Port to listen on (0 = auto-select from master range)',
                default: 0,
            },
            allowed_tools: {
                type: 'array',
                description: 'Tools this agent can execute (empty = all)',
                items: { type: 'string' },
                default: [],
            },
        },
        required: [],
    },
    async execute(toolCallId, params, signal, onUpdate, context) {
        log.debug('agent_serve toolCallId:', toolCallId);
        log.debug('agent_serve raw params:', params);
        // Handle case where params might be toolCallId
        let actualParams = params;
        if (typeof params === 'string' && params.startsWith('call_')) {
            log.warn('Received toolCallId as params, using default params');
            actualParams = {};
        }
        // Normalize parameter names
        const normalizedParams = {
            name: actualParams.name || actualParams.agent_name || 'master',
            port: actualParams.port || 0,
            allowed_tools: actualParams.allowed_tools || actualParams.allowedTools || [],
        };
        log.debug('agent_serve normalized params:', normalizedParams);
        const name = normalizedParams.name;
        const requestedPort = normalizedParams.port;
        const allowedTools = normalizedParams.allowed_tools;
        try {
            // Check if already serving (using singleton)
            if (agentMaster.isServing()) {
                const config = agentMaster.getConfig();
                return {
                    content: [{
                            type: 'text',
                            text: `Already serving as agent "${config?.name}" on port ${config?.port}`,
                        }],
                    details: { already_serving: true },
                };
            }
            const agentId = uuidv4();
            const workspace = join(homedir(), '.clawx', 'agents', agentId);
            if (!existsSync(workspace)) {
                mkdirSync(workspace, { recursive: true });
            }
            // Determine actual port with safety checks
            let actualPort;
            try {
                const { acquirePort } = await import('../utils/agent-utils.js');
                actualPort = await acquirePort(requestedPort, 'master');
                log.debug(`Acquired port: ${actualPort} (requested: ${requestedPort})`);
            }
            catch (error) {
                if (error.message.includes('already in use')) {
                    return {
                        content: [{
                                type: 'text',
                                text: `❌ ${error.message}\n\n` +
                                    `Use \`agent_cleanup_port --port ${requestedPort} --force true\` to free the port,\n` +
                                    `or try a different port with \`agent_serve --port <new_port>\`.`,
                            }],
                        details: { error: error.message, port: requestedPort },
                        isError: true,
                    };
                }
                throw error;
            }
            // Create agent config
            const config = {
                id: agentId,
                name,
                port: actualPort,
                workspace,
                masterEndpoint: `http://localhost:${actualPort}`, // Self as master
                allowedTools,
            };
            const configPath = join(workspace, 'agent-config.json');
            writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
            const peerActivityLogPath = join(workspace, 'peer-activity.log');
            try {
                appendFileSync(peerActivityLogPath, `[${new Date().toISOString()}] agent_started name=${name} id=${agentId} port=${actualPort}\n`, 'utf8');
            }
            catch { }
            log.info(`Starting agent server: ${name} (${agentId}) on port ${actualPort}`);
            // Start server
            const server = await startAgentServer(config);
            // Store in singleton for later access
            agentMaster.setServer(server, config);
            // Register self in registry
            const registry = new AgentRegistryManager();
            // Remove any existing self agents to avoid duplicates
            const existingAgents = registry.getAgents();
            let removedCount = 0;
            for (const agent of existingAgents) {
                if (agent.type === 'self') {
                    registry.removeAgent(agent.id);
                    removedCount++;
                    log.debug(`Removed existing self agent: ${agent.name} (${agent.id})`);
                }
            }
            const agentIdentity = {
                id: agentId,
                name,
                type: 'self',
                status: 'idle',
                capabilities: allowedTools.length > 0 ? allowedTools : ['all'],
                endpoint: `http://localhost:${server.port}`,
                workspace,
                created: Date.now(),
                lastHeartbeat: Date.now(),
            };
            registry.upsertAgent(agentIdentity);
            registry.save();
            if (removedCount > 0) {
                log.info(`Replaced ${removedCount} existing self agent(s)`);
            }
            // Clean up stale offline local workers to prevent port exhaustion
            // The registry accumulates entries across sessions; remove unreachable locals
            const allAgents = registry.getAgents();
            let cleanedCount = 0;
            for (const agent of allAgents) {
                if (agent.type !== 'local')
                    continue;
                // Check if actually alive
                let alive = false;
                if (agent.endpoint) {
                    try {
                        const res = await fetch(`${agent.endpoint}/health`, { signal: AbortSignal.timeout(1500) });
                        alive = res.ok;
                    }
                    catch { }
                }
                if (!alive) {
                    registry.removeAgent(agent.id);
                    cleanedCount++;
                }
            }
            if (cleanedCount > 0) {
                registry.save();
                log.info(`Cleaned ${cleanedCount} stale offline agent(s) from registry`);
            }
            // Auto-rehydrate workers with autoStart=true in background
            const rehydrateContext = {
                __activeMasterConfig: config,
                masterEndpoint: `http://localhost:${server.port}`,
            };
            let rehydrateInfo = '';
            try {
                const { agentRehydrateWorkersTool } = await import('./agentRehydrateWorkers.js');
                const rehydrateResult = await agentRehydrateWorkersTool.execute('auto-rehydrate', { include_offline: true, only_current_master: false }, undefined, undefined, rehydrateContext);
                const details = rehydrateResult?.details;
                if (details?.restored?.length > 0 || details?.alive?.length > 0) {
                    const parts = [];
                    if (details.alive?.length > 0)
                        parts.push(`${details.alive.length} already alive`);
                    if (details.restored?.length > 0)
                        parts.push(`${details.restored.length} restored`);
                    if (details.failed?.length > 0)
                        parts.push(`${details.failed.length} failed`);
                    rehydrateInfo = `\nAuto-rehydrated workers: ${parts.join(', ')}`;
                }
            }
            catch (err) {
                log.debug('Auto-rehydration skipped:', err instanceof Error ? err.message : String(err));
            }
            // Auto-spawn persistent specialists
            let specialistInfo = '';
            try {
                const { listSpecialists } = await import('../utils/specialist-utils.js');
                const { agentSpawnLocalTool } = await import('./agentSpawnLocal.js');
                const specialists = listSpecialists();
                const spawned = [];
                const skippedSpecs = [];
                for (const spec of specialists) {
                    // Check if already running — but verify reachability to avoid skipping stale entries
                    const existingAgent = registry.getAgentByName(spec.name);
                    if (existingAgent && (existingAgent.status === 'idle' || existingAgent.status === 'working')) {
                        let reachable = false;
                        if (existingAgent.endpoint) {
                            try {
                                const res = await fetch(`${existingAgent.endpoint}/health`, { signal: AbortSignal.timeout(2000) });
                                reachable = res.ok;
                            }
                            catch { }
                        }
                        if (reachable) {
                            skippedSpecs.push(spec.name);
                            continue;
                        }
                        // Stale entry — remove it so spawn can proceed
                        log.debug(`Specialist ${spec.name} registered but unreachable, respawning`);
                        registry.removeAgent(existingAgent.id);
                    }
                    const spawnResult = await agentSpawnLocalTool.execute(`specialist-${spec.name}`, {
                        name: spec.name,
                        allowed_tools: spec.allowedTools.length > 0 ? spec.allowedTools : undefined,
                    }, undefined, undefined, rehydrateContext);
                    if (!spawnResult?.isError) {
                        // Mark as persistent in registry
                        const spawnedAgent = registry.getAgentByName(spec.name);
                        if (spawnedAgent) {
                            spawnedAgent.persistent = true;
                            registry.upsertAgent(spawnedAgent);
                        }
                        // Apply persona
                        try {
                            const { agentPersonaSetTool } = await import('./agentPersonaSet.js');
                            await agentPersonaSetTool.execute(`persona-${spec.name}`, {
                                agent_name: spec.name,
                                name: spec.persona.name,
                                role: spec.persona.role,
                                tone: spec.persona.tone,
                                strengths: spec.persona.strengths,
                                goals: spec.persona.goals,
                                replace: true,
                            }, undefined, undefined, rehydrateContext);
                        }
                        catch (personaErr) {
                            log.debug(`Specialist ${spec.name} persona set failed:`, personaErr);
                        }
                        // Run setup command if defined
                        if (spec.setup) {
                            try {
                                const { agentSendTool } = await import('./agentSend.js');
                                await agentSendTool.execute(`setup-${spec.name}`, { agent_name: spec.name, tool: 'bash', params: { command: spec.setup } }, undefined, undefined, rehydrateContext);
                            }
                            catch (setupErr) {
                                log.debug(`Specialist ${spec.name} setup failed:`, setupErr);
                            }
                        }
                        spawned.push(spec.name);
                    }
                }
                if (spawned.length > 0 || skippedSpecs.length > 0) {
                    const parts = [];
                    if (spawned.length > 0)
                        parts.push(`${spawned.length} spawned (${spawned.join(', ')})`);
                    if (skippedSpecs.length > 0)
                        parts.push(`${skippedSpecs.length} already running`);
                    specialistInfo = `\nSpecialists: ${parts.join(', ')}`;
                }
                registry.save();
            }
            catch (err) {
                log.debug('Specialist auto-spawn skipped:', err instanceof Error ? err.message : String(err));
            }
            return {
                content: [{
                        type: 'text',
                        text: `✅ Now serving as agent "${name}" (ID: ${agentId})\n` +
                            `Endpoint: http://localhost:${server.port}\n` +
                            `Workspace: ${workspace}\n` +
                            `Port: ${server.port} (requested: ${requestedPort === 0 ? 'auto' : requestedPort})\n` +
                            `Allowed tools: ${allowedTools.length > 0 ? allowedTools.join(', ') : 'all'}` +
                            rehydrateInfo +
                            specialistInfo +
                            '\n\nUse agent_list to see registered agents.\n' +
                            'Use agent_spawn_local to spawn worker agents.\n' +
                            'Use agent_send to send tasks to agents.',
                    }],
                details: {
                    agent_id: agentId,
                    agent_name: name,
                    endpoint: `http://localhost:${server.port}`,
                    workspace,
                    allowed_tools: allowedTools,
                    config_path: configPath,
                    port: server.port,
                    requested_port: requestedPort,
                },
            };
        }
        catch (error) {
            log.error('Failed to start agent server:', error);
            return {
                content: [{
                        type: 'text',
                        text: `❌ Failed to start agent server: ${error instanceof Error ? error.message : String(error)}`,
                    }],
                details: { error: error instanceof Error ? error.message : String(error) },
                isError: true,
            };
        }
    },
};
//# sourceMappingURL=agentServe.js.map