import { rawTokensSinceLastCompaction } from "../branch.js";
/**
 * Regex matching spectral's internal retryable error detection.
 * When the last assistant message in agent_end has stopReason "error" matching this pattern,
 * Pi will auto-retry — we must not trigger compaction between attempts.
 */
const RETRYABLE_ERROR_RE = /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i;
export function registerCompactionTrigger(ext, runtime) {
    ext.on("agent_end", (event, ctx) => {
        runtime.ensureConfig(ctx.cwd);
        if (runtime.config.passive === true)
            return;
        if (runtime.compactInFlight)
            return;
        // Don't trigger compaction if Pi will auto-retry — the agent hasn't truly finished.
        // Pi emits agent_end before its own retry check, so we must detect this ourselves.
        // The next agent_end (after retry succeeds or exhausts attempts) will re-evaluate.
        const lastAssistant = [...event.messages].reverse().find((m) => m.role === "assistant");
        if (lastAssistant
            && lastAssistant.stopReason === "error"
            && lastAssistant.errorMessage
            && RETRYABLE_ERROR_RE.test(lastAssistant.errorMessage)) {
            return;
        }
        const entries = ctx.sessionManager.getBranch();
        const tokens = rawTokensSinceLastCompaction(entries);
        if (tokens < runtime.config.compactionThresholdTokens)
            return;
        // Capture ctx properties synchronously — the setTimeout + async work below
        // may outlive the extension ctx (stale after session replacement/reload).
        const hasUI = ctx.hasUI;
        const ui = ctx.ui;
        if (hasUI)
            ui?.notify(`Observational memory: compaction threshold reached (~${tokens.toLocaleString()} tokens); triggering compaction`, "info");
        runtime.compactInFlight = true;
        setTimeout(async () => {
            if (runtime.observerPromise) {
                try {
                    await runtime.observerPromise;
                }
                catch {
                    // errors already surfaced via launchObserverTask
                }
            }
            // After awaiting observerPromise, ctx may be stale.
            // Use captured hasUI/ui for notification; wrap ctx access in try/catch.
            try {
                if (!ctx.isIdle()) {
                    runtime.compactInFlight = false;
                    if (hasUI)
                        ui?.notify("Observational memory: compaction deferred — agent became busy after observer wait", "info");
                    return;
                }
                const currentEntries = ctx.sessionManager.getBranch();
                const currentTokens = rawTokensSinceLastCompaction(currentEntries);
                if (currentTokens < runtime.config.compactionThresholdTokens) {
                    runtime.compactInFlight = false;
                    if (hasUI)
                        ui?.notify("Observational memory: compaction skipped — another compaction already ran during observer wait", "info");
                    return;
                }
                ctx.compact({
                    onComplete: () => {
                        runtime.compactInFlight = false;
                        if (hasUI)
                            ui?.notify("Observational memory: compaction complete", "info");
                    },
                    onError: (error) => {
                        runtime.compactInFlight = false;
                        if (error.message === "Compaction cancelled") {
                            // We already notified the user with the real reason before returning { cancel: true }.
                            return;
                        }
                        if (hasUI)
                            ui?.notify(`Observational memory: ${error.message}`, "error");
                    },
                });
            }
            catch (error) {
                runtime.compactInFlight = false;
                const msg = error instanceof Error ? error.message : String(error);
                if (hasUI)
                    ui?.notify(`Observational memory: compact threw: ${msg}`, "error");
            }
        }, 0);
    });
}
