"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var TypeORMMemoryStorage_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeORMMemoryStorage = void 0;
const node_crypto_1 = require("node:crypto");
const storage_1 = require("@mastra/core/storage");
const di_1 = require("@n8n/di");
const instance_ai_1 = require("@n8n/instance-ai");
const typeorm_1 = require("@n8n/typeorm");
const utils_1 = require("@n8n/utils");
const n8n_workflow_1 = require("n8n-workflow");
const instance_ai_message_repository_1 = require("../repositories/instance-ai-message.repository");
const instance_ai_observational_memory_repository_1 = require("../repositories/instance-ai-observational-memory.repository");
const instance_ai_resource_repository_1 = require("../repositories/instance-ai-resource.repository");
const instance_ai_thread_repository_1 = require("../repositories/instance-ai-thread.repository");
const PATCH_ONLY_METADATA_KEYS = ['instanceAiPlannedTasks', 'instanceAiTasks'];
function countLines(value) {
    return value === '' ? 0 : value.split(/\r?\n/u).length;
}
function buildResourceTraceMetadata(resourceId) {
    const separatorIndex = resourceId.indexOf(':');
    return {
        resource_id: resourceId,
        memory_scope: separatorIndex === -1 ? 'user' : 'user-role',
        ...(separatorIndex !== -1 && separatorIndex < resourceId.length - 1
            ? { memory_role: resourceId.slice(separatorIndex + 1) }
            : {}),
    };
}
function summarizeLoadedResource(resource) {
    const workingMemory = resource?.workingMemory;
    return {
        found: resource !== null,
        has_working_memory: typeof workingMemory === 'string' && workingMemory.length > 0,
        working_memory_chars: typeof workingMemory === 'string' ? workingMemory.length : 0,
        working_memory_lines: typeof workingMemory === 'string' ? countLines(workingMemory) : 0,
        metadata_keys: resource?.metadata ? Object.keys(resource.metadata).length : 0,
    };
}
let TypeORMMemoryStorage = TypeORMMemoryStorage_1 = class TypeORMMemoryStorage extends storage_1.MemoryStorage {
    constructor(threadRepo, messageRepo, resourceRepo, omRepo) {
        super();
        this.threadRepo = threadRepo;
        this.messageRepo = messageRepo;
        this.resourceRepo = resourceRepo;
        this.omRepo = omRepo;
        this.supportsObservationalMemory = true;
    }
    async dangerouslyClearAll() {
        await this.omRepo.clear();
        await this.messageRepo.clear();
        await this.threadRepo.clear();
        await this.resourceRepo.clear();
    }
    async getThreadById({ threadId }) {
        const entity = await this.threadRepo.findOneBy({ id: threadId });
        if (!entity)
            return null;
        return this.toStorageThread(entity);
    }
    async listThreads(args) {
        const page = args.page ?? 0;
        const perPageInput = args.perPage;
        const perPage = perPageInput === false ? Number.MAX_SAFE_INTEGER : (perPageInput ?? 100);
        const { field, direction } = this.parseOrderBy(args.orderBy);
        const where = {};
        if (args.filter?.resourceId)
            where.resourceId = args.filter.resourceId;
        const metadataFilter = args.filter?.metadata;
        const hasMetadataFilter = metadataFilter && Object.keys(metadataFilter).length > 0;
        if (hasMetadataFilter) {
            this.validateMetadataKeys(metadataFilter);
            const allEntities = await this.threadRepo.find({
                where,
                order: { [field]: direction },
            });
            const allThreads = allEntities
                .map((e) => ({
                id: e.id,
                title: e.title,
                resourceId: e.resourceId,
                metadata: e.metadata ?? undefined,
                createdAt: e.createdAt,
                updatedAt: e.updatedAt,
            }))
                .filter((t) => {
                if (!t.metadata)
                    return false;
                return Object.entries(args.filter.metadata).every(([k, v]) => t.metadata[k] === v);
            });
            const total = allThreads.length;
            const offset = page * perPage;
            const paged = allThreads.slice(offset, offset + perPage);
            return {
                threads: paged,
                total,
                page,
                perPage: perPageInput ?? 100,
                hasMore: offset + perPage < total,
            };
        }
        const [entities, total] = await this.threadRepo.findAndCount({
            where,
            order: { [field]: direction },
            skip: page * perPage,
            take: perPage,
        });
        const threads = entities.map((e) => ({
            id: e.id,
            title: e.title,
            resourceId: e.resourceId,
            metadata: e.metadata ?? undefined,
            createdAt: e.createdAt,
            updatedAt: e.updatedAt,
        }));
        return {
            threads,
            total,
            page,
            perPage: perPageInput ?? 100,
            hasMore: (page + 1) * perPage < total,
        };
    }
    async saveThread({ thread, }) {
        return await this.serializeThreadMutation(thread.id, async () => {
            const existing = await this.threadRepo.findOneBy({ id: thread.id });
            if (existing) {
                const safeIncoming = { ...(thread.metadata ?? {}) };
                for (const key of PATCH_ONLY_METADATA_KEYS) {
                    delete safeIncoming[key];
                }
                existing.title = thread.title ?? existing.title;
                existing.resourceId = thread.resourceId ?? existing.resourceId;
                existing.metadata = {
                    ...(existing.metadata ?? {}),
                    ...safeIncoming,
                };
                const saved = await this.threadRepo.save(existing);
                return this.toStorageThread(saved);
            }
            const entity = this.threadRepo.create({
                id: thread.id,
                resourceId: thread.resourceId,
                title: thread.title ?? '',
                metadata: thread.metadata ?? null,
            });
            const saved = await this.threadRepo.save(entity);
            return this.toStorageThread(saved);
        });
    }
    async updateThread({ id, title, metadata, }) {
        return await this.serializeThreadMutation(id, async () => {
            const entity = await this.threadRepo.findOneByOrFail({ id });
            const safeIncoming = { ...metadata };
            for (const key of PATCH_ONLY_METADATA_KEYS) {
                delete safeIncoming[key];
            }
            entity.title = title;
            entity.metadata = {
                ...(entity.metadata ?? {}),
                ...safeIncoming,
            };
            const updated = await this.threadRepo.save(entity);
            return this.toStorageThread(updated);
        });
    }
    async patchThread({ threadId, update, }) {
        return await this.serializeThreadMutation(threadId, async () => {
            const entity = await this.threadRepo.findOneBy({ id: threadId });
            if (!entity)
                return null;
            const current = this.toStorageThread(entity);
            const patch = update({
                ...current,
                metadata: { ...(current.metadata ?? {}) },
            });
            if (!patch)
                return current;
            if (patch.title !== undefined) {
                entity.title = patch.title;
            }
            if (patch.metadata !== undefined) {
                entity.metadata = patch.metadata;
            }
            const updated = await this.threadRepo.save(entity);
            return this.toStorageThread(updated);
        });
    }
    async deleteThread({ threadId }) {
        await this.threadRepo.delete(threadId);
    }
    toStorageThread(entity) {
        return {
            id: entity.id,
            title: entity.title,
            resourceId: entity.resourceId,
            metadata: entity.metadata ?? undefined,
            createdAt: entity.createdAt,
            updatedAt: entity.updatedAt,
        };
    }
    async serializeThreadMutation(threadId, mutation) {
        const queues = TypeORMMemoryStorage_1.threadMutationQueues;
        const previous = queues.get(threadId) ?? Promise.resolve();
        const previousSettled = previous.catch(() => { });
        let releaseCurrent;
        const current = new Promise((resolve) => {
            releaseCurrent = resolve;
        });
        const queued = previousSettled.then(async () => await current);
        queues.set(threadId, queued);
        await previousSettled;
        try {
            return await mutation();
        }
        finally {
            releaseCurrent();
            if (queues.get(threadId) === queued) {
                queues.delete(threadId);
            }
        }
    }
    async listMessages(args) {
        if (args.include?.length) {
            const included = await this.getIncludedMessages(args);
            return {
                messages: included,
                total: included.length,
                page: 0,
                perPage: false,
                hasMore: false,
            };
        }
        const threadIds = Array.isArray(args.threadId) ? args.threadId : [args.threadId];
        const page = args.page ?? 0;
        const perPageInput = args.perPage;
        const perPage = perPageInput === false ? Number.MAX_SAFE_INTEGER : (perPageInput ?? 40);
        const qb = this.messageRepo
            .createQueryBuilder('m')
            .where('m.threadId IN (:...threadIds)', { threadIds });
        if (args.resourceId) {
            qb.andWhere('m.resourceId = :resourceId', {
                resourceId: args.resourceId,
            });
        }
        if (args.filter?.dateRange?.start) {
            const op = args.filter.dateRange.startExclusive ? '>' : '>=';
            qb.andWhere(`m.createdAt ${op} :start`, {
                start: args.filter.dateRange.start,
            });
        }
        if (args.filter?.dateRange?.end) {
            const op = args.filter.dateRange.endExclusive ? '<' : '<=';
            qb.andWhere(`m.createdAt ${op} :end`, {
                end: args.filter.dateRange.end,
            });
        }
        const { field = 'createdAt', direction = 'ASC' } = args.orderBy ?? {};
        qb.orderBy(`m.${field}`, direction);
        const total = await qb.getCount();
        qb.skip(page * perPage).take(perPage);
        const entities = await qb.getMany();
        const messages = entities.map((e) => this.entityToMessage(e));
        return {
            messages,
            total,
            page,
            perPage: perPageInput ?? 40,
            hasMore: (page + 1) * perPage < total,
        };
    }
    async getIncludedMessages(args) {
        const seen = new Set();
        const result = [];
        const threadIds = Array.isArray(args.threadId) ? args.threadId : [args.threadId];
        for (const inc of args.include ?? []) {
            const anchorThreadIds = inc.threadId ? [inc.threadId] : threadIds;
            const anchor = await this.messageRepo.findOneBy({ id: inc.id });
            if (!anchor)
                continue;
            const collected = [];
            if (inc.withPreviousMessages && inc.withPreviousMessages > 0) {
                const before = await this.messageRepo
                    .createQueryBuilder('m')
                    .where('m.threadId IN (:...threadIds)', { threadIds: anchorThreadIds })
                    .andWhere('m.createdAt < :anchorTime', { anchorTime: anchor.createdAt })
                    .orderBy('m.createdAt', 'DESC')
                    .take(inc.withPreviousMessages)
                    .getMany();
                collected.push(...before.reverse().map((e) => this.entityToMessage(e)));
            }
            collected.push(this.entityToMessage(anchor));
            if (inc.withNextMessages && inc.withNextMessages > 0) {
                const after = await this.messageRepo
                    .createQueryBuilder('m')
                    .where('m.threadId IN (:...threadIds)', { threadIds: anchorThreadIds })
                    .andWhere('m.createdAt > :anchorTime', { anchorTime: anchor.createdAt })
                    .orderBy('m.createdAt', 'ASC')
                    .take(inc.withNextMessages)
                    .getMany();
                collected.push(...after.map((e) => this.entityToMessage(e)));
            }
            for (const msg of collected) {
                if (!seen.has(msg.id)) {
                    seen.add(msg.id);
                    result.push(msg);
                }
            }
        }
        result.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
        return result;
    }
    async listMessagesByResourceId(args) {
        const page = args.page ?? 0;
        const perPageInput = args.perPage;
        const perPage = perPageInput === false ? Number.MAX_SAFE_INTEGER : (perPageInput ?? 40);
        const qb = this.messageRepo
            .createQueryBuilder('m')
            .where('m.resourceId = :resourceId', { resourceId: args.resourceId });
        if (args.filter?.dateRange?.start) {
            const op = args.filter.dateRange.startExclusive ? '>' : '>=';
            qb.andWhere(`m.createdAt ${op} :start`, {
                start: args.filter.dateRange.start,
            });
        }
        if (args.filter?.dateRange?.end) {
            const op = args.filter.dateRange.endExclusive ? '<' : '<=';
            qb.andWhere(`m.createdAt ${op} :end`, {
                end: args.filter.dateRange.end,
            });
        }
        const { field = 'createdAt', direction = 'ASC' } = args.orderBy ?? {};
        qb.orderBy(`m.${field}`, direction);
        const total = await qb.getCount();
        qb.skip(page * perPage).take(perPage);
        const entities = await qb.getMany();
        return {
            messages: entities.map((e) => this.entityToMessage(e)),
            total,
            page,
            perPage: perPageInput ?? 40,
            hasMore: (page + 1) * perPage < total,
        };
    }
    async listMessagesById({ messageIds, }) {
        if (messageIds.length === 0)
            return { messages: [] };
        const entities = await this.messageRepo.findBy({ id: (0, typeorm_1.In)(messageIds) });
        return { messages: entities.map((e) => this.entityToMessage(e)) };
    }
    async saveMessages({ messages, }) {
        const entities = messages.map((m) => {
            if (!m.threadId) {
                throw new Error(`Message ${m.id} is missing required threadId`);
            }
            const entity = this.messageRepo.create({
                id: m.id,
                threadId: m.threadId,
                content: JSON.stringify(m.content),
                role: m.role,
                type: m.type ?? null,
                resourceId: m.resourceId ?? null,
            });
            if (m.createdAt) {
                entity.createdAt = m.createdAt;
            }
            return entity;
        });
        const saved = await this.messageRepo.save(entities);
        return { messages: saved.map((e) => this.entityToMessage(e)) };
    }
    async updateMessages({ messages, }) {
        const result = [];
        for (const msg of messages) {
            const existing = await this.messageRepo.findOneBy({ id: msg.id });
            if (!existing)
                continue;
            const updates = {};
            if (msg.role)
                updates.role = msg.role;
            if (msg.type !== undefined)
                updates.type = msg.type;
            if (msg.content) {
                const existingContent = (0, n8n_workflow_1.jsonParse)(existing.content);
                if (msg.content.metadata !== undefined)
                    existingContent.metadata = msg.content.metadata;
                if (msg.content.content !== undefined)
                    existingContent.content = msg.content.content;
                updates.content = JSON.stringify(existingContent);
            }
            if (Object.keys(updates).length > 0) {
                await this.messageRepo.update(msg.id, updates);
            }
            const updated = await this.messageRepo.findOneByOrFail({ id: msg.id });
            result.push(this.entityToMessage(updated));
        }
        return result;
    }
    async deleteMessages(messageIds) {
        if (messageIds.length === 0)
            return;
        await this.messageRepo.delete(messageIds);
    }
    async getResourceById({ resourceId, }) {
        return await (0, instance_ai_1.withCurrentTraceSpan)({
            name: 'memory_load',
            tags: ['memory', 'internal'],
            metadata: buildResourceTraceMetadata(resourceId),
            inputs: { resource_id: resourceId },
            processOutputs: summarizeLoadedResource,
        }, async () => {
            const entity = await this.resourceRepo.findOneBy({ id: resourceId });
            if (!entity)
                return null;
            return {
                id: entity.id,
                workingMemory: entity.workingMemory ?? undefined,
                metadata: entity.metadata ?? undefined,
                createdAt: entity.createdAt,
                updatedAt: entity.updatedAt,
            };
        });
    }
    async saveResource({ resource, }) {
        const entity = this.resourceRepo.create({
            id: resource.id,
            workingMemory: resource.workingMemory ?? null,
            metadata: resource.metadata ?? null,
        });
        const saved = await this.resourceRepo.save(entity);
        return {
            id: saved.id,
            workingMemory: saved.workingMemory ?? undefined,
            metadata: saved.metadata ?? undefined,
            createdAt: saved.createdAt,
            updatedAt: saved.updatedAt,
        };
    }
    async updateResource({ resourceId, workingMemory, metadata, }) {
        let existing = await this.resourceRepo.findOneBy({ id: resourceId });
        if (!existing) {
            existing = await this.resourceRepo.save(this.resourceRepo.create({
                id: resourceId,
                workingMemory: workingMemory ?? null,
                metadata: metadata ?? null,
            }));
        }
        else {
            if (workingMemory !== undefined)
                existing.workingMemory = workingMemory;
            if (metadata !== undefined)
                existing.metadata = metadata;
            existing = await this.resourceRepo.save(existing);
        }
        return {
            id: existing.id,
            workingMemory: existing.workingMemory ?? undefined,
            metadata: existing.metadata ?? undefined,
            createdAt: existing.createdAt,
            updatedAt: existing.updatedAt,
        };
    }
    async cloneThread(args) {
        const source = await this.getThreadById({
            threadId: args.sourceThreadId,
        });
        if (!source)
            throw new Error(`Source thread ${args.sourceThreadId} not found`);
        const newThreadId = args.newThreadId ?? (0, node_crypto_1.randomUUID)();
        const cloneMetadata = {
            ...(source.metadata ?? {}),
            ...(args.metadata ?? {}),
            mastra: {
                ...(source.metadata?.mastra ?? {}),
                clone: {
                    sourceThreadId: args.sourceThreadId,
                    clonedAt: new Date(),
                },
            },
        };
        const newThread = await this.saveThread({
            thread: {
                id: newThreadId,
                resourceId: args.resourceId ?? source.resourceId,
                title: args.title ?? source.title,
                metadata: cloneMetadata,
                createdAt: new Date(),
                updatedAt: new Date(),
            },
        });
        const { messages: sourceMessages } = await this.listMessages({
            threadId: args.sourceThreadId,
            perPage: false,
            orderBy: { field: 'createdAt', direction: 'ASC' },
        });
        let messagesToClone = sourceMessages;
        if (args.options?.messageLimit) {
            messagesToClone = messagesToClone.slice(-args.options.messageLimit);
        }
        const messageIdMap = {};
        const clonedMessages = messagesToClone.map((m) => {
            const newId = (0, utils_1.generateNanoId)();
            messageIdMap[m.id] = newId;
            return { ...m, id: newId, threadId: newThreadId };
        });
        if (clonedMessages.length > 0) {
            await this.saveMessages({ messages: clonedMessages });
        }
        return { thread: newThread, clonedMessages, messageIdMap };
    }
    omLookupKey(scope, threadId, resourceId) {
        return `${scope}:${threadId ?? 'null'}:${resourceId}`;
    }
    entityToOMRecord(entity) {
        return {
            id: entity.id,
            scope: entity.scope,
            threadId: entity.threadId,
            resourceId: entity.resourceId,
            createdAt: entity.createdAt,
            updatedAt: entity.updatedAt,
            lastObservedAt: entity.lastObservedAt ?? undefined,
            originType: entity.originType,
            generationCount: entity.generationCount,
            activeObservations: entity.activeObservations,
            bufferedObservationChunks: (entity.bufferedObservationChunks ??
                undefined),
            bufferedObservations: entity.bufferedObservations ?? undefined,
            bufferedObservationTokens: entity.bufferedObservationTokens ?? undefined,
            bufferedMessageIds: entity.bufferedMessageIds ?? undefined,
            bufferedReflection: entity.bufferedReflection ?? undefined,
            bufferedReflectionTokens: entity.bufferedReflectionTokens ?? undefined,
            bufferedReflectionInputTokens: entity.bufferedReflectionInputTokens ?? undefined,
            reflectedObservationLineCount: entity.reflectedObservationLineCount ?? undefined,
            observedMessageIds: entity.observedMessageIds ?? undefined,
            observedTimezone: entity.observedTimezone ?? undefined,
            totalTokensObserved: entity.totalTokensObserved,
            observationTokenCount: entity.observationTokenCount,
            pendingMessageTokens: entity.pendingMessageTokens,
            isReflecting: entity.isReflecting,
            isObserving: entity.isObserving,
            isBufferingObservation: entity.isBufferingObservation,
            isBufferingReflection: entity.isBufferingReflection,
            lastBufferedAtTokens: entity.lastBufferedAtTokens,
            lastBufferedAtTime: entity.lastBufferedAtTime,
            config: typeof entity.config === 'string' ? (0, n8n_workflow_1.jsonParse)(entity.config) : entity.config,
            metadata: entity.metadata ?? undefined,
        };
    }
    async getObservationalMemory(threadId, resourceId) {
        const lookupKey = this.omLookupKey(threadId ? 'thread' : 'resource', threadId, resourceId);
        const entity = await this.omRepo.findOne({
            where: { lookupKey },
            order: { generationCount: 'DESC' },
        });
        if (!entity)
            return null;
        return this.entityToOMRecord(entity);
    }
    async getObservationalMemoryHistory(threadId, resourceId, limit = 10) {
        const scope = threadId ? 'thread' : 'resource';
        const entities = await this.omRepo.find({
            where: { scope, threadId: threadId ?? undefined, resourceId },
            order: { generationCount: 'DESC' },
            take: limit,
        });
        return entities.map((e) => this.entityToOMRecord(e));
    }
    async initializeObservationalMemory(input) {
        const id = (0, utils_1.generateNanoId)();
        const lookupKey = this.omLookupKey(input.scope, input.threadId, input.resourceId);
        const entity = this.omRepo.create({
            id,
            lookupKey,
            scope: input.scope,
            threadId: input.threadId,
            resourceId: input.resourceId,
            originType: 'initial',
            config: JSON.stringify(input.config),
            observedTimezone: input.observedTimezone ?? null,
        });
        const saved = await this.omRepo.save(entity);
        return this.entityToOMRecord(saved);
    }
    async insertObservationalMemoryRecord(record) {
        const lookupKey = this.omLookupKey(record.scope, record.threadId, record.resourceId);
        const entity = this.omRepo.create({
            id: record.id,
            lookupKey,
            scope: record.scope,
            threadId: record.threadId,
            resourceId: record.resourceId,
            activeObservations: record.activeObservations,
            originType: record.originType,
            config: JSON.stringify(record.config),
            generationCount: record.generationCount,
            lastObservedAt: record.lastObservedAt ?? null,
            pendingMessageTokens: record.pendingMessageTokens,
            totalTokensObserved: record.totalTokensObserved,
            observationTokenCount: record.observationTokenCount,
            isObserving: record.isObserving,
            isReflecting: record.isReflecting,
            observedMessageIds: record.observedMessageIds ?? null,
            observedTimezone: record.observedTimezone ?? null,
            bufferedObservations: record.bufferedObservations ?? null,
            bufferedObservationTokens: record.bufferedObservationTokens ?? null,
            bufferedMessageIds: record.bufferedMessageIds ?? null,
            bufferedReflection: record.bufferedReflection ?? null,
            bufferedReflectionTokens: record.bufferedReflectionTokens ?? null,
            bufferedReflectionInputTokens: record.bufferedReflectionInputTokens ?? null,
            reflectedObservationLineCount: record.reflectedObservationLineCount ?? null,
            bufferedObservationChunks: (record.bufferedObservationChunks ?? null),
            isBufferingObservation: record.isBufferingObservation,
            isBufferingReflection: record.isBufferingReflection,
            lastBufferedAtTokens: record.lastBufferedAtTokens,
            lastBufferedAtTime: record.lastBufferedAtTime ?? null,
            metadata: record.metadata ?? null,
        });
        await this.omRepo.save(entity);
    }
    async updateActiveObservations(input) {
        const entity = await this.omRepo.findOneByOrFail({ id: input.id });
        const existingIds = entity.observedMessageIds ?? [];
        const mergedIds = [...new Set([...existingIds, ...(input.observedMessageIds ?? [])])];
        await this.omRepo.update(input.id, {
            activeObservations: input.observations,
            observationTokenCount: input.tokenCount,
            lastObservedAt: input.lastObservedAt,
            observedMessageIds: mergedIds,
            observedTimezone: input.observedTimezone ?? entity.observedTimezone,
            totalTokensObserved: entity.totalTokensObserved + input.tokenCount,
        });
    }
    async updateBufferedObservations(input) {
        const entity = await this.omRepo.findOneByOrFail({ id: input.id });
        const chunks = entity.bufferedObservationChunks ?? [];
        chunks.push(input.chunk);
        const updates = {
            bufferedObservationChunks: chunks,
        };
        if (input.lastBufferedAtTime) {
            updates.lastBufferedAtTime = input.lastBufferedAtTime;
        }
        await this.omRepo.update(input.id, updates);
    }
    async swapBufferedToActive(input) {
        const entity = await this.omRepo.findOneByOrFail({ id: input.id });
        const record = this.entityToOMRecord(entity);
        const chunks = record.bufferedObservationChunks ?? [];
        if (chunks.length === 0) {
            return {
                chunksActivated: 0,
                messageTokensActivated: 0,
                observationTokensActivated: 0,
                messagesActivated: 0,
                activatedCycleIds: [],
                activatedMessageIds: [],
            };
        }
        const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
        const tokensToRemove = input.currentPendingTokens - retentionFloor;
        let removedTokens = 0;
        let activatedCount = 0;
        const activated = [];
        const remaining = [];
        for (const chunk of chunks) {
            if (removedTokens < tokensToRemove || (input.forceMaxActivation && remaining.length === 0)) {
                activated.push(chunk);
                removedTokens += chunk.messageTokens ?? 0;
                activatedCount++;
            }
            else {
                remaining.push(chunk);
            }
        }
        let totalMessageTokens = 0;
        let totalObsTokens = 0;
        let totalMessages = 0;
        const allMsgIds = [];
        const cycleIds = [];
        const observationParts = [];
        for (const chunk of activated) {
            totalMessageTokens += chunk.messageTokens ?? 0;
            totalObsTokens += chunk.tokenCount ?? 0;
            totalMessages += chunk.messageIds?.length ?? 0;
            allMsgIds.push(...(chunk.messageIds ?? []));
            if (chunk.cycleId)
                cycleIds.push(chunk.cycleId);
            if (chunk.observations)
                observationParts.push(chunk.observations);
        }
        const newActive = record.activeObservations
            ? record.activeObservations + '\n' + observationParts.join('\n')
            : observationParts.join('\n');
        const lastActivatedChunk = activated[activated.length - 1];
        const newLastObservedAt = input.lastObservedAt ??
            (lastActivatedChunk?.lastObservedAt
                ? new Date(lastActivatedChunk.lastObservedAt)
                : record.lastObservedAt);
        const existingMsgIds = record.observedMessageIds ?? [];
        const mergedMsgIds = [...new Set([...existingMsgIds, ...allMsgIds])];
        await this.omRepo.update(input.id, {
            activeObservations: newActive,
            observationTokenCount: record.observationTokenCount + totalObsTokens,
            bufferedObservationChunks: remaining.length > 0 ? remaining : null,
            lastObservedAt: newLastObservedAt ?? null,
            observedMessageIds: mergedMsgIds,
            totalTokensObserved: record.totalTokensObserved + totalObsTokens,
            isBufferingObservation: false,
        });
        return {
            chunksActivated: activatedCount,
            messageTokensActivated: totalMessageTokens,
            observationTokensActivated: totalObsTokens,
            messagesActivated: totalMessages,
            activatedCycleIds: cycleIds,
            activatedMessageIds: allMsgIds,
            observations: observationParts.join('\n'),
        };
    }
    async createReflectionGeneration(input) {
        const { currentRecord, reflection, tokenCount } = input;
        const newId = (0, utils_1.generateNanoId)();
        const lookupKey = this.omLookupKey(currentRecord.scope, currentRecord.threadId, currentRecord.resourceId);
        const entity = this.omRepo.create({
            id: newId,
            lookupKey,
            scope: currentRecord.scope,
            threadId: currentRecord.threadId,
            resourceId: currentRecord.resourceId,
            activeObservations: reflection,
            originType: 'reflection',
            config: JSON.stringify(currentRecord.config),
            generationCount: currentRecord.generationCount + 1,
            lastObservedAt: currentRecord.lastObservedAt ?? null,
            observationTokenCount: tokenCount,
            totalTokensObserved: currentRecord.totalTokensObserved,
            pendingMessageTokens: currentRecord.pendingMessageTokens,
            isObserving: false,
            isReflecting: false,
            isBufferingObservation: false,
            isBufferingReflection: false,
            lastBufferedAtTokens: 0,
            lastBufferedAtTime: null,
            observedTimezone: currentRecord.observedTimezone ?? null,
            metadata: currentRecord.metadata ?? null,
        });
        const saved = await this.omRepo.save(entity);
        return this.entityToOMRecord(saved);
    }
    async updateBufferedReflection(input) {
        await this.omRepo.update(input.id, {
            bufferedReflection: input.reflection,
            bufferedReflectionTokens: input.tokenCount,
            bufferedReflectionInputTokens: input.inputTokenCount,
            reflectedObservationLineCount: input.reflectedObservationLineCount,
        });
    }
    async swapBufferedReflectionToActive(input) {
        const { currentRecord, tokenCount } = input;
        const lines = currentRecord.activeObservations.split('\n');
        const reflectedCount = currentRecord.reflectedObservationLineCount ?? lines.length;
        const unreflected = lines.slice(reflectedCount).join('\n');
        const newActive = currentRecord.bufferedReflection
            ? unreflected
                ? `${currentRecord.bufferedReflection}\n${unreflected}`
                : currentRecord.bufferedReflection
            : unreflected;
        const newId = (0, utils_1.generateNanoId)();
        const lookupKey = this.omLookupKey(currentRecord.scope, currentRecord.threadId, currentRecord.resourceId);
        const entity = this.omRepo.create({
            id: newId,
            lookupKey,
            scope: currentRecord.scope,
            threadId: currentRecord.threadId,
            resourceId: currentRecord.resourceId,
            activeObservations: newActive,
            originType: 'reflection',
            config: JSON.stringify(currentRecord.config),
            generationCount: currentRecord.generationCount + 1,
            lastObservedAt: currentRecord.lastObservedAt ?? null,
            observationTokenCount: tokenCount,
            totalTokensObserved: currentRecord.totalTokensObserved,
            pendingMessageTokens: currentRecord.pendingMessageTokens,
            isObserving: false,
            isReflecting: false,
            isBufferingObservation: false,
            isBufferingReflection: false,
            lastBufferedAtTokens: 0,
            lastBufferedAtTime: null,
            observedTimezone: currentRecord.observedTimezone ?? null,
            metadata: currentRecord.metadata ?? null,
        });
        const saved = await this.omRepo.save(entity);
        return this.entityToOMRecord(saved);
    }
    async setReflectingFlag(id, isReflecting) {
        await this.omRepo.update(id, { isReflecting });
    }
    async setObservingFlag(id, isObserving) {
        await this.omRepo.update(id, { isObserving });
    }
    async setBufferingObservationFlag(id, isBuffering, lastBufferedAtTokens) {
        const updates = {
            isBufferingObservation: isBuffering,
        };
        if (isBuffering && lastBufferedAtTokens !== undefined) {
            updates.lastBufferedAtTokens = lastBufferedAtTokens;
        }
        await this.omRepo.update(id, updates);
    }
    async setBufferingReflectionFlag(id, isBuffering) {
        await this.omRepo.update(id, { isBufferingReflection: isBuffering });
    }
    async clearObservationalMemory(threadId, resourceId) {
        const scope = threadId ? 'thread' : 'resource';
        const lookupKey = this.omLookupKey(scope, threadId, resourceId);
        await this.omRepo.delete({ lookupKey });
    }
    async setPendingMessageTokens(id, tokenCount) {
        await this.omRepo.update(id, { pendingMessageTokens: tokenCount });
    }
    entityToMessage(entity) {
        return {
            id: entity.id,
            role: entity.role,
            createdAt: entity.createdAt,
            threadId: entity.threadId,
            resourceId: entity.resourceId ?? undefined,
            type: entity.type ?? undefined,
            content: (0, n8n_workflow_1.jsonParse)(entity.content),
        };
    }
};
exports.TypeORMMemoryStorage = TypeORMMemoryStorage;
TypeORMMemoryStorage.threadMutationQueues = new Map();
exports.TypeORMMemoryStorage = TypeORMMemoryStorage = TypeORMMemoryStorage_1 = __decorate([
    (0, di_1.Service)(),
    __metadata("design:paramtypes", [instance_ai_thread_repository_1.InstanceAiThreadRepository,
        instance_ai_message_repository_1.InstanceAiMessageRepository,
        instance_ai_resource_repository_1.InstanceAiResourceRepository,
        instance_ai_observational_memory_repository_1.InstanceAiObservationalMemoryRepository])
], TypeORMMemoryStorage);
//# sourceMappingURL=typeorm-memory-storage.js.map