import { AgentLoopRuntimeService } from '../../../../core/runtime/loop/index.js';
import { FileConversationSessionService } from '../../../../core/chat/engine/sessions/service.js';
import { ConversationEngineHostNormalizer } from './host/index.js';
import { ConversationTurnContextBuilder } from './context/index.js';
import { ConversationTurnPreflightService } from './preflight/index.js';
import { ConversationTurnMemoryMaintenance } from './memory/index.js';
import { ConversationTurnPersistenceService } from './persistence/index.js';
import { ChatSessionLeases } from '../../../../core/chat/engine/sessions/leases/index.js';
import { ChatSessionRecords } from '../../../../core/chat/engine/sessions/records/index.js';
import { FileChatSessionRepository } from '../../../../core/chat/engine/sessions/repository/index.js';
export class EngineConversationTurnService {
    config;
    sessions;
    constructor(config) {
        this.config = config;
        this.sessions = new FileConversationSessionService(config);
    }
    async submit(input) {
        const normalizedHost = ConversationEngineHostNormalizer.normalize(input.host);
        const runtimeConfigInput = this.config;
        const turnInput = input;
        return await EngineConversationTurnService.run({
            ...runtimeConfigInput,
            ...turnInput,
            ...EngineConversationTurnService.hostInput(normalizedHost, input),
            memoryMaintenanceMode: input.memoryMaintenanceMode ?? this.config.memoryMaintenanceMode,
            approvalPolicies: input.approvalPolicies ?? this.config.approvalPolicies,
            traceSummarizerRegistry: input.traceSummarizerRegistry ?? this.config.traceSummarizerRegistry,
        });
    }
    async continue(input) {
        const session = this.sessions.require(input.sessionId);
        if (!input.prompt && (!session.history.length || !session.lastContinuePrompt)) {
            throw new Error('There is no interrupted or prior run to continue yet.');
        }
        return await this.submit({
            ...input,
            prompt: input.prompt ?? session.lastContinuePrompt ?? '',
        });
    }
    clearLease(input) {
        if (!this.sessions.read(input.sessionId)?.lease) {
            return;
        }
        this.sessions.releaseLease(input.sessionId, input.owner);
    }
    static async run(args) {
        const contextInput = args;
        const context = ConversationTurnContextBuilder.build(contextInput);
        const { sessions, session, runtime, tools, toolNames, leaseOwner } = context;
        const host = EngineConversationTurnService.turnHost(args);
        const source = `chat session ${session.id}`;
        const preflightInput = args;
        const agentLoopInput = args;
        const persistenceInput = args;
        const memoryRuntime = {
            memoryRoot: runtime.memoryDir,
            llm: runtime.llm,
            source,
            onEvent: host.onEvent,
        };
        try {
            const preflight = await ConversationTurnPreflightService.prepare({
                ...preflightInput,
                sessionId: session.id,
                fallbackHistory: session.history,
                model: runtime.model,
                systemContext: runtime.systemContext,
                toolNames,
                summarizer: { credentialSource: runtime.providerCredentialSource },
                leaseOwner,
                sessions,
                host,
            });
            if (!preflight.ok) {
                throw new Error(preflight.message);
            }
            const result = await AgentLoopRuntimeService.run({
                ...agentLoopInput,
                goal: args.prompt,
                model: runtime.model,
                apiKey: runtime.apiKey,
                stateDir: args.stateRoot,
                memoryDir: runtime.memoryDir,
                llm: runtime.llm,
                tools,
                includeDefaultTools: false,
                history: preflight.compacted.history,
                systemContext: runtime.systemContext,
                onEvent: host.onEvent,
                approveToolCall: host.approveToolCall,
            });
            const maintenanceMode = args.memoryMaintenanceMode ?? 'background';
            const resultForPersistence = maintenanceMode === 'inline'
                ? await ConversationTurnMemoryMaintenance.runInline({
                    ...memoryRuntime,
                    result,
                })
                : result;
            const persisted = await ConversationTurnPersistenceService.persistCompleted({
                ...persistenceInput,
                result: resultForPersistence,
                session: preflight.session ?? session,
                sessions,
                model: runtime.model,
                systemContext: runtime.systemContext,
                toolNames,
                historyForTokenEstimate: session.history,
                credentialSource: runtime.providerCredentialSource,
                host,
            });
            if (maintenanceMode === 'background') {
                ConversationTurnMemoryMaintenance.scheduleBackground({
                    ...memoryRuntime,
                    trace: result.trace,
                    traceFile: persisted.traceFile,
                    sessionStoragePath: args.sessionStoragePath,
                    sessionId: session.id,
                    runId: result.state?.runId ?? `session-${session.id}`,
                });
            }
            return {
                outcome: resultForPersistence.outcome,
                summary: persisted.summary,
                session: persisted.session,
            };
        }
        finally {
            EngineConversationTurnService.clearLeaseFromStorage(args.sessionStoragePath, session.id, leaseOwner);
        }
    }
    static clearLeaseFromStorage(sessionStoragePath, sessionId, owner) {
        const repository = new FileChatSessionRepository({ sessionStoragePath });
        const sessions = repository.list();
        const session = sessions.find((candidate) => candidate.id === sessionId);
        if (!session?.lease) {
            return;
        }
        const released = ChatSessionLeases.release(session, owner);
        if (released === session) {
            return;
        }
        repository.save(sessions.map((candidate) => (candidate.id === sessionId ? ChatSessionRecords.touch(released) : candidate)));
    }
    static hostInput(normalizedHost, input) {
        return {
            host: normalizedHost.turnHost,
            onTraceEvent: normalizedHost.onTraceEvent,
            shouldStop: input.shouldStop,
        };
    }
    static turnHost(args) {
        if (!args.onCompactionStatus) {
            return args.host ?? {};
        }
        return {
            ...args.host,
            onCompactionStatus: (event, phase) => {
                args.onCompactionStatus?.(event);
                args.host?.onCompactionStatus?.(event, phase);
            },
        };
    }
}
//# sourceMappingURL=service.js.map