/**
 * Memory Context Builder Module
 *
 * Manages memory integration and context building for the agent.
 * Handles:
 * - Memory retrieval and relevance filtering
 * - Context window management
 * - Memory persistence and project context
 * - Cross-session context building
 *
 * @module agent/context
 */
import { EventEmitter } from "events";
import { logger } from "../../utils/logger.js";
import { getErrorMessage } from "../../errors/index.js";
import { getEnhancedMemory, } from "../../memory/index.js";
/**
 * Default configuration
 */
export const DEFAULT_MEMORY_CONTEXT_CONFIG = {
    enabled: true,
    maxContextMemories: 10,
    minImportanceScore: 0.5,
    autoSave: true,
    includedTypes: ["fact", "decision", "pattern", "error"],
};
/**
 * MemoryContextBuilder - Manages memory integration for agent context
 *
 * This class builds relevant context from the memory system for
 * inclusion in agent conversations. It handles:
 * - Semantic search across memories
 * - Importance filtering and ranking
 * - Token budget management
 * - Project-specific context
 *
 * @example
 * ```typescript
 * const builder = new MemoryContextBuilder({
 *   enabled: true,
 *   maxContextMemories: 10
 * });
 *
 * await builder.setProjectContext("/path/to/project");
 *
 * const context = await builder.buildContext("How do I fix authentication?");
 * console.log(context.items);
 * ```
 */
