/**
 * Dependency-graph wiring for `core/pipeline`.
 *
 * The pipeline owns the long-lived objects every algorithm subscriber
 * needs (runners, buses, subscribers). Wiring is deterministic and
 * synchronous so adapters can reason about lifecycle without chasing
 * microtasks — with one exception: `init()` is async because it warms
 * the embedder/LLM bridges.
 */
import { rootLogger } from "../logger/index.js";
import { createSessionManager, createEpisodeManager, createSessionEventBus, createIntentClassifier, createRelationClassifier, adaptSessionsRepo, adaptEpisodesRepo, } from "../session/index.js";
import { createCaptureEventBus, createCaptureRunner, attachCaptureSubscriber, } from "../capture/index.js";
import { createRewardEventBus, createRewardRunner, attachRewardSubscriber, } from "../reward/index.js";
import { attachL2Subscriber, createL2EventBus, } from "../memory/l2/index.js";
import { attachL3Subscriber, createL3EventBus, } from "../memory/l3/index.js";
import { attachSkillSubscriber, createSkillEventBus, } from "../skill/index.js";
import { attachFeedbackSubscriber, createFeedbackEventBus, } from "../feedback/index.js";
import { createRetrievalEventBus, } from "../retrieval/index.js";
import { wrapRetrievalRepos } from "./retrieval-repos.js";
// ─── Algorithm config slice helper ────────────────────────────────────────
/**
 * Translate the validated `ResolvedConfig.algorithm` block (shaped by
 * the TypeBox schema) into the typed configs consumed by each subscriber.
 *
 * Some fields are shared across subscribers (γ / τ / decay half-life) and
 * live on the reward block in `config.yaml`. We copy them into the
 * downstream slices here so subscribers never peek into other blocks.
 */
