/**
 * Brain Working Memory Eviction Policies — Sprint A (brain-eviction).
 *
 * INTEGRATION SKETCH (for coordinator wave B):
 * ─────────────────────────────────────────────
 * In `src/ports/brain.ts`, extend `WorkingMemoryPort` with:
 *
 *   evict(policy: EvictionPolicy, targetSize: number): Promise<string[]>;
 *
 * And in `InMemoryWorkingMemory`, implement:
 *
 *   async evict(policy: EvictionPolicy, targetSize: number): Promise<string[]> {
 *     const now = Date.now();
 *     const entries: WorkingMemoryEntry[] = [...this.store.entries()]
 *       .filter(([, v]) => v.expiresAt > now)
 *       .map(([compoundKey, v]) => ({
 *         id: compoundKey,
 *         content: v.value,
 *         lastAccessedAt: v.lastAccessedAt ?? v.expiresAt,
 *         importance: v.importance ?? 0.5,
 *         agentTag: v.agentTag,
 *         pinned: v.pinned,
 *         size: v.size,
 *       }));
 *     const toEvict = policy.select(entries, targetSize);
 *     for (const id of toEvict) this.store.delete(id);
 *     return toEvict;
 *   }
 *
 * The `WorkingMemoryEntry` type in this module mirrors the shape callers
 * must provide — no import loop with `brain.ts` is required.
 */
// ─── Helpers ─────────────────────────────────────────────────────────────────
/** Filter out pinned entries and decide how many need to go. */
function evictableAndCount(entries, targetSize) {
    if (entries.length <= targetSize)
        return { evictable: [], count: 0 };
    const evictable = entries.filter((e) => !e.pinned);
    const count = entries.length - targetSize;
    return { evictable, count };
}
// ─── LRU Policy ──────────────────────────────────────────────────────────────
/**
 * Evicts entries with the oldest `lastAccessedAt` first.
 * Ties are broken by `id` ASC for determinism.
 */
export const lruPolicy = {
    name: "lru",
    select(entries, targetSize) {
        const { evictable, count } = evictableAndCount(entries, targetSize);
        if (count === 0)
            return [];
        const sorted = [...evictable].sort((a, b) => {
            const diff = a.lastAccessedAt - b.lastAccessedAt;
            return diff !== 0 ? diff : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
        });
        return sorted.slice(0, count).map((e) => e.id);
    },
};
/**
 * Scores each entry as:
 *   score = recencyWeight * normalizedRecency + importanceWeight * importance
 *
 * Normalized recency: 1 = most recent, 0 = oldest (linear across range).
 * When all `lastAccessedAt` values are equal, normalizedRecency = 0.5 for all.
 *
 * Evicts entries with the lowest score first.
 * Ties broken by `id` ASC.
 */
export function importanceWeightedPolicy(opts) {
    const rw = opts?.recencyWeight ?? 0.5;
    const iw = opts?.importanceWeight ?? 0.5;
    return {
        name: "importance-weighted",
        select(entries, targetSize) {
            const { evictable, count } = evictableAndCount(entries, targetSize);
            if (count === 0)
                return [];
            // Compute normalized recency
            const times = evictable.map((e) => e.lastAccessedAt);
            const minTime = Math.min(...times);
            const maxTime = Math.max(...times);
            const timeRange = maxTime - minTime;
            const scored = evictable.map((e) => {
                const normalizedRecency = timeRange === 0 ? 0.5 : (e.lastAccessedAt - minTime) / timeRange;
                const score = rw * normalizedRecency + iw * e.importance;
                return { id: e.id, score };
            });
            scored.sort((a, b) => {
                const diff = a.score - b.score;
                return diff !== 0 ? diff : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
            });
            return scored.slice(0, count).map((s) => s.id);
        },
    };
}
// ─── AgentControlled Policy ──────────────────────────────────────────────────
/**
 * Evicts entries whose `agentTag` has the lowest priority in the map first.
 * Entries with no `agentTag` (or a tag not in the map) receive priority 0.
 * Higher priority value = kept longer.
 * Ties broken by `id` ASC.
 */
export function agentControlledPolicy(priorities) {
    return {
        name: "agent-controlled",
        select(entries, targetSize) {
            const { evictable, count } = evictableAndCount(entries, targetSize);
            if (count === 0)
                return [];
            const getPriority = (e) => e.agentTag !== undefined ? (priorities.get(e.agentTag) ?? 0) : 0;
            const sorted = [...evictable].sort((a, b) => {
                const diff = getPriority(a) - getPriority(b);
                return diff !== 0 ? diff : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
            });
            return sorted.slice(0, count).map((e) => e.id);
        },
    };
}
//# sourceMappingURL=eviction-policy.js.map