/**
 * BrainPort — knowledge archival + retrieval contract (4 memory tiers).
 *
 * Tier 1 — working: get/set with scope='run' + ttlMs auto-expire (A6, sprint-562).
 * Tier 2 — episodic: since/record per agentId (A6, sprint-562).
 * Tier 3 — semantic: query/archive shared across agents (C4, sprint-564).
 * Tier 4 — procedural: resolveSkills for learned/private/shared skills (C4, sprint-564).
 */
export class InMemoryWorkingMemory {
    store = new Map();
    async set(runId, key, value, opts) {
        this.store.set(`${runId}:${key}`, {
            value,
            expiresAt: Date.now() + (opts?.ttlMs ?? 300_000),
        });
    }
    async get(runId, key) {
        const entry = this.store.get(`${runId}:${key}`);
        if (!entry)
            return null;
        if (Date.now() > entry.expiresAt) {
            this.store.delete(`${runId}:${key}`);
            return null;
        }
        return entry.value;
    }
    async delete(runId, key) {
        this.store.delete(`${runId}:${key}`);
    }
}
export class InMemoryEpisodicMemory {
    events = [];
    async record(agentId, runId, event, metadata, opts) {
        this.events.push({
            agentId,
            runId,
            event,
            metadata,
            timestamp: Date.now(),
            traceId: opts?.traceId,
        });
    }
    async since(agentId, sinceMs, opts) {
        const filtered = this.events
            .filter((e) => e.agentId === agentId && e.timestamp >= sinceMs)
            .sort((a, b) => b.timestamp - a.timestamp);
        return opts?.limit ? filtered.slice(0, opts.limit) : filtered;
    }
    async queryByTrace(traceId, opts) {
        // Insertion order is preserved (Array.filter is stable; events are pushed in order).
        const filtered = this.events.filter((e) => e.traceId === traceId);
        return opts?.limit ? filtered.slice(0, opts.limit) : filtered;
    }
}
export class InMemorySemanticMemory {
    entries = [];
    async query(q, filters) {
        let results = this.entries.filter((e) => e.content.toLowerCase().includes(q.toLowerCase()));
        if (filters?.category)
            results = results.filter((e) => e.category === filters.category);
        if (filters?.tags && filters.tags.length > 0) {
            results = results.filter((e) => e.tags?.some((t) => filters.tags?.includes(t)));
        }
        return filters?.limit ? results.slice(0, filters.limit) : results;
    }
    async archive(entry) {
        const created = {
            id: `semantic-${this.entries.length + 1}`,
            content: entry.content,
            category: entry.category,
            tags: entry.tags,
            metadata: entry.metadata,
            created_at: new Date().toISOString(),
        };
        this.entries.push(created);
        return created;
    }
}
export class InMemoryProceduralMemory {
    skills = new Map();
    shared = new Map();
    async resolveSkills(agentId) {
        const own = this.skills.get(agentId) ?? [];
        const sharedSkills = [];
        for (const [fromAgent, skillNames] of this.shared) {
            const fromSkills = this.skills.get(fromAgent) ?? [];
            for (const name of skillNames) {
                const skill = fromSkills.find((s) => s.name === name);
                if (skill)
                    sharedSkills.push({ ...skill, source: "shared" });
            }
        }
        return [...own, ...sharedSkills];
    }
    async registerSkill(agentId, skill) {
        const existing = this.skills.get(agentId) ?? [];
        this.skills.set(agentId, [...existing, skill]);
    }
    async shareSkill(skillName, fromAgentId, toAgentIds) {
        for (const _toAgent of toAgentIds) {
            const shared = this.shared.get(fromAgentId) ?? [];
            if (!shared.includes(skillName)) {
                this.shared.set(fromAgentId, [...shared, skillName]);
            }
        }
    }
}
// ─── Typed errors ─────────────────────────────────────────────────────────────
/**
 * Thrown when the Brain API is unreachable (connection refused, DNS failure,
 * timeout on connect). Callers can retry with backoff or fall back to a local cache.
 */
