"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentAnalyzeSessionService = exports.AnalyzeSessionPreparationError = void 0;
const agentRuntime_1 = require("../../agentRuntime");
const providerManager_1 = require("../../services/providerManager");
const providerSnapshot_1 = require("../../services/providerManager/providerSnapshot");
const traceProcessorService_1 = require("../../services/traceProcessorService");
const enhancedSessionContext_1 = require("../../agent/context/enhancedSessionContext");
class AnalyzeSessionPreparationError extends Error {
    constructor(message, options) {
        super(message);
        this.name = 'AnalyzeSessionPreparationError';
        this.code = options.code;
        this.httpStatus = options.httpStatus;
        this.hint = options.hint;
    }
}
exports.AnalyzeSessionPreparationError = AnalyzeSessionPreparationError;
function normalizeReferenceTraceId(value) {
    return typeof value === 'string' && value.trim() ? value.trim() : undefined;
}
function comparisonSourceForReference(referenceTraceId) {
    return referenceTraceId ? 'raw_trace_pair' : undefined;
}
function readPersistedReferenceTraceId(stateSnapshot, metadata) {
    return normalizeReferenceTraceId(stateSnapshot?.referenceTraceId)
        ?? normalizeReferenceTraceId(metadata?.referenceTraceId);
}
function assertReferenceTraceCompatible(input) {
    const existingReferenceTraceId = normalizeReferenceTraceId(input.existingReferenceTraceId);
    const requestedReferenceTraceId = normalizeReferenceTraceId(input.requestedReferenceTraceId);
    if (existingReferenceTraceId && requestedReferenceTraceId && existingReferenceTraceId !== requestedReferenceTraceId) {
        throw new AnalyzeSessionPreparationError('referenceTraceId mismatch for requested session', {
            code: 'REFERENCE_TRACE_ID_MISMATCH',
            httpStatus: 400,
            hint: `This comparison session uses referenceTraceId=${existingReferenceTraceId}. Start a new chat to compare against a different reference trace.`,
        });
    }
    if (!existingReferenceTraceId && requestedReferenceTraceId) {
        throw new AnalyzeSessionPreparationError('referenceTraceId mismatch for requested session', {
            code: 'REFERENCE_TRACE_ID_MISMATCH',
            httpStatus: 400,
            hint: 'This session was created for single-trace analysis. Start a new chat to run raw trace comparison.',
        });
    }
}
class AgentAnalyzeSessionService {
    constructor(deps) {
        this.assistantAppService = deps.assistantAppService;
        this.createSessionLogger = deps.createSessionLogger;
        this.sessionPersistenceService = deps.sessionPersistenceService;
        this.sessionContextManager = deps.sessionContextManager ?? enhancedSessionContext_1.sessionContextManager;
        this.buildRecoveredResultFromContext = deps.buildRecoveredResultFromContext;
    }
    prepareSession(input) {
        const { traceId, query, requestedSessionId, options = {} } = input;
        const providerScope = input.providerScope;
        const requestedReferenceTraceId = normalizeReferenceTraceId(input.referenceTraceId ?? options.referenceTraceId);
        let inheritedReferenceTraceId;
        const explicitProviderId = input.providerId !== undefined
            ? input.providerId
            : options.providerId;
        const providerSvc = (0, providerManager_1.getProviderService)();
        if (typeof explicitProviderId === 'string' && !providerSvc.getRawProvider(explicitProviderId, providerScope)) {
            throw new AnalyzeSessionPreparationError(`Provider not found: ${explicitProviderId}`, {
                code: 'PROVIDER_NOT_FOUND',
                httpStatus: 404,
            });
        }
        const activeProviderId = explicitProviderId !== undefined
            ? undefined
            : providerSvc.getRawEffectiveProvider(providerScope)?.id;
        const sessionProviderId = explicitProviderId !== undefined
            ? explicitProviderId
            : activeProviderId ?? null;
        const resolveProviderSnapshotHash = (providerId, runtimeOverride) => (0, providerSnapshot_1.resolveProviderRuntimeSnapshot)(providerSvc, providerId, runtimeOverride, providerScope).snapshotHash;
        const sessionProviderSnapshotHash = resolveProviderSnapshotHash(sessionProviderId);
        if (requestedSessionId) {
            const existingSession = this.assistantAppService.getSession(requestedSessionId);
            if (existingSession && existingSession.traceId === traceId) {
                assertReferenceTraceCompatible({
                    requestedSessionId,
                    existingReferenceTraceId: existingSession.referenceTraceId,
                    requestedReferenceTraceId,
                });
                inheritedReferenceTraceId = normalizeReferenceTraceId(existingSession.referenceTraceId);
            }
            const liveSessionProviderId = existingSession && existingSession.traceId === traceId
                ? explicitProviderId !== undefined
                    ? explicitProviderId
                    : existingSession.providerId ?? null
                : sessionProviderId;
            const liveSessionProviderMismatch = Boolean(existingSession &&
                existingSession.traceId === traceId &&
                explicitProviderId !== undefined &&
                (existingSession.providerId ?? null) !== explicitProviderId);
            const liveSessionProviderSnapshotHash = existingSession?.providerSnapshotHash
                ? resolveProviderSnapshotHash(liveSessionProviderId)
                : null;
            const liveSessionProviderSnapshotMismatch = Boolean(existingSession &&
                existingSession.traceId === traceId &&
                !liveSessionProviderMismatch &&
                existingSession.providerSnapshotHash &&
                liveSessionProviderSnapshotHash &&
                existingSession.providerSnapshotHash !== liveSessionProviderSnapshotHash);
            if (existingSession && existingSession.traceId === traceId) {
                if (liveSessionProviderMismatch) {
                    existingSession.logger.info('AgentRoutes', 'Provider changed; starting a new SDK session', {
                        previousProviderId: existingSession.providerId,
                        nextProviderId: sessionProviderId,
                    });
                    console.log(`[AgentRoutes] Provider changed for ${requestedSessionId}, creating a new agent session`);
                }
                else if (liveSessionProviderSnapshotMismatch) {
                    const previousProviderSnapshotHash = existingSession.providerSnapshotHash;
                    if (existingSession.orchestratorUpdateHandler) {
                        existingSession.orchestrator.off('update', existingSession.orchestratorUpdateHandler);
                        existingSession.orchestratorUpdateHandler = undefined;
                    }
                    if (typeof existingSession.orchestrator.cleanupSession === 'function') {
                        existingSession.orchestrator.cleanupSession(existingSession.sessionId);
                    }
                    existingSession.orchestrator = (0, agentRuntime_1.createAgentOrchestrator)({
                        traceProcessorService: (0, traceProcessorService_1.getTraceProcessorService)(),
                        providerId: liveSessionProviderId,
                        providerScope,
                    });
                    existingSession.providerId = liveSessionProviderId;
                    existingSession.providerSnapshotHash = liveSessionProviderSnapshotHash;
                    existingSession.providerSnapshotChanged = true;
                    existingSession.providerSnapshotChangeReason = 'provider_snapshot_hash_mismatch';
                    existingSession.referenceTraceId = requestedReferenceTraceId ?? inheritedReferenceTraceId;
                    existingSession.comparisonSource = comparisonSourceForReference(existingSession.referenceTraceId);
                    existingSession.runSequence = Number.isFinite(existingSession.runSequence)
                        ? Math.max(0, Math.floor(existingSession.runSequence))
                        : 0;
                    existingSession.logger.warn('AgentRoutes', 'Provider snapshot changed; starting a fresh SDK session', {
                        providerId: liveSessionProviderId,
                        previousProviderSnapshotHash,
                        nextProviderSnapshotHash: liveSessionProviderSnapshotHash,
                    });
                    existingSession.query = query;
                    existingSession.status = 'pending';
                    existingSession.lastActivityAt = Date.now();
                    console.log(`[AgentRoutes] Provider snapshot changed for ${requestedSessionId}, refreshed SDK session`);
                    return {
                        sessionId: requestedSessionId,
                        session: existingSession,
                        isNewSession: false,
                    };
                }
                else {
                    const effectiveReferenceTraceId = requestedReferenceTraceId ?? inheritedReferenceTraceId;
                    existingSession.providerId ?? (existingSession.providerId = null);
                    existingSession.providerSnapshotHash ?? (existingSession.providerSnapshotHash = liveSessionProviderSnapshotHash);
                    existingSession.providerSnapshotChanged = false;
                    existingSession.providerSnapshotChangeReason = undefined;
                    existingSession.referenceTraceId = effectiveReferenceTraceId;
                    existingSession.comparisonSource = comparisonSourceForReference(effectiveReferenceTraceId);
                    existingSession.runSequence = Number.isFinite(existingSession.runSequence)
                        ? Math.max(0, Math.floor(existingSession.runSequence))
                        : 0;
                    existingSession.logger.info('AgentRoutes', 'Continuing multi-turn dialogue', {
                        turnQuery: query,
                        previousQuery: existingSession.query,
                    });
                    existingSession.query = query;
                    existingSession.status = 'pending';
                    existingSession.lastActivityAt = Date.now();
                    console.log(`[AgentRoutes] Reusing agent session ${requestedSessionId} for multi-turn dialogue`);
                    return {
                        sessionId: requestedSessionId,
                        session: existingSession,
                        isNewSession: false,
                    };
                }
            }
            const persistedSession = liveSessionProviderMismatch
                ? null
                : this.sessionPersistenceService.getSession(requestedSessionId);
            if (persistedSession && persistedSession.traceId !== traceId) {
                throw new AnalyzeSessionPreparationError('traceId mismatch for requested session', {
                    code: 'TRACE_ID_MISMATCH',
                    httpStatus: 400,
                    hint: `This session belongs to traceId=${persistedSession.traceId}. Switch to that trace or start a new chat.`,
                });
            }
            if (persistedSession && persistedSession.traceId === traceId) {
                const stateSnapshot = this.sessionPersistenceService.loadSessionStateSnapshot(requestedSessionId);
                const persistedReferenceTraceId = readPersistedReferenceTraceId(stateSnapshot, persistedSession.metadata);
                assertReferenceTraceCompatible({
                    requestedSessionId,
                    existingReferenceTraceId: persistedReferenceTraceId,
                    requestedReferenceTraceId,
                });
                inheritedReferenceTraceId = persistedReferenceTraceId;
                const restoredContext = this.sessionPersistenceService.loadSessionContext(requestedSessionId);
                if (restoredContext) {
                    this.sessionContextManager.set(requestedSessionId, traceId, restoredContext);
                    const snapshotProviderId = stateSnapshot?.agentRuntimeProviderId;
                    const restoredProviderId = explicitProviderId !== undefined
                        ? explicitProviderId
                        : stateSnapshot
                            ? snapshotProviderId ?? null
                            : sessionProviderId;
                    const snapshotProviderMismatch = Boolean(explicitProviderId !== undefined &&
                        stateSnapshot &&
                        snapshotProviderId !== explicitProviderId);
                    if (!snapshotProviderMismatch && typeof snapshotProviderId === 'string' && !providerSvc.getRawProvider(snapshotProviderId, providerScope)) {
                        throw new AnalyzeSessionPreparationError(`Provider not found: ${snapshotProviderId}`, {
                            code: 'PROVIDER_NOT_FOUND',
                            httpStatus: 404,
                            hint: 'This persisted session was created with a Provider Manager profile that no longer exists. Recreate the provider or start a new chat.',
                        });
                    }
                    const restoredProviderSnapshotHash = !snapshotProviderMismatch
                        ? resolveProviderSnapshotHash(restoredProviderId, restoredProviderId ? undefined : stateSnapshot?.agentRuntimeKind)
                        : null;
                    const snapshotProviderHashMismatch = Boolean(!snapshotProviderMismatch &&
                        stateSnapshot?.agentRuntimeProviderSnapshotHash &&
                        restoredProviderSnapshotHash &&
                        stateSnapshot.agentRuntimeProviderSnapshotHash !== restoredProviderSnapshotHash);
                    if (snapshotProviderMismatch) {
                        console.log(`[AgentRoutes] Provider override changed for ${requestedSessionId}, creating a new agent session`);
                    }
                    else {
                        const restoredOrchestrator = (0, agentRuntime_1.createAgentOrchestrator)({
                            traceProcessorService: (0, traceProcessorService_1.getTraceProcessorService)(),
                            providerId: restoredProviderId,
                            runtimeOverride: restoredProviderId ? undefined : stateSnapshot?.agentRuntimeKind,
                            providerScope,
                        });
                        const focusSnapshot = this.sessionPersistenceService.loadFocusStore(requestedSessionId);
                        if (focusSnapshot && typeof restoredOrchestrator.getFocusStore === 'function') {
                            restoredOrchestrator.getFocusStore().loadSnapshot(focusSnapshot);
                            restoredOrchestrator.getFocusStore().syncWithEntityStore(restoredContext.getEntityStore());
                        }
                        // P0-2: Restore cached architecture to prevent re-detection failure after trace unload
                        const archSnapshot = this.sessionPersistenceService.loadArchitectureSnapshot(requestedSessionId);
                        if (archSnapshot && typeof restoredOrchestrator.restoreArchitectureCache === 'function') {
                            restoredOrchestrator.restoreArchitectureCache(traceId, archSnapshot);
                        }
                        const traceAgentStateSnapshot = this.sessionPersistenceService.loadTraceAgentState(requestedSessionId);
                        if (traceAgentStateSnapshot) {
                            restoredContext.setTraceAgentState(traceAgentStateSnapshot);
                        }
                        // Restore SDK runtime internal maps/state from the unified snapshot.
                        // Mirrors the explicit /resume endpoint so both paths recover the
                        // full agent state, not just SessionContext.
                        if (stateSnapshot &&
                            !snapshotProviderHashMismatch &&
                            typeof restoredOrchestrator.restoreFromSnapshot === 'function') {
                            restoredOrchestrator.restoreFromSnapshot(requestedSessionId, traceId, stateSnapshot);
                        }
                        else if (snapshotProviderHashMismatch) {
                            console.log(`[AgentRoutes] Provider snapshot changed for ${requestedSessionId}, skipping SDK session restoration`);
                        }
                        const restoredTurns = restoredContext.getAllTurns();
                        const latestTurn = restoredTurns.length > 0 ? restoredTurns[restoredTurns.length - 1] : null;
                        const recoveredResult = this.buildRecoveredResultFromContext(requestedSessionId, restoredContext);
                        const restoredSequence = Math.max(0, restoredTurns.length);
                        const fallbackRestoredRun = restoredSequence > 0
                            ? {
                                runId: `run-${requestedSessionId}-${restoredSequence}-recovered`,
                                requestId: `recovered-${requestedSessionId}-${restoredSequence}`,
                                sequence: restoredSequence,
                                query: latestTurn?.query || persistedSession.question,
                                startedAt: latestTurn?.timestamp || persistedSession.createdAt,
                                completedAt: persistedSession.updatedAt,
                                status: 'completed',
                            }
                            : undefined;
                        const restoredRun = stateSnapshot?.lastRun || stateSnapshot?.activeRun || fallbackRestoredRun;
                        const restoredLogger = this.createSessionLogger(requestedSessionId);
                        restoredLogger.setMetadata({
                            traceId,
                            query,
                            architecture: 'agent-driven',
                            resumed: true,
                            referenceTraceId: requestedReferenceTraceId ?? inheritedReferenceTraceId,
                        });
                        restoredLogger.info('AgentRoutes', 'Session restored from persistence in analyze()', {
                            turnCount: restoredTurns.length,
                            entityStoreStats: restoredContext.getEntityStore().getStats(),
                        });
                        if (snapshotProviderHashMismatch) {
                            restoredLogger.warn('AgentRoutes', 'Provider snapshot changed; fresh SDK runtime will be used', {
                                providerId: restoredProviderId,
                                previousProviderSnapshotHash: stateSnapshot?.agentRuntimeProviderSnapshotHash,
                                nextProviderSnapshotHash: restoredProviderSnapshotHash,
                            });
                        }
                        // Reconstruct query/conclusion history from persisted turns
                        const restoredQueryHistory = [];
                        const restoredConclusionHistory = [];
                        for (let i = 0; i < restoredTurns.length; i++) {
                            const t = restoredTurns[i];
                            if (t.query) {
                                restoredQueryHistory.push({ turn: i + 1, query: t.query, timestamp: t.timestamp || persistedSession.createdAt });
                            }
                            if (t.result && typeof t.result.conclusion === 'string') {
                                restoredConclusionHistory.push({
                                    turn: i + 1,
                                    conclusion: t.result.conclusion,
                                    confidence: t.result.confidence ?? 0,
                                    timestamp: t.timestamp || persistedSession.createdAt,
                                });
                            }
                        }
                        // R4: Restore runtime arrays from SQLite for cross-restart report continuity
                        const runtimeArrays = this.sessionPersistenceService.loadRuntimeArrays(requestedSessionId);
                        // Source priority for session arrays: snapshot (newer unified format)
                        // > runtimeArrays (legacy fallback) > reconstructed from turns.
                        // agentDialogue + agentResponses live only in the snapshot — prior
                        // code hardcoded them to [], which is what Codex flagged as lost state.
                        const effectiveReferenceTraceId = requestedReferenceTraceId ?? inheritedReferenceTraceId;
                        const restoredSession = {
                            orchestrator: restoredOrchestrator,
                            sessionId: requestedSessionId,
                            sseClients: [],
                            result: recoveredResult || undefined,
                            status: 'pending',
                            traceId,
                            query,
                            tenantId: persistedSession.metadata?.tenantId,
                            workspaceId: persistedSession.metadata?.workspaceId,
                            userId: persistedSession.metadata?.userId,
                            providerId: restoredProviderId,
                            providerSnapshotHash: restoredProviderSnapshotHash ?? sessionProviderSnapshotHash,
                            providerSnapshotChanged: snapshotProviderHashMismatch || undefined,
                            providerSnapshotChangeReason: snapshotProviderHashMismatch
                                ? 'provider_snapshot_hash_mismatch'
                                : undefined,
                            referenceTraceId: effectiveReferenceTraceId,
                            comparisonSource: stateSnapshot?.comparisonSource
                                ?? comparisonSourceForReference(effectiveReferenceTraceId),
                            comparisonReportSection: stateSnapshot?.comparisonReportSection,
                            createdAt: persistedSession.createdAt,
                            lastActivityAt: Date.now(),
                            logger: restoredLogger,
                            hypotheses: stateSnapshot?.hypotheses || runtimeArrays?.hypotheses || [],
                            agentDialogue: stateSnapshot?.agentDialogue || [],
                            dataEnvelopes: stateSnapshot?.dataEnvelopes || runtimeArrays?.dataEnvelopes || [],
                            claimSupport: stateSnapshot?.claimSupport,
                            claimVerificationResult: stateSnapshot?.claimVerificationResult,
                            identityResolutions: stateSnapshot?.identityResolutions,
                            agentResponses: stateSnapshot?.agentResponses || [],
                            conversationOrdinal: stateSnapshot?.conversationOrdinal || 0,
                            conversationSteps: stateSnapshot?.conversationSteps || runtimeArrays?.conversationSteps || [],
                            runSequence: stateSnapshot?.runSequence || restoredSequence,
                            activeRun: stateSnapshot?.activeRun || restoredRun,
                            lastRun: stateSnapshot?.lastRun || restoredRun,
                            queryHistory: stateSnapshot?.queryHistory || runtimeArrays?.queryHistory || restoredQueryHistory,
                            conclusionHistory: stateSnapshot?.conclusionHistory
                                || runtimeArrays?.conclusionHistory
                                || restoredConclusionHistory,
                            sseEventSeq: 0,
                            sseEventBuffer: [],
                        };
                        this.assistantAppService.setSession(requestedSessionId, restoredSession);
                        restoredLogger.info('AgentRoutes', 'Continuing multi-turn dialogue from persisted context', {
                            turnQuery: query,
                            previousQuery: latestTurn?.query || persistedSession.question,
                            turnCount: restoredTurns.length,
                            runtimeArraysRestored: !!runtimeArrays,
                            restoredSteps: runtimeArrays?.conversationSteps?.length || 0,
                            restoredEnvelopes: runtimeArrays?.dataEnvelopes?.length || 0,
                        });
                        console.log(`[AgentRoutes] Restored agent session ${requestedSessionId} from persistence for multi-turn dialogue`);
                        return {
                            sessionId: requestedSessionId,
                            session: restoredSession,
                            isNewSession: false,
                        };
                    }
                }
                console.log(`[AgentRoutes] Requested session ${requestedSessionId} has no persisted context, creating new session`);
            }
            else {
                console.log(`[AgentRoutes] Requested session ${requestedSessionId} not found, creating new session`);
            }
        }
        const effectiveReferenceTraceId = requestedReferenceTraceId ?? inheritedReferenceTraceId;
        const sessionId = `agent-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
        const orchestrator = (0, agentRuntime_1.createAgentOrchestrator)({
            traceProcessorService: (0, traceProcessorService_1.getTraceProcessorService)(),
            providerId: sessionProviderId,
            providerScope,
        });
        const logger = this.createSessionLogger(sessionId);
        logger.setMetadata({
            traceId,
            query,
            architecture: 'agent-driven',
            referenceTraceId: effectiveReferenceTraceId,
        });
        logger.info('AgentRoutes', 'Agent-driven analysis session created', { options });
        const session = {
            orchestrator,
            sessionId,
            sseClients: [],
            status: 'pending',
            traceId,
            query,
            providerId: sessionProviderId,
            providerSnapshotHash: sessionProviderSnapshotHash,
            providerSnapshotChanged: false,
            referenceTraceId: effectiveReferenceTraceId,
            comparisonSource: comparisonSourceForReference(effectiveReferenceTraceId),
            createdAt: Date.now(),
            lastActivityAt: Date.now(),
            logger,
            hypotheses: [],
            agentDialogue: [],
            dataEnvelopes: [],
            claimSupport: [],
            identityResolutions: [],
            agentResponses: [],
            conversationOrdinal: 0,
            conversationSteps: [],
            runSequence: 0,
            queryHistory: [],
            conclusionHistory: [],
            sseEventSeq: 0,
            sseEventBuffer: [],
        };
        this.assistantAppService.setSession(sessionId, session);
        return {
            sessionId,
            session,
            isNewSession: true,
        };
    }
}
exports.AgentAnalyzeSessionService = AgentAnalyzeSessionService;
//# sourceMappingURL=agentAnalyzeSessionService.js.map