var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { logger } from "adminforth";
import { randomUUID } from "crypto";
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
import { getErrorMessage, isAbortError } from "./errors.js";
export class AgentTurnService {
    constructor(lifecycle, contextBuilder, modeResolver, modelFactory, promptBuilder, runtime, streamConsumer) {
        this.lifecycle = lifecycle;
        this.contextBuilder = contextBuilder;
        this.modeResolver = modeResolver;
        this.modelFactory = modelFactory;
        this.promptBuilder = promptBuilder;
        this.runtime = runtime;
        this.streamConsumer = streamConsumer;
    }
    prepareTurn(input) {
        return __awaiter(this, void 0, void 0, function* () {
            const sequenceDebugCollector = createSequenceDebugCollector();
            const { turnId, previousUserMessages } = yield this.lifecycle.start(input);
            const context = yield this.contextBuilder.build({
                base: input,
                turnId,
            });
            return {
                prompt: input.prompt,
                sessionId: input.sessionId,
                turnId,
                previousUserMessages,
                modeName: input.modeName,
                context,
                observability: {
                    emit: undefined,
                    sequenceDebugSink: sequenceDebugCollector,
                },
            };
        });
    }
    runAgentTurn(input) {
        return __awaiter(this, void 0, void 0, function* () {
            const selectedMode = this.modeResolver.resolve(input.modeName);
            const [models, messages] = yield Promise.all([
                this.modelFactory.create(selectedMode.completionAdapter),
                this.promptBuilder.build({
                    prompt: input.prompt,
                    previousUserMessages: input.previousUserMessages,
                    adminUser: input.context.adminUser,
                    completionAdapter: selectedMode.completionAdapter,
                    chatSurface: input.context.chatSurface,
                    abortSignal: input.context.abortSignal,
                }),
            ]);
            const stream = yield this.runtime.stream({
                models,
                messages,
                context: input.context,
                observability: input.observability,
            });
            return this.streamConsumer.consume({
                stream: stream,
                abortSignal: input.context.abortSignal,
                emit: input.observability.emit,
            });
        });
    }
    runAndPersistAgentResponse(input) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const preparedTurn = yield this.prepareTurn(input);
            preparedTurn.observability.emit = input.emit;
            let fullResponse = "";
            let aborted = false;
            let failed = false;
            try {
                const agentResponse = yield this.runAgentTurn(preparedTurn);
                fullResponse = agentResponse.text;
            }
            catch (error) {
                if (((_a = input.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) || isAbortError(error)) {
                    aborted = true;
                    logger.info(input.abortLogMessage);
                }
                else {
                    failed = true;
                    fullResponse = getErrorMessage(error);
                    logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
                }
            }
            preparedTurn.observability.sequenceDebugSink.flush();
            yield this.lifecycle.finish({
                turnId: preparedTurn.turnId,
                responseText: fullResponse,
                debugHistory: preparedTurn.observability.sequenceDebugSink.getHistory(),
            });
            return {
                text: fullResponse,
                turnId: preparedTurn.turnId,
                aborted,
                failed,
            };
        });
    }
    handleTurn(input) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            yield input.emit({
                type: "turn-started",
                messageId: randomUUID(),
            });
            const agentResponse = yield this.runAndPersistAgentResponse({
                prompt: input.prompt,
                sessionId: input.sessionId,
                modeName: input.modeName,
                userTimeZone: input.userTimeZone,
                currentPage: input.currentPage,
                chatSurface: input.chatSurface,
                adminPublicOrigin: input.adminPublicOrigin,
                abortSignal: input.abortSignal,
                adminUser: input.adminUser,
                emit: input.emit,
                failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
                abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
            });
            if (agentResponse.failed) {
                yield input.emit({
                    type: "error",
                    error: agentResponse.text,
                });
            }
            else if (!agentResponse.aborted) {
                yield input.emit({
                    type: "response",
                    text: agentResponse.text,
                    sessionId: input.sessionId,
                    turnId: agentResponse.turnId,
                });
            }
            yield input.emit({
                type: "finish",
            });
            return agentResponse;
        });
    }
}
