import { Router } from 'express';
import { createAgentRuntime } from '../adapters/agent-runtime.js';
import { verifyToken } from '../middleware/auth.js';
import * as agentService from '../services/agent.js';
import * as workspaceService from '../services/workspace.js';
import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
import { buildAgentPrompt } from '../ws/agent-prompt.js';
import { wrapOnEventWithHooks } from '../services/hook-engine.js';
import { buildWorkflowEditorSystemPrompt, createWorkflowEditorFunctionTools } from '../services/builtin-tools/workflow-editor-tools.js';
const router = Router();
router.post('/run', async (req, res) => {
    const body = req.body;
    if (!verifyRequestKey(req, body)) {
        res.status(401).json({ error: 'Unauthorized' });
        return;
    }
    const workspaceId = resolveWorkspaceId(body.workspaceId);
    const workspace = workspaceService.getById(workspaceId);
    const agentConfigId = (body.agentId ?? body.agentid)?.trim();
    if (!agentConfigId) {
        res.status(400).json({ error: 'agentid is required' });
        return;
    }
    const preset = agentService.listPresets(workspaceId).find((agent) => agent.id === agentConfigId);
    if (!preset || preset.enabled === false) {
        res.status(404).json({ error: 'agent preset not found' });
        return;
    }
    const userPrompt = resolveUserPrompt(body);
    if (!userPrompt) {
        res.status(400).json({ error: 'message, prompt, or messages is required' });
        return;
    }
    const session = agentService.create(workspaceId, preset.role, preset.id);
    const startTime = Date.now();
    const mcpConfig = body.mcps ?? body.mcp ?? preset.mcps;
    const mcpServers = agentService.getMcpServers(mcpConfig);
    const requestedSkills = normalizeSkills(body.skills ?? body.skill) ?? preset.skills;
    const configDir = agentService.getAgentConfigDir(workspaceId, { ...preset, skills: requestedSkills });
    const skills = agentService.getAvailableSkillNames(configDir, requestedSkills);
    const workflowAgent = normalizeWorkflowAgent(body.workflowAgent);
    const functionTools = workflowAgent
        ? createWorkflowEditorFunctionTools({
            workflow: workflowAgent.workflow,
            nodeDefinitions: workflowAgent.nodeDefinitions,
        })
        : [];
    const runtimeKind = workflowAgent ? 'langchain' : preset.runtimeKind;
    const systemPrompt = workflowAgent
        ? buildWorkflowEditorSystemPrompt(workflowAgent.workflow, workflowAgent.selectedNodes)
        : body.systemPrompt ?? preset.systemPrompt;
    const output = [];
    const workingDir = agentService.resolveWorkingDir(workspaceId, preset);
    let completed = false;
    prepareSse(res);
    writeSse(res, 'session', { session, workspaceId });
    const runtime = createAgentRuntime({
        kind: runtimeKind,
        provider: preset.modelProvider,
        model: preset.modelId,
        apiKey: preset.apiKey,
        baseURL: getRuntimeBaseURL(preset.modelProvider, preset.apiBase),
        adapterBaseURL: preset.apiBase,
        ...getThinkingRuntimeConfig(preset),
    });
    res.on('close', () => {
        if (!completed && !res.writableEnded)
            runtime.stop();
    });
    try {
        agentService.updateStatus(workspaceId, session.id, 'active');
        writeSse(res, 'status', { agentId: session.id, status: 'active' });
        const result = await runtime.execute(buildAgentPrompt(workspaceId, systemPrompt, userPrompt, normalizeMessages(body.messages), {
            runtimeKind,
            mcpServers: Object.keys(mcpServers ?? {}),
            skills,
            boundDirs: workspace?.boundDirs ?? [],
            workingDir,
            excludeNativeClaudeMd: runtimeKind === 'claude-code',
            builtInTools: functionTools.map((tool) => ({ name: tool.name, description: tool.description })),
        }), workingDir, {
            maxTurns: normalizeMaxTurns(body.maxTurns),
            mcpServers,
            skills,
            functionTools,
            configDir,
            sandboxDirs: preset.sandboxDirs,
            userPrompt,
            outputStyle: body.outputStyle ?? preset.outputStyle,
            onEvent: wrapOnEventWithHooks((event) => {
                if (event.type === 'output')
                    output.push(event.line);
                writeSse(res, event.type, serializeRuntimeEvent(event));
            }, workspaceId, workspace?.hooksEnabled),
        });
        completed = true;
        const displayOutput = output.length ? output : result.output;
        agentService.complete(workspaceId, session.id, result.success ? undefined : result.error, {
            runtime: runtimeKind,
            model: preset.modelId,
            summary: result.summary,
            output: displayOutput,
            durationMs: Date.now() - startTime,
            usage: result.usage,
            costUsd: result.costUsd,
        });
        writeSse(res, 'done', {
            agentId: session.id,
            success: result.success,
            summary: result.summary,
            artifacts: result.artifacts,
            error: result.error,
            output: displayOutput,
            usage: result.usage,
            costUsd: result.costUsd,
            durationMs: Date.now() - startTime,
        });
        res.end();
    }
    catch (err) {
        completed = true;
        const error = err instanceof Error ? err.message : String(err);
        agentService.complete(workspaceId, session.id, error, {
            runtime: runtimeKind,
            model: preset.modelId,
            summary: error,
            output: output.length ? output : [error],
            durationMs: Date.now() - startTime,
        });
        writeSse(res, 'error', { agentId: session.id, error });
        res.end();
    }
});
function verifyRequestKey(req, body) {
    const auth = req.headers.authorization;
    const bearer = auth?.startsWith('Bearer ') ? auth.slice(7) : undefined;
    const headerKey = typeof req.headers['x-agent-spaces-key'] === 'string'
        ? req.headers['x-agent-spaces-key']
        : undefined;
    return verifyToken(body.key ?? bearer ?? headerKey ?? null);
}
function resolveWorkspaceId(workspaceId) {
    const explicit = workspaceId?.trim();
    if (explicit)
        return explicit;
    return workspaceService.getAll()[0]?.id ?? 'default';
}
function prepareSse(res) {
    res.status(200);
    res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
    res.setHeader('Cache-Control', 'no-cache, no-transform');
    res.setHeader('Connection', 'keep-alive');
    res.setHeader('X-Accel-Buffering', 'no');
    res.flushHeaders?.();
    res.socket?.setNoDelay?.(true);
}
function writeSse(res, event, data) {
    res.write(`event: ${event}\n`);
    res.write(`data: ${JSON.stringify(data)}\n\n`);
    // Force flush — compression middleware and some proxies buffer small writes
    const flushable = res;
    if (typeof flushable.flush === 'function')
        flushable.flush();
}
function resolveUserPrompt(body) {
    const direct = body.prompt ?? body.message;
    if (typeof direct === 'string' && direct.trim())
        return direct.trim();
    const messages = normalizeMessages(body.messages);
    const lastUserMessage = [...messages].reverse().find((message) => message.senderId === 'user');
    return lastUserMessage?.content?.trim() ?? '';
}
function normalizeMessages(messages) {
    if (!Array.isArray(messages))
        return [];
    return messages
        .filter((message) => message && typeof message.content === 'string')
        .map((message, index) => ({
        id: `sse-message-${index}`,
        channelId: 'sse',
        senderId: message.senderId ?? 'user',
        senderRole: message.senderRole,
        content: message.content,
        type: 'text',
        status: message.status ?? 'completed',
        createdAt: new Date(0).toISOString(),
        updatedAt: new Date(0).toISOString(),
        parts: message.parts,
    }));
}
function normalizeSkills(input) {
    if (!input)
        return undefined;
    const values = Array.isArray(input) ? input : [input];
    return values.map((item) => String(item).trim()).filter(Boolean);
}
function normalizeMaxTurns(value) {
    return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 100;
}
function normalizeWorkflowAgent(input) {
    if (!input || typeof input !== 'object')
        return null;
    if (!isWorkflow(input.workflow))
        return null;
    const nodeDefinitions = Array.isArray(input.nodeDefinitions)
        ? input.nodeDefinitions.filter(isNodeDefinition)
        : [];
    if (!nodeDefinitions.length)
        return null;
    const selectedNodes = Array.isArray(input.selectedNodes)
        ? input.selectedNodes.filter(isWorkflowNode)
        : undefined;
    return { workflow: input.workflow, nodeDefinitions, selectedNodes };
}
function isWorkflow(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value))
        return false;
    const record = value;
    return typeof record.id === 'string'
        && typeof record.name === 'string'
        && Array.isArray(record.nodes)
        && Array.isArray(record.edges)
        && record.nodes.every(isWorkflowNode);
}
function isWorkflowNode(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value))
        return false;
    const record = value;
    const position = record.position;
    return typeof record.id === 'string'
        && typeof record.type === 'string'
        && typeof record.label === 'string'
        && Boolean(position)
        && typeof position?.x === 'number'
        && typeof position?.y === 'number'
        && typeof record.data === 'object'
        && record.data !== null
        && !Array.isArray(record.data);
}
function isNodeDefinition(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value))
        return false;
    const record = value;
    return typeof record.type === 'string'
        && typeof record.label === 'string'
        && typeof record.category === 'string'
        && typeof record.description === 'string'
        && Array.isArray(record.properties);
}
function serializeRuntimeEvent(event) {
    if (event.type === 'tool_use') {
        return {
            type: event.type,
            id: event.id,
            name: event.name,
            input: event.input,
            line: event.line,
        };
    }
    return event;
}
function getRuntimeBaseURL(provider, apiBase) {
    if (provider === 'openai-responses-to-anthropic-messages'
        || provider === 'openai-chat-completions-to-anthropic-messages')
        return undefined;
    return apiBase;
}
export default router;
//# sourceMappingURL=agent-sse.js.map