"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AutogenParser = void 0;
const spanDeduplicator_1 = require("../utils/spanDeduplicator");
const toolCallAnalyzer_1 = require("../utils/toolCallAnalyzer");
const toolArgumentFields_1 = require("../utils/toolArgumentFields");
const utilts_1 = require("../utils/utilts");
/**
 */
class AutogenParser {
    constructor() {
        this.name = 'autogen';
        this.localContentHashes = new Set();
        this.deduplicator = new spanDeduplicator_1.SpanDeduplicator();
        this.toolAnalyzer = new toolCallAnalyzer_1.ToolCallAnalyzer();
    }
    /**
     * Local deduplication logic for AutoGen to avoid affecting other frameworks
     */
    isLocalDuplicate(span) {
        // Tool execution spans should NEVER be deduplicated by content
        // Each execute_tool call is a distinct invocation even if the tool name is the same
        const opName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.operation.name', 'gen_ai.operation.name');
        if (opName === 'execute_tool') {
            return false; // Always treat tool executions as unique
        }
        // Generate a hash similar to SpanDeduplicator but with AutoGen specifics
        const input = String(span.attributes['genai.workflow.input'] ||
            span.attributes['gen_ai.workflow.input'] ||
            span.attributes['genai.prompt'] ||
            span.attributes['gen_ai.prompt'] ||
            span.attributes['message'] ||
            '').substring(0, 500);
        const output = String(span.attributes['genai.workflow.output'] ||
            span.attributes['gen_ai.workflow.output'] ||
            span.attributes['genai.completion'] ||
            span.attributes['gen_ai.completion'] ||
            '').substring(0, 500);
        const entityName = span.attributes['gen_ai.agent.name'] ||
            span.attributes['recipient_agent_type'] ||
            span.attributes['gen_ai.tool.name'] ||
            '';
        const keyParts = [
            span.name.includes('autogen process') ? 'agent_process' : span.name,
            entityName,
            input,
            output,
        ];
        const hash = keyParts.join(':');
        if (this.localContentHashes.has(hash))
            return true;
        this.localContentHashes.add(hash);
        return false;
    }
    canParse(spans) {
        return spans.some(s => s.attributes['gen_ai.system'] === 'autogen' ||
            s.attributes['gen_ai.framework'] === 'autogen' ||
            s.attributes['genai.system'] === 'autogen' ||
            s.attributes['genai.framework'] === 'autogen' ||
            String(s.name || '').toLowerCase().includes('autogen'));
    }
    getTools(spans) {
        this.deduplicator.reset();
        this.localContentHashes.clear();
        const tools = [];
        const existingSpanIds = new Set();
        // CANONICAL RULE: Build parent validation map
        // Tools must have a valid agent process span as parent
        const validAgentSpanIds = new Set();
        for (const span of spans) {
            if (this.isAgentSpan(span, spans)) {
                validAgentSpanIds.add(span.id);
            }
        }
        // Strategy 1: Explicit tool spans (FunctionTool executions)
        const toolCallDetailsById = this.buildToolCallDetailsMap(spans);
        for (const span of spans) {
            if (this.deduplicator.isDuplicate(span))
                continue;
            if (this.isLocalDuplicate(span))
                continue;
            if (this.isInfrastructureSpan(span, spans))
                continue;
            if (this.isExplicitToolSpan(span)) {
                // CANONICAL RULE: Validate parent is a valid agent span
                if (!span.parentSpanId || !validAgentSpanIds.has(span.parentSpanId)) {
                    continue; // Skip tools without valid agent parent
                }
                const tool = this.extractTool(span, spans, toolCallDetailsById);
                if (tool) {
                    tools.push(tool);
                    existingSpanIds.add(span.id);
                }
            }
        }
        // Strategy 2: Parse tool calls from agent message attributes
        // (Only if no explicit tool spans found)
        if (tools.length === 0) {
            const agentSpans = spans.filter(s => s.name.startsWith('autogen process') &&
                !this.isInfrastructureSpan(s, spans) &&
                this.isAgentSpan(s, spans) // CANONICAL RULE: Must be valid agent
            );
            for (const span of agentSpans) {
                const toolCalls = this.extractToolCallsFromMessage(span);
                for (const toolCall of toolCalls) {
                    if (!existingSpanIds.has(span.id)) {
                        tools.push({
                            ...toolCall,
                            spanId: span.id,
                            executionType: 'decision',
                            metadata: { source: 'message_tool_call', agentSpanId: span.id }
                        });
                        existingSpanIds.add(span.id);
                    }
                }
            }
        }
        return this.enrichToolsMetadata(tools, spans);
    }
    getAgents(spans) {
        this.deduplicator.reset();
        const agents = [];
        const seenAgents = new Set();
        const parentSpanIds = new Set();
        for (const s of spans) {
            if (s.parentSpanId)
                parentSpanIds.add(s.parentSpanId);
        }
        const spanById = new Map(spans.map((s) => [s.id, s]));
        const toolCallDetailsById = this.buildToolCallDetailsMap(spans);
        for (const span of spans) {
            if (this.deduplicator.isDuplicate(span) && !parentSpanIds.has(span.id))
                continue;
            if (this.isAgentSpan(span)) {
                const agent = this.extractAgent(span);
                if (agent && !seenAgents.has(agent.id)) {
                    const toolDefinitions = (0, toolArgumentFields_1.collectAgentToolDefinitions)(agent.id, spans, spanById, (sp) => this.isToolSpan(sp), {
                        mergeMessageDetails: (sp) => {
                            const toolCallId = utilts_1.FrameworkUtils.getAttr(sp.attributes, 'gen_ai.tool.call.id', 'genai.tool.call.id');
                            return toolCallId ? toolCallDetailsById.get(String(toolCallId)) : undefined;
                        },
                        resolveToolDisplayName: (sp, merged, details) => this.toolAnalyzer.extractToolName(sp) || details?.name || merged.name,
                    });
                    agents.push(toolDefinitions.length > 0 ? { ...agent, toolDefinitions } : agent);
                    seenAgents.add(agent.id);
                }
            }
        }
        return agents;
    }
    getModels(spans) {
        this.deduplicator.reset();
        const models = [];
        // CANONICAL RULE: Build parent validation map
        // Models must have either an agent or tool span as parent
        const validParentSpanIds = new Set();
        for (const span of spans) {
            if (this.isAgentSpan(span, spans) || this.isExplicitToolSpan(span)) {
                validParentSpanIds.add(span.id);
            }
        }
        for (const span of spans) {
            if (this.deduplicator.isDuplicate(span))
                continue;
            if (this.isModelSpan(span)) {
                // CANONICAL RULE: Validate parent is either agent or tool
                if (span.parentSpanId && validParentSpanIds.has(span.parentSpanId)) {
                    const model = this.extractModel(span);
                    if (model)
                        models.push(model);
                }
                // Note: Skip models without valid parent (orphaned model calls)
            }
        }
        return models;
    }
    generateDag(spans) {
        // Keep span-level DAG behavior aligned with other framework parsers.
        this.deduplicator.reset();
        const uniqueSpans = spans.filter((s) => !this.deduplicator.isDuplicate(s));
        const nodes = [];
        const nodeMap = new Map();
        for (const span of uniqueSpans) {
            const node = {
                id: span.id,
                type: this.getNodeType(span, uniqueSpans),
                name: span.name,
                children: [],
                metadata: {
                    timestamp: span.timestamp,
                    duration: span.duration,
                    parentSpanId: span.parentSpanId,
                    attributes: span.attributes,
                },
            };
            nodes.push(node);
            nodeMap.set(span.id, node);
        }
        for (const span of uniqueSpans) {
            if (!span.parentSpanId)
                continue;
            const parentNode = nodeMap.get(span.parentSpanId);
            if (parentNode && !parentNode.children.includes(span.id)) {
                parentNode.children.push(span.id);
            }
        }
        const byTime = [...uniqueSpans].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
        const firstSpanId = byTime.length > 0 && nodeMap.has(byTime[0].id) ? byTime[0].id : undefined;
        const lastSpanId = byTime.length > 0 && nodeMap.has(byTime[byTime.length - 1].id)
            ? byTime[byTime.length - 1].id
            : undefined;
        if (firstSpanId != null) {
            nodes.push({
                id: 'prompt',
                type: 'prompt',
                name: 'Prompt',
                children: [firstSpanId],
                metadata: {}
            });
        }
        if (lastSpanId != null) {
            const lastNode = nodeMap.get(lastSpanId);
            if (lastNode)
                lastNode.children = [...lastNode.children, 'response'];
            nodes.push({
                id: 'response',
                type: 'response',
                name: 'Response',
                children: [],
                metadata: {}
            });
        }
        return nodes;
    }
    generateLinearFlow(spans) {
        this.deduplicator.reset();
        const allowedTypes = new Set(['agent', 'tool', 'llm']);
        const uniqueSpans = spans
            .filter(s => !this.deduplicator.isDuplicate(s))
            .filter(s => !this.isInfrastructureSpan(s, spans))
            .filter(s => allowedTypes.has(this.getNodeType(s, spans)))
            .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
        return uniqueSpans.map((span, index) => {
            const type = this.getNodeType(span, spans);
            const displayName = this.getLinearDisplayName(span, type);
            return {
                stepNumber: index + 1,
                spanId: span.id,
                type,
                name: displayName,
                timestamp: span.timestamp,
                duration: span.duration,
                input: utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.workflow.input', 'gen_ai.workflow.input', 'genai.prompt', 'gen_ai.prompt', 'gen_ai.tool.input', 'genai.tool.input'),
                output: utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.workflow.output', 'gen_ai.workflow.output', 'genai.completion', 'gen_ai.completion', 'gen_ai.tool.output', 'genai.tool.output'),
                metadata: {
                    parentSpanId: span.parentSpanId,
                    operationName: utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.operation.name', 'gen_ai.operation.name'),
                    recipientAgentType: utilts_1.FrameworkUtils.getAttr(span.attributes, 'recipient_agent_type'),
                    senderAgentType: utilts_1.FrameworkUtils.getAttr(span.attributes, 'sender_agent_type')
                }
            };
        });
    }
    // ========== Infrastructure Filtering ==========
    /**
     * Check if a span represents AutoGen infrastructure (not user-defined logic)
     */
    isInfrastructureSpan(span, allSpans) {
        // Pattern 1: HTTP spans
        if (this.isHttpInfrastructure(span))
            return true;
        // Pattern 2: Messaging spans
        if (this.isMessagingInfrastructure(span))
            return true;
        // Pattern 3: UserIdentified infrastructure names
        const name = String(span.name).toLowerCase();
        const agentName = String(span.attributes['gen_ai.agent.name'] || '').toLowerCase();
        const recipientType = String(span.attributes['recipient_agent_type'] || '').toLowerCase();
        const recipientClass = String(span.attributes['recipient_agent_class'] || '').toLowerCase();
        if (name.includes('userproxy') || agentName.includes('userproxy') || recipientType.includes('userproxy'))
            return true;
        if (name.includes('groupchatmanager') || agentName.includes('groupchatmanager') || recipientType.includes('groupchatmanager'))
            return true;
        if (recipientClass.includes('roundrobingroupchatmanager'))
            return true;
        // Pattern 4: Inactive agent (received message only, no processing)
        if (this.isInactiveAgent(span, allSpans))
            return true;
        return false;
    }
    isHttpInfrastructure(span) {
        return (span.attributes['http.method'] !== undefined ||
            span.attributes['http.url'] !== undefined ||
            span.attributes['asgi.event.type'] !== undefined);
    }
    isMessagingInfrastructure(span) {
        const op = span.attributes['messaging.operation'];
        return op === 'create' || op === 'publish' || op === 'receive';
    }
    isUserProxy(span) {
        const agentType = String(span.attributes['recipient_agent_type'] ||
            span.attributes['sender_agent_type'] || '');
        return agentType.includes('UserProxy');
    }
    isGroupChatManager(span) {
        const agentType = String(span.attributes['recipient_agent_type'] ||
            span.attributes['sender_agent_type'] || '');
        const agentClass = String(span.attributes['recipient_agent_class'] ||
            span.attributes['sender_agent_class'] || '');
        return agentType.includes('GroupChatManager') ||
            agentClass === 'RoundRobinGroupChatManager';
    }
    isInactiveAgent(span, allSpans) {
        const hasChildWork = allSpans ? this.hasChildWorkSpans(span, allSpans) : false;
        return (span.name.startsWith('autogen process') &&
            span.duration === 0 &&
            span.attributes['messaging.operation'] === 'process' &&
            !hasChildWork);
    }
    // ========== Agent Detection ==========
    /**
     * CANONICAL RULES for agent detection (AutoGen-specific):
     * ALL of the following must be true:
     * 1. messaging.operation === "process"
     * 2. duration > 0 OR has child work spans (model/tool/invoke_agent)
     * 3. recipient_agent_type exists
     * 4. recipient_agent_class exists
     * 5. NOT infrastructure (UserProxy, GroupChatManager)
     */
    isAgentSpan(span, allSpans) {
        // Strategy 1: Look for invoke_agent spans (OpenLIT standard)
        if (span.attributes['gen_ai.operation.name'] === 'invoke_agent' &&
            span.attributes['gen_ai.agent.name']) {
            // Must not be infrastructure
            if (this.isInfrastructureSpan(span, allSpans))
                return false;
            return true;
        }
        if (span.attributes['messaging.operation'] === 'process') {
            return false;
        }
        // Strategy 2: Canonical rules for autogen process spans
        // MUST have recipient_agent_type and recipient_agent_class
        const agentType = String(span.attributes['recipient_agent_type'] || '').trim();
        if (!agentType)
            return false;
        const agentClass = String(span.attributes['recipient_agent_class'] || '').trim();
        if (!agentClass)
            return false;
        // Filter infrastructure
        if (this.isInfrastructureSpan(span, allSpans))
            return false;
        if (span.name.includes('autogen process') || span.name.includes('process')) {
            return false;
        }
        // CANONICAL RULE: PROOF-OF-WORK PARITY
        // Accept if: duration > 1ms OR has child work spans
        // Note: Tiny durations (e.g. < 1ms) without children are usually just framework overhead.
        const hasSignificantDuration = span.duration > 1000000;
        const hasChildWork = allSpans ? this.hasChildWorkSpans(span, allSpans) : false;
        if (!hasSignificantDuration && !hasChildWork) {
            return false; // No meaningful work (likely just a message receipt or framework overhead)
        }
        return true;
    }
    /**
     * Extract agent using stable identifiers with enhanced metadata
     * CANONICAL RULE: Use recipient_agent_type ONLY as primary identity (strip UUID suffix)
     */
    extractAgent(span) {
        const agentName = String(span.attributes['gen_ai.agent.name'] || '').trim();
        const operationName = String(span.attributes['gen_ai.operation.name'] || '').trim();
        const agentClass = String(span.attributes['recipient_agent_class'] || '').trim();
        if (!operationName && !agentName)
            return null;
        // MESSAGE METADATA
        const messageId = utilts_1.FrameworkUtils.getAttr(span.attributes, 'message_id');
        return (0, utilts_1.buildCanonicalAgent)({
            id: span.id,
            name: agentName,
            type: 'autogen',
            ...((0, toolArgumentFields_1.getAgentDescriptionFromAttrs)(span.attributes) != null && {
                description: (0, toolArgumentFields_1.getAgentDescriptionFromAttrs)(span.attributes),
            }),
            role: this.inferRoleFromAgentName(agentName),
            metadata: {
                spanId: span.id,
                timestamp: span.timestamp,
                duration: span.duration,
                agentClass: agentClass,
                messageId: messageId,
            },
        });
    }
    inferRoleFromAgentName(name) {
        const n = name.toLowerCase();
        if (n.includes('news'))
            return 'news_analysis';
        if (n.includes('financial'))
            return 'financial_analysis';
        if (n.includes('technical'))
            return 'technical_analysis';
        if (n.includes('cio') || n.includes('chief') || n.includes('investment'))
            return 'synthesis';
        return undefined;
    }
    // ========== Tool Detection ==========
    /**
     * Check for explicit tool spans
     * Patterns:
     * - genai.tool.name attribute
     * - gen_ai.operation.name: execute_tool
     * - Span name starts with "tool "
     * - FunctionTool pattern (snake_case function name)
     */
    isExplicitToolSpan(span) {
        const name = String(span.name ?? '');
        const toolName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.tool.name', 'gen_ai.tool.name');
        const opName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.operation.name', 'gen_ai.operation.name');
        if (opName === 'execute_tool')
            return true;
        // RESTRICTIVE RULE: Chat spans are NEVER tool executions, even if they mention tools
        if (opName === 'chat' || span.name.includes('chat ')) {
            return false;
        }
        // RESTRICTIVE RULE: Multi-tools are declarations, not single executions
        if (toolName && String(toolName).includes(',')) {
            return false;
        }
        // Fallback: If it has a tool name it's likely a tool, but only if not chat
        if (toolName)
            return true;
        return false;
    }
    isToolSpan(span) {
        return this.isExplicitToolSpan(span);
    }
    buildToolCallDetailsMap(spans) {
        const details = new Map();
        for (const span of spans) {
            const messageAttr = span.attributes?.message;
            if (typeof messageAttr !== 'string')
                continue;
            const parsed = this.safeParseJson(messageAttr);
            if (!parsed || typeof parsed !== 'object')
                continue;
            const candidates = [];
            const parsedRecord = parsed;
            if (parsedRecord.message && typeof parsedRecord.message === 'object') {
                candidates.push(parsedRecord.message);
            }
            if (parsedRecord.response?.chat_message && typeof parsedRecord.response.chat_message === 'object') {
                candidates.push(parsedRecord.response.chat_message);
            }
            candidates.push(parsedRecord);
            for (const candidate of candidates) {
                this.collectToolCallRequestData(candidate, details);
                this.collectToolCallExecutionData(candidate, details);
                this.collectToolCallSummaryData(candidate, details);
            }
        }
        return details;
    }
    collectToolCallRequestData(candidate, details) {
        if (candidate?.type !== 'ToolCallRequestEvent' || !Array.isArray(candidate?.content))
            return;
        for (const item of candidate.content) {
            const callId = typeof item?.id === 'string' ? item.id : undefined;
            if (!callId)
                continue;
            const existing = details.get(callId) ?? {};
            details.set(callId, {
                ...existing,
                ...(typeof item?.name === 'string' ? { name: item.name } : {}),
                ...(item?.arguments !== undefined ? { arguments: item.arguments } : {}),
            });
        }
    }
    collectToolCallExecutionData(candidate, details) {
        if (candidate?.type !== 'ToolCallExecutionEvent' || !Array.isArray(candidate?.content))
            return;
        for (const item of candidate.content) {
            const callId = typeof item?.call_id === 'string' ? item.call_id : undefined;
            if (!callId)
                continue;
            const existing = details.get(callId) ?? {};
            details.set(callId, {
                ...existing,
                ...(typeof item?.name === 'string' ? { name: item.name } : {}),
                ...(item?.content !== undefined ? { output: item.content } : {}),
            });
        }
    }
    collectToolCallSummaryData(candidate, details) {
        if (candidate?.type !== 'ToolCallSummaryMessage')
            return;
        if (Array.isArray(candidate?.tool_calls)) {
            for (const item of candidate.tool_calls) {
                const callId = typeof item?.id === 'string' ? item.id : undefined;
                if (!callId)
                    continue;
                const existing = details.get(callId) ?? {};
                details.set(callId, {
                    ...existing,
                    ...(typeof item?.name === 'string' ? { name: item.name } : {}),
                    ...(item?.arguments !== undefined ? { arguments: item.arguments } : {}),
                });
            }
        }
        if (Array.isArray(candidate?.results)) {
            for (const item of candidate.results) {
                const callId = typeof item?.call_id === 'string' ? item.call_id : undefined;
                if (!callId)
                    continue;
                const existing = details.get(callId) ?? {};
                details.set(callId, {
                    ...existing,
                    ...(typeof item?.name === 'string' ? { name: item.name } : {}),
                    ...(item?.content !== undefined ? { output: item.content } : {}),
                });
            }
        }
    }
    safeParseJson(value) {
        try {
            return JSON.parse(value);
        }
        catch {
            return null;
        }
    }
    /**
     * Extract tool with enhanced agent ownership metadata
     */
    extractTool(span, allSpans, toolCallDetailsById) {
        const toolCallId = utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.tool.call.id', 'genai.tool.call.id');
        const toolCallDetails = toolCallId ? toolCallDetailsById?.get(String(toolCallId)) : undefined;
        const base = (0, toolArgumentFields_1.extractOpenLitStyleToolFieldsFromSpan)(span);
        const merged = (0, toolArgumentFields_1.mergeToolCallMessageDetailsIntoOpenLitFields)(span, toolCallDetails, base);
        const toolName = this.toolAnalyzer.extractToolName(span) || toolCallDetails?.name || merged.name;
        if (!toolName)
            return null;
        // ENHANCED METADATA: Find parent agent
        const parentSpan = allSpans.find(s => s.id === span.parentSpanId);
        const agentName = parentSpan ? this.extractAgentIdentity(parentSpan) : undefined;
        const messageId = parentSpan ? utilts_1.FrameworkUtils.getAttr(parentSpan.attributes, 'message_id') : undefined;
        // RETRY GROUP DETECTION
        const retryKey = messageId && agentName ? `${messageId}_${agentName}` : undefined;
        return (0, utilts_1.buildCanonicalTool)({
            id: span.id,
            name: toolName,
            ...(merged.description != null && { description: merged.description }),
            input: merged.input,
            output: merged.output,
            argument: merged.argument,
            argument_defination: merged.argument_defination,
            duration: span.duration,
            timestamp: span.timestamp,
            spanId: span.id,
            executionType: 'actual',
            metadata: {
                source: 'explicit_tool_span',
                agentSpanId: span.parentSpanId,
                agentName: agentName,
                messageId: messageId,
                retryGroupId: retryKey,
                ...(toolCallId ? { toolCallId } : {}),
            },
        });
    }
    /**
     * Extract tool calls from agent message attribute
     * Parses message JSON for tool_calls array
     */
    extractToolCallsFromMessage(span) {
        const tools = [];
        const messageAttr = span.attributes['message'];
        if (!messageAttr || typeof messageAttr !== 'string') {
            return tools;
        }
        try {
            const parsed = JSON.parse(messageAttr);
            const toolCalls = parsed.response?.chat_message?.tool_calls || [];
            for (const tc of toolCalls) {
                if (tc.function?.name) {
                    tools.push((0, utilts_1.buildCanonicalTool)({
                        id: `${span.id}_${tc.function.name}`,
                        name: tc.function.name,
                        input: tc.function.arguments,
                        output: undefined,
                        argument: tc.function.arguments,
                        argument_defination: (0, toolArgumentFields_1.inferArgumentDefinationFromInput)(tc.function.arguments),
                        duration: 0,
                        timestamp: span.timestamp,
                        spanId: span.id,
                        executionType: 'decision',
                    }));
                }
            }
        }
        catch {
            // Ignore parse errors
        }
        return tools;
    }
    // ========== Model Detection ==========
    enrichToolsMetadata(tools, spans) {
        const spanById = new Map(spans.map((s) => [s.id, s]));
        return tools.map((tool) => {
            const span = spanById.get(tool.spanId);
            const attrs = span?.attributes ?? {};
            const argument_defination = tool.argument_defination ??
                (0, toolArgumentFields_1.getToolArgumentDefinitionFromAttrs)(attrs) ??
                (0, toolArgumentFields_1.inferArgumentDefinationFromInput)(tool.argument ?? tool.input);
            const description = tool.description ?? (0, toolArgumentFields_1.getToolDescriptionFromAttrs)(attrs);
            return {
                ...tool,
                argument: tool.argument ?? tool.input,
                ...(argument_defination !== undefined && { argument_defination }),
                ...(description != null && { description }),
            };
        });
    }
    isModelSpan(span) {
        return utilts_1.FrameworkUtils.getModelAttr(span.attributes, 'gen_ai.request.model', 'genai.request.model') !== undefined;
    }
    extractModel(span) {
        const attrs = span.attributes;
        // Get model name - try multiple attribute formats
        const modelName = utilts_1.FrameworkUtils.getModelAttrResponseFirst(attrs);
        if (!modelName)
            return null;
        // Extract token usage - try multiple formats
        const inputTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.input_tokens', 'genai.usage.inputtokens'));
        const outputTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.output_tokens', 'genai.usage.outputtokens'));
        let totalTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.total_tokens', 'genai.usage.totaltokens'));
        if (!totalTokens && (inputTokens || outputTokens))
            totalTokens = inputTokens + outputTokens;
        // Extract cache tokens (for prompt caching)
        const { cacheRead, cacheWrite } = utilts_1.FrameworkUtils.getCacheTokens(attrs);
        return (0, utilts_1.buildCanonicalExtendedModel)({
            id: span.id,
            provider: utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.system', 'genai.system') || 'openai',
            modelName: String(modelName),
            tokenUsage: {
                inputTokens,
                outputTokens,
                totalTokens,
                ...(cacheRead != null && { cacheRead }),
                ...(cacheWrite != null && { cacheWrite }),
            },
            spanId: span.id,
            timestamp: span.timestamp,
            temperature: utilts_1.FrameworkUtils.safeParseFloat(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.request.temperature', 'genai.request.temperature')),
            cost: utilts_1.FrameworkUtils.safeParseFloat(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.cost', 'genai.usage.cost')) || 0,
            ttft: utilts_1.FrameworkUtils.safeParseFloat(attrs['gen_ai.server.time_to_first_token']),
            serverTimePerOutputToken: utilts_1.FrameworkUtils.safeParseFloat(attrs['gen_ai.server.time_per_output_token']),
            prompt: utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.prompt', 'genai.prompt'),
            completion: utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.completion', 'genai.completion'),
            duration: utilts_1.FrameworkUtils.safeParseFloat(span.duration ?? 0) ?? 0,
        });
    }
    getNodeType(span, allSpans) {
        if (this.isModelSpan(span))
            return 'llm';
        if (this.isToolSpan(span))
            return 'tool';
        if (this.isAgentSpan(span))
            return 'agent';
        return 'workflow';
    }
    getLinearDisplayName(span, type) {
        if (type === 'tool') {
            const toolName = this.toolAnalyzer.extractToolName(span);
            if (toolName)
                return toolName;
            const fromAttr = utilts_1.FrameworkUtils.getAttr(span.attributes, 'genai.tool.name', 'gen_ai.tool.name');
            if (fromAttr)
                return String(fromAttr);
            return String(span.name || 'Tool').replace(/^tool\s+/i, '').trim() || 'Tool';
        }
        if (type === 'llm') {
            const modelName = utilts_1.FrameworkUtils.getModelAttrResponseFirst(span.attributes);
            return modelName ? String(modelName) : 'Model';
        }
        if (type === 'agent') {
            if (span.attributes['gen_ai.operation.name'] === 'invoke_agent' &&
                span.attributes['gen_ai.agent.name']) {
                return String(span.attributes['gen_ai.agent.name']);
            }
        }
        return span.name;
    }
    normalizeLinearName(name) {
        return String(name || '').trim().toLowerCase();
    }
    getLinearAgentPriority(span, allSpans) {
        let score = 0;
        // Prefer canonical "autogen process" agent spans over invoke stubs.
        if (span.name.startsWith('autogen process') &&
            span.attributes['messaging.operation'] === 'process') {
            score += 100;
        }
        if (utilts_1.FrameworkUtils.getAttr(span.attributes, 'recipient_agent_type')) {
            score += 10;
        }
        if (span.duration > 0) {
            score += 5;
        }
        if (this.hasChildWorkSpans(span, allSpans)) {
            score += 5;
        }
        if (
        // span.attributes['gen_ai.operation.name'] === 'invoke_agent' &&
        utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.agent.name')) {
            score += 1;
        }
        return score;
    }
    // ========== Retry Detection ==========
    /**
     * Detect retries based on message_id
     *
     * Retry criteria:
     * - Same message_id
     * - Same agent type (after UUID stripping)
     * - Within 5-second window
     */
    detectRetries(spans) {
        const retryMap = new Map();
        // Group agent spans by (message_id, agent_type)
        for (const span of spans) {
            if (!this.isAgentSpan(span, spans))
                continue;
            const messageId = utilts_1.FrameworkUtils.getAttr(span.attributes, 'message_id');
            const agentType = this.extractAgentIdentity(span);
            if (!messageId)
                continue;
            const key = `${messageId}_${agentType}`;
            if (!retryMap.has(key)) {
                retryMap.set(key, {
                    messageId,
                    agentType,
                    spans: [],
                    totalTokens: 0,
                    totalCost: 0,
                    retryCount: 0,
                    timestamps: []
                });
            }
            const group = retryMap.get(key);
            group.spans.push(span);
            group.timestamps.push(span.timestamp);
            // Aggregate tokens and cost from child model spans
            const modelSpans = this.getChildModelSpans(span, spans);
            for (const modelSpan of modelSpans) {
                const tokens = this.extractTokenUsage(modelSpan);
                const cost = utilts_1.FrameworkUtils.safeParseFloat(utilts_1.FrameworkUtils.getAttr(modelSpan.attributes, 'gen_ai.usage.cost', 'genai.usage.cost')) || 0;
                group.totalTokens += tokens.totalTokens || 0;
                group.totalCost += cost;
            }
        }
        // Filter to only retries (more than 1 span per message_id)
        const retries = Array.from(retryMap.values()).filter(g => g.spans.length > 1);
        // Validate time window (within 5 seconds)
        return retries.filter(group => {
            const times = group.timestamps.map(t => new Date(t).getTime());
            const minTime = Math.min(...times);
            const maxTime = Math.max(...times);
            const windowMs = maxTime - minTime;
            return windowMs <= 5000; // 5 seconds
        }).map(group => ({
            ...group,
            retryCount: group.spans.length - 1 // First attempt is not a retry
        }));
    }
    /**
     * Extract agent identity (strip UUID)
     */
    extractAgentIdentity(span) {
        const rawType = utilts_1.FrameworkUtils.getAttr(span.attributes, 'recipient_agent_type') || '';
        // UUID pattern: _[8-4-4-4-12 hex chars]
        const uuidPattern = /_[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
        return rawType.replace(uuidPattern, '').trim();
    }
    /**
     * Get child model spans for an agent span
     */
    getChildModelSpans(agentSpan, allSpans) {
        return allSpans.filter(span => span.parentSpanId === agentSpan.id &&
            this.isModelSpan(span));
    }
    /**
     * Extract token usage from model span
     */
    extractTokenUsage(span) {
        const attrs = span.attributes;
        const inputTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.input_tokens', 'genai.usage.inputtokens')) || 0;
        const outputTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.output_tokens', 'genai.usage.outputtokens')) || 0;
        let totalTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.total_tokens', 'genai.usage.totaltokens')) || 0;
        if (!totalTokens && (inputTokens || outputTokens)) {
            totalTokens = inputTokens + outputTokens;
        }
        return { inputTokens, outputTokens, totalTokens };
    }
    /**
     * Aggregate retry metrics
     */
    aggregateRetryMetrics(retries) {
        const byAgent = {};
        let totalRetries = 0;
        let wastedTokens = 0;
        let wastedCost = 0;
        for (const group of retries) {
            totalRetries += group.retryCount;
            // Wasted resources = total - first attempt
            // Assume first attempt used 1/N of total resources
            const wastedTokensForGroup = group.totalTokens * (group.retryCount / group.spans.length);
            const wastedCostForGroup = group.totalCost * (group.retryCount / group.spans.length);
            wastedTokens += wastedTokensForGroup;
            wastedCost += wastedCostForGroup;
            if (!byAgent[group.agentType]) {
                byAgent[group.agentType] = { retries: 0, tokens: 0, cost: 0 };
            }
            byAgent[group.agentType].retries += group.retryCount;
            byAgent[group.agentType].tokens += wastedTokensForGroup;
            byAgent[group.agentType].cost += wastedCostForGroup;
        }
        return {
            totalRetries,
            wastedTokens,
            wastedCost,
            byAgent
        };
    }
    // ========== Structural Parity Methods ==========
    /**
     * 1️⃣ CREATE ROOT ORCHESTRATOR AGENT
     * AutoGen equivalent of LangGraph's root workflow node
     */
    createRootOrchestratorAgent(spans) {
        // Find the root span (no parentSpanId, or HTTP endpoint span)
        const rootSpan = spans.find(s => !s.parentSpanId ||
            s.attributes['http.route'] === '/eval/{framework}' ||
            s.name.includes('POST /eval'));
        if (!rootSpan)
            return null;
        // Find GroupChat creation span (messaging.operation: create)
        const groupChatSpan = spans.find(s => s.attributes['messaging.operation'] === 'create' &&
            s.name.includes('autogen'));
        const orchestratorSpan = groupChatSpan || rootSpan;
        return {
            id: orchestratorSpan.id,
            name: 'AutoGen Session',
            type: 'autogen',
            role: 'orchestrator',
            metadata: {
                spanId: orchestratorSpan.id,
                timestamp: orchestratorSpan.timestamp,
                duration: orchestratorSpan.duration,
                isRootContainer: true,
                framework: 'autogen'
            }
        };
    }
    /**
     * 2️⃣ DETECT FAN-OUT GROUPS
     * Same message_id processed by different agents
     */
    detectFanOutGroups(spans) {
        const messageMap = new Map();
        for (const span of spans) {
            if (!this.isAgentSpan(span, spans))
                continue;
            const messageId = utilts_1.FrameworkUtils.getAttr(span.attributes, 'message_id');
            if (!messageId)
                continue;
            const agentType = this.extractAgentIdentity(span);
            if (!messageMap.has(messageId)) {
                messageMap.set(messageId, { agentTypes: new Set(), spans: [] });
            }
            const group = messageMap.get(messageId);
            group.agentTypes.add(agentType);
            group.spans.push(span);
        }
        // Filter to only fan-outs (multiple different agents)
        return Array.from(messageMap.entries())
            .filter(([_, group]) => group.agentTypes.size > 1)
            .map(([messageId, group]) => ({
            messageId,
            agentTypes: Array.from(group.agentTypes),
            spans: group.spans
        }));
    }
    /**
     * 3️⃣ CANONICAL AGENT IDENTITY
     * For cross-trace aggregation
     */
    createCanonicalIdentity(framework, agentType, agentClass) {
        // Simple hash: combine framework + agentType + agentClass
        const canonicalId = `${framework}:${agentType}:${agentClass}`;
        return {
            canonicalId,
            framework,
            agentType,
            agentClass
        };
    }
    /**
     * 4️⃣ PROOF-OF-WORK PARITY
     * Check if agent has child work spans (model/tool/invoke_agent)
     */
    hasChildWorkSpans(agentSpan, allSpans) {
        return allSpans.some(childSpan => childSpan.parentSpanId === agentSpan.id &&
            (this.isModelSpan(childSpan) ||
                this.isToolSpan(childSpan) ||
                (childSpan.attributes['gen_ai.operation.name'] === 'invoke_agent' &&
                    Boolean(childSpan.attributes['gen_ai.agent.name']))));
    }
}
exports.AutogenParser = AutogenParser;
//# sourceMappingURL=autogen.js.map