"use strict";
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.LearningRecorder = void 0;
exports.getDefaultLearningRecorder = getDefaultLearningRecorder;
exports.startNewSession = startNewSession;
exports.recordSessionAutoContinue = recordSessionAutoContinue;
exports.trackToolUsage = trackToolUsage;
exports.detectPatterns = detectPatterns;
exports.reflectOnSession = reflectOnSession;
exports.consolidateMemory = consolidateMemory;
exports.getEvolvedPrompt = getEvolvedPrompt;
exports.evolvePrompt = evolvePrompt;
exports.getUnpromotedPatterns = getUnpromotedPatterns;
exports.markPatternPromoted = markPatternPromoted;
exports.onSessionEnd = onSessionEnd;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const logger_1 = require("../core/logger");
const silentFailureSink_1 = require("../observability/silentFailureSink");
/** Project-scoped, per-session learning state. Replaces the legacy module
 * globals (which races under concurrent chats and writes to whatever the
 * process cwd happens to be). */
class LearningRecorder {
    projectDir;
    sessionId;
    baseDir;
    trackerFile;
    patternsFile;
    reflectionsFile;
    evolvedPromptFile;
    memoryDir;
    toolLog = [];
    autoContinueCount = 0;
    model = '';
    constructor(projectDir, sessionId) {
        this.projectDir = projectDir;
        this.sessionId = sessionId ?? Date.now().toString(36);
        this.baseDir = path.join(projectDir, '.harness', 'learning');
        this.trackerFile = path.join(this.baseDir, 'tool-usage.jsonl');
        this.patternsFile = path.join(this.baseDir, 'detected-patterns.json');
        this.reflectionsFile = path.join(this.baseDir, 'reflections.jsonl');
        this.evolvedPromptFile = path.join(this.baseDir, 'evolved-prompt.md');
        this.memoryDir = path.join(projectDir, '.harness', 'memory');
    }
    getSessionTools() { return [...this.toolLog]; }
    getAutoContinueCount() { return this.autoContinueCount; }
    getModel() { return this.model; }
    recordAutoContinue(model) {
        this.autoContinueCount++;
        this.model = model;
    }
    async trackToolUsage(tool, input, success, durationMs) {
        const entry = {
            timestamp: new Date().toISOString(),
            sessionId: this.sessionId,
            tool,
            input,
            success,
            durationMs,
        };
        this.toolLog.push(entry);
        try {
            await fs.mkdir(this.baseDir, { recursive: true });
            await fs.appendFile(this.trackerFile, JSON.stringify(entry) + '\n');
        }
        catch (err) {
            (0, silentFailureSink_1.recordSwallowed)('learning.trackToolUsage', err);
        }
    }
    async detectPatterns() {
        return detectPatternsImpl(this.trackerFile, this.patternsFile);
    }
    async reflectOnSession() {
        return reflectOnSessionImpl(this);
    }
    async consolidateMemory() {
        return consolidateMemoryImpl(this);
    }
    async getEvolvedPrompt(basePrompt) {
        return getEvolvedPromptImpl(this, basePrompt);
    }
    async evolvePrompt(addition) {
        return evolvePromptImpl(this, addition);
    }
    async getUnpromotedPatterns() {
        return getUnpromotedPatternsImpl(this.patternsFile);
    }
    async markPatternPromoted(patternId) {
        return markPatternPromotedImpl(this.patternsFile, patternId);
    }
    async onSessionEnd() {
        const reflection = await this.reflectOnSession();
        const newPatterns = await this.detectPatterns();
        const unpromoted = newPatterns.filter(p => !p.promoted);
        if (unpromoted.length > 0) {
            logger_1.logger.info('Learning', `${unpromoted.length} patterns ready for skill promotion`);
        }
        // Consolidate the session into a digest so .harness/memory/consolidated-digest.md
        // actually grows over time. Best-effort: a consolidation failure must not
        // wipe the reflection/pattern signal that already succeeded.
        let digest;
        try {
            digest = await this.consolidateMemory();
        }
        catch (err) {
            (0, silentFailureSink_1.recordSwallowed)('learning.onSessionEnd.consolidateMemory', err, { sessionId: this.sessionId });
        }
        return { reflection, newPatterns: unpromoted, digest };
    }
}
exports.LearningRecorder = LearningRecorder;
// --- Default recorder (backward compat for callers that didn't pass a recorder) ---
let defaultRecorder = new LearningRecorder(process.cwd());
/** Returns the current default recorder (bound to `process.cwd()` at first
 * use, then rebound by `startNewSession()`). New code should construct its
 * own LearningRecorder rather than relying on this. */