export class MemoryContextBuilder extends EventEmitter {
    config;
    memory = null;
    initialized = false;
    constructor(config = {}) {
        super();
        this.config = { ...DEFAULT_MEMORY_CONTEXT_CONFIG, ...config };
    }
    /**
     * Initialize the memory system lazily
     */
    getMemory() {
        if (!this.memory) {
            this.memory = getEnhancedMemory({
                enabled: this.config.enabled,
                embeddingEnabled: true,
                useSQLite: true,
                maxMemories: 10000,
                autoSummarize: true,
                ...this.config.memoryConfig,
            });
            this.initialized = true;
        }
        return this.memory;
    }
    /**
     * Set the project context for memory operations
     */
    async setProjectContext(projectPath) {
        try {
            const memory = this.getMemory();
            await memory.setProjectContext(projectPath);
            this.config.projectPath = projectPath;
            this.emit("project:set", { path: projectPath });
            logger.debug("Project context set for memory", { path: projectPath });
        }
        catch (error) {
            logger.warn("Failed to set project context for memory", {
                error: getErrorMessage(error),
            });
            this.emit("error", {
                operation: "setProjectContext",
                error: getErrorMessage(error),
            });
        }
    }
    /**
     * Build context from memory for a given query
     *
     * @param query - The user's query or message
     * @param tokenBudget - Optional token budget to respect
     * @returns Built context with relevant memories
     */
    async buildContext(query, tokenBudget) {
        if (!this.config.enabled) {
            return {
                items: [],
                estimatedTokens: 0,
                sources: [],
                truncated: false,
            };
        }
        try {
            const memory = this.getMemory();
            // Search for relevant memories using recall with query
            const searchOptions = {
                query,
                limit: this.config.maxContextMemories * 2, // Get extra for filtering
                types: this.config.includedTypes,
                minImportance: this.config.minImportanceScore,
            };
            const results = await memory.recall(searchOptions);
            this.emit("memory:retrieved", { count: results.length, query });
            // Convert to context items
            const items = results.map((entry) => ({
                id: entry.id,
                type: entry.type,
                content: entry.content,
                importance: entry.importance,
                createdAt: entry.createdAt,
                metadata: entry.metadata,
            }));
            // Sort by importance
            items.sort((a, b) => b.importance - a.importance);
            // Apply token budget if specified
            let truncated = false;
            let estimatedTokens = 0;
            const selectedItems = [];
            const sourceCount = new Map();
            for (const item of items) {
                if (selectedItems.length >= this.config.maxContextMemories) {
                    truncated = true;
                    break;
                }
                // Rough token estimate (4 chars per token)
                const itemTokens = Math.ceil(item.content.length / 4);
                if (tokenBudget && estimatedTokens + itemTokens > tokenBudget) {
                    truncated = true;
                    break;
                }
                selectedItems.push(item);
                estimatedTokens += itemTokens;
                // Track source types
                const count = sourceCount.get(item.type) || 0;
                sourceCount.set(item.type, count + 1);
            }
            // Build sources summary
            const sources = Array.from(sourceCount.entries()).map(([type, count]) => ({
                type,
                count,
            }));
            this.emit("context:built", {
                itemCount: selectedItems.length,
                estimatedTokens,
            });
            return {
                items: selectedItems,
                estimatedTokens,
                sources,
                truncated,
            };
        }
        catch (error) {
            logger.warn("Failed to build memory context", {
                error: getErrorMessage(error),
            });
            this.emit("error", {
                operation: "buildContext",
                error: getErrorMessage(error),
            });
            return {
                items: [],
                estimatedTokens: 0,
                sources: [],
                truncated: false,
            };
        }
    }
    /**
     * Format context items for inclusion in system prompt
     */
    formatContextForPrompt(context) {
        if (context.items.length === 0) {
            return "";
        }
        const lines = [
            "## Relevant Context from Memory",
            "",
        ];
        for (const item of context.items) {
            const typeEmoji = this.getTypeEmoji(item.type);
            lines.push(`${typeEmoji} **${item.type}** (importance: ${(item.importance * 100).toFixed(0)}%)`);
            lines.push(item.content);
            lines.push("");
        }
        return lines.join("\n");
    }
    /**
     * Get emoji for memory type
     */
    getTypeEmoji(type) {
        const emojis = {
            fact: "📌",
            decision: "🎯",
            pattern: "🔄",
            error: "🔧",
            preference: "⚙️",
            context: "📎",
            summary: "📝",
            instruction: "📋",
            definition: "📖",
        };
        return emojis[type] || "📝";
    }
    /**
     * Save a memory entry
     */
    async saveMemory(content, type, metadata) {
        if (!this.config.enabled || !this.config.autoSave) {
            return null;
        }
        try {
            const memory = this.getMemory();
            const entry = await memory.store({
                content,
                type,
                metadata,
            });
            this.emit("memory:saved", { type, id: entry.id });
            logger.debug("Memory saved", { type, id: entry.id });
            return entry.id;
        }
        catch (error) {
            logger.warn("Failed to save memory", {
                error: getErrorMessage(error),
            });
            this.emit("error", {
                operation: "saveMemory",
                error: getErrorMessage(error),
            });
            return null;
        }
    }
    /**
     * Save a conversation exchange to memory
     */
    async saveConversation(userMessage, assistantResponse) {
        const content = `User: ${userMessage}\nAssistant: ${assistantResponse}`;
        return this.saveMemory(content, "summary", {
            userMessage,
            assistantResponse,
        });
    }
    /**
     * Save a code pattern to memory
     */
    async saveCodePattern(code, language, description) {
        const content = description
            ? `${description}\n\`\`\`${language}\n${code}\n\`\`\``
            : `\`\`\`${language}\n${code}\n\`\`\``;
        return this.saveMemory(content, "pattern", {
            language,
            description,
        });
    }
    /**
     * Save an error solution to memory
     */
    async saveErrorSolution(error, solution, context) {
        const content = `Error: ${error}\nSolution: ${solution}${context ? `\nContext: ${context}` : ""}`;
        return this.saveMemory(content, "error", {
            error,
            solution,
            context,
        });
    }
    /**
     * Save a decision to memory
     */
    async saveDecision(decision, reasoning) {
        const content = reasoning
            ? `Decision: ${decision}\nReasoning: ${reasoning}`
            : `Decision: ${decision}`;
        return this.saveMemory(content, "decision", { reasoning });
    }
    /**
     * Get memory statistics
     */
    getStatistics() {
        if (!this.config.enabled) {
            return null;
        }
        try {
            const memory = this.getMemory();
            return memory.getStats();
        }
        catch (error) {
            logger.warn("Failed to get memory statistics", {
                error: getErrorMessage(error),
            });
            return null;
        }
    }
    /**
     * Clear all memories
     */
    async clearMemories() {
        if (!this.config.enabled) {
            return false;
        }
        try {
            const memory = this.getMemory();
            await memory.clear();
            logger.info("All memories cleared");
            return true;
        }
        catch (error) {
            logger.warn("Failed to clear memories", {
                error: getErrorMessage(error),
            });
            return false;
        }
    }
    /**
     * Get configuration
     */
    getConfig() {
        return { ...this.config };
    }
    /**
     * Update configuration
     */
    updateConfig(config) {
        this.config = { ...this.config, ...config };
    }
    /**
     * Check if memory is enabled
     */
    isEnabled() {
        return this.config.enabled;
    }
    /**
     * Enable or disable memory
     */
    setEnabled(enabled) {
        this.config.enabled = enabled;
    }
    /**
     * Check if initialized
     */
    isInitialized() {
        return this.initialized;
    }
    /**
     * Dispose and cleanup
     */
    dispose() {
        this.removeAllListeners();
        if (this.memory) {
            this.memory.dispose();
            this.memory = null;
        }
        this.initialized = false;
    }
}
/**
 * Create a MemoryContextBuilder instance
 */
