import { EventEmitter } from "events";
import { getEnhancedMemory } from "../memory/index.js";
import { getErrorMessage } from "../errors/index.js";
import { logger } from "../utils/logger.js";
// Import facades
import { AgentContextFacade } from "./facades/agent-context-facade.js";
import { SessionFacade } from "./facades/session-facade.js";
import { ModelRoutingFacade } from "./facades/model-routing-facade.js";
import { InfrastructureFacade } from "./facades/infrastructure-facade.js";
import { MessageHistoryManager } from "./facades/message-history-manager.js";
/**
 * Abstract base class for all agents in the CodeBuddy system.
 *
 * This class acts as a lightweight orchestrator that delegates to specialized facades:
 * - AgentContextFacade: Context and memory management
 * - SessionFacade: Session persistence and checkpoints
 * - ModelRoutingFacade: Model routing and cost tracking
 * - InfrastructureFacade: MCP, sandbox, hooks, plugins
 * - MessageHistoryManager: Chat history operations
 *
 * Concrete implementations (like `CodeBuddyAgent`) must implement:
 * - `processUserMessage`: For single-turn interactions
 * - `processUserMessageStream`: For streaming interactions
 * - `executeTool`: For handling tool calls
 */
export class BaseAgent extends EventEmitter {
    /**
     * Maximum number of chat history entries to keep in memory.
     * @deprecated Use MessageHistoryManager config instead
     */
    static MAX_HISTORY_SIZE = 1000;
    /**
     * Maximum number of LLM messages to keep in memory.
     * @deprecated Use MessageHistoryManager config instead
     */
    static MAX_MESSAGES_SIZE = 500;
    /** Threshold for considering a tool result "large" (in characters) */
    static LARGE_RESULT_THRESHOLD = 5000;
    // ============================================================================
    // Facades (initialized by subclasses via initializeFacades)
    // ============================================================================
    /** Context and memory management facade */
    contextFacade;
    /** Session and checkpoint management facade */
    sessionFacade;
    /** Model routing and cost management facade */
    routingFacade;
    /** Infrastructure services facade */
    infrastructureFacade;
    /** Message history manager */
    historyManager;
    // ============================================================================
    // Legacy Properties (for backward compatibility with subclasses)
    // ============================================================================
    /**
     * @deprecated Use historyManager.getChatHistoryRef() instead
     */
    get chatHistory() {
        return this.historyManager.getChatHistoryRef();
    }
    set chatHistory(value) {
        this.historyManager.setChatHistory(value);
    }
    /**
     * @deprecated Use historyManager.getMessagesRef() instead
     */
    get messages() {
        return this.historyManager.getMessagesRef();
    }
    set messages(value) {
        this.historyManager.setMessages(value);
    }
    /** Controller for aborting ongoing operations */
    abortController = null;
    /** Whether garbage collection has been explicitly triggered this session */
    gcTriggered = false;
    // Infrastructure managers (kept for backward compatibility with subclasses)
    tokenCounter;
    contextManager;
    checkpointManager;
    sessionStore;
    modeManager;
    sandboxManager;
    mcpClient;
    costTracker;
    promptCacheManager;
    hooksManager;
    modelRouter;
    marketplace;
    repairCoordinator;
    // Configuration & State
    /** Maximum number of tool rounds allowed per user request */
    maxToolRounds = 50;
    /** Whether "YOLO mode" (fully autonomous) is enabled */
    yoloMode = false;
    /** Whether to use RAG for selecting relevant tools */
    useRAGToolSelection = true;
    /** Whether to execute independent tool calls in parallel */
    parallelToolExecution = true;
    /** Cost limit for the current session in USD */
    sessionCostLimit = 10;
    /** Current accumulated cost of the session in USD */
    sessionCost = 0;
    /** Whether to use model routing optimization */
    useModelRouting = false;
    /** Result of the last model routing decision */
    lastRoutingDecision = null;
    /** Whether memory system is enabled */
    memoryEnabled = true;
    /** Memory system instance (lazy-loaded) */
    _memory = null;
    /**
     * Lazy-loaded memory system for cross-session context persistence
     */
    get memory() {
        if (!this._memory) {
            this._memory = getEnhancedMemory({
                enabled: this.memoryEnabled,
                embeddingEnabled: true,
                useSQLite: true,
                maxMemories: 10000,
                autoSummarize: true,
            });
            const cwd = process.cwd();
            if (cwd) {
                this._memory.setProjectContext(cwd).catch(err => {
                    logger.warn('Failed to set project context for memory', { error: getErrorMessage(err) });
                });
            }
        }
        return this._memory;
    }
    constructor() {
        super();
        // Initialize history manager with default config
        this.historyManager = new MessageHistoryManager({
            maxHistorySize: BaseAgent.MAX_HISTORY_SIZE,
            maxMessagesSize: BaseAgent.MAX_MESSAGES_SIZE,
        });
    }
    /**
     * Initialize all facades after managers are set up.
     * Should be called by subclass constructors after setting up managers.
     */
    initializeFacades() {
        // Context facade
        this.contextFacade = new AgentContextFacade({
            tokenCounter: this.tokenCounter,
            contextManager: this.contextManager,
            getSessionId: () => this.sessionStore?.getCurrentSessionId() ?? undefined,
        });
        // Session facade
        this.sessionFacade = new SessionFacade({
            checkpointManager: this.checkpointManager,
            sessionStore: this.sessionStore,
        });
        // Routing facade
        this.routingFacade = new ModelRoutingFacade({
            modelRouter: this.modelRouter,
            costTracker: this.costTracker,
        });
        // Infrastructure facade
        this.infrastructureFacade = new InfrastructureFacade({
            mcpClient: this.mcpClient,
            sandboxManager: this.sandboxManager,
            hooksManager: this.hooksManager,
            promptCacheManager: this.promptCacheManager,
            marketplace: this.marketplace,
        });
    }
    // ============================================================================
    // Core Methods
    // ============================================================================
    getChatHistory() {
        return this.historyManager.getChatHistory();
    }
    clearChat() {
        this.historyManager.clearAll(true);
        this.emit("chat:cleared");
    }
    abortCurrentOperation() {
        if (this.abortController) {
            this.abortController.abort();
            this.emit("operation:aborted");
        }
    }
    dispose() {
        // Dispose facades
        if (this.contextFacade)
            this.contextFacade.dispose();
        if (this.repairCoordinator)
            this.repairCoordinator.dispose();
        if (this.historyManager)
            this.historyManager.dispose();
        this.abortCurrentOperation();
        this.emit("disposed");
    }
    // ============================================================================
    // Mode Management (delegates to modeManager directly)
    // ============================================================================
    getMode() {
        return this.modeManager.getMode();
    }
    setMode(mode) {
        this.modeManager.setMode(mode);
        this.emit("mode:changed", mode);
    }
    getModeStatus() {
        return this.modeManager.formatModeStatus();
    }
    isToolAllowedInCurrentMode(toolName) {
        return this.modeManager.isToolAllowed(toolName);
    }
    // ============================================================================
    // Cost & Session State (delegates to routingFacade)
    // ============================================================================
    isYoloModeEnabled() {
        return this.yoloMode;
    }
    getSessionCost() {
        return this.routingFacade?.getSessionCost() ?? 0;
    }
    getSessionCostLimit() {
        return this.routingFacade?.getSessionCostLimit() ?? 10;
    }
    setSessionCostLimit(limit) {
        if (this.routingFacade) {
            this.routingFacade.setSessionCostLimit(limit);
        }
    }
    isSessionCostLimitReached() {
        return this.routingFacade?.isSessionCostLimitReached() ?? false;
    }
    // ============================================================================
    // Checkpoint Management (delegates to sessionFacade)
    // ============================================================================
    createCheckpoint(description) {
        this.sessionFacade.createCheckpoint(description);
    }
    rewindToLastCheckpoint() {
        return this.sessionFacade.rewindToLastCheckpoint();
    }
    getCheckpointList() {
        return this.sessionFacade.getCheckpointList();
    }
    getCheckpointManager() {
        return this.sessionFacade.getCheckpointManager();
    }
    // ============================================================================
    // Session Management (delegates to sessionFacade)
    // ============================================================================
    getSessionStore() {
        return this.sessionFacade.getSessionStore();
    }
    async saveCurrentSession() {
        await this.sessionFacade.saveCurrentSession(this.historyManager.getChatHistory());
    }
    async getSessionList() {
        return this.sessionFacade.getSessionList();
    }
    async exportCurrentSession(outputPath) {
        return this.sessionFacade.exportCurrentSession(outputPath);
    }
    // ============================================================================
    // Sandbox Management (delegates to infrastructureFacade)
    // ============================================================================
    getSandboxStatus() {
        return this.infrastructureFacade.getSandboxStatus();
    }
    validateCommand(command) {
        return this.infrastructureFacade.validateCommand(command);
    }
    // ============================================================================
    // MCP Management (delegates to infrastructureFacade)
    // ============================================================================
    async connectMCPServers() {
        await this.infrastructureFacade.connectMCPServers();
    }
    getMCPStatus() {
        return this.infrastructureFacade.getMCPStatus();
    }
    async getMCPTools() {
        return this.infrastructureFacade.getMCPTools();
    }
    getMCPClient() {
        return this.infrastructureFacade.getMCPClient();
    }
    initializeMCP() {
        this.infrastructureFacade.initializeMCP();
    }
    // ============================================================================
    // Context Management (delegates to contextFacade)
    // ============================================================================
    getContextStats() {
        return this.contextFacade.getStats(this.historyManager.getMessages());
    }
    formatContextStats() {
        return this.contextFacade.formatStats(this.historyManager.getMessages());
    }
    updateContextConfig(config) {
        this.contextFacade.updateConfig(config);
    }
    // ============================================================================
    // Prompt Cache (delegates to infrastructureFacade)
    // ============================================================================
    getPromptCacheManager() {
        return this.infrastructureFacade.getPromptCacheManager();
    }
    getPromptCacheStats() {
        return this.infrastructureFacade.getPromptCacheStats();
    }
    formatPromptCacheStats() {
        return this.infrastructureFacade.formatPromptCacheStats();
    }
    // ============================================================================
    // Lifecycle Hooks (delegates to infrastructureFacade)
    // ============================================================================
    getHooksManager() {
        return this.infrastructureFacade.getHooksManager();
    }
    getHooksStatus() {
        return this.infrastructureFacade.getHooksStatus();
    }
    // ============================================================================
    // Model Routing (delegates to routingFacade)
    // ============================================================================
    setModelRouting(enabled) {
        this.routingFacade.setModelRouting(enabled);
    }
    isModelRoutingEnabled() {
        return this.routingFacade.isModelRoutingEnabled();
    }
    getModelRouter() {
        return this.routingFacade.getModelRouter();
    }
    getLastRoutingDecision() {
        return this.routingFacade.getLastRoutingDecision();
    }
    getModelRoutingStats() {
        return this.routingFacade.getStats();
    }
    formatModelRoutingStats() {
        return this.routingFacade.formatStats();
    }
    // ============================================================================
    // Memory System (delegates to contextFacade)
    // ============================================================================
    async remember(type, content, options = {}) {
        return this.contextFacade.remember(type, content, options);
    }
    async recall(query, options = {}) {
        return this.contextFacade.recall(query, options);
    }
    async getMemoryContext(query) {
        return this.contextFacade.getMemoryContext(query);
    }
    async storeConversationSummary(summary, topics, decisions) {
        await this.contextFacade.storeConversationSummary(summary, topics, decisions, this.historyManager.getMessages().length);
    }
    setMemoryEnabled(enabled) {
        this.contextFacade.setMemoryEnabled(enabled);
    }
    isMemoryEnabled() {
        return this.contextFacade.isMemoryEnabled();
    }
    getMemoryStats() {
        return this.contextFacade.getMemoryStats();
    }
    formatMemoryStatus() {
        return this.contextFacade.formatMemoryStatus();
    }
    // ============================================================================
    // Utility (delegates to historyManager)
    // ============================================================================
    /**
     * @deprecated Trimming is now automatic via MessageHistoryManager
     */
    trimHistory(_maxHistorySize, _maxMessagesSize) {
        // No-op: MessageHistoryManager handles trimming automatically
        // This method is kept for backward compatibility
    }
    /**
     * Get current history statistics for debugging
     */
    getHistoryStats() {
        return this.historyManager.getStats();
    }
    /**
     * Get comprehensive memory metrics for monitoring and debugging.
     * Includes history sizes, context manager metrics, and estimated memory usage.
     */
    getAgentMemoryMetrics() {
        const stats = this.historyManager.getStats();
        let estimatedBytes = 0;
        // Estimate chat history size
        for (const entry of this.historyManager.getChatHistoryRef()) {
            estimatedBytes += (entry.content?.length || 0) * 2; // UTF-16 chars
            estimatedBytes += (entry.toolResult?.output?.length || 0) * 2;
            estimatedBytes += 100; // Object overhead
        }
        // Estimate messages size
        for (const msg of this.historyManager.getMessagesRef()) {
            if (typeof msg.content === 'string') {
                estimatedBytes += msg.content.length * 2;
            }
            estimatedBytes += 50; // Object overhead
        }
        // Get context manager metrics if available
        let contextMetrics;
        if (this.contextManager && typeof this.contextManager.getMemoryMetrics === 'function') {
            contextMetrics = this.contextManager.getMemoryMetrics();
        }
        return {
            chatHistorySize: stats.chatHistorySize,
            messagesSize: stats.messagesSize,
            maxHistory: stats.maxHistory,
            maxMessages: stats.maxMessages,
            contextMetrics,
            estimatedMemoryBytes: estimatedBytes,
            cachedToolResults: 0, // Managed by historyManager now
            gcTriggered: this.gcTriggered,
        };
    }
    /**
     * Format memory metrics as a human-readable string.
     * Useful for /memory or /debug commands.
     */
    formatMemoryMetrics() {
        const metrics = this.getAgentMemoryMetrics();
        const memoryMB = (metrics.estimatedMemoryBytes / (1024 * 1024)).toFixed(2);
        const lines = [
            '=== Agent Memory Metrics ===',
            '',
            'History:',
            `  Chat entries: ${metrics.chatHistorySize}/${metrics.maxHistory}`,
            `  LLM messages: ${metrics.messagesSize}/${metrics.maxMessages}`,
            `  Estimated size: ${memoryMB} MB`,
            `  GC triggered: ${metrics.gcTriggered ? 'Yes' : 'No'}`,
        ];
        if (metrics.contextMetrics) {
            lines.push('');
            lines.push('Context Manager:');
            lines.push(`  Summaries: ${metrics.contextMetrics.summaryCount}`);
            lines.push(`  Summary tokens: ${metrics.contextMetrics.summaryTokens.toLocaleString()}`);
            lines.push(`  Peak messages: ${metrics.contextMetrics.peakMessageCount}`);
            lines.push(`  Compressions: ${metrics.contextMetrics.compressionCount}`);
            lines.push(`  Tokens saved: ${metrics.contextMetrics.totalTokensSaved.toLocaleString()}`);
        }
        return lines.join('\n');
    }
    /**
     * Force garbage collection and cleanup of agent memory.
     * Should be called during long sessions or when memory pressure is detected.
     *
     * @returns Object with cleanup statistics
     */
    forceMemoryCleanup() {
        const beforeStats = this.historyManager.getStats();
        // More aggressive trimming - keep only 50% of max
        const aggressiveHistoryLimit = Math.floor(BaseAgent.MAX_HISTORY_SIZE / 2);
        const aggressiveMessagesLimit = Math.floor(BaseAgent.MAX_MESSAGES_SIZE / 2);
        // Temporarily create a new history manager with aggressive limits
        const aggressiveManager = new MessageHistoryManager({
            maxHistorySize: aggressiveHistoryLimit,
            maxMessagesSize: aggressiveMessagesLimit,
        });
        // Transfer data (will auto-trim)
        const currentHistory = this.historyManager.getChatHistory();
        const currentMessages = this.historyManager.getMessages();
        aggressiveManager.setChatHistory(currentHistory);
        aggressiveManager.setMessages(currentMessages);
        // Copy back trimmed data
        this.historyManager.setChatHistory(aggressiveManager.getChatHistory());
        this.historyManager.setMessages(aggressiveManager.getMessages());
        aggressiveManager.dispose();
        // Force context manager cleanup if available
        let contextCleanup;
        if (this.contextManager && typeof this.contextManager.forceCleanup === 'function') {
            contextCleanup = this.contextManager.forceCleanup();
        }
        this.gcTriggered = true;
        const afterStats = this.historyManager.getStats();
        const result = {
            entriesTrimmed: beforeStats.chatHistorySize - afterStats.chatHistorySize,
            messagesTrimmed: beforeStats.messagesSize - afterStats.messagesSize,
            contextCleanup,
        };
        logger.info('Forced memory cleanup', result);
        this.emit('memory:cleanup', result);
        return result;
    }
    /**
     * Check if memory usage is high and cleanup should be triggered.
     * Returns true if memory pressure is detected.
     */
    isMemoryPressureHigh() {
        const stats = this.historyManager.getStats();
        const historyUsage = stats.chatHistorySize / stats.maxHistory;
        const messagesUsage = stats.messagesSize / stats.maxMessages;
        // Memory pressure if either is above 80%
        return historyUsage > 0.8 || messagesUsage > 0.8;
    }
}
//# sourceMappingURL=base-agent.js.map