"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseAgent = void 0;
/**
 * SmartPerfetto Base Agent
 *
 * Phase 2.1: Abstract base class for all AI domain agents
 *
 * This class provides the foundation for AI-driven domain agents that:
 * 1. Use Skills as tools through AI reasoning
 * 2. Can request information from other agents
 * 3. Build and verify hypotheses
 * 4. Generate evidence-backed findings
 *
 * The Think-Act-Reflect loop:
 * 1. Understand: Parse task and build understanding
 * 2. Plan: Decide which tools to use
 * 3. Execute: Run tools and collect results
 * 4. Reflect: Evaluate results and decide next steps
 * 5. Respond: Generate response with findings
 */
const events_1 = require("events");
const sqlIncludeInjector_1 = require("../../../agentv3/sqlIncludeInjector");
const agentProtocol_1 = require("../../types/agentProtocol");
const llmJson_1 = require("../../../utils/llmJson");
const skillEngine_1 = require("../../../services/skillEngine");
const DEFAULT_UPGRADE_CONFIG = {
    enabled: true,
    minFailedSteps: 1,
    maxRetries: 2,
    requireExplicitObjective: true,
};
const TASK_UNDERSTANDING_JSON_SCHEMA = {
    name: 'agent_task_understanding_json@1.0.0',
    validate: (value) => {
        if (!(0, llmJson_1.isPlainObject)(value))
            return false;
        if (value.objective !== undefined && typeof value.objective !== 'string')
            return false;
        if (value.questions !== undefined && !(0, llmJson_1.isStringArray)(value.questions))
            return false;
        if (value.relevantAreas !== undefined && !(0, llmJson_1.isStringArray)(value.relevantAreas))
            return false;
        if (value.recommendedTools !== undefined && !(0, llmJson_1.isStringArray)(value.recommendedTools))
            return false;
        if (value.constraints !== undefined && !(0, llmJson_1.isStringArray)(value.constraints))
            return false;
        const confidence = value.confidence;
        if (confidence !== undefined && confidence !== null && typeof confidence !== 'number')
            return false;
        return true;
    },
};
const EXECUTION_PLAN_JSON_SCHEMA = {
    name: 'agent_execution_plan_json@1.0.0',
    validate: (value) => {
        if (!(0, llmJson_1.isPlainObject)(value))
            return false;
        const steps = value.steps;
        if (steps !== undefined && steps !== null && !Array.isArray(steps))
            return false;
        const expectedOutcomes = value.expectedOutcomes;
        if (expectedOutcomes !== undefined && expectedOutcomes !== null && !(0, llmJson_1.isStringArray)(expectedOutcomes))
            return false;
        const estimatedTimeMs = value.estimatedTimeMs;
        if (estimatedTimeMs !== undefined && estimatedTimeMs !== null && typeof estimatedTimeMs !== 'number')
            return false;
        const confidence = value.confidence;
        if (confidence !== undefined && confidence !== null && typeof confidence !== 'number')
            return false;
        return true;
    },
};
const REFLECTION_JSON_SCHEMA = {
    name: 'agent_reflection_json@1.0.0',
    validate: (value) => {
        if (!(0, llmJson_1.isPlainObject)(value))
            return false;
        if (value.insights !== undefined && !(0, llmJson_1.isStringArray)(value.insights))
            return false;
        const objectivesMet = value.objectivesMet;
        if (objectivesMet !== undefined && objectivesMet !== null && typeof objectivesMet !== 'boolean')
            return false;
        const findingsConfidence = value.findingsConfidence;
        if (findingsConfidence !== undefined && findingsConfidence !== null && typeof findingsConfidence !== 'number')
            return false;
        if (value.gaps !== undefined && !(0, llmJson_1.isStringArray)(value.gaps))
            return false;
        if (value.nextSteps !== undefined && !(0, llmJson_1.isStringArray)(value.nextSteps))
            return false;
        const hypothesisUpdates = value.hypothesisUpdates;
        if (hypothesisUpdates !== undefined && hypothesisUpdates !== null && !Array.isArray(hypothesisUpdates))
            return false;
        const questionsForOthers = value.questionsForOthers;
        if (questionsForOthers !== undefined && questionsForOthers !== null && !Array.isArray(questionsForOthers))
            return false;
        return true;
    },
};
// =============================================================================
// Base Agent Abstract Class
// =============================================================================
/**
 * Abstract base class for domain-specific AI agents
 *
 * Each domain agent (Frame, CPU, Memory, Binder, etc.) extends this class
 * and provides:
 * 1. Domain-specific tools (wrapped Skills)
 * 2. Domain-specific reasoning prompts
 * 3. Domain-specific hypothesis generation
 */