export function extractAlgorithmConfig(deps) {
    const alg = deps.config.algorithm;
    return {
        lightweightMemory: {
            enabled: alg.lightweightMemory.enabled,
        },
        capture: alg.capture,
        reward: alg.reward,
        l2Induction: {
            minSimilarity: alg.l2Induction.minSimilarity,
            candidateTtlDays: alg.l2Induction.candidateTtlDays,
            gamma: alg.reward.gamma,
            tauSoftmax: alg.reward.tauSoftmax,
            useLlm: alg.l2Induction.useLlm,
            minTraceValue: alg.l2Induction.minTraceValue,
            minEpisodesForInduction: alg.l2Induction.minEpisodesForInduction,
            inductionTraceCharCap: alg.l2Induction.traceCharCap,
            gainEmaAlpha: alg.l2Induction.gainEmaAlpha,
        },
        l3Abstraction: alg.l3Abstraction,
        skill: alg.skill,
        feedback: alg.feedback,
        retrieval: {
            tier1TopK: alg.retrieval.tier1TopK,
            tier2TopK: alg.retrieval.tier2TopK,
            tier3TopK: alg.retrieval.tier3TopK,
            candidatePoolFactor: alg.retrieval.candidatePoolFactor,
            weightCosine: alg.retrieval.weightCosine,
            weightPriority: alg.retrieval.weightPriority,
            mmrLambda: alg.retrieval.mmrLambda,
            includeLowValue: alg.retrieval.includeLowValue,
            rrfConstant: alg.retrieval.rrfConstant,
            minSkillEta: alg.retrieval.minSkillEta,
            minTraceSim: alg.retrieval.minTraceSim,
            tagFilter: alg.retrieval.tagFilter,
            keywordTopK: alg.retrieval.keywordTopK,
            relativeThresholdFloor: alg.retrieval.relativeThresholdFloor,
            skillEtaBlend: alg.retrieval.skillEtaBlend,
            smartSeed: alg.retrieval.smartSeed,
            smartSeedRatio: alg.retrieval.smartSeedRatio,
            multiChannelBypass: alg.retrieval.multiChannelBypass,
            skillInjectionMode: alg.retrieval.skillInjectionMode,
            skillSummaryChars: alg.retrieval.skillSummaryChars,
            decayHalfLifeDays: alg.reward.decayHalfLifeDays,
            llmFilterEnabled: alg.lightweightMemory.enabled ? true : alg.retrieval.llmFilterEnabled,
            llmFilterMaxKeep: alg.retrieval.llmFilterMaxKeep,
            llmFilterMinCandidates: alg.lightweightMemory.enabled ? 1 : alg.retrieval.llmFilterMinCandidates,
            llmFilterCandidateBodyChars: alg.retrieval.llmFilterCandidateBodyChars,
            lightweightMemory: alg.lightweightMemory.enabled,
        },
        session: {
            followUpMode: alg.session.followUpMode,
            mergeMaxGapMs: alg.session.mergeMaxGapMs,
        },
    };
}
// ─── Bus wiring ───────────────────────────────────────────────────────────
export function buildPipelineBuses() {
    return {
        session: createSessionEventBus(),
        capture: createCaptureEventBus(),
        reward: createRewardEventBus(),
        l2: createL2EventBus(),
        l3: createL3EventBus(),
        skill: createSkillEventBus(),
        feedback: createFeedbackEventBus(),
        retrieval: createRetrievalEventBus(),
    };
}
export function buildPipelineSubscribers(deps, buses, algorithm, session) {
    const log = deps.log ?? rootLogger.child({ channel: "core.pipeline" });
    const captureRunner = createCaptureRunner({
        tracesRepo: deps.repos.traces,
        embeddingRetryQueue: deps.repos.embeddingRetryQueue,
        episodesRepo: adaptEpisodesRepo(deps.repos.episodes),
        embedder: deps.embedder,
        llm: deps.llm,
        reflectLlm: deps.reflectLlm,
        bus: buses.capture,
        cfg: algorithm.capture,
        now: deps.now,
    });
    const rewardRunner = createRewardRunner({
        tracesRepo: deps.repos.traces,
        episodesRepo: deps.repos.episodes,
        feedbackRepo: deps.repos.feedback,
        llm: deps.llm,
        bus: buses.reward,
        cfg: algorithm.reward,
        evaluator: {
            reflectionProvider: deps.reflectLlm?.provider,
            reflectionModel: deps.reflectLlm?.model,
            scorerProvider: deps.llm?.provider,
            scorerModel: deps.llm?.model,
        },
        now: deps.now,
        // Wire the live episode snapshot so the R_human scorer sees the
        // real user / assistant turns of the episode. Without this, the
        // reward runner falls back to `fallbackSnapshotFromRow` — a row
        // that has no `turns` — and every episode's task summary
        // degenerates to "(no user text)" / "(no agent text)". That
        // forces the scoring LLM to return `rHuman = 0`, which then
        // keeps every trace's V at exactly 0 and prevents L2 induction
        // (since `minTraceValue` is 0.1 by default), which in turn
        // starves L3 and the skill crystallizer. This is the single
        // biggest cause of "empty Experiences / World Models / Skills
        // pages after weeks of usage".
        getEpisodeSnapshot: session
            ? (id) => session.sessionManager.getEpisode(id)
            : undefined,
    });
    const captureSub = attachCaptureSubscriber(buses.session, captureRunner, {});
    const rewardSub = attachRewardSubscriber(buses.capture, rewardRunner, algorithm.reward, {});
    const l2Handle = attachL2Subscriber({
        db: deps.db,
        repos: deps.repos,
        rewardBus: buses.reward,
        l2Bus: buses.l2,
        llm: deps.llm,
        log: log.child({ channel: "core.memory.l2" }),
        config: algorithm.l2Induction,
        thresholds: {
            minSupport: algorithm.skill.minSupport,
            minGain: algorithm.skill.minGain,
            archiveGain: deps.config.algorithm.l2Induction.archiveGain,
        },
    });
    const l3Handle = attachL3Subscriber({
        repos: deps.repos,
        l2Bus: buses.l2,
        l3Bus: buses.l3,
        llm: deps.llm,
        log: log.child({ channel: "core.memory.l3" }),
        config: algorithm.l3Abstraction,
    });
    const skillHandle = attachSkillSubscriber({
        repos: deps.repos,
        embedder: deps.embedder,
        llm: deps.llm,
        bus: buses.skill,
        l2Bus: buses.l2,
        rewardBus: buses.reward,
        log: log.child({ channel: "core.skill" }),
        config: algorithm.skill,
    });
    const feedbackHandle = attachFeedbackSubscriber({
        repos: deps.repos,
        llm: deps.llm,
        embedder: deps.embedder,
        bus: buses.feedback,
        log: log.child({ channel: "core.feedback" }),
        config: algorithm.feedback,
    });
    return {
        captureRunner,
        rewardRunner,
        l2: l2Handle,
        l3: l3Handle,
        skills: skillHandle,
        feedback: feedbackHandle,
        subscriptions: {
            capture: captureSub,
            reward: rewardSub,
        },
    };
}
export function buildPipelineSession(deps, bus) {
    const llmDisabled = deps.config.algorithm.lightweightMemory.enabled;
    const intent = createIntentClassifier({
        llm: deps.llm ?? undefined,
        disableLlm: llmDisabled,
    });
    const relation = createRelationClassifier({
        llm: deps.llm ?? undefined,
        disableLlm: llmDisabled,
    });
    const episodeManager = createEpisodeManager({
        sessionsRepo: adaptSessionsRepo(deps.repos.sessions),
        episodesRepo: adaptEpisodesRepo(deps.repos.episodes),
        bus,
        now: deps.now,
    });
    const sessionManager = createSessionManager({
        sessionsRepo: adaptSessionsRepo(deps.repos.sessions),
        episodesRepo: adaptEpisodesRepo(deps.repos.episodes),
        intentClassifier: intent,
        bus,
        episodeManager,
        now: deps.now,
        lightweightMemory: llmDisabled,
    });
    return { intent, relation, sessionManager, episodeManager };
}
// ─── Retrieval deps ───────────────────────────────────────────────────────
export function buildRetrievalDeps(deps, algorithm) {
    const embedder = deps.embedder;
    return {
        repos: wrapRetrievalRepos(deps.repos, deps.namespace),
        embedder: embedder
            ? {
                embed: (text, role) => embedder.embedOne({ text, role: role ?? "query" }),
            }
            : {
                // Degraded mode: empty vector so vector-scoring falls back to
                // zeros. Tier 2/3 still work via tags.
                embed: async () => new Float32Array(0),
            },
        config: algorithm.retrieval,
        namespace: deps.namespace,
        now: deps.now ?? Date.now,
        llm: deps.llm,
    };
}
// ─── Log helpers ──────────────────────────────────────────────────────────
export function pipelineLogger(deps) {
    return deps.log ?? rootLogger.child({ channel: "core.pipeline" });
}
/** Called by tests that need to assert on the wiring. */
export { createRetrievalEventBus, createSessionEventBus };
//# sourceMappingURL=deps.js.map