import { createBranchSummaryMessage, createCustomMessage, messageIsLlmVisible, userLikeContentBlockIsLlmVisible } from "../messages.js";
import { normalizeDerivedSessionEntries } from "../session-entry-normalization.js";
import { buildContextDeletionFilteredPath, buildEffectiveContextDeletionFilters, } from "../session-manager.js";
import { estimateContentBlockTokens, estimateTokens } from "./compaction.js";
import { CONTEXT_COMPACTION_AUTO_QUERY, } from "./context-compaction-types.js";
import { normalizeContextCompactionParameters, normalizeContextCompactionQuery, } from "./context-compaction-strategy.js";
function getMessageFromEntry(entry) {
    if (entry.type === "message") {
        return entry.message;
    }
    if (entry.type === "custom_message") {
        return createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp, entry.excludeFromContext);
    }
    if (entry.type === "branch_summary" && typeof entry.summary === "string") {
        return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
    }
    return undefined;
}
export function isExcludedFromLlmContext(message) {
    return !messageIsLlmVisible(message);
}
function getContextEligibleMessageFromEntry(entry) {
    const message = getMessageFromEntry(entry);
    if (!message || isExcludedFromLlmContext(message))
        return undefined;
    return message;
}
function safelySerialize(value) {
    try {
        const serialized = JSON.stringify(value);
        if (typeof serialized === "string")
            return serialized;
    }
    catch {
        // Preserve a finite diagnostic projection for cyclic future payloads.
    }
    return "[unserializable content]";
}
function textFromUnknownContent(content) {
    if (typeof content === "string")
        return content;
    if (!Array.isArray(content))
        return safelySerialize(content);
    return content.map((block) => textFromContentBlock(block)).join("\n");
}
function textFromContentBlock(block) {
    if (!block || typeof block !== "object")
        return String(block);
    const record = block;
    if (record.type === "text" && typeof record.text === "string")
        return record.text;
    if (record.type === "thinking" && typeof record.thinking === "string")
        return record.thinking;
    if (record.type === "toolCall") {
        const name = typeof record.name === "string" ? record.name : "tool";
        const id = typeof record.id === "string" ? record.id : "unknown";
        const args = "arguments" in record ? safelySerialize(record.arguments) : "";
        return `toolCall ${id} ${name} ${args}`.trim();
    }
    if (record.type === "image")
        return "[image]";
    return safelySerialize(record);
}
function getToolCallIdFromBlock(block) {
    if (!block || typeof block !== "object")
        return undefined;
    const record = block;
    if (record.type !== "toolCall")
        return undefined;
    return typeof record.id === "string" ? record.id : undefined;
}
function getToolResultCallId(message) {
    if (message.role !== "toolResult")
        return undefined;
    const callId = message.toolCallId;
    return typeof callId === "string" ? callId : undefined;
}
function contentBlocksForEntry(entryId, message, protectedEntry, existingDeletedBlocks) {
    const content = message.content;
    if (!Array.isArray(content))
        return [];
    return content
        .map((block, blockIndex) => {
        if (existingDeletedBlocks?.has(blockIndex)) {
            return undefined;
        }
        const type = block && typeof block === "object" && typeof block.type === "string"
            ? (block.type)
            : "unknown";
        const text = textFromContentBlock(block);
        const llmVisible = message.role === "user" || message.role === "custom"
            ? userLikeContentBlockIsLlmVisible(block)
            : true;
        return {
            entryId,
            blockIndex,
            type,
            text,
            tokenEstimate: estimateContentBlockTokens(block, llmVisible),
            protected: protectedEntry,
            llmVisible,
            toolCallId: getToolCallIdFromBlock(block),
        };
    })
        .filter((block) => block !== undefined);
}
function messageText(message) {
    switch (message.role) {
        case "bashExecution":
            return `Ran ${message.command}\n${message.output}`;
        case "branchSummary":
            return message.summary;
        case "custom":
        case "toolResult":
        case "user":
            return textFromUnknownContent(message.content);
        case "assistant":
            return textFromUnknownContent(message.content);
        case "compactionSummary":
            // Legacy summary-compaction message type retained in the upstream AgentMessage union
            // after summary compaction was removed; surface its archival summary text.
            return message.summary;
        default: {
            // Exhaustiveness guard: adding a new AgentMessage role must fail the build here instead
            // of silently degrading to an empty string.
            const _exhaustiveCheck = message;
            void _exhaustiveCheck;
            return "";
        }
    }
}
export function hasAssistantError(message) {
    return message.role === "assistant" && message.stopReason === "error";
}
export function hasToolResultError(message) {
    return message.role === "toolResult" && message.isError === true;
}
export function hasFailedBashExecution(message) {
    return message.role === "bashExecution" && typeof message.exitCode === "number" && message.exitCode !== 0;
}
export function autoDetectContextCompactionQuery(pathEntries) {
    const derivedPathEntries = normalizeDerivedSessionEntries(pathEntries);
    for (let index = derivedPathEntries.length - 1; index >= 0; index--) {
        const entry = derivedPathEntries[index];
        if (entry.type === "context_compaction")
            continue;
        const message = getContextEligibleMessageFromEntry(entry);
        if (!message || message.role !== "user")
            continue;
        const text = messageText(message).trim();
        if (text.length > 0)
            return normalizeContextCompactionQuery(text, CONTEXT_COMPACTION_AUTO_QUERY);
    }
    return CONTEXT_COMPACTION_AUTO_QUERY;
}
function isProtectedEntry(entry, message, recentEntryIds) {
    if (recentEntryIds.has(entry.id))
        return true;
    if (message.role === "user")
        return true;
    if (message.role === "custom")
        return true;
    if (message.role === "branchSummary")
        return true;
    if (hasAssistantError(message) || hasToolResultError(message))
        return true;
    if (hasFailedBashExecution(message))
        return true;
    if (entry.type === "branch_summary")
        return true;
    return false;
}
export function prepareContextCompaction(pathEntries, settings, options = {}) {
    const derivedPathEntries = normalizeDerivedSessionEntries(pathEntries);
    if (derivedPathEntries.length === 0)
        return undefined;
    const effectiveDeletionFilters = buildEffectiveContextDeletionFilters(derivedPathEntries);
    const filteredPathEntries = buildContextDeletionFilteredPath(derivedPathEntries, effectiveDeletionFilters);
    const parameters = normalizeContextCompactionParameters({ ...settings, ...options }, autoDetectContextCompactionQuery(filteredPathEntries));
    const rawEntryById = new Map(derivedPathEntries.map((entry) => [entry.id, entry]));
    const messageEntryIds = filteredPathEntries
        .filter((entry) => entry.type !== "context_compaction" && getContextEligibleMessageFromEntry(entry) !== undefined)
        .map((entry) => entry.id);
    const recentEntryIds = new Set(parameters.preserve_recent > 0 ? messageEntryIds.slice(-parameters.preserve_recent) : []);
    const protectedEntryIds = new Set();
    const entries = [];
    for (const entry of filteredPathEntries) {
        if (entry.type === "context_compaction")
            continue;
        const message = getContextEligibleMessageFromEntry(entry);
        if (!message)
            continue;
        const rawEntry = rawEntryById.get(entry.id) ?? entry;
        const protectedEntry = isProtectedEntry(entry, message, recentEntryIds);
        if (protectedEntry)
            protectedEntryIds.add(entry.id);
        const rawMessage = getContextEligibleMessageFromEntry(rawEntry) ?? message;
        const contentBlocks = contentBlocksForEntry(entry.id, rawMessage, protectedEntry, effectiveDeletionFilters.deletedContentBlocks.get(entry.id));
        const toolCallIds = contentBlocks.map((block) => block.toolCallId).filter((id) => id !== undefined);
        const text = contentBlocks.length > 0 ? contentBlocks.map((block) => block.text).join("\n") : messageText(message);
        entries.push({
            entryId: entry.id,
            entryType: entry.type,
            role: message.role,
            text,
            tokenEstimate: estimateTokens(message),
            protected: protectedEntry,
            contentBlocks,
            message,
            toolCallIds,
            toolResultFor: getToolResultCallId(message),
        });
    }
    if (entries.length < 2)
        return undefined;
    return {
        branchEntries: derivedPathEntries,
        parameters,
        transcript: {
            entries,
            protectedEntryIds: [...protectedEntryIds],
            tokensBefore: entries.reduce((total, entry) => total + entry.tokenEstimate, 0),
            settings,
            parameters,
        },
    };
}
//# sourceMappingURL=context-transcript-analysis.js.map