class BaseAgent extends events_1.EventEmitter {
    constructor(config, modelRouter, skillDefs) {
        super();
        /** Skill definitions for lazy tool initialization */
        this.skillDefinitions = [];
        /** Whether tools have been lazily loaded from skill definitions */
        this.toolsLoaded = false;
        /** Cached SkillExecutor with registered skills */
        this.skillExecutorCache = null;
        /** Current shared context */
        this.sharedContext = null;
        /** Reasoning trace */
        this.reasoningTrace = [];
        /** Current iteration */
        this.currentIteration = 0;
        /** Upgrade path configuration */
        this.upgradeConfig = { ...DEFAULT_UPGRADE_CONFIG };
        /** Track upgrade attempts per task */
        this.upgradeAttempts = 0;
        // ==========================================================================
        // Dynamic SQL Generation (v2.0 - Agent Autonomy)
        // ==========================================================================
        /**
         * SQL generator instance (lazy initialized).
         * Provides agents with the ability to generate dynamic SQL queries.
         */
        this.sqlGenerator = null;
        this.sqlValidator = null;
        this.config = config;
        this.modelRouter = modelRouter;
        this.tools = new Map();
        // Register any tools already in config (non-skill tools)
        for (const tool of config.tools) {
            this.tools.set(tool.name, tool);
        }
        // Store skill definitions for lazy loading
        if (skillDefs) {
            this.skillDefinitions = skillDefs;
        }
    }
    /**
     * Resolve tools for a task context.
     * Generic: if orchestrator specifies `additionalData.focusTools`, restricts to those tools.
     * Otherwise falls back to subclass's getRecommendedTools().
     * This allows any orchestrator to control agent tool selection without scenario-specific flags.
     */
    resolveToolsForTask(context) {
        const focusTools = context.additionalData?.focusTools;
        if (Array.isArray(focusTools) && focusTools.length > 0) {
            // Only return focusTools that this agent actually has registered
            const validTools = focusTools.filter((t) => this.tools.has(t));
            if (validTools.length > 0)
                return validTools;
        }
        return this.getRecommendedTools(context);
    }
    // ==========================================================================
    // Core Agent Loop
    // ==========================================================================
    /**
     * Execute a task through the Think-Act-Reflect loop
     */
    async executeTask(task, sharedContext) {
        const startTime = Date.now();
        this.sharedContext = sharedContext;
        this.reasoningTrace = [];
        this.currentIteration = 0;
        this.resetUpgradeState(); // Reset upgrade attempts for new task
        // Ensure skill-based tools are loaded (lazy initialization)
        await this.ensureToolsLoaded();
        this.emit('task_started', { agentId: this.config.id, taskId: task.id });
        try {
            // 1. Understand the task
            this.addReasoningStep('observation', 'Analyzing task requirements');
            const understanding = await this.understand(task);
            this.emit('understanding_complete', { agentId: this.config.id, understanding });
            // 2. Plan execution
            this.addReasoningStep('analysis', 'Creating execution plan');
            const plan = await this.plan(understanding, task);
            this.emit('plan_created', { agentId: this.config.id, plan });
            // 3. Execute plan
            this.addReasoningStep('action', `Executing ${plan.steps.length} steps`);
            const result = await this.execute(plan, task);
            this.emit('execution_complete', { agentId: this.config.id, result });
            // 4. Reflect on results
            this.addReasoningStep('analysis', 'Reflecting on results');
            const reflection = await this.reflect(result, task);
            this.emit('reflection_complete', { agentId: this.config.id, reflection });
            // 5. Generate response
            const response = await this.respond(reflection, result, task, startTime);
            this.emit('task_completed', { agentId: this.config.id, response });
            return response;
        }
        catch (error) {
            this.emit('task_failed', { agentId: this.config.id, taskId: task.id, error: error.message });
            return {
                agentId: this.config.id,
                taskId: task.id,
                success: false,
                findings: [],
                confidence: 0,
                executionTimeMs: Date.now() - startTime,
                reasoning: this.reasoningTrace,
            };
        }
    }
    // ==========================================================================
    // Think-Act-Reflect Steps
    // ==========================================================================
    /**
     * Step 1: Understand the task
     */
    async understand(task) {
        const prompt = this.buildUnderstandingPrompt(task);
        const response = await this.modelRouter.callWithFallback(prompt, 'intent_understanding', {
            sessionId: this.sharedContext?.sessionId,
            traceId: this.sharedContext?.traceId,
            jsonMode: true,
            promptId: `agent.${this.config.id}.understand`,
            promptVersion: '1.0.0',
            contractVersion: TASK_UNDERSTANDING_JSON_SCHEMA.name,
        });
        try {
            const parsed = (0, llmJson_1.parseLlmJson)(response.response, TASK_UNDERSTANDING_JSON_SCHEMA);
            return {
                objective: parsed.objective || task.description,
                questions: parsed.questions || [],
                relevantAreas: parsed.relevantAreas || [],
                recommendedTools: parsed.recommendedTools || this.resolveToolsForTask(task.context),
                constraints: parsed.constraints || [],
                confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.7,
            };
        }
        catch (error) {
            console.warn(`[${this.config.id}] Failed to parse understanding response`);
        }
        // Fallback to basic understanding
        return {
            objective: task.description,
            questions: [],
            relevantAreas: [this.config.domain],
            recommendedTools: this.resolveToolsForTask(task.context),
            constraints: [],
            confidence: 0.5,
        };
    }
    /**
     * Step 2: Plan execution
     */
    async plan(understanding, task) {
        const prompt = this.buildPlanningPrompt(understanding, task);
        const focusedTools = this.resolveToolsForTask(task.context);
        const hasToolFocus = Array.isArray(task.context.additionalData?.focusTools)
            && task.context.additionalData.focusTools.length > 0;
        const focusedToolSet = new Set(focusedTools);
        const response = await this.modelRouter.callWithFallback(prompt, 'planning', {
            sessionId: this.sharedContext?.sessionId,
            traceId: this.sharedContext?.traceId,
            jsonMode: true,
            promptId: `agent.${this.config.id}.plan`,
            promptVersion: '1.0.0',
            contractVersion: EXECUTION_PLAN_JSON_SCHEMA.name,
        });
        try {
            const parsed = (0, llmJson_1.parseLlmJson)(response.response, EXECUTION_PLAN_JSON_SCHEMA);
            const plannedSteps = (parsed.steps || [])
                .map((s, i) => ({
                stepNumber: i + 1,
                toolName: s.toolName || s.tool,
                params: s.params || {},
                purpose: s.purpose || `Execute ${s.toolName || s.tool}`,
                // Note: dependsOn is intentionally stripped — domain agent tools are
                // independent analysis units. The LLM-generated dependencies cause
                // cascade failures when tool names are filtered out, as step numbers
                // get re-assigned but dependsOn values aren't updated.
            }))
                .filter((step) => step.toolName && this.tools.has(step.toolName))
                .filter((step) => !hasToolFocus || focusedToolSet.has(step.toolName))
                // Deduplicate tools: LLM may return duplicate tool calls which cause redundant execution.
                // Keep the first occurrence of each tool (preserves LLM's priority order).
                .filter((step, index, self) => self.findIndex((s) => s.toolName === step.toolName) === index);
            if (plannedSteps.length === 0) {
                const recommendedTools = understanding.recommendedTools.length > 0
                    ? understanding.recommendedTools
                    : focusedTools;
                const fallbackTools = hasToolFocus
                    ? recommendedTools.filter((toolName) => focusedToolSet.has(toolName))
                    : recommendedTools;
                const finalFallbackTools = fallbackTools.length > 0 ? fallbackTools : focusedTools;
                return {
                    steps: finalFallbackTools.map((toolName, i) => ({
                        stepNumber: i + 1,
                        toolName,
                        params: {},
                        purpose: `Execute ${toolName}`,
                    })),
                    expectedOutcomes: parsed.expectedOutcomes || [],
                    estimatedTimeMs: typeof parsed.estimatedTimeMs === 'number' ? parsed.estimatedTimeMs : 30000,
                    confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
                };
            }
            return {
                steps: plannedSteps.map((step, i) => ({
                    ...step,
                    stepNumber: i + 1,
                })),
                expectedOutcomes: parsed.expectedOutcomes || [],
                estimatedTimeMs: typeof parsed.estimatedTimeMs === 'number' ? parsed.estimatedTimeMs : 30000,
                confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.7,
            };
        }
        catch (error) {
            console.warn(`[${this.config.id}] Failed to parse planning response`);
        }
        // Fallback to using recommended tools
        return {
            steps: focusedTools.map((toolName, i) => ({
                stepNumber: i + 1,
                toolName,
                params: {},
                purpose: `Execute ${toolName}`,
            })),
            expectedOutcomes: [`Analyze ${this.config.domain}`],
            estimatedTimeMs: 30000,
            confidence: 0.5,
        };
    }
    /**
     * Step 3: Execute the plan
     */
    async execute(plan, task) {
        const stepResults = [];
        const allFindings = [];
        let anyStepSucceeded = false;
        // Build tool context
        const toolContext = {
            sessionId: this.sharedContext?.sessionId || '',
            traceId: this.sharedContext?.traceId || '',
            traceProcessorService: task.context.additionalData?.traceProcessorService,
            packageName: task.context.additionalData?.packageName,
            timeRange: task.context.timeRange,
            aiService: task.context.additionalData?.aiService,
            additionalContext: {
                ...(task.context.additionalData || {}),
                taskId: task.id,
                agentId: this.config.id,
            },
        };
        // Log plan steps for debugging duplicate execution issues
        console.log(`[${this.config.id}] Executing plan with ${plan.steps.length} step(s): [${plan.steps.map(s => s.toolName).join(', ')}]`);
        // Execute each step
        for (const step of plan.steps) {
            // Check dependencies
            if (step.dependsOn) {
                const pendingDeps = step.dependsOn.filter(dep => !stepResults.find(r => r.stepNumber === dep && r.result.success));
                if (pendingDeps.length > 0) {
                    console.warn(`[${this.config.id}] Skipping step ${step.stepNumber} - dependencies not met`);
                    continue;
                }
            }
            // Get tool
            const tool = this.tools.get(step.toolName);
            if (!tool) {
                console.warn(`[${this.config.id}] Tool not found: ${step.toolName}`);
                stepResults.push({
                    stepNumber: step.stepNumber,
                    toolName: step.toolName,
                    result: { success: false, error: `Tool not found: ${step.toolName}`, executionTimeMs: 0 },
                    observations: [`Tool ${step.toolName} not available`],
                });
                continue;
            }
            // Execute tool
            this.emit('tool_executing', { agentId: this.config.id, toolName: step.toolName, step: step.stepNumber });
            const result = await tool.execute(step.params, toolContext);
            this.emit('tool_completed', { agentId: this.config.id, toolName: step.toolName, success: result.success });
            // Collect findings
            if (result.findings) {
                allFindings.push(...result.findings);
            }
            // Generate observations
            const observations = this.generateObservations(result);
            stepResults.push({
                stepNumber: step.stepNumber,
                toolName: step.toolName,
                result,
                observations,
            });
            if (result.success) {
                anyStepSucceeded = true;
            }
        }
        // Agent execution succeeds if at least one tool returned data successfully.
        // Individual tool failures (e.g., missing tables) don't invalidate the whole analysis.
        let success = anyStepSucceeded || stepResults.length === 0;
        // ==========================================================================
        // Upgrade Path: Try dynamic SQL when predefined Skills return insufficient results
        // ==========================================================================
        if (this.upgradeConfig.enabled && !anyStepSucceeded && allFindings.length === 0) {
            const upgradeCheck = this.checkUpgradeEligibility(task, stepResults, toolContext);
            if (upgradeCheck.eligible && upgradeCheck.suggestedObjective) {
                this.emit('upgrade_attempting', {
                    agentId: this.config.id,
                    reason: upgradeCheck.reason,
                    objective: upgradeCheck.suggestedObjective,
                    attempt: this.upgradeAttempts + 1,
                });
                const upgradeResult = await this.tryDynamicSQLUpgrade(upgradeCheck.suggestedObjective, toolContext, task);
                if (upgradeResult.success) {
                    // Merge upgrade results
                    if (upgradeResult.findings) {
                        allFindings.push(...upgradeResult.findings);
                    }
                    stepResults.push({
                        stepNumber: stepResults.length + 1,
                        toolName: '_dynamic_sql_upgrade',
                        result: upgradeResult,
                        observations: [`Dynamic SQL upgrade succeeded: ${upgradeResult.data?.rows?.length || 0} rows returned`],
                    });
                    anyStepSucceeded = true;
                    success = true;
                    this.emit('upgrade_succeeded', {
                        agentId: this.config.id,
                        findingsCount: upgradeResult.findings?.length || 0,
                        rowCount: upgradeResult.data?.rows?.length || 0,
                    });
                }
                else {
                    this.emit('upgrade_failed', {
                        agentId: this.config.id,
                        error: upgradeResult.error,
                    });
                }
            }
        }
        return {
            steps: stepResults,
            findings: allFindings,
            success,
            totalTimeMs: stepResults.reduce((sum, r) => sum + r.result.executionTimeMs, 0),
        };
    }
    // ==========================================================================
    // Upgrade Path Methods (v2.0)
    // ==========================================================================
    /**
     * Check if the agent should attempt a dynamic SQL upgrade.
     *
     * Upgrade is eligible when:
     * 1. Upgrade path is enabled
     * 2. Predefined skills returned no useful results
     * 3. We haven't exceeded retry limits
     * 4. Task has a clear objective we can convert to SQL
     */
    checkUpgradeEligibility(task, stepResults, toolContext) {
        // Check if upgrade is enabled
        if (!this.upgradeConfig.enabled) {
            return { eligible: false, reason: 'Upgrade path disabled' };
        }
        // Check retry limit
        if (this.upgradeAttempts >= this.upgradeConfig.maxRetries) {
            return { eligible: false, reason: `Max upgrade retries (${this.upgradeConfig.maxRetries}) exceeded` };
        }
        // Check if trace processor is available
        if (!toolContext.traceProcessorService) {
            return { eligible: false, reason: 'TraceProcessorService not available' };
        }
        // Count failed steps
        const failedSteps = stepResults.filter(s => !s.result.success);
        const failedStepNames = failedSteps.map(s => s.toolName);
        if (failedSteps.length < this.upgradeConfig.minFailedSteps) {
            return { eligible: false, reason: `Not enough failed steps (${failedSteps.length} < ${this.upgradeConfig.minFailedSteps})` };
        }
        // Build suggested objective from task description
        const suggestedObjective = this.buildUpgradeObjective(task, failedStepNames);
        if (!suggestedObjective && this.upgradeConfig.requireExplicitObjective) {
            return { eligible: false, reason: 'Could not derive clear objective from task' };
        }
        return {
            eligible: true,
            reason: `${failedSteps.length} steps failed, attempting dynamic SQL upgrade`,
            suggestedObjective: suggestedObjective || task.description,
            failedSteps: failedStepNames,
        };
    }
    /**
     * Build an objective for dynamic SQL based on the failed task.
     * Subclasses can override for domain-specific objective building.
     */
    buildUpgradeObjective(task, failedTools) {
        // Extract key information from task
        const { description, context } = task;
        const timeRange = context.timeRange;
        const packageName = context.additionalData?.packageName;
        // Build a focused objective
        const parts = [];
        // Add main objective
        parts.push(description);
        // Add time constraints if available
        if (timeRange?.start && timeRange?.end) {
            parts.push(`时间范围: ${timeRange.start} - ${timeRange.end}`);
        }
        // Add package/process filter if available
        if (packageName) {
            parts.push(`目标进程: ${packageName}`);
        }
        // Add context about what failed
        if (failedTools.length > 0) {
            parts.push(`注意: 以下预定义分析失败 [${failedTools.join(', ')}]，请尝试其他方式获取数据`);
        }
        return parts.join('\n');
    }
    /**
     * Attempt to execute dynamic SQL for the given objective.
     *
     * This is the "escape hatch" when predefined Skills can't satisfy the analysis need.
     */
    async tryDynamicSQLUpgrade(objective, toolContext, task) {
        this.upgradeAttempts++;
        const startTime = Date.now();
        console.log(`[${this.config.id}] Attempting dynamic SQL upgrade (attempt ${this.upgradeAttempts})`);
        try {
            const result = await this.generateAndExecuteSQL(objective, toolContext);
            if (!result.success) {
                return {
                    success: false,
                    error: result.error || 'Dynamic SQL execution failed',
                    executionTimeMs: Date.now() - startTime,
                    metadata: {
                        kind: 'sql',
                        toolName: '_dynamic_sql_upgrade',
                        type: 'dynamic_sql_upgrade',
                        objective,
                        upgradeAttempt: this.upgradeAttempts,
                        ...(toolContext.packageName && { packageName: toolContext.packageName }),
                        ...(toolContext.timeRange && { timeRange: toolContext.timeRange }),
                        ...(typeof result.repairAttempts === 'number' && { repairAttempts: result.repairAttempts }),
                        ...(Array.isArray(result.repairErrors) && result.repairErrors.length > 0 && { repairErrorsCount: result.repairErrors.length }),
                        sql: result.generatedSQL?.sql,
                        explanation: result.generatedSQL?.explanation,
                    },
                };
            }
            // Convert query result to findings if we got data
            const findings = [];
            if (result.queryResult?.rows && result.queryResult.rows.length > 0) {
                // Generate a finding summarizing the dynamic query results
                findings.push({
                    id: `dynamic_sql_${this.config.id}_${Date.now()}`,
                    category: 'dynamic_analysis',
                    type: 'dynamic_sql_result',
                    severity: 'info',
                    title: `动态查询结果 (${result.queryResult.rows.length} 行)`,
                    description: result.generatedSQL?.explanation || '通过动态 SQL 获取的分析数据',
                    source: this.config.id,
                    confidence: 0.7, // Lower confidence for dynamic results
                    details: {
                        sql: result.generatedSQL?.sql,
                        rowCount: result.queryResult.rows.length,
                        columns: result.queryResult.columns,
                        // Include first few rows as preview
                        sampleData: result.queryResult.rows.slice(0, 5),
                        objective,
                        upgradeAttempt: this.upgradeAttempts,
                    },
                });
            }
            return {
                success: true,
                data: result.queryResult,
                findings,
                executionTimeMs: Date.now() - startTime,
                metadata: {
                    kind: 'sql',
                    toolName: '_dynamic_sql_upgrade',
                    type: 'dynamic_sql_upgrade',
                    objective,
                    upgradeAttempt: this.upgradeAttempts,
                    ...(toolContext.packageName && { packageName: toolContext.packageName }),
                    ...(toolContext.timeRange && { timeRange: toolContext.timeRange }),
                    ...(typeof result.repairAttempts === 'number' && { repairAttempts: result.repairAttempts }),
                    ...(Array.isArray(result.repairErrors) && result.repairErrors.length > 0 && { repairErrorsCount: result.repairErrors.length }),
                    sql: result.generatedSQL?.sql,
                    explanation: result.generatedSQL?.explanation,
                },
            };
        }
        catch (error) {
            console.error(`[${this.config.id}] Dynamic SQL upgrade failed:`, error.message);
            return {
                success: false,
                error: `Dynamic SQL upgrade failed: ${error.message}`,
                executionTimeMs: Date.now() - startTime,
                metadata: {
                    kind: 'sql',
                    toolName: '_dynamic_sql_upgrade',
                    type: 'dynamic_sql_upgrade',
                    objective,
                    upgradeAttempt: this.upgradeAttempts,
                    ...(toolContext.packageName && { packageName: toolContext.packageName }),
                    ...(toolContext.timeRange && { timeRange: toolContext.timeRange }),
                    error: String(error?.message || error),
                },
            };
        }
    }
    /**
     * Reset upgrade attempts counter. Called at the start of each task.
     */
    resetUpgradeState() {
        this.upgradeAttempts = 0;
    }
    /**
     * Configure the upgrade path behavior.
     * Subclasses can call this to customize upgrade behavior.
     */
    configureUpgrade(config) {
        this.upgradeConfig = { ...this.upgradeConfig, ...config };
    }
    /**
     * Step 4: Reflect on results
     */
    async reflect(result, task) {
        const prompt = this.buildReflectionPrompt(result, task);
        const response = await this.modelRouter.callWithFallback(prompt, 'evaluation', {
            sessionId: this.sharedContext?.sessionId,
            traceId: this.sharedContext?.traceId,
            jsonMode: true,
            promptId: `agent.${this.config.id}.reflect`,
            promptVersion: '1.0.0',
            contractVersion: REFLECTION_JSON_SCHEMA.name,
        });
        try {
            const parsed = (0, llmJson_1.parseLlmJson)(response.response, REFLECTION_JSON_SCHEMA);
            return {
                insights: parsed.insights || [],
                objectivesMet: parsed.objectivesMet ?? result.success,
                findingsConfidence: typeof parsed.findingsConfidence === 'number' ? parsed.findingsConfidence : 0.5,
                gaps: parsed.gaps || [],
                nextSteps: parsed.nextSteps || [],
                hypothesisUpdates: parsed.hypothesisUpdates || [],
                questionsForOthers: parsed.questionsForOthers || [],
            };
        }
        catch (error) {
            console.warn(`[${this.config.id}] Failed to parse reflection response`);
        }
        // Fallback reflection
        return {
            insights: result.steps.flatMap(s => s.observations),
            objectivesMet: result.success,
            findingsConfidence: result.success ? 0.6 : 0.3,
            gaps: [],
            nextSteps: [],
            hypothesisUpdates: [],
            questionsForOthers: [],
        };
    }
    /**
     * Step 5: Generate final response
     */
    async respond(reflection, result, task, startTime) {
        // Generate hypotheses based on findings
        const hypotheses = await this.generateHypotheses(result.findings, task);
        // Convert hypotheses to updates
        const hypothesisUpdates = hypotheses.map(h => ({
            hypothesisId: h.id,
            action: 'support',
            newConfidence: h.confidence,
            reason: `Generated from ${this.config.id} analysis`,
        }));
        // Add reflection hypothesis updates
        hypothesisUpdates.push(...reflection.hypothesisUpdates);
        const questionsForAgents = (reflection.questionsForOthers || [])
            .map((q) => {
            // LLMs sometimes return strings or partial objects; normalize to InterAgentQuestion
            if (typeof q === 'string') {
                return {
                    fromAgent: this.config.id,
                    toAgent: 'system_admin',
                    question: q,
                    priority: 5,
                };
            }
            if (!q || typeof q !== 'object')
                return null;
            const question = typeof q.question === 'string' ? q.question : undefined;
            if (!question)
                return null;
            const toAgent = typeof q.toAgent === 'string' ? q.toAgent : 'system_admin';
            const priority = typeof q.priority === 'number' ? q.priority : 5;
            const context = q.context && typeof q.context === 'object' ? q.context : undefined;
            return {
                fromAgent: this.config.id,
                toAgent,
                question,
                context,
                priority,
            };
        })
            .filter(Boolean);
        return {
            agentId: this.config.id,
            taskId: task.id,
            success: result.success,
            findings: result.findings,
            hypothesisUpdates,
            questionsForAgents,
            suggestions: reflection.nextSteps,
            confidence: reflection.findingsConfidence,
            executionTimeMs: Date.now() - startTime,
            toolResults: result.steps.map(s => s.result),
            reasoning: this.reasoningTrace,
        };
    }
    // ==========================================================================
    // Lazy Tool Loading
    // ==========================================================================
    /**
     * Ensure skill-based tools are loaded.
     * Called at the start of executeTask() — by this time the skill registry
     * is guaranteed to be initialized (the orchestrator awaits it before dispatching).
     */
    async ensureToolsLoaded() {
        if (this.toolsLoaded)
            return;
        if (this.skillDefinitions.length === 0) {
            this.toolsLoaded = true;
            return;
        }
        await (0, skillEngine_1.ensureSkillRegistryInitialized)();
        const missingRequiredSkills = [];
        for (const skillDef of this.skillDefinitions) {
            const toolName = skillDef.toolName || skillDef.skillId;
            if (this.tools.has(toolName))
                continue;
            const skill = skillEngine_1.skillRegistry.getSkill(skillDef.skillId);
            if (!skill) {
                if (skillDef.required === false) {
                    console.warn(`[${this.config.id}] Optional skill not found: ${skillDef.skillId}`);
                    continue;
                }
                missingRequiredSkills.push(skillDef.skillId);
                continue;
            }
            const tool = {
                name: toolName,
                description: this.resolveSkillToolDescription(skillDef, skill),
                skillId: skillDef.skillId,
                category: skillDef.category,
                parameters: skill.inputs?.map((input) => ({
                    name: input.name,
                    type: input.type,
                    required: input.required,
                    description: input.description || input.name,
                    default: input.default,
                })),
                execute: this.createSkillToolExecutor(toolName, skillDef.skillId, skillDef.category),
            };
            this.tools.set(tool.name, tool);
        }
        if (missingRequiredSkills.length > 0) {
            throw new Error(`[${this.config.id}] Required skills missing: ${missingRequiredSkills.join(', ')}`);
        }
        this.toolsLoaded = true;
        if (this.tools.size > 0) {
            console.log(`[${this.config.id}] Loaded ${this.tools.size} tools from skills`);
        }
    }
    resolveSkillToolDescription(skillDef, skill) {
        const base = (skillDef.description && skillDef.description.trim())
            || (skill?.meta?.description && String(skill.meta.description).trim())
            || (skill?.meta?.display_name && String(skill.meta.display_name).trim())
            || skillDef.skillId;
        const hint = skillDef.descriptionHint && skillDef.descriptionHint.trim();
        return hint ? `${base}。${hint}` : base;
    }
    /**
     * Create a tool executor function for a given skill ID.
     * Shared across all domain agents — eliminates code duplication.
     */
    createSkillToolExecutor(toolName, skillId, category) {
        return async (params, context) => {
            const startTime = Date.now();
            const additional = (context.additionalContext || {});
            const scopeLabel = typeof additional.scopeLabel === 'string' ? additional.scopeLabel.trim() : '';
            const baseMetadata = {
                kind: 'skill',
                toolName,
                skillId,
                category,
                executionMode: 'agent',
                ...(context.packageName && { packageName: context.packageName }),
                ...(context.timeRange && { timeRange: context.timeRange }),
                ...(scopeLabel && { scopeLabel }),
            };
            try {
                if (!context.traceProcessorService) {
                    return {
                        success: false,
                        error: 'TraceProcessorService not available',
                        executionTimeMs: Date.now() - startTime,
                        metadata: baseMetadata,
                    };
                }
                // SkillExecutor instances have their own registry; ensure skills are registered
                // before executing (otherwise every tool call will fail with "Skill not found").
                const executor = await this.getOrCreateSkillExecutor(context.traceProcessorService, context.aiService);
                const execParams = { ...params };
                // Normalize package/process parameters:
                // - Prefer explicit context.packageName when present
                // - Avoid overwriting user-provided params with undefined
                if (typeof context.packageName === 'string' && context.packageName.trim()) {
                    execParams.package = context.packageName.trim();
                }
                // Backwards-compatible aliases used by some legacy skills.
                const pkg = typeof execParams.package === 'string' ? execParams.package.trim() : '';
                if (pkg) {
                    if (execParams.package_name === undefined)
                        execParams.package_name = pkg;
                    if (execParams.process_name === undefined)
                        execParams.process_name = pkg;
                }
                if (context.timeRange) {
                    // Pass timestamps as strings to preserve precision for values > Number.MAX_SAFE_INTEGER.
                    // SQL template substitution (${start_ts}) handles string values correctly.
                    execParams.start_ts = String(context.timeRange.start);
                    execParams.end_ts = String(context.timeRange.end);
                    // Legacy GPU/other skills use seconds-based range fields.
                    // Provide them as integer seconds (string) to keep SQL template substitution simple.
                    try {
                        const startNs = BigInt(execParams.start_ts);
                        const endNs = BigInt(execParams.end_ts);
                        const startSec = (startNs / 1000000000n).toString();
                        const endSec = (endNs / 1000000000n).toString();
                        if (execParams.time_range_start === undefined)
                            execParams.time_range_start = startSec;
                        if (execParams.time_range_end === undefined)
                            execParams.time_range_end = endSec;
                    }
                    catch {
                        // Best-effort only; keep start_ts/end_ts as the source of truth.
                    }
                }
                // Generic skill params: orchestrators pass extra skill parameters via additionalContext.skillParams.
                // This allows controlling skill behavior without the baseAgent needing to know specific skill IDs.
                if (additional.skillParams && typeof additional.skillParams === 'object') {
                    for (const [key, val] of Object.entries(additional.skillParams)) {
                        if (val !== undefined && val !== null) {
                            execParams[key] = val;
                        }
                    }
                }
                const result = await executor.execute(skillId, context.traceId, execParams, {});
                const findings = this.extractFindingsFromResult(result, skillId, category);
                const data = this.extractDataFromResult(result);
                // Derive group: if a task has both a timeRange and a scopeLabel, it's part of a
                // multi-interval analysis and should be grouped by its interval.
                // This is generic — works for scrolling intervals, startup phases, ANR windows, etc.
                const derivedGroup = (typeof additional.group === 'string' && additional.group) // explicit override
                    ? additional.group
                    : (context.timeRange && scopeLabel) // auto-derive from timeRange
                        ? `interval_${String(context.timeRange.start)}`
                        : undefined;
                const dataEnvelopes = skillEngine_1.SkillExecutor.toDataEnvelopes(result, undefined, {
                    traceId: context.traceId,
                    traceSide: 'current',
                }).map((env) => {
                    const executionId = typeof additional.executionId === 'string' ? additional.executionId :
                        typeof additional.taskId === 'string' ? additional.taskId :
                            `${skillId}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
                    // Make envelope source unique per execution so frontend can render repeated analyses
                    // (e.g., per-interval deep dives) without deduping them away.
                    env.meta.source = `${env.meta.source}#${executionId}`;
                    if (scopeLabel) {
                        env.display.title = `${scopeLabel} · ${env.display.title}`;
                    }
                    // Assign group for frontend interval grouping
                    if (derivedGroup) {
                        env.display.group = derivedGroup;
                    }
                    // Grouped results are collapsible; collapse empty tables by default
                    if (env.display.group) {
                        env.display.collapsible = true;
                        const payload = env.data;
                        const rows = payload?.rows;
                        if (!rows || !Array.isArray(rows) || rows.length === 0) {
                            env.display.defaultCollapsed = true;
                        }
                    }
                    return env;
                });
                console.log(`[${this.config.id}] Skill ${skillId} executed: success=${result.success}, displayResults=${result.displayResults?.length || 0}, dataEnvelopes=${dataEnvelopes.length}`);
                return {
                    success: result.success,
                    data,
                    findings,
                    dataEnvelopes: dataEnvelopes.length > 0 ? dataEnvelopes : undefined,
                    error: result.error,
                    executionTimeMs: Date.now() - startTime,
                    metadata: {
                        ...baseMetadata,
                        // Keep params compact; this is for provenance/debugging, not full replay.
                        paramsKeys: Object.keys(execParams).slice(0, 40),
                        ...(derivedGroup && { group: derivedGroup }),
                        ...(result.identityResolution && { identityResolution: result.identityResolution }),
                    },
                };
            }
            catch (error) {
                return {
                    success: false,
                    error: error.message,
                    executionTimeMs: Date.now() - startTime,
                    metadata: baseMetadata,
                };
            }
        };
    }
    async getOrCreateSkillExecutor(traceProcessorService, aiService) {
        await (0, skillEngine_1.ensureSkillRegistryInitialized)();
        if (this.skillExecutorCache &&
            this.skillExecutorCache.traceProcessorService === traceProcessorService &&
            this.skillExecutorCache.aiService === aiService) {
            return this.skillExecutorCache.executor;
        }
        const executor = (0, skillEngine_1.createSkillExecutor)(traceProcessorService, aiService);
        executor.registerSkills(skillEngine_1.skillRegistry.getAllSkills());
        executor.setFragmentRegistry(skillEngine_1.skillRegistry.getFragmentCache());
        this.skillExecutorCache = {
            traceProcessorService,
            aiService,
            executor,
        };
        return executor;
    }
    /**
     * Extract findings from skill execution result.
     * Subclasses can override for domain-specific extraction logic.
     */
    extractFindingsFromResult(result, skillId, category) {
        const findings = [];
        if (result.diagnostics && result.diagnostics.length > 0) {
            for (const diag of result.diagnostics) {
                findings.push({
                    id: `${skillId}_${Date.now()}_${findings.length}`,
                    category,
                    severity: diag.severity,
                    title: diag.diagnosis,
                    description: diag.suggestions?.join('; ') || diag.diagnosis,
                    source: skillId,
                    confidence: typeof diag.confidence === 'number' ? diag.confidence : 0.8,
                    details: diag.evidence,
                });
            }
        }
        // Deterministic insight finding from synthesize summary (洞见摘要) if available.
        // This improves "agent-ness" by turning structured skill outputs into citeable findings,
        // reducing reliance on ai_summary.
        const synthFinding = this.extractSynthesizeSummaryFinding(result, skillId, category);
        if (synthFinding) {
            findings.push(synthFinding);
        }
        return findings;
    }
    extractSynthesizeSummaryFinding(result, skillId, category) {
        const displayResults = Array.isArray(result.displayResults) ? result.displayResults : [];
        if (displayResults.length === 0)
            return null;
        const dr = displayResults.find(d => d &&
            typeof d === 'object' &&
            d.format === 'summary' &&
            d?.data &&
            d.data.summary &&
            typeof d.data.summary.content === 'string');
        if (!dr)
            return null;
        const summary = dr.data.summary;
        const content = String(summary.content || '').trim();
        if (!content)
            return null;
        const metrics = Array.isArray(summary.metrics) ? summary.metrics : [];
        const severities = new Set(metrics.map((m) => m?.severity).filter(Boolean));
        const severity = severities.has('critical') ? 'critical'
            : severities.has('warning') ? 'warning'
                : 'info';
        const title = `洞见摘要 · ${result.skillName || skillId}`;
        const MAX_DESC = 600;
        const description = content.length > MAX_DESC ? content.slice(0, MAX_DESC - 1) + '…' : content;
        return {
            id: `${skillId}_synth_${Date.now()}`,
            category,
            severity,
            title,
            description,
            source: skillId,
            confidence: 0.75,
            details: metrics.length > 0 ? { metrics: metrics.slice(0, 12) } : undefined,
        };
    }
    /**
     * Extract data from skill execution result
     */
    extractDataFromResult(result) {
        if (result.displayResults && result.displayResults.length > 0) {
            const data = {};
            for (const dr of result.displayResults) {
                data[dr.stepId] = dr.data;
            }
            return data;
        }
        if (result.rawResults) {
            const data = {};
            for (const [stepId, stepResult] of Object.entries(result.rawResults)) {
                data[stepId] = stepResult.data;
            }
            return data;
        }
        return null;
    }
    // ==========================================================================
    // Helper Methods
    // ==========================================================================
    /**
     * Add a step to the reasoning trace
     */
    addReasoningStep(type, content, confidence = 0.8) {
        this.reasoningTrace.push({
            step: this.reasoningTrace.length + 1,
            type,
            content,
            confidence,
            timestamp: Date.now(),
        });
    }
    /**
     * Generate observations from a tool result
     */
    generateObservations(result) {
        const observations = [];
        if (!result.success) {
            observations.push(`Tool execution failed: ${result.error}`);
            return observations;
        }
        if (result.findings && result.findings.length > 0) {
            observations.push(`Found ${result.findings.length} issues`);
            const critical = result.findings.filter(f => f.severity === 'critical').length;
            if (critical > 0) {
                observations.push(`${critical} critical issues identified`);
            }
        }
        if (result.data) {
            if (typeof result.data === 'object') {
                const keys = Object.keys(result.data);
                if (keys.length > 0) {
                    observations.push(`Returned data with ${keys.length} fields`);
                }
            }
        }
        return observations;
    }
    /**
     * Create a hypothesis
     */
    createHypothesis(description, confidence, supportingEvidence = []) {
        return {
            id: (0, agentProtocol_1.createHypothesisId)(),
            description,
            confidence,
            status: 'proposed',
            supportingEvidence,
            contradictingEvidence: [],
            proposedBy: this.config.id,
            createdAt: Date.now(),
            updatedAt: Date.now(),
        };
    }
    /**
     * Get tool by name
     */
    getTool(name) {
        return this.tools.get(name);
    }
    /**
     * Get all tools
     */
    getAllTools() {
        return Array.from(this.tools.values());
    }
    /**
     * Get tool descriptions for LLM, including parameter info.
     * This helps the LLM understand that tools are self-contained (with built-in SQL)
     * and only need simple parameters to execute.
     */
    getToolDescriptionsForLLM() {
        if (this.tools.size > 0) {
            return this.getAllTools()
                .map(t => {
                let desc = `- ${t.name}: ${t.description}`;
                if (t.parameters && t.parameters.length > 0) {
                    const paramList = t.parameters
                        .map(p => `${p.name}(${p.type}${p.required ? ',必填' : ''})`)
                        .join(', ');
                    desc += `\n  参数: [${paramList}]`;
                }
                else {
                    desc += `\n  参数: 无（自动从上下文获取 package/start_ts/end_ts）`;
                }
                return desc;
            })
                .join('\n');
        }
        // Fallback: use skill definitions even before tools are loaded
        if (this.skillDefinitions.length > 0) {
            return this.skillDefinitions
                .map(s => `- ${s.toolName}: ${s.description || s.descriptionHint || s.skillId}`)
                .join('\n');
        }
        return '（无可用工具）';
    }
    /**
     * Get the full "available tools" section for LLM prompts.
     * Includes tool list, parameter info, usage clarification, and negative examples.
     * Use this in buildUnderstandingPrompt / buildPlanningPrompt instead of raw getToolDescriptionsForLLM().
     */
    getToolSectionForPrompt() {
        return `## 可用工具（只能使用以下工具）
${this.getToolDescriptionsForLLM()}

## 工具使用规则

### ✅ 正确做法
- 只使用上方列表中的工具名称
- params 留空 {} 使用默认行为（package/start_ts/end_ts 自动注入）
- 如需指定可选参数，只填写工具文档中列出的参数

### ❌ 禁止行为（会导致执行失败）
- 使用未列出的工具名（如 analyze_gpu、check_memory 等不存在的工具）
- 在 params 中写 SQL 查询（工具已内置 SQL）
- 在 params 中填写 package/start_ts/end_ts（这些会自动注入）
- 猜测工具名称或参数名称
- 使用自然语言描述代替工具调用

### ADB 工具特殊说明
- adb_* 工具用于通过 ADB 获取设备信息/执行操作
- 默认只读模式，除非 mode=full 否则不要尝试改变设备状态
- 使用前先调用 adb_status 确认 enabled/selectedSerial`;
    }
    /**
     * Format common task context fields for prompts
     */
    formatTaskContext(task) {
        const lines = [];
        if (task.context.domain) {
            lines.push(`- 任务领域: ${task.context.domain}`);
        }
        const adbContext = task.context.additionalData?.adbContext;
        if (adbContext && typeof adbContext === 'object') {
            const mode = adbContext.mode ?? 'auto';
            const enabled = adbContext.enabled ? 'true' : 'false';
            const selected = adbContext?.availability?.selectedSerial || 'none';
            const matchStatus = adbContext?.traceMatch?.status || 'unknown';
            const matchConfidence = typeof adbContext?.traceMatch?.confidence === 'number'
                ? adbContext.traceMatch.confidence.toFixed(2)
                : 'n/a';
            lines.push(`- ADB 协同: mode=${mode}, enabled=${enabled}, serial=${selected}, match=${matchStatus}(${matchConfidence})`);
        }
        if (task.context.timeRange) {
            lines.push(`- 时间范围: ${task.context.timeRange.start} ~ ${task.context.timeRange.end}`);
        }
        if (task.context.evidenceNeeded && task.context.evidenceNeeded.length > 0) {
            lines.push(`- 需输出证据: ${task.context.evidenceNeeded.join(', ')}`);
        }
        const historyContext = task.context.additionalData?.historyContext;
        if (typeof historyContext === 'string' && historyContext.trim()) {
            const trimmed = historyContext.trim();
            const MAX_CHARS = 2200; // prompt budget protection
            lines.push('');
            lines.push('对话上下文摘要（同一 trace，避免重复与遗忘）:');
            lines.push(trimmed.length > MAX_CHARS ? trimmed.slice(0, MAX_CHARS) + '…' : trimmed);
        }
        return lines.length > 0 ? lines.join('\n') : '';
    }
    /**
     * Set shared context
     */
    setSharedContext(context) {
        this.sharedContext = context;
    }
    /**
     * Get or create SQL generator instance.
     */
    async getSQLGenerator() {
        if (!this.sqlGenerator) {
            const { SQLGenerator } = await Promise.resolve().then(() => __importStar(require('../../tools/sqlGenerator')));
            this.sqlGenerator = new SQLGenerator(this.modelRouter);
        }
        return this.sqlGenerator;
    }
    /**
     * Get or create SQL validator instance.
     */
    async getSQLValidator() {
        if (!this.sqlValidator) {
            const { SQLValidator } = await Promise.resolve().then(() => __importStar(require('../../tools/sqlValidator')));
            this.sqlValidator = new SQLValidator();
        }
        return this.sqlValidator;
    }
    /**
     * Generate and execute a dynamic SQL query.
     *
     * This gives agents true autonomy to explore data beyond predefined Skills.
     * The method:
     * 1. Generates SQL using LLM based on the objective
     * 2. Validates the SQL for safety
     * 3. Executes the query
     * 4. Returns results with metadata
     *
     * @param objective - What the agent wants to analyze
     * @param context - Tool context with trace processor service
     * @param schemaContext - Optional schema context (auto-detected if not provided)
     * @returns Query result with data and metadata
     */
    async generateAndExecuteSQL(objective, context, schemaContext) {
        const startTime = Date.now();
        if (!context.traceProcessorService) {
            return {
                success: false,
                error: 'TraceProcessorService not available',
                executionTimeMs: Date.now() - startTime,
            };
        }
        try {
            const generator = await this.getSQLGenerator();
            const validator = await this.getSQLValidator();
            // Get schema context if not provided
            let schema = schemaContext;
            if (!schema) {
                const { detectSchema } = await Promise.resolve().then(() => __importStar(require('../../tools/sqlGenerator')));
                schema = await detectSchema(context.traceProcessorService, context.traceId);
            }
            // Generate SQL
            const genResult = await generator.generateSQL(objective, schema);
            if (!genResult.success || !genResult.sql) {
                return {
                    success: false,
                    error: genResult.error || 'SQL generation failed',
                    validationErrors: genResult.validationErrors,
                    executionTimeMs: Date.now() - startTime,
                };
            }
            const MAX_REPAIR_ATTEMPTS = 2;
            let repairAttempts = 0;
            const repairErrors = [];
            const attemptedSql = new Set();
            let currentGeneratedSQL = genResult.sql;
            let sqlToExecute = validator.ensureLimit(currentGeneratedSQL.sql, 1000);
            attemptedSql.add(sqlToExecute);
            while (true) {
                // Validate SQL (safety gate)
                const validation = validator.validate(sqlToExecute);
                if (!validation.valid) {
                    this.emit('sql_validation_failed', {
                        agentId: this.config.id,
                        sql: sqlToExecute,
                        errors: validation.errors.map(e => e.message),
                    });
                    const errMsg = `SQL validation failed: ${validation.errors.map(e => e.message).join(', ')}`;
                    repairErrors.push(errMsg);
                    if (repairAttempts >= MAX_REPAIR_ATTEMPTS) {
                        return {
                            success: false,
                            error: errMsg,
                            validationErrors: validation.errors.map(e => e.message),
                            generatedSQL: currentGeneratedSQL,
                            repairAttempts,
                            repairErrors,
                            executionTimeMs: Date.now() - startTime,
                        };
                    }
                    repairAttempts++;
                    const repaired = await generator.repairSQL({
                        objective,
                        schemaContext: schema,
                        previousSQL: sqlToExecute,
                        error: errMsg,
                    });
                    if (!repaired.success || !repaired.sql) {
                        return {
                            success: false,
                            error: repaired.error || errMsg,
                            validationErrors: repaired.validationErrors || validation.errors.map(e => e.message),
                            generatedSQL: currentGeneratedSQL,
                            repairAttempts,
                            repairErrors,
                            executionTimeMs: Date.now() - startTime,
                        };
                    }
                    currentGeneratedSQL = repaired.sql;
                    sqlToExecute = validator.ensureLimit(currentGeneratedSQL.sql, 1000);
                    if (attemptedSql.has(sqlToExecute)) {
                        return {
                            success: false,
                            error: `SQL repair loop produced duplicate SQL (stopping)`,
                            validationErrors: [errMsg],
                            generatedSQL: currentGeneratedSQL,
                            repairAttempts,
                            repairErrors,
                            executionTimeMs: Date.now() - startTime,
                        };
                    }
                    attemptedSql.add(sqlToExecute);
                    continue;
                }
                const { sql: executableSql, injected } = (0, sqlIncludeInjector_1.injectStdlibIncludes)(sqlToExecute);
                // Log for debugging
                console.log(`[${this.config.id}] Executing dynamic SQL for: ${objective}`);
                console.log(`[${this.config.id}] SQL: ${executableSql.slice(0, 200)}...`);
                // Emit event for tracking
                this.emit('sql_generated', {
                    agentId: this.config.id,
                    objective,
                    sql: executableSql,
                    riskLevel: currentGeneratedSQL.riskLevel,
                    stdlibInjectedModules: injected,
                    repairAttempts,
                });
                try {
                    // Execute SQL
                    const queryResult = await context.traceProcessorService.query(context.traceId, executableSql);
                    const executionError = queryResult && typeof queryResult.error === 'string'
                        ? queryResult.error.trim()
                        : '';
                    if (executionError) {
                        throw new Error(executionError);
                    }
                    const executionTimeMs = Date.now() - startTime;
                    // Process results
                    const data = this.processSQLResult(queryResult);
                    return {
                        success: true,
                        data,
                        generatedSQL: currentGeneratedSQL,
                        queryResult,
                        validation,
                        repairAttempts,
                        repairErrors: repairErrors.length > 0 ? repairErrors : undefined,
                        executionTimeMs,
                    };
                }
                catch (error) {
                    const errMsg = String(error?.message || error);
                    repairErrors.push(errMsg);
                    if (repairAttempts >= MAX_REPAIR_ATTEMPTS) {
                        return {
                            success: false,
                            error: errMsg,
                            generatedSQL: currentGeneratedSQL,
                            repairAttempts,
                            repairErrors,
                            executionTimeMs: Date.now() - startTime,
                        };
                    }
                    repairAttempts++;
                    const repaired = await generator.repairSQL({
                        objective,
                        schemaContext: schema,
                        previousSQL: sqlToExecute,
                        error: errMsg,
                    });
                    if (!repaired.success || !repaired.sql) {
                        return {
                            success: false,
                            error: repaired.error || errMsg,
                            validationErrors: repaired.validationErrors,
                            generatedSQL: currentGeneratedSQL,
                            repairAttempts,
                            repairErrors,
                            executionTimeMs: Date.now() - startTime,
                        };
                    }
                    currentGeneratedSQL = repaired.sql;
                    sqlToExecute = validator.ensureLimit(currentGeneratedSQL.sql, 1000);
                    if (attemptedSql.has(sqlToExecute)) {
                        return {
                            success: false,
                            error: `SQL repair loop produced duplicate SQL (stopping)`,
                            generatedSQL: currentGeneratedSQL,
                            repairAttempts,
                            repairErrors,
                            executionTimeMs: Date.now() - startTime,
                        };
                    }
                    attemptedSql.add(sqlToExecute);
                }
            }
        }
        catch (error) {
            console.warn(`[${this.config.id}] Dynamic SQL execution failed: ${error.message}`);
            return {
                success: false,
                error: error.message,
                executionTimeMs: Date.now() - startTime,
            };
        }
    }
    /**
     * Process raw SQL query result into structured data.
     */
    processSQLResult(queryResult) {
        if (!queryResult)
            return null;
        // Handle columnar format { columns, rows }
        if (queryResult.columns && queryResult.rows) {
            const columns = queryResult.columns;
            const rows = queryResult.rows;
            return rows.map(row => {
                const obj = {};
                for (let i = 0; i < columns.length; i++) {
                    obj[columns[i]] = row[i];
                }
                return obj;
            });
        }
        // Return as-is if already in object format
        return queryResult;
    }
    /**
     * Check if dynamic SQL generation is enabled for this agent.
     * Subclasses can override to control when SQL generation is allowed.
     */
    isDynamicSQLEnabled() {
        // By default, enabled for all agents
        // Can be controlled via config or environment variable
        return process.env.AGENT_DYNAMIC_SQL !== 'false';
    }
}
exports.BaseAgent = BaseAgent;
exports.default = BaseAgent;
//# sourceMappingURL=baseAgent.js.map