export function createMemoryContextBuilder(config) {
    return new MemoryContextBuilder(config);
}
// Singleton instance
let builderInstance = null;
/**
 * Get global MemoryContextBuilder instance
 */
export function getMemoryContextBuilder(config) {
    if (!builderInstance) {
        builderInstance = createMemoryContextBuilder(config);
    }
    return builderInstance;
}
/**
 * Reset global MemoryContextBuilder
 */
export function resetMemoryContextBuilder() {
    if (builderInstance) {
        builderInstance.dispose();
    }
    builderInstance = null;
}
/**
 * Smart Suggestions Manager
 *
 * Learns from user input patterns and successful tool chains
 * to provide intelligent tool suggestions.
 */
export class SmartSuggestionsManager extends EventEmitter {
    patterns = new Map();
    toolChainHistory = [];
    maxHistory = 500;
    keywordToolMap;
    constructor() {
        super();
        // Initialize common keyword -> tool mappings
        this.keywordToolMap = new Map([
            ['read', ['read_file', 'list_directory']],
            ['file', ['read_file', 'write_file', 'edit_file']],
            ['write', ['write_file', 'edit_file']],
            ['edit', ['edit_file', 'search_replace']],
            ['search', ['ripgrep_search', 'codebase_search', 'grep_search']],
            ['find', ['ripgrep_search', 'list_directory', 'glob_search']],
            ['grep', ['ripgrep_search', 'grep_search']],
            ['list', ['list_directory', 'glob_search']],
            ['run', ['bash', 'execute_command']],
            ['execute', ['bash', 'execute_command']],
            ['bash', ['bash']],
            ['terminal', ['bash', 'execute_command']],
            ['command', ['bash', 'execute_command']],
            ['git', ['bash', 'git_status', 'git_diff']],
            ['commit', ['bash', 'git_commit']],
            ['test', ['bash', 'run_tests']],
            ['npm', ['bash']],
            ['install', ['bash']],
            ['build', ['bash']],
            ['create', ['write_file', 'create_directory']],
            ['delete', ['delete_file', 'bash']],
            ['remove', ['delete_file', 'bash']],
            ['copy', ['bash', 'write_file']],
            ['move', ['bash']],
            ['rename', ['bash', 'edit_file']],
            ['web', ['fetch_url', 'web_search']],
            ['fetch', ['fetch_url']],
            ['url', ['fetch_url']],
            ['api', ['fetch_url', 'bash']],
            ['image', ['view_image', 'generate_image']],
            ['screenshot', ['take_screenshot']],
            ['think', ['thinking', 'extended_thinking']],
            ['plan', ['thinking', 'create_plan']],
            ['analyze', ['codebase_search', 'read_file', 'thinking']],
            ['refactor', ['edit_file', 'search_replace']],
            ['debug', ['bash', 'read_file', 'thinking']],
            ['fix', ['edit_file', 'bash']],
            ['error', ['read_file', 'bash', 'thinking']],
            ['todo', ['ripgrep_search', 'edit_file']],
            ['comment', ['edit_file']],
            ['document', ['write_file', 'edit_file']],
            ['readme', ['read_file', 'write_file']],
        ]);
    }
    /**
     * Get tool suggestions based on user input
     */
    suggestTools(input) {
        const suggestions = [];
        const inputLower = input.toLowerCase();
        const words = inputLower.split(/\s+/);
        // 1. Keyword-based suggestions
        const keywordSuggestions = this.suggestFromKeywords(words);
        suggestions.push(...keywordSuggestions);
        // 2. Pattern-based suggestions (from learned patterns)
        const patternSuggestions = this.suggestFromPatterns(inputLower);
        suggestions.push(...patternSuggestions);
        // 3. History-based suggestions
        const historySuggestions = this.suggestFromHistory();
        suggestions.push(...historySuggestions);
        // Deduplicate and sort by confidence
        const seen = new Set();
        const uniqueSuggestions = suggestions.filter(s => {
            if (seen.has(s.toolName))
                return false;
            seen.add(s.toolName);
            return true;
        });
        return uniqueSuggestions
            .sort((a, b) => b.confidence - a.confidence)
            .slice(0, 10);
    }
    /**
     * Get suggestions from keyword mappings
     */
    suggestFromKeywords(words) {
        const suggestions = [];
        const toolScores = new Map();
        for (const word of words) {
            // Check direct match
            if (this.keywordToolMap.has(word)) {
                const tools = this.keywordToolMap.get(word);
                for (let i = 0; i < tools.length; i++) {
                    const tool = tools[i];
                    const score = (tools.length - i) / tools.length; // Higher score for earlier tools
                    toolScores.set(tool, (toolScores.get(tool) || 0) + score);
                }
            }
            // Check partial matches
            for (const [keyword, tools] of this.keywordToolMap) {
                if (keyword.includes(word) || word.includes(keyword)) {
                    for (const tool of tools) {
                        toolScores.set(tool, (toolScores.get(tool) || 0) + 0.3);
                    }
                }
            }
        }
        // Convert to suggestions
        for (const [tool, score] of toolScores) {
            suggestions.push({
                toolName: tool,
                confidence: Math.min(1, score),
                reason: 'Matches keywords in your input',
                basedOn: 'keyword',
            });
        }
        return suggestions;
    }
    /**
     * Get suggestions from learned patterns
     */
    suggestFromPatterns(input) {
        const suggestions = [];
        for (const [, pattern] of this.patterns) {
            // Check if input matches any keywords in the pattern
            const matchCount = pattern.keywords.filter(kw => input.includes(kw)).length;
            if (matchCount > 0) {
                const matchScore = matchCount / pattern.keywords.length;
                for (const tool of pattern.suggestedTools) {
                    suggestions.push({
                        toolName: tool,
                        confidence: matchScore * pattern.successRate,
                        reason: `Learned pattern (${(pattern.successRate * 100).toFixed(0)}% success rate)`,
                        basedOn: 'pattern',
                    });
                }
            }
        }
        return suggestions;
    }
    /**
     * Get suggestions from recent successful tool chains
     */
    suggestFromHistory() {
        const suggestions = [];
        const recentSuccess = this.toolChainHistory
            .filter(h => h.success)
            .slice(-20);
        // Count tool frequencies in successful chains
        const toolFreq = new Map();
        for (const chain of recentSuccess) {
            for (const tool of chain.tools) {
                toolFreq.set(tool, (toolFreq.get(tool) || 0) + 1);
            }
        }
        // Convert to suggestions
        const maxFreq = Math.max(...Array.from(toolFreq.values()), 1);
        for (const [tool, freq] of toolFreq) {
            suggestions.push({
                toolName: tool,
                confidence: (freq / maxFreq) * 0.5, // Lower confidence for history-based
                reason: `Used in ${freq} recent successful interactions`,
                basedOn: 'history',
            });
        }
        return suggestions;
    }
    /**
     * Learn from a successful tool chain
     */
    learnFromSuccess(input, tools) {
        if (tools.length === 0)
            return;
        // Extract keywords from input
        const keywords = input.toLowerCase()
            .split(/\s+/)
            .filter(w => w.length > 2);
        if (keywords.length === 0)
            return;
        const patternKey = keywords.sort().join('|');
        const existing = this.patterns.get(patternKey);
        if (existing) {
            existing.frequency++;
            existing.successRate = (existing.successRate * (existing.frequency - 1) + 1) / existing.frequency;
            existing.lastUsed = new Date();
            // Merge tools
            for (const tool of tools) {
                if (!existing.suggestedTools.includes(tool)) {
                    existing.suggestedTools.push(tool);
                }
            }
        }
        else {
            this.patterns.set(patternKey, {
                keywords,
                suggestedTools: [...tools],
                frequency: 1,
                successRate: 1,
                lastUsed: new Date(),
            });
        }
        // Record in history
        this.toolChainHistory.push({
            tools,
            success: true,
            timestamp: new Date(),
        });
        // Trim history
        if (this.toolChainHistory.length > this.maxHistory) {
            this.toolChainHistory = this.toolChainHistory.slice(-this.maxHistory);
        }
        this.emit('pattern:learned', { keywords, tools });
    }
    /**
     * Learn from a failed tool chain
     */
    learnFromFailure(input, tools) {
        const keywords = input.toLowerCase()
            .split(/\s+/)
            .filter(w => w.length > 2);
        if (keywords.length === 0)
            return;
        const patternKey = keywords.sort().join('|');
        const existing = this.patterns.get(patternKey);
        if (existing) {
            existing.frequency++;
            existing.successRate = (existing.successRate * (existing.frequency - 1)) / existing.frequency;
            existing.lastUsed = new Date();
        }
        // Record in history
        this.toolChainHistory.push({
            tools,
            success: false,
            timestamp: new Date(),
        });
        // Trim history
        if (this.toolChainHistory.length > this.maxHistory) {
            this.toolChainHistory = this.toolChainHistory.slice(-this.maxHistory);
        }
    }
    /**
     * Add a custom keyword -> tool mapping
     */
    addKeywordMapping(keyword, tools) {
        const existing = this.keywordToolMap.get(keyword) || [];
        const merged = [...new Set([...existing, ...tools])];
        this.keywordToolMap.set(keyword, merged);
    }
    /**
     * Get learned patterns
     */
    getLearnedPatterns() {
        return Array.from(this.patterns.values())
            .sort((a, b) => b.frequency - a.frequency);
    }
    /**
     * Format suggestions for display
     */
    formatSuggestions(suggestions) {
        if (suggestions.length === 0) {
            return 'No tool suggestions available.';
        }
        const lines = [];
        lines.push('Suggested Tools:');
        lines.push('-'.repeat(40));
        for (const suggestion of suggestions) {
            const confidence = (suggestion.confidence * 100).toFixed(0);
            lines.push(`  ${suggestion.toolName} (${confidence}% confidence)`);
            lines.push(`    ${suggestion.reason}`);
        }
        return lines.join('\n');
    }
    /**
     * Clear learned data
     */
    clear() {
        this.patterns.clear();
        this.toolChainHistory = [];
    }
    /**
     * Dispose and cleanup
     */
    dispose() {
        this.clear();
        this.removeAllListeners();
    }
}
// Singleton for SmartSuggestionsManager
let suggestionsInstance = null;
/**
 * Get global SmartSuggestionsManager instance
 */
export function getSmartSuggestionsManager() {
    if (!suggestionsInstance) {
        suggestionsInstance = new SmartSuggestionsManager();
    }
    return suggestionsInstance;
}
/**
 * Reset global SmartSuggestionsManager
 */
export function resetSmartSuggestionsManager() {
    if (suggestionsInstance) {
        suggestionsInstance.dispose();
    }
    suggestionsInstance = null;
}
//# sourceMappingURL=memory-context-builder.js.map