"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.rebuildSemanticMemory = rebuildSemanticMemory;
exports.searchSemanticMemory = searchSemanticMemory;
exports.getSemanticMemoryEntry = getSemanticMemoryEntry;
exports.getSemanticMemoryContext = getSemanticMemoryContext;
exports.buildMemoryPalace = buildMemoryPalace;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const sessionStorage_1 = require("./sessionStorage");
async function rebuildSemanticMemory(projectDir) {
    const sessions = await sessionStorage_1.SessionStorage.listSessions(projectDir);
    const entries = [];
    for (const session of sessions) {
        const storage = new sessionStorage_1.SessionStorage(projectDir, session.model, session.sessionId);
        const events = await storage.readAll();
        entries.push(...eventsToEntries(session, events));
    }
    await writeIndex(projectDir, entries);
    return entries;
}
async function searchSemanticMemory(projectDir, query, limit = 8) {
    const entries = await readOrBuildIndex(projectDir);
    const queryTokens = tokenize(query);
    if (queryTokens.length === 0) {
        return [];
    }
    return entries
        .map((entry) => ({ entry, score: scoreEntry(queryTokens, entry.tokens) }))
        .filter((result) => result.score > 0)
        .sort((a, b) => b.score - a.score)
        .slice(0, limit);
}
async function getSemanticMemoryEntry(projectDir, entryId) {
    const entries = await readOrBuildIndex(projectDir);
    return entries.find((entry) => entry.id === entryId) ?? null;
}
async function getSemanticMemoryContext(projectDir, entryId, windowSize = 3) {
    const entry = await getSemanticMemoryEntry(projectDir, entryId);
    if (!entry)
        return null;
    const storage = new sessionStorage_1.SessionStorage(projectDir, 'unknown', entry.sessionId);
    const events = await storage.readAll();
    const eventIndex = events.findIndex((event) => event.id === entryId);
    if (eventIndex < 0) {
        return { entry, events: [] };
    }
    const start = Math.max(0, eventIndex - windowSize);
    const end = Math.min(events.length, eventIndex + windowSize + 1);
    return {
        entry,
        events: events.slice(start, end).map((event) => ({
            id: event.id,
            sessionId: entry.sessionId,
            timestamp: event.timestamp,
            kind: event.type,
            text: eventToText(event).slice(0, 800),
            isAnchor: event.id === entryId,
        })),
    };
}
async function buildMemoryPalace(projectDir, query) {
    const entries = await readOrBuildIndex(projectDir);
    const queryTokens = query && query.trim() ? tokenize(query) : [];
    const groups = new Map();
    for (const entry of entries) {
        const roomId = roomIdForKind(entry.kind);
        groups.set(roomId, [...(groups.get(roomId) ?? []), entry]);
    }
    const rooms = Array.from(groups.entries()).map(([id, roomEntries]) => toRoom(id, roomEntries, queryTokens));
    if (queryTokens.length > 0) {
        // Sort rooms by their best anchor's relevance (descending), break ties by
        // entry count so a brand-new high-relevance room still wins, then by
        // alphabetical title for deterministic output.
        rooms.sort((a, b) => {
            const aScore = a.relevance ?? 0;
            const bScore = b.relevance ?? 0;
            if (bScore !== aScore)
                return bScore - aScore;
            if (b.entryCount !== a.entryCount)
                return b.entryCount - a.entryCount;
            return a.title.localeCompare(b.title);
        });
    }
    else {
        rooms.sort((a, b) => b.entryCount - a.entryCount || a.title.localeCompare(b.title));
    }
    return {
        generatedAt: new Date().toISOString(),
        entryCount: entries.length,
        roomCount: rooms.length,
        rooms,
    };
}
function eventsToEntries(session, events) {
    return events.flatMap((event) => {
        const text = eventToText(event);
        if (!text) {
            return [];
        }
        return [{
                id: event.id,
                sessionId: session.sessionId,
                timestamp: event.timestamp,
                kind: event.type,
                text,
                tokens: tokenize(text),
            }];
    });
}
function eventToText(event) {
    switch (event.data.kind) {
        case 'message':
            return event.data.message.content ?? '';
        case 'tool_result':
            return event.data.result.output;
        case 'compact_boundary':
            return event.data.summary;
        case 'continuity_checkpoint':
            return [
                event.data.checkpoint.currentGoal,
                event.data.checkpoint.summary,
                event.data.checkpoint.nextAction,
                ...event.data.checkpoint.recentMessages,
            ].join('\n');
        case 'system':
            return event.data.content;
        default:
            return '';
    }
}
function tokenize(text) {
    return Array.from(new Set(text.toLowerCase().match(/[a-z0-9_./-]{3,}/g) ?? []));
}
function scoreEntry(queryTokens, entryTokens) {
    const entrySet = new Set(entryTokens);
    const matches = queryTokens.filter((token) => entrySet.has(token)).length;
    return matches / Math.sqrt(Math.max(1, entryTokens.length));
}
async function readOrBuildIndex(projectDir) {
    try {
        const raw = await fs.readFile(indexPath(projectDir), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return rebuildSemanticMemory(projectDir);
    }
}
async function writeIndex(projectDir, entries) {
    const filePath = indexPath(projectDir);
    await fs.mkdir(path.dirname(filePath), { recursive: true });
    await fs.writeFile(filePath, JSON.stringify(entries, null, 2), 'utf-8');
}
function indexPath(projectDir) {
    return path.join(projectDir, '.harness', 'memory', 'semantic-index.json');
}
function toRoom(id, entries, queryTokens = []) {
    // When a query is supplied, surface the most relevant anchors first so the
    // model sees memory that matches what the user is asking about, not just
    // the freshest events in the largest room.
    let sorted;
    let relevance = 0;
    if (queryTokens.length > 0) {
        const scored = entries.map((entry) => ({ entry, score: scoreEntry(queryTokens, entry.tokens) }));
        relevance = scored.reduce((max, s) => s.score > max ? s.score : max, 0);
        sorted = scored
            .sort((a, b) => {
            if (b.score !== a.score)
                return b.score - a.score;
            return b.entry.timestamp.localeCompare(a.entry.timestamp);
        })
            .map((s) => s.entry);
    }
    else {
        sorted = [...entries].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
    }
    const room = {
        id,
        title: roomTitle(id),
        entryCount: entries.length,
        sessions: Array.from(new Set(entries.map((entry) => entry.sessionId))).sort(),
        anchors: sorted.slice(0, 6).map((entry) => ({
            id: entry.id,
            sessionId: entry.sessionId,
            timestamp: entry.timestamp,
            kind: entry.kind,
            text: entry.text.slice(0, 260),
        })),
    };
    if (queryTokens.length > 0)
        room.relevance = relevance;
    return room;
}
function roomIdForKind(kind) {
    if (kind.includes('checkpoint') || kind.includes('compact'))
        return 'continuity';
    if (kind.includes('tool'))
        return 'tools';
    if (kind.includes('message'))
        return 'conversation';
    return 'system';
}
function roomTitle(id) {
    switch (id) {
        case 'continuity': return 'Continuity Hall';
        case 'tools': return 'Tool Workshop';
        case 'conversation': return 'Conversation Gallery';
        default: return 'System Archive';
    }
}
//# sourceMappingURL=semanticMemory.js.map