function getDefaultLearningRecorder() {
    return defaultRecorder;
}
// --- Backward-compat free-function API (delegates to default recorder) ---
function startNewSession() {
    defaultRecorder = new LearningRecorder(process.cwd());
    return defaultRecorder.sessionId;
}
function recordSessionAutoContinue(model) {
    defaultRecorder.recordAutoContinue(model);
}
async function trackToolUsage(tool, input, success, durationMs) {
    return defaultRecorder.trackToolUsage(tool, input, success, durationMs);
}
// --- 2. Pattern Detection ---
async function detectPatterns() {
    return defaultRecorder.detectPatterns();
}
async function detectPatternsImpl(trackerFile, patternsFile) {
    try {
        const raw = await fs.readFile(trackerFile, 'utf-8');
        const entries = raw.trim().split('\n')
            .map(line => { try {
            return JSON.parse(line);
        }
        catch {
            return null;
        } })
            .filter((e) => e !== null);
        // Group by session
        const sessions = new Map();
        for (const entry of entries) {
            const list = sessions.get(entry.sessionId) ?? [];
            list.push(entry);
            sessions.set(entry.sessionId, list);
        }
        // Find repeated tool sequences (2-5 tools long)
        const sequenceCounts = new Map();
        for (const [sessionId, tools] of sessions) {
            const toolNames = tools.map(t => t.tool);
            for (let len = 2; len <= Math.min(5, toolNames.length); len++) {
                for (let i = 0; i <= toolNames.length - len; i++) {
                    const seq = toolNames.slice(i, i + len);
                    const key = seq.join(' → ');
                    const existing = sequenceCounts.get(key);
                    if (existing) {
                        if (!existing.sessions.includes(sessionId)) {
                            existing.sessions.push(sessionId);
                        }
                        existing.last = tools[i].timestamp;
                    }
                    else {
                        sequenceCounts.set(key, {
                            tools: seq,
                            sessions: [sessionId],
                            first: tools[i].timestamp,
                            last: tools[i].timestamp,
                        });
                    }
                }
            }
        }
        // Only keep patterns seen in 3+ different sessions
        const patterns = [];
        for (const [key, data] of sequenceCounts) {
            if (data.sessions.length >= 3) {
                patterns.push({
                    id: key.replace(/[^a-z0-9]/gi, '-').toLowerCase(),
                    toolSequence: data.tools,
                    occurrences: data.sessions.length,
                    firstSeen: data.first,
                    lastSeen: data.last,
                    suggestedSkillName: suggestSkillName(data.tools),
                    promoted: false,
                });
            }
        }
        // Sort by occurrence count (most common first)
        patterns.sort((a, b) => b.occurrences - a.occurrences);
        // Save detected patterns
        await fs.writeFile(patternsFile, JSON.stringify(patterns, null, 2));
        logger_1.logger.info('Learning', `Detected ${patterns.length} patterns from ${sessions.size} sessions`);
        return patterns;
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.detectPatterns', err);
        return [];
    }
}
function suggestSkillName(tools) {
    const unique = [...new Set(tools)];
    if (unique.length === 1)
        return `auto-${unique[0]}`;
    if (unique.includes('file_read') && unique.includes('file_write'))
        return 'auto-read-modify';
    if (unique.includes('grep') && unique.includes('file_edit'))
        return 'auto-search-and-fix';
    if (unique.includes('bash') && unique.includes('file_read'))
        return 'auto-diagnose';
    return 'auto-pattern-' + unique.slice(0, 3).join('-');
}
// --- 3. Reflection ---
async function reflectOnSession() {
    return defaultRecorder.reflectOnSession();
}
async function reflectOnSessionImpl(recorder) {
    const sessionToolLog = recorder.getSessionTools();
    const sessionAutoContinueCount = recorder.getAutoContinueCount();
    const sessionModel = recorder.getModel();
    const currentSessionId = recorder.sessionId;
    const toolsUsed = sessionToolLog.map(t => t.tool);
    const successes = sessionToolLog.filter(t => t.success).length;
    const total = sessionToolLog.length;
    const successRate = total > 0 ? successes / total : 1;
    const insights = [];
    const improvements = [];
    // Analyze failure patterns
    const failures = sessionToolLog.filter(t => !t.success);
    if (failures.length > 0) {
        const failedTools = [...new Set(failures.map(f => f.tool))];
        insights.push(`Tools that failed: ${failedTools.join(', ')}`);
        if (failedTools.includes('bash')) {
            improvements.push('Consider checking command validity before execution');
        }
        if (failedTools.includes('file_read')) {
            improvements.push('Verify file paths exist before attempting to read');
        }
    }
    // Detect tool overuse
    const toolCounts = new Map();
    for (const t of sessionToolLog) {
        toolCounts.set(t.tool, (toolCounts.get(t.tool) ?? 0) + 1);
    }
    for (const [tool, count] of toolCounts) {
        if (count > 10) {
            insights.push(`High usage of '${tool}' (${count} calls) — consider a more targeted approach`);
        }
    }
    // Detect repeated failures on the same tool
    const failureCounts = new Map();
    for (const f of failures) {
        failureCounts.set(f.tool, (failureCounts.get(f.tool) ?? 0) + 1);
    }
    for (const [tool, count] of failureCounts) {
        if (count >= 3) {
            improvements.push(`'${tool}' failed ${count} times — learn to handle this error case`);
        }
    }
    // Success rate insight
    if (successRate < 0.7 && total > 3) {
        insights.push(`Low success rate (${Math.round(successRate * 100)}%) — session had many tool failures`);
    }
    if (successRate === 1 && total > 5) {
        insights.push(`Perfect success rate across ${total} tool calls`);
    }
    // Auto-continue autonomy feedback
    if (sessionAutoContinueCount > 0) {
        insights.push(`Auto-continue fired ${sessionAutoContinueCount} time(s) for model ${sessionModel || 'unknown'} — model needed prompting to complete work autonomously`);
        if (sessionAutoContinueCount >= 3) {
            improvements.push(`Model ${sessionModel || 'unknown'} frequently stops to ask permission — consider a stronger autonomy system prompt or a different model for autonomous tasks`);
        }
    }
    const reflection = {
        timestamp: new Date().toISOString(),
        sessionId: currentSessionId,
        toolsUsed: [...new Set(toolsUsed)],
        successRate,
        insights,
        suggestedImprovements: improvements,
    };
    // Persist reflection
    try {
        await fs.mkdir(recorder.baseDir, { recursive: true });
        await fs.appendFile(recorder.reflectionsFile, JSON.stringify(reflection) + '\n');
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.persistReflection', err);
    }
    // Write insights to memory if there are any
    if (insights.length > 0 || improvements.length > 0) {
        try {
            await fs.mkdir(recorder.memoryDir, { recursive: true });
            const date = new Date().toISOString().split('T')[0];
            const entry = `\n### ${date}: Session Reflection (${currentSessionId})\n` +
                (insights.length > 0 ? `**Insights:** ${insights.join('; ')}\n` : '') +
                (improvements.length > 0 ? `**Improvements:** ${improvements.join('; ')}\n` : '');
            const notesPath = path.join(recorder.memoryDir, 'notes.md');
            try {
                await fs.access(notesPath);
            }
            catch {
                await fs.writeFile(notesPath, '# Notes\n\nGeneral observations and context.\n');
            }
            await fs.appendFile(notesPath, entry);
        }
        catch (err) {
            (0, silentFailureSink_1.recordSwallowed)('learning.writeReflectionMemory', err);
        }
    }
    logger_1.logger.info('Learning', `Reflection: ${total} tools, ${Math.round(successRate * 100)}% success, ${insights.length} insights`);
    return reflection;
}
// --- 4. Dreaming / Consolidation ---
async function consolidateMemory() {
    return defaultRecorder.consolidateMemory();
}
async function consolidateMemoryImpl(recorder) {
    const parts = [];
    // Read all memory files
    for (const file of ['decisions.md', 'patterns.md', 'notes.md']) {
        try {
            parts.push(await fs.readFile(path.join(recorder.memoryDir, file), 'utf-8'));
        }
        catch (err) {
            (0, silentFailureSink_1.recordSwallowed)('learning.readMemoryFile', err);
        }
    }
    // Read reflections
    try {
        const raw = await fs.readFile(recorder.reflectionsFile, 'utf-8');
        const reflections = raw.trim().split('\n')
            .map(line => { try {
            return JSON.parse(line);
        }
        catch {
            return null;
        } })
            .filter((r) => r !== null);
        if (reflections.length > 0) {
            const allInsights = reflections.flatMap(r => r.insights);
            const allImprovements = reflections.flatMap(r => r.suggestedImprovements);
            const avgSuccess = reflections.reduce((s, r) => s + r.successRate, 0) / reflections.length;
            parts.push(`\n--- Reflection Summary (${reflections.length} sessions) ---\n` +
                `Average success rate: ${Math.round(avgSuccess * 100)}%\n` +
                `Common insights: ${[...new Set(allInsights)].slice(0, 10).join('; ')}\n` +
                `Improvements: ${[...new Set(allImprovements)].slice(0, 10).join('; ')}`);
        }
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.readReflections', err);
    }
    // Read detected patterns
    try {
        const patterns = JSON.parse(await fs.readFile(recorder.patternsFile, 'utf-8'));
        if (patterns.length > 0) {
            parts.push(`\n--- Detected Patterns ---\n` +
                patterns.slice(0, 5).map(p => `• ${p.toolSequence.join(' → ')} (seen ${p.occurrences}x, suggested skill: ${p.suggestedSkillName})`).join('\n'));
        }
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.readPatterns', err);
    }
    if (parts.length === 0) {
        return 'No memories to consolidate yet.';
    }
    // Build consolidated summary
    const consolidated = parts.join('\n\n');
    // Write a consolidated digest
    try {
        const digestPath = path.join(recorder.baseDir, 'consolidated-digest.md');
        const date = new Date().toISOString().split('T')[0];
        const digest = `# Consolidated Knowledge Digest\n\nLast updated: ${date}\n\n${consolidated}`;
        await fs.writeFile(digestPath, digest);
        logger_1.logger.info('Learning', 'Memory consolidated into digest');
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.writeConsolidatedDigest', err);
    }
    return consolidated;
}
// --- 5. System Prompt Evolution ---
async function getEvolvedPrompt(basePrompt) {
    return defaultRecorder.getEvolvedPrompt(basePrompt);
}
async function getEvolvedPromptImpl(recorder, basePrompt) {
    // Layer 1: base prompt
    let prompt = basePrompt;
    // Layer 2: append learned patterns from memory
    try {
        const patternsPath = path.join(recorder.memoryDir, 'patterns.md');
        const patterns = await fs.readFile(patternsPath, 'utf-8');
        if (patterns.trim().length > 50) {
            prompt += '\n\n--- Learned Patterns ---\n' + patterns.slice(0, 2000);
        }
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.readLearnedPatterns', err);
    }
    // Layer 3: append consolidated improvements from reflections
    try {
        const raw = await fs.readFile(recorder.reflectionsFile, 'utf-8');
        const reflections = raw.trim().split('\n')
            .map(line => { try {
            return JSON.parse(line);
        }
        catch {
            return null;
        } })
            .filter((r) => r !== null);
        const improvements = [...new Set(reflections.flatMap(r => r.suggestedImprovements))];
        if (improvements.length > 0) {
            prompt += '\n\n--- Self-Improvements (learned from past sessions) ---\n' +
                improvements.slice(0, 10).map(i => `• ${i}`).join('\n');
        }
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.readReflectionsForPrompt', err);
    }
    // Layer 4: append user-evolved prompt additions
    try {
        const evolved = await fs.readFile(recorder.evolvedPromptFile, 'utf-8');
        if (evolved.trim().length > 0) {
            prompt += '\n\n--- Evolved Instructions ---\n' + evolved;
        }
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.readEvolvedPrompt', err);
    }
    return prompt;
}
async function evolvePrompt(addition) {
    return defaultRecorder.evolvePrompt(addition);
}
async function evolvePromptImpl(recorder, addition) {
    try {
        await fs.mkdir(recorder.baseDir, { recursive: true });
        const existing = await fs.readFile(recorder.evolvedPromptFile, 'utf-8').catch(() => '');
        await fs.writeFile(recorder.evolvedPromptFile, existing + '\n' + addition);
        logger_1.logger.info('Learning', 'System prompt evolved with new instruction');
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.evolvePrompt', err);
    }
}
// --- 6. Pattern-to-Skill Promotion ---
async function getUnpromotedPatterns() {
    return defaultRecorder.getUnpromotedPatterns();
}
async function getUnpromotedPatternsImpl(patternsFile) {
    try {
        const patterns = JSON.parse(await fs.readFile(patternsFile, 'utf-8'));
        return patterns.filter(p => !p.promoted && p.occurrences >= 3);
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.getUnpromotedPatterns', err);
        return [];
    }
}
async function markPatternPromoted(patternId) {
    return defaultRecorder.markPatternPromoted(patternId);
}
async function markPatternPromotedImpl(patternsFile, patternId) {
    try {
        const patterns = JSON.parse(await fs.readFile(patternsFile, 'utf-8'));
        const pattern = patterns.find(p => p.id === patternId);
        if (pattern) {
            pattern.promoted = true;
            await fs.writeFile(patternsFile, JSON.stringify(patterns, null, 2));
        }
    }
    catch (err) {
        (0, silentFailureSink_1.recordSwallowed)('learning.markPatternPromoted', err);
    }
}
// --- Public: Session Lifecycle ---
async function onSessionEnd() {
    return defaultRecorder.onSessionEnd();
}
//# sourceMappingURL=engine.js.map