import { createCompactionActions, triggerCompactionIfNeeded } from '../core/session/compaction-bridge.js';
import { SessionCompaction } from '../core/session/session-compaction.js';
/** Strips a trailing `[assistant(error), toolResult...]` group so retries see clean state. */
function removeLastErrorGroup(agent, onAgentMessagesReplaced) {
    const messages = agent.state.messages;
    let i = messages.length - 1;
    while (i >= 0 && messages[i].role === 'toolResult')
        i--;
    if (i >= 0 &&
        messages[i].role === 'assistant' &&
        messages[i].stopReason === 'error') {
        const nextMessages = messages.slice(0, i);
        agent.replaceMessages(nextMessages);
        onAgentMessagesReplaced?.(nextMessages);
    }
}
function findLastAssistantMessage(messages) {
    for (let i = messages.length - 1; i >= 0; i--) {
        if (messages[i].role === 'assistant')
            return messages[i];
    }
    return undefined;
}
export function createEmbeddedCompactionWiring(options) {
    const compaction = new SessionCompaction({ complete: options.completeFn });
    const compactionActions = createCompactionActions({
        extensionRunner: () => undefined,
        sessionManager: options.sessionStore,
        agent: options.agent,
        removeLastErrorGroup: () => removeLastErrorGroup(options.agent, options.onAgentMessagesReplaced),
        onMessagesReplaced: options.onMessagesReplaced
    });
    const triggerDeps = {
        agent: options.agent,
        compaction,
        compactionActions,
        model: () => options.agent.state.model,
        modelRegistry: options.modelRegistry,
        sessionManager: options.sessionStore,
        getContextWindowOverride: () => {
            const limit = options.getPerTurnOptions().baseTokenLimit;
            return limit && limit > 0 ? limit : undefined;
        },
        onAgentMessagesReplaced: options.onAgentMessagesReplaced
    };
    // Serialize triggers so an overlapping post-turn fires after a pending
    // pre-submit settles. Compaction runs are short and never re-entrant.
    let pending = Promise.resolve();
    return {
        triggerIfNeeded(skipAbortedCheck) {
            // Capture the per-turn heartbeat wrap SYNCHRONOUSLY, before scheduling the
            // async trigger body. The body runs as a microtask after funnel.handle
            // returns; by the time it awaits triggerCompactionIfNeeded, the agent
            // loop's finally may have already cleared _activeWrap. Capturing here
            // keeps a valid wrap reference for the whole compaction lifecycle.
            // Passing it as a per-call argument all the way to runAuto means
            // trigger-check no-ops (decision='none') and the early-return paths in
            // runAuto still emit zero compaction_* telemetry — only the actual LLM
            // call + persist span gets a started/completed pair.
            const heartbeatWrap = options.getHeartbeatWrap?.();
            const next = pending.then(async () => {
                const lastAssistant = findLastAssistantMessage(options.agent.state.messages);
                if (!lastAssistant)
                    return;
                await triggerCompactionIfNeeded(triggerDeps, lastAssistant, skipAbortedCheck, {
                    heartbeatWrap
                });
            });
            pending = next.catch(() => undefined);
            return next;
        },
        whenIdle() {
            return pending;
        }
    };
}
//# sourceMappingURL=compaction-wiring.js.map