export class BrainUnavailableError extends Error {
    cause;
    constructor(message, cause) {
        super(message);
        this.cause = cause;
        this.name = "BrainUnavailableError";
        Object.setPrototypeOf(this, new.target.prototype);
    }
}
/**
 * Thrown when the Brain API returns 429 (rate limit exceeded).
 * Callers should retry after the delay indicated in the `retryAfterMs` field
 * (extracted from the Retry-After header) or use exponential backoff.
 */
export class BrainRateLimitError extends Error {
    cause;
    /** Retry-After delay in milliseconds. Derived from the HTTP Retry-After header. */
    retryAfterMs;
    constructor(message, retryAfterMs, cause) {
        super(message);
        this.cause = cause;
        this.name = "BrainRateLimitError";
        this.retryAfterMs = retryAfterMs;
        Object.setPrototypeOf(this, new.target.prototype);
    }
}
import { SpanStatusCode, trace } from "@opentelemetry/api";
const PORT_TRACER = trace.getTracer("vauban-agent-sdk.ports", "0.1.0");
/**
 * Wrap any BrainPort implementation with OTel spans.
 * archiveKnowledge and queryKnowledge calls each get a span with the category
 * and content preview. Gracefully degrades to noop spans when no OTel SDK
 * is installed.
 *
 * Usage:
 *   const raw: BrainPort = buildHttpBrain(...);
 *   const traced = createTracedBrainPort(raw);
 *   await traced.archiveKnowledge({ content: "..." }) // emits "brain.archiveKnowledge" span
 */
export function createTracedBrainPort(impl) {
    return {
        working: impl.working,
        episodic: impl.episodic,
        semantic: impl.semantic,
        procedural: impl.procedural,
        archivePostmortem: impl.archivePostmortem
            ? impl.archivePostmortem.bind(impl)
            : undefined,
        archiveLesson: impl.archiveLesson
            ? impl.archiveLesson.bind(impl)
            : undefined,
        async archiveKnowledge(entry) {
            return PORT_TRACER.startActiveSpan("brain.archiveKnowledge", {
                attributes: {
                    "brain.entry.category": entry.category ?? "unknown",
                    "brain.entry.content_preview": entry.content.slice(0, 200),
                    "brain.entry.tags": entry.tags?.join(",") ?? "",
                    "vauban.port.name": "brain",
                },
            }, async (span) => {
                try {
                    const result = await impl.archiveKnowledge(entry);
                    if (result) {
                        span.setAttribute("brain.entry.id", result.id);
                    }
                    span.setStatus({ code: SpanStatusCode.OK });
                    return result;
                }
                catch (err) {
                    const message = err instanceof Error ? err.message : String(err);
                    span.setStatus({ code: SpanStatusCode.ERROR, message });
                    if (err instanceof Error)
                        span.recordException(err);
                    throw err;
                }
                finally {
                    span.end();
                }
            });
        },
        async queryKnowledge(query, filters) {
            if (!impl.queryKnowledge)
                return [];
            const qk = impl.queryKnowledge.bind(impl);
            return PORT_TRACER.startActiveSpan("brain.queryKnowledge", {
                attributes: {
                    "brain.query.preview": query.slice(0, 200),
                    "brain.query.category": filters?.category ?? "none",
                    "brain.query.limit": filters?.limit ?? -1,
                    "vauban.port.name": "brain",
                },
            }, async (span) => {
                try {
                    const result = await qk(query, filters);
                    span.setAttribute("brain.query.result_count", result.length);
                    span.setStatus({ code: SpanStatusCode.OK });
                    return result;
                }
                catch (err) {
                    const message = err instanceof Error ? err.message : String(err);
                    span.setStatus({ code: SpanStatusCode.ERROR, message });
                    if (err instanceof Error)
                        span.recordException(err);
                    throw err;
                }
                finally {
                    span.end();
                }
            });
        },
    };
}
//# sourceMappingURL=brain.js.map