import { logger } from "../logger.js";
/**
 * Pure decision for whether the wake loop should force an automatic refresh.
 * Kept separate from the side-effecting refresh so the gating logic is unit
 * testable in isolation.
 */
export function shouldAutoRefresh(cfg, timeoutStreak, lastAutoRefreshAtMs, nowMs) {
    if (!cfg.enabled)
        return false;
    if (timeoutStreak < cfg.timeoutStreak)
        return false;
    if (nowMs - lastAutoRefreshAtMs < cfg.cooldownMs)
        return false;
    return true;
}
const DEFAULT_SYSTEM_MESSAGE = "The owner manually triggered a full context refresh. A new session has been created. " +
    "The summary of your previous session has been injected as context above. " +
    "Review it, check your memory and todo list, and continue where you left off.";
/**
 * Core of a context refresh, shared by the manual `/refresh` command handler
 * and the automatic compaction-timeout trigger:
 *   1. summarize the current transcript (tail summary with fallback),
 *   2. reset the active session (rotating its id and injecting the summary),
 *   3. enqueue a system message so the next wake cycle has continuity.
 *
 * Callers are responsible for the `agentState.compacting` mutex and any
 * channel-specific user messaging — this function only performs the reset and
 * returns whether it ran.
 */
export async function performContextRefresh(deps, options) {
    const { session, config, messageQueue } = deps;
    const activeSession = session.activeSession;
    const sessionId = activeSession.getSessionId();
    if (!sessionId) {
        return { refreshed: false, summary: null };
    }
    // Summarize the current transcript before resetting. Shared pipeline with
    // the startup engine-switch boundary: see `summarizeWithFallback`.
    const transcriptInfo = activeSession.getTranscriptInfo();
    const transcriptPath = transcriptInfo.kind === "claude" || transcriptInfo.kind === "pi-coding-agent"
        ? transcriptInfo.sessionFilePath ?? activeSession.getTranscriptPath()
        : null;
    const { summarizeWithFallback } = await import("./transcript-summarizer.js");
    const finalSummary = await summarizeWithFallback(transcriptPath, config);
    // Perform the full session reset.
    session.resetActiveSession(finalSummary);
    // Re-enqueue a system message so the new session is delivered the context.
    messageQueue.enqueue({
        id: `refresh_${options.reason}_${Date.now()}`,
        channel: "system",
        sender: "system",
        text: options.systemMessage ?? DEFAULT_SYSTEM_MESSAGE,
        attachments: [],
        timestamp: new Date().toISOString(),
    });
    logger.system(`Context refresh complete (reason=${options.reason})`);
    return { refreshed: true, summary: finalSummary };
}
async function waitForIdle(agentState, timeoutMs) {
    return new Promise((resolve) => {
        const deadline = Date.now() + timeoutMs;
        const poll = setInterval(() => {
            if (!agentState.busy) {
                clearInterval(poll);
                resolve(true);
            }
            else if (Date.now() >= deadline) {
                clearInterval(poll);
                resolve(false);
            }
        }, 500);
    });
}
/**
 * Single orchestration path for a context refresh, shared by the manual
 * `/refresh` command handler and the automatic compaction-timeout-streak gate.
 *
 * Owns the `agentState.compacting` mutex and (optionally) stopping an in-flight
 * turn, then delegates the actual summarize/reset/enqueue to
 * {@link performContextRefresh}. Channel-specific messaging is supplied via
 * {@link RunContextRefreshHooks} so callers keep their own locale/recipient
 * logic without re-implementing the mutex and refresh sequence.
 *
 * The automatic path passes no requester hooks and simply notifies the owner —
 * it never re-emits a `/refresh` command; it only runs at the idle gate.
 */
export async function runContextRefresh(deps, options) {
    const { session, config, messageQueue, agentState } = deps;
    const { reason, systemMessage, stopActiveTurn, stopTimeoutMs = 30_000, hooks } = options;
    // Claim the compacting mutex upfront so the main loop's wake gate won't start
    // a new turn on the same session while we reset it.
    agentState.compacting = true;
    try {
        if (stopActiveTurn && agentState.busy) {
            await hooks?.onBusyStopping?.();
            if (!agentState.stopRequested) {
                agentState.requestStop();
            }
            const stopped = await waitForIdle(agentState, stopTimeoutMs);
            if (!stopped) {
                return { status: "stop_timeout" };
            }
        }
        if (!session.activeSession.getSessionId()) {
            return { status: "no_session" };
        }
        await hooks?.onRefreshing?.();
        const result = await performContextRefresh({ session, config, messageQueue }, { reason, systemMessage });
        if (!result.refreshed) {
            return { status: "no_session" };
        }
        await hooks?.onRefreshed?.(result);
        return { status: "refreshed", summary: result.summary };
    }
    finally {
        agentState.compacting = false;
    }
}
//# sourceMappingURL=context-refresh.js.map