var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
import { EXCLUDED_MESSAGE_TYPES, TEXT_ATTACHMENT_MIME_TYPES, } from "@superblocksteam/library-shared/types";
import { getErrorMeta } from "../../util/logger.js";
import llmobs from "../llmobs/index.js";
import { getLastUserMessage } from "./extract-history.js";
import { inferTextAttachmentType, sanitizeAiProviderResponse, sanitizeToolOutput, } from "./utils.js";
export function isAiChatMessage(item) {
    return "role" in item && "content" in item;
}
export function isAiChatSummary(item) {
    return "summary" in item && "relevantFilePaths" in item;
}
const CHAT_PAYLOAD_INELIGIBILITY_MARKER = "ineligibleSinceRestore";
function isEligibleSummary(item) {
    // Filter out summaries marked as ineligible (from checkpoint restores)
    return isAiChatSummary(item) && !(CHAT_PAYLOAD_INELIGIBILITY_MARKER in item);
}
/**
 * Default maximum number of messages to load from the server.
 * This prevents large requests from failing when there are many messages.
 */
export const DEFAULT_MESSAGE_LIMIT = 3000;
let ChatSessionStore = (() => {
    let _instanceExtraInitializers = [];
    let _getMessages_decorators;
    let _getLatestSummary_decorators;
    return class ChatSessionStore {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
            _getMessages_decorators = [llmobs.decorate({
                    kind: "task",
                    name: "ChatSessionStore.getMessages",
                })];
            _getLatestSummary_decorators = [llmobs.decorate({
                    kind: "task",
                    name: "ChatSessionStore.getLatestSummary",
                })];
            __esDecorate(this, null, _getMessages_decorators, { kind: "method", name: "getMessages", static: false, private: false, access: { has: obj => "getMessages" in obj, get: obj => obj.getMessages }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _getLatestSummary_decorators, { kind: "method", name: "getLatestSummary", static: false, private: false, access: { has: obj => "getLatestSummary" in obj, get: obj => obj.getLatestSummary }, metadata: _metadata }, null, _instanceExtraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        applicationId = __runInitializers(this, _instanceExtraInitializers);
        rpcClient;
        logger;
        messages;
        inFlightLoadFromServer;
        loadedFromServer = "no";
        inProgressMessages = {};
        messageLimit;
        constructor(config) {
            this.rpcClient = config.rpcClient;
            this.applicationId = config.applicationId;
            this.messages = [];
            this.logger = config.logger;
            this.messageLimit = config.messageLimit ?? DEFAULT_MESSAGE_LIMIT;
            // NOTE: This is used to serialize messages to be after any load.
            // We initialize it as resolved to handle the prompt-from-empty-state case.
            // TODO(ai): consider replacing this with a queue-based approach
            this.inFlightLoadFromServer = Promise.resolve();
        }
        /***
         * Validates and sanitizes the attachments array
         * @param attachments - The attachments array to validate
         * @returns A tuple containing:
         *   - a boolean indicating if the attachments are valid
         *   - the sanitized attachments array
         */
        validateAttachments(attachments) {
            if (!attachments) {
                return [true, undefined];
            }
            if (typeof attachments !== "object" || !Array.isArray(attachments)) {
                return [false, undefined];
            }
            const validAttachments = attachments
                .map((attachment) => {
                if (typeof attachment !== "object" || !("type" in attachment)) {
                    return undefined;
                }
                // Validate image attachment
                if (attachment.type === "image") {
                    if (typeof attachment.image !== "string") {
                        return undefined;
                    }
                    return {
                        type: attachment.type,
                        image: attachment.image,
                        fileName: typeof attachment.fileName === "string"
                            ? attachment.fileName
                            : undefined,
                    };
                }
                // Validate PDF attachment
                if (attachment.type === "pdf") {
                    if (typeof attachment.data !== "string") {
                        return undefined;
                    }
                    return {
                        type: attachment.type,
                        data: attachment.data,
                        fileName: typeof attachment.fileName === "string"
                            ? attachment.fileName
                            : undefined,
                        size: attachment.size,
                    };
                }
                // Validate archive attachment (zip, tgz, gz)
                if (attachment.type === "archive") {
                    if (typeof attachment.data !== "string") {
                        return undefined;
                    }
                    return {
                        type: attachment.type,
                        data: attachment.data,
                        fileName: typeof attachment.fileName === "string"
                            ? attachment.fileName
                            : undefined,
                        size: attachment.size,
                    };
                }
                const normalizedTextAttachmentType = typeof attachment.content === "string"
                    ? inferTextAttachmentType({
                        attachmentType: attachment.type,
                        mimeType: attachment.mimeType,
                        fileName: typeof attachment.fileName === "string"
                            ? attachment.fileName
                            : "",
                    })
                    : null;
                // Validate text attachments and normalize any text-like subtype to the
                // stable server-side text attachment shape.
                if (normalizedTextAttachmentType) {
                    if (typeof attachment.content !== "string") {
                        return undefined;
                    }
                    return {
                        type: normalizedTextAttachmentType,
                        content: attachment.content,
                        fileName: typeof attachment.fileName === "string"
                            ? attachment.fileName
                            : undefined,
                        mimeType: typeof attachment.mimeType === "string"
                            ? attachment.mimeType
                            : TEXT_ATTACHMENT_MIME_TYPES[normalizedTextAttachmentType],
                        size: attachment.size,
                    };
                }
                if (attachment.type === "uploaded") {
                    if (typeof attachment.url !== "string" ||
                        typeof attachment.mediaType !== "string") {
                        return undefined;
                    }
                    return {
                        type: "uploaded",
                        url: attachment.url,
                        signedUrl: typeof attachment.signedUrl === "string"
                            ? attachment.signedUrl
                            : undefined,
                        mediaType: attachment.mediaType,
                        fileName: typeof attachment.fileName === "string"
                            ? attachment.fileName
                            : undefined,
                        label: typeof attachment.label === "string"
                            ? attachment.label
                            : undefined,
                        insight: typeof attachment.insight === "string"
                            ? attachment.insight
                            : undefined,
                        storageKey: typeof attachment.storageKey === "string"
                            ? attachment.storageKey
                            : undefined,
                        signedUrlExpiresAt: typeof attachment.signedUrlExpiresAt === "string"
                            ? attachment.signedUrlExpiresAt
                            : undefined,
                        scopeType: attachment.scopeType === "app" || attachment.scopeType === "org"
                            ? attachment.scopeType
                            : undefined,
                        applicationId: typeof attachment.applicationId === "string"
                            ? attachment.applicationId
                            : undefined,
                    };
                }
                return undefined;
            })
                .filter(Boolean);
            return [validAttachments.length === attachments.length, validAttachments];
        }
        validateChatMessageV1(payload) {
            try {
                if (!payload || typeof payload !== "object") {
                    return null;
                }
                const msg = payload;
                // Validate required fields. Legacy roles "server" and "ai" (written by
                // older server paths such as setApplicationHash for `draftCommitted`
                // messages, and recognized as assistant-like in commitMessage.ts) are
                // coerced to "assistant" so existing chat history keeps rendering.
                if (typeof msg.role !== "string") {
                    this.logger.warn(`[ai-service] Invalid message role: ${msg.role}`);
                    return null;
                }
                const normalizedRole = msg.role === "server" || msg.role === "ai" ? "assistant" : msg.role;
                if (!["user", "assistant", "system"].includes(normalizedRole)) {
                    this.logger.warn(`[ai-service] Invalid message role after normalize: ${msg.role}`);
                    return null;
                }
                msg.role = normalizedRole;
                if (typeof msg.content !== "string") {
                    this.logger.warn(`[ai-service] Invalid message content (type=${typeof msg.content})`);
                    return null;
                }
                if (typeof msg.type !== "string") {
                    this.logger.warn(`[ai-service] Invalid message type: ${msg.type}`);
                    return null;
                }
                // Validate optional fields with defaults
                const timestamp = typeof msg.timestamp === "number" ? msg.timestamp : Date.now();
                const group = typeof msg.group === "string" ? msg.group : undefined;
                const status = typeof msg.status === "string" ? msg.status : undefined;
                const action = typeof msg.action === "string" ? msg.action : undefined;
                const title = typeof msg.title === "string" ? msg.title : undefined;
                const streaming = typeof msg.streaming === "boolean" ? msg.streaming : undefined;
                const integrations = Array.isArray(msg.integrations)
                    ? msg.integrations
                    : undefined;
                const entities = Array.isArray(msg.entities)
                    ? msg.entities
                    : undefined;
                const [isAttachmentsValid, attachments] = this.validateAttachments(msg.attachments);
                if (!isAttachmentsValid) {
                    this.logger.warn(`[ai-service] Invalid attachments`);
                    return null;
                }
                if (msg.type === "tool") {
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "tool",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        status: status,
                        action: action,
                        args: msg.args,
                        tool: msg.tool,
                        toolCallId: typeof msg.toolCallId === "string" ? msg.toolCallId : undefined,
                        title,
                    };
                }
                else if (msg.type === "tool-result") {
                    if (typeof msg.toolCallId !== "string" ||
                        typeof msg.toolName !== "string") {
                        this.logger.warn(`[ai-service] Invalid tool-result message: missing required fields`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content || "",
                        type: "tool-result",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        status: status,
                        toolCallId: msg.toolCallId,
                        toolName: msg.toolName,
                        output: msg.output,
                        title,
                    };
                }
                else if (msg.type === "tool-error") {
                    if (typeof msg.toolCallId !== "string" ||
                        typeof msg.toolName !== "string") {
                        this.logger.warn(`[ai-service] Invalid tool-error message: missing required fields`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content || "",
                        type: "tool-error",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        status: status,
                        toolCallId: msg.toolCallId,
                        toolName: msg.toolName,
                        error: msg.error instanceof Error
                            ? msg.error.message
                            : typeof msg.error === "string"
                                ? msg.error
                                : (JSON.stringify(msg.error, null, 2) ?? "Unknown error"),
                        title,
                        errorCode: msg.errorCode,
                        errorSummary: typeof msg.errorSummary === "string"
                            ? msg.errorSummary
                            : undefined,
                    };
                }
                else if (msg.group === "checklist") {
                    if (typeof msg.checklistData !== "object") {
                        this.logger.warn(`[ai-service] Invalid checklist data`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "tool",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        group,
                        status,
                        action,
                        checklistData: msg.checklistData,
                        title,
                    };
                }
                else if (msg.type === "multi_choice") {
                    if (!Array.isArray(msg.choices) || msg.choices.length === 0) {
                        this.logger.warn(`[ai-service] Invalid choices for multi_choice message`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "multi_choice",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        choices: msg.choices,
                        selectionType: typeof msg.selectionType === "string"
                            ? msg.selectionType
                            : "single",
                        title,
                        questionType: typeof msg.questionType === "string"
                            ? msg.questionType
                            : undefined,
                        subtitle: typeof msg.subtitle === "string" ? msg.subtitle : undefined,
                        details: typeof msg.details === "string" ? msg.details : undefined,
                        knowledgeCandidates: Array.isArray(msg.knowledgeCandidates)
                            ? msg.knowledgeCandidates
                            : undefined,
                    };
                }
                else if (msg.type === "remember_knowledge_draft") {
                    if (!Array.isArray(msg.candidates) || msg.candidates.length === 0) {
                        this.logger.warn(`[ai-service] Invalid candidates for remember_knowledge_draft message`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "remember_knowledge_draft",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        candidates: msg.candidates,
                        title,
                    };
                }
                else if (msg.type === "multi_choice_response") {
                    if (typeof msg.responseToMessageId !== "string") {
                        this.logger.warn(`[ai-service] Invalid responseToMessageId for multi_choice_response`);
                        return null;
                    }
                    if (!Array.isArray(msg.selectedChoiceIndices)) {
                        this.logger.warn(`[ai-service] Invalid selectedChoiceIndices for multi_choice_response`);
                        return null;
                    }
                    const response = {
                        role: msg.role,
                        content: msg.content,
                        type: "multi_choice_response",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        responseToMessageId: msg.responseToMessageId,
                        selectedChoiceIndices: msg.selectedChoiceIndices,
                        selectedScopes: Array.isArray(msg.selectedScopes)
                            ? msg.selectedScopes
                            : undefined,
                        savedFacts: Array.isArray(msg.savedFacts)
                            ? msg.savedFacts
                            : undefined,
                        title,
                    };
                    return response;
                }
                else if (msg.type === "remember_knowledge_draft_response") {
                    if (typeof msg.responseToMessageId !== "string") {
                        this.logger.warn(`[ai-service] Invalid responseToMessageId for remember_knowledge_draft_response`);
                        return null;
                    }
                    if (!Array.isArray(msg.selectedCandidateIndices)) {
                        this.logger.warn(`[ai-service] Invalid selectedCandidateIndices for remember_knowledge_draft_response`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "remember_knowledge_draft_response",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        responseToMessageId: msg.responseToMessageId,
                        selectedCandidateIndices: msg.selectedCandidateIndices,
                        selectedScopes: Array.isArray(msg.selectedScopes)
                            ? msg.selectedScopes
                            : undefined,
                        savedFacts: Array.isArray(msg.savedFacts)
                            ? msg.savedFacts
                            : undefined,
                        title,
                    };
                }
                else if (msg.type === "confirm") {
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "confirm",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        knowledgeEntry: typeof msg.knowledgeEntry === "object" &&
                            msg.knowledgeEntry !== null
                            ? msg.knowledgeEntry
                            : undefined,
                        title,
                    };
                }
                else if (msg.type === "searchable_dropdown") {
                    if (!Array.isArray(msg.options) || msg.options.length === 0) {
                        this.logger.warn(`[ai-service] Invalid options for searchable_dropdown message`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "searchable_dropdown",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        options: msg.options,
                        placeholder: typeof msg.placeholder === "string" ? msg.placeholder : undefined,
                        title,
                    };
                }
                else if (msg.type === "searchable_dropdown_response") {
                    if (typeof msg.responseToMessageId !== "string") {
                        this.logger.warn(`[ai-service] Invalid responseToMessageId for searchable_dropdown_response`);
                        return null;
                    }
                    if (typeof msg.selectedValue !== "string") {
                        this.logger.warn(`[ai-service] Invalid selectedValue for searchable_dropdown_response`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "searchable_dropdown_response",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        responseToMessageId: msg.responseToMessageId,
                        selectedValue: msg.selectedValue,
                        selectedLabel: typeof msg.selectedLabel === "string"
                            ? msg.selectedLabel
                            : msg.selectedValue,
                        title,
                    };
                }
                else if (msg.type === "confirm_response") {
                    if (typeof msg.responseToMessageId !== "string") {
                        this.logger.warn(`[ai-service] Invalid responseToMessageId for confirm_response`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "confirm_response",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        responseToMessageId: msg.responseToMessageId,
                        approved: Boolean(msg.approved),
                        selectedScopes: Array.isArray(msg.selectedScopes)
                            ? msg.selectedScopes
                            : undefined,
                        savedFacts: Array.isArray(msg.savedFacts)
                            ? msg.savedFacts
                            : undefined,
                        title,
                    };
                }
                else if (msg.type === "plan_response") {
                    if (typeof msg.responseToMessageId !== "string") {
                        this.logger.warn(`[ai-service] Invalid responseToMessageId for plan_response`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "plan_response",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        responseToMessageId: msg.responseToMessageId,
                        title,
                        approved: typeof msg.approved === "boolean" ? msg.approved : undefined,
                        enableTesting: typeof msg.enableTesting === "boolean"
                            ? msg.enableTesting
                            : undefined,
                    };
                }
                else if (msg.type === "test_run_start") {
                    if (typeof msg.testRunId !== "string") {
                        this.logger.warn(`[ai-service] Invalid test_run_start message: missing testRunId`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "test_run_start",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        testRunId: msg.testRunId,
                        header: typeof msg.header === "string" ? msg.header : "End-to-end Testing",
                        testCases: Array.isArray(msg.testCases) ? msg.testCases : undefined,
                    };
                }
                else if (msg.type === "test_case_update") {
                    if (typeof msg.testRunId !== "string") {
                        this.logger.warn(`[ai-service] Invalid test_case_update message: missing testRunId`);
                        return null;
                    }
                    if (typeof msg.testCaseId !== "string") {
                        this.logger.warn(`[ai-service] Invalid test_case_update message: missing testCaseId`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "test_case_update",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        testRunId: msg.testRunId,
                        testCaseId: msg.testCaseId,
                        testCaseStatus: msg.testCaseStatus,
                    };
                }
                else if (msg.type === "test_run_end") {
                    if (typeof msg.testRunId !== "string") {
                        this.logger.warn(`[ai-service] Invalid test_run_end message: missing testRunId`);
                        return null;
                    }
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "test_run_end",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        testRunId: msg.testRunId,
                        testStatus: typeof msg.testStatus === "string"
                            ? msg.testStatus
                            : "completed",
                        summary: typeof msg.summary === "string" ? msg.summary : undefined,
                    };
                }
                else if (msg.type === "retry_notification") {
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "retry_notification",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        retryType: msg.retryType,
                        attempt: typeof msg.attempt === "number" ? msg.attempt : undefined,
                        delaySeconds: typeof msg.delaySeconds === "number" ? msg.delaySeconds : undefined,
                        fromProvider: typeof msg.fromProvider === "string" ? msg.fromProvider : undefined,
                        toProvider: typeof msg.toProvider === "string" ? msg.toProvider : undefined,
                        reason: typeof msg.reason === "string" ? msg.reason : undefined,
                    };
                }
                else if (msg.type === "knowledge") {
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "knowledge",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        status,
                        facts: msg.facts,
                    };
                }
                else if (msg.type === "app_knowledge_saved") {
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "app_knowledge_saved",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        knowledgeCount: typeof msg.knowledgeCount === "number" ? msg.knowledgeCount : 0,
                        responseToMessageId: typeof msg.responseToMessageId === "string"
                            ? msg.responseToMessageId
                            : undefined,
                        savedFacts: Array.isArray(msg.savedFacts)
                            ? msg.savedFacts
                            : undefined,
                    };
                }
                else if (msg.type === "next_steps") {
                    return {
                        role: msg.role,
                        content: msg.content,
                        type: "next_steps",
                        id: typeof msg.id === "string" ? msg.id : undefined,
                        timestamp,
                        nextSteps: Array.isArray(msg.nextSteps) ? msg.nextSteps : [],
                    };
                }
                const baseMessage = {
                    role: msg.role,
                    content: msg.content,
                    type: msg.type,
                    id: typeof msg.id === "string" ? msg.id : undefined,
                    timestamp,
                    group,
                    status: status,
                    action: action,
                    attachments,
                    title,
                };
                if (msg.type === "plan") {
                    if (streaming !== undefined) {
                        baseMessage.streaming = streaming;
                    }
                    if (integrations) {
                        baseMessage.integrations = integrations;
                    }
                    if (entities) {
                        baseMessage.entities = entities;
                    }
                }
                // Preserve commitId for commitRestored, commitRestoring, and draftCommitted messages
                if ((msg.type === "commitRestored" ||
                    msg.type === "commitRestoring" ||
                    msg.type === "draftCommitted") &&
                    typeof msg.commitId === "string") {
                    baseMessage.commitId =
                        msg.commitId;
                }
                return baseMessage;
            }
            catch (error) {
                this.logger.error("[ai-service] Failed to validate message payload", getErrorMeta(error));
                return null;
            }
        }
        validateChatSummaryV1(payload) {
            try {
                if (!payload || typeof payload !== "object") {
                    return null;
                }
                const summary = payload;
                // Validate required fields
                if (typeof summary.summary !== "string") {
                    this.logger.warn(`[ai-service] Invalid summary content`);
                    return null;
                }
                if (!Array.isArray(summary.relevantFilePaths)) {
                    this.logger.warn(`[ai-service] Invalid relevantFilePaths - must be array`);
                    return null;
                }
                if (!summary.relevantFilePaths.every((path) => typeof path === "string")) {
                    this.logger.warn(`[ai-service] Invalid relevantFilePaths - all items must be strings`);
                    return null;
                }
                if (!Array.isArray(summary.goals)) {
                    this.logger.warn(`[ai-service] Invalid goals - must be array`);
                    return null;
                }
                if (!summary.goals.every((goal) => typeof goal === "string")) {
                    this.logger.warn(`[ai-service] Invalid goals - all items must be strings`);
                    return null;
                }
                if (!Array.isArray(summary.decisions)) {
                    this.logger.warn(`[ai-service] Invalid decisions - must be array`);
                    return null;
                }
                if (!summary.decisions.every((decision) => typeof decision === "string")) {
                    this.logger.warn(`[ai-service] Invalid decisions - all items must be strings`);
                    return null;
                }
                // Validate optional fields with defaults
                const timestamp = typeof summary.timestamp === "number" ? summary.timestamp : Date.now();
                return {
                    summary: summary.summary,
                    relevantFilePaths: summary.relevantFilePaths,
                    goals: summary.goals,
                    decisions: summary.decisions,
                    adviceToAgents: summary.adviceToAgents,
                    timestamp,
                };
            }
            catch (error) {
                this.logger.error("[ai-service] Failed to validate summary payload", getErrorMeta(error));
                return null;
            }
        }
        validateChatItemV1(payload) {
            // First try to validate as a message
            const messageResult = this.validateChatMessageV1(payload);
            if (messageResult) {
                return messageResult;
            }
            // If not a valid message, try to validate as a summary
            const summaryResult = this.validateChatSummaryV1(payload);
            if (summaryResult) {
                return summaryResult;
            }
            return null;
        }
        /**
         * Shallow bucket for why a chat payload failed validation (for EE/Datadog).
         * Must stay bounded — no free-form message bodies.
         */
        async ensureLoadedFromServer(options = { forceReload: false }) {
            const { forceReload } = options;
            if (forceReload) {
                this.loadedFromServer = "no";
            }
            // NOTE: we will want to re-evaluate this if/when the auth token associated with this sandbox is changed
            // the existing solution is that each time the page loaded by the client, we re-fetch from the server,
            // so this may be a non-issue.
            if (this.loadedFromServer === "loaded" ||
                this.loadedFromServer === "loading") {
                await this.inFlightLoadFromServer;
                return;
            }
            try {
                this.loadedFromServer = "loading";
                this.inFlightLoadFromServer = this.rpcClient.call(async (client) => {
                    const applicationId = this.applicationId;
                    const { data } = await client.call.v1.ai.chat.getMessages({
                        applicationId,
                        limit: this.messageLimit,
                    });
                    const rawMessages = data?.messages ?? [];
                    const mapped = rawMessages.map((msg) => {
                        const chatItem = this.validateChatItemV1(msg.chatPayload);
                        if (chatItem && isAiChatMessage(chatItem)) {
                            chatItem.authorEmail = msg.authorEmail;
                            chatItem.authorName = msg.authorName;
                        }
                        return chatItem;
                    });
                    const droppedTotal = mapped.filter((item) => item === null).length;
                    const fromRpcTotal = rawMessages.length;
                    this.messages =
                        mapped
                            .filter((item) => item !== null)
                            .sort((a, b) => {
                            return (a.timestamp || 0) - (b.timestamp || 0);
                        }) ?? [];
                    this.logger.info(`[ai-service] chatHistoryLoadSummary accepted=${this.messages.length} dropped=${droppedTotal} total=${fromRpcTotal}`);
                    if (droppedTotal > 0) {
                        this.logger.warn(`[ai-service] chatHistoryDroppedMessages count=${droppedTotal}`);
                    }
                });
                await this.inFlightLoadFromServer;
                this.loadedFromServer = "loaded";
            }
            catch (error) {
                this.loadedFromServer = "no";
                this.logger.error("[ai-service] Failed to load chat messages from server", getErrorMeta(error));
            }
        }
        getLastUserMessage() {
            const messages = this.messages.filter(isAiChatMessage);
            return getLastUserMessage(messages);
        }
        async getMessages(options) {
            await this.ensureLoadedFromServer({
                forceReload: options?.force ?? false,
            });
            return this.messages.filter(isAiChatMessage);
        }
        /**
         * Get the most recent commitId from draftCommitted or commitRestored messages.
         * Waits for messages to load from server first.
         * Returns undefined if no commits found.
         */
        async getLatestCommitId() {
            await this.ensureLoadedFromServer();
            // Find most recent message with a commitId (iterate backwards)
            // - draftCommitted: created when a draft is accepted
            // - commitRestored: created when restoring to a checkpoint (contains new SAVEPOINT_RESTORE commit)
            for (let i = this.messages.length - 1; i >= 0; i--) {
                const msg = this.messages[i];
                if (msg &&
                    isAiChatMessage(msg) &&
                    (msg.type === "draftCommitted" || msg.type === "commitRestored") &&
                    "commitId" in msg &&
                    typeof msg.commitId === "string") {
                    return msg.commitId;
                }
            }
            return undefined;
        }
        async newChat() {
            try {
                this.logger.info("[ai-service] chatSessionNewStart");
                await this.rpcClient.call(async (client) => {
                    const applicationId = this.applicationId;
                    return await client.call.v1.ai.chat.new({ applicationId });
                });
                this.loadedFromServer = "no";
                this.messages = [];
                this.logger.info("[ai-service] chatSessionNewSuccess");
            }
            catch (error) {
                this.logger.error("[ai-service] Failed to create new chat", getErrorMeta(error));
                throw error;
            }
        }
        async truncateAfterTimestamp(sinceTimestamp) {
            try {
                this.logger.info("[ai-service] chatSessionTruncateStart");
                // Truncate chat messages on server
                await this.rpcClient.call(async (client) => {
                    const applicationId = this.applicationId;
                    return await client.call.v1.ai.chat.truncateAfterTimestamp({
                        applicationId,
                        sinceTimestamp,
                    });
                });
                // Reload messages from server after truncation
                await this.ensureLoadedFromServer({ forceReload: true });
                this.logger.info(`[ai-service] chatSessionTruncateSuccess messages=${this.messages.length}`);
            }
            catch (error) {
                this.logger.error("[ai-service] Failed to truncate chat messages", getErrorMeta(error));
                throw error;
            }
        }
        async recordUser(text, options = {}) {
            const message = {
                role: "user",
                content: text,
                id: crypto.randomUUID(),
                timestamp: Date.now(),
                type: "text",
            };
            if (options.attachments) {
                message.attachments = options.attachments;
            }
            return (await this.appendMessage(message)) ?? "";
        }
        async recordUserAcceptance() {
            const message = {
                role: "user",
                content: "",
                id: crypto.randomUUID(),
                timestamp: Date.now(),
                type: "draftAccepted",
            };
            return (await this.appendMessage(message)) ?? "";
        }
        async recordUserRejection() {
            const message = {
                role: "user",
                content: "",
                id: crypto.randomUUID(),
                timestamp: Date.now(),
                type: "draftRejected",
            };
            return (await this.appendMessage(message)) ?? "";
        }
        async recordUserResponse(text, responseType, metadata) {
            const baseMessage = {
                role: "user",
                content: text,
                id: crypto.randomUUID(),
                timestamp: Date.now(),
                type: responseType,
                responseToMessageId: metadata.responseToMessageId,
            };
            if (responseType === "multi_choice_response" &&
                "selectedChoiceIndices" in metadata &&
                metadata.selectedChoiceIndices) {
                baseMessage.selectedChoiceIndices =
                    metadata.selectedChoiceIndices;
                baseMessage.selectedScopes =
                    metadata.selectedScopes;
            }
            else if (responseType === "remember_knowledge_draft_response" &&
                "selectedCandidateIndices" in metadata &&
                metadata.selectedCandidateIndices) {
                baseMessage.selectedCandidateIndices = metadata.selectedCandidateIndices;
                baseMessage.selectedScopes =
                    metadata.selectedScopes;
            }
            else if (responseType === "confirm_response") {
                baseMessage.approved =
                    metadata.approved ?? false;
                baseMessage.selectedScopes =
                    metadata.selectedScopes;
            }
            else if (responseType === "plan_response") {
                baseMessage.approved =
                    metadata.approved ?? true;
                baseMessage.enableTesting =
                    metadata.enableTesting;
            }
            else if (responseType === "searchable_dropdown_response") {
                baseMessage.selectedValue =
                    metadata.selectedValue ?? "";
                baseMessage.selectedLabel =
                    metadata.selectedLabel ?? "";
            }
            return (await this.appendMessage(baseMessage)) ?? "";
        }
        recordInProgressMessage(payload) {
            // given an id and a partial message (string) either add or append the message to the inProgressMessages record
            const { id, text, type } = payload;
            const key = `${id}-${type}`;
            if (this.inProgressMessages[key]) {
                this.inProgressMessages[key] += text;
            }
            else {
                this.inProgressMessages[key] = text;
            }
        }
        async commitMessage(id, type, group) {
            const key = `${id}-${type}`;
            const message = this.inProgressMessages[key];
            if (!message) {
                return;
            }
            delete this.inProgressMessages[key];
            // Per-chunk sanitization of streamed deltas is unreliable (the sentinel
            // regex can't span chunk boundaries), so we sanitize the fully
            // accumulated text here before persistence. This keeps any provider-side
            // HTML error pages out of the chat history even when the streaming UI
            // briefly displayed them.
            const sanitized = sanitizeAiProviderResponse(message) ?? message;
            await this.recordAssistant({
                role: "assistant",
                content: sanitized,
                type,
                group,
            });
        }
        async recordAssistant(payload) {
            let message = {
                ...payload,
                id: payload.id ?? crypto.randomUUID(),
                timestamp: payload.timestamp ?? Date.now(),
            };
            if (message.type === "tool-result" &&
                "toolName" in message &&
                "output" in message) {
                const m = message;
                message = {
                    ...m,
                    output: sanitizeToolOutput(m.toolName, m.output),
                };
            }
            await this.appendMessage(message);
        }
        async recordAssistantChangeInfo(payload) {
            const message = {
                role: "assistant",
                group: payload.type,
                content: payload.name,
                action: payload.action,
                status: payload.status,
                type: "changeInfo",
                id: crypto.randomUUID(),
                timestamp: Date.now(),
            };
            await this.appendMessage(message);
        }
        async appendMessage(message) {
            await this.ensureLoadedFromServer();
            const localMessageCountBefore = this.messages.length;
            this.logger.info(`[ai-service] chatMessagePushStart count=${localMessageCountBefore}`);
            this.messages.push(message);
            const payload = {
                ...message,
            };
            // Skip sending excluded message types to the server
            if ("type" in payload &&
                typeof payload.type === "string" &&
                EXCLUDED_MESSAGE_TYPES.includes(payload.type)) {
                this.logger.info("[ai-service] chatMessagePushSkipped (excluded type)");
                return undefined;
            }
            try {
                const response = await this.rpcClient.call(async (client) => {
                    const applicationId = this.applicationId;
                    return await client.call.v1.ai.chat.pushMessage({
                        applicationId,
                        payload,
                    });
                });
                this.logger.info("[ai-service] chatMessagePushSuccess");
                return response.data?.chatmessageId;
            }
            catch (error) {
                this.logger.error("[ai-service] Failed to write chat message to server", getErrorMeta(error));
                return undefined;
            }
        }
        async submitWorkflowSummary(summary) {
            await this.rpcClient.call(async (client) => {
                return await client.call.v1.ai.billing.workflowSummary.submit(summary);
            });
        }
        async calculatePromptCost(promptId, usage) {
            const response = await this.rpcClient.call(async (client) => {
                return await client.call.v1.ai.billing.promptCost.calculate({
                    promptId,
                    usage,
                });
            });
            return response.data?.totalCreditsUsed ?? 0;
        }
        determineSummaryOutcome(summary, allMessages) {
            // Find the index of this summary in all messages
            const summaryIndex = allMessages.findIndex((item) => isAiChatSummary(item) &&
                item.timestamp === summary.timestamp &&
                item.summary === summary.summary);
            if (summaryIndex === -1) {
                return "unknown";
            }
            // Look for acceptance/rejection messages after this summary
            const messagesAfterSummary = allMessages.slice(summaryIndex + 1);
            for (const item of messagesAfterSummary) {
                if (isAiChatMessage(item) && item.role === "user") {
                    if (item.type === "draftAccepted") {
                        return "accepted";
                    }
                    if (item.type === "draftRejected") {
                        return "rejected";
                    }
                }
            }
            return "unknown";
        }
        async recordSummary(summaryData) {
            const summaryPayload = {
                ...summaryData,
                timestamp: Date.now(),
            };
            await this.appendMessage(summaryPayload);
        }
        async getSummaries() {
            await this.ensureLoadedFromServer();
            return this.messages.filter(isEligibleSummary);
        }
        async getLatestSummary() {
            return (await this.getLatestSummaries(1))[0];
        }
        async getLatestSummaries(count) {
            const summaries = await this.getSummaries();
            const sortedSummaries = summaries
                .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
                .slice(0, count);
            // Add outcome information to each summary
            return sortedSummaries.map((summary) => ({
                ...summary,
                outcome: this.determineSummaryOutcome(summary, this.messages),
            }));
        }
        /**
         * Invalidate the loaded cache to force a fresh reload from the server
         * Used when checkpoint is restored to ensure ineligible summaries are filtered out
         */
        invalidateCache() {
            this.loadedFromServer = "no";
            this.messages = [];
        }
    };
})();
export { ChatSessionStore };
//# sourceMappingURL=chat-session-store.js.map