"use strict";
// Personal Knowledge Graph — append-only entity + edge store.
//
// Stores three kinds of facts as JSONL lines under
// `.harness/jarvis/knowledge.jsonl`:
//
//   { kind: 'entity', id, type, name, attributes, source, observedAt }
//   { kind: 'edge',   id, from, to, relation, weight, source, observedAt }
//   { kind: 'fact',   id, subject, predicate, object, confidence, source, observedAt }
//
// Append-only matches the codebase pattern (sessions, learning candidates).
// Recall is in-memory after one full read; for production volume swap the
// reader for a SQLite or DuckDB layer behind the same interface.
//
// Entity types kept intentionally open-string so callers can grow the schema
// without forcing migrations: 'person', 'project', 'file', 'session',
// 'tool', 'deadline', 'decision', 'topic'.
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.appendRecord = appendRecord;
exports.readAll = readAll;
exports.recall = recall;
exports.findEntityByName = findEntityByName;
exports.upsertEntity = upsertEntity;
exports.getKnowledgeGraphStatus = getKnowledgeGraphStatus;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
function graphPath(projectDir) {
    return path.join(projectDir, '.harness', 'jarvis', 'knowledge.jsonl');
}
function makeId(kind) {
    return `${kind}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
async function appendRecord(projectDir, record) {
    const filePath = graphPath(projectDir);
    await fs.mkdir(path.dirname(filePath), { recursive: true });
    const enriched = {
        ...record,
        id: record.id ?? makeId(record.kind),
        observedAt: record.observedAt ?? new Date().toISOString(),
    };
    await fs.appendFile(filePath, JSON.stringify(enriched) + '\n', 'utf8');
    return enriched;
}
async function readAll(projectDir) {
    try {
        const raw = await fs.readFile(graphPath(projectDir), 'utf8');
        const out = [];
        for (const line of raw.split('\n')) {
            const trimmed = line.trim();
            if (!trimmed)
                continue;
            try {
                out.push(JSON.parse(trimmed));
            }
            catch {
                // Tolerate truncated trailing lines, matching session storage convention
            }
        }
        return out;
    }
    catch {
        return [];
    }
}
/** Lightweight token-overlap recall. Replace with embeddings when volume warrants. */
async function recall(projectDir, query, limit = 20) {
    const records = await readAll(projectDir);
    const tokens = tokenize(query);
    const scored = [];
    for (const record of records) {
        const haystack = recordHaystack(record);
        const score = scoreOverlap(tokens, haystack);
        if (score > 0)
            scored.push({ record, score });
    }
    scored.sort((a, b) => b.score - a.score);
    const top = scored.slice(0, limit).map((s) => s.record);
    return {
        query,
        entities: top.filter((r) => r.kind === 'entity'),
        edges: top.filter((r) => r.kind === 'edge'),
        facts: top.filter((r) => r.kind === 'fact'),
    };
}
/** Resolve an entity by name (case-insensitive exact match), most recent wins. */
async function findEntityByName(projectDir, name, type) {
    const records = await readAll(projectDir);
    const matches = records.filter((r) => r.kind === 'entity' && r.name.toLowerCase() === name.toLowerCase() && (!type || r.type === type));
    return matches.sort((a, b) => b.observedAt.localeCompare(a.observedAt))[0];
}
/** Merge an entity by name+type, returning the existing id if found, else creating. */
async function upsertEntity(projectDir, type, name, attributes, source) {
    const existing = await findEntityByName(projectDir, name, type);
    if (existing) {
        // Append a delta record so history is preserved
        await appendRecord(projectDir, {
            kind: 'entity',
            id: existing.id,
            type,
            name,
            attributes: { ...(existing.attributes ?? {}), ...attributes },
            source,
        });
        return { ...existing, attributes: { ...(existing.attributes ?? {}), ...attributes } };
    }
    const created = await appendRecord(projectDir, { kind: 'entity', type, name, attributes, source });
    return created;
}
function tokenize(text) {
    return new Set(text
        .toLowerCase()
        .replace(/[^a-z0-9\s]/g, ' ')
        .split(/\s+/)
        .filter((t) => t.length > 2));
}
function recordHaystack(record) {
    if (record.kind === 'entity')
        return `${record.type} ${record.name} ${JSON.stringify(record.attributes ?? {})}`;
    if (record.kind === 'edge')
        return `${record.relation} ${record.from} ${record.to}`;
    return `${record.subject} ${record.predicate} ${record.object}`;
}
function scoreOverlap(query, haystack) {
    const haystackTokens = tokenize(haystack);
    let hits = 0;
    for (const t of query)
        if (haystackTokens.has(t))
            hits++;
    return hits;
}
async function getKnowledgeGraphStatus(projectDir) {
    const records = await readAll(projectDir);
    return {
        records: records.length,
        entities: records.filter((r) => r.kind === 'entity').length,
        edges: records.filter((r) => r.kind === 'edge').length,
        facts: records.filter((r) => r.kind === 'fact').length,
        lastObservedAt: records.length ? records[records.length - 1].observedAt : undefined,
    };
}
//# sourceMappingURL=knowledgeGraph.js.map