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 { createAgent, summarizationMiddleware, humanInTheLoopMiddleware } from "langchain";
import { createApiBasedToolsMiddleware } from "./middleware/apiToolsMiddleware.js";
import { createSequenceDebugMiddleware } from "./middleware/sequenceDebug.js";
import { createSteerMiddleware } from "./middleware/steerMiddleware.js";
import { createAgentLlmMetricsLogger } from "./agentModels.js";
import { contextSchema, toLangchainAgentContext } from "./agentContext.js";
function createHumanInTheLoopInterrupts(apiBasedTools) {
    return Object.fromEntries(Object.entries(apiBasedTools)
        .filter(([, apiBasedTool]) => { var _a; return ((_a = apiBasedTool.agent) === null || _a === void 0 ? void 0 : _a.isDangerous) === true; })
        .map(([toolName]) => [
        toolName,
        {
            allowedDecisions: ["approve", "reject"],
        },
    ]));
}
export class AgentRuntime {
    constructor(options) {
        this.options = options;
    }
    stream(input) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const apiBasedTools = this.options.toolProvider.getApiBasedTools();
            const tools = yield this.options.toolProvider.getTools(apiBasedTools);
            const adminforth = this.options.getAdminforth();
            const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools, adminforth);
            // The domain exposes only the narrow DebugSink; the concrete collector (with the
            // model-call hooks the debug middleware needs) is an llm-layer detail.
            const sequenceDebugSink = input.observability.sequenceDebugSink;
            const sequenceDebugMiddleware = createSequenceDebugMiddleware(sequenceDebugSink);
            const hitlMiddleware = humanInTheLoopMiddleware({
                interruptOn: createHumanInTheLoopInterrupts(apiBasedTools),
                descriptionPrefix: "Tool execution pending approval",
            });
            const steerMiddleware = createSteerMiddleware(this.options.steerBuffer);
            const middleware = [
                steerMiddleware,
                apiBasedToolsMiddleware,
                hitlMiddleware,
                ...((_a = input.models.modelMiddleware) !== null && _a !== void 0 ? _a : []),
                sequenceDebugMiddleware,
                summarizationMiddleware({
                    model: input.models.summaryModel,
                    trigger: { tokens: 1024 * 64 },
                    keep: { messages: 10 },
                }),
            ];
            const agent = createAgent({
                name: this.options.name,
                model: input.models.model,
                checkpointer: this.options.getCheckpointer(),
                tools,
                contextSchema,
                middleware,
            });
            return agent.stream(input.input, {
                streamMode: ["messages", "updates"],
                recursionLimit: 100,
                callbacks: [createAgentLlmMetricsLogger()],
                signal: input.context.abortSignal,
                configurable: Object.assign({ thread_id: input.context.sessionId }, (input.branchFromCheckpointId
                    ? { checkpoint_id: input.branchFromCheckpointId }
                    : {})),
                context: toLangchainAgentContext(Object.assign(Object.assign({}, input.context), { adminBaseUrl: adminforth.config.baseUrlSlashed, emit: input.observability.emit, sequenceDebugSink })),
            });
        });
    }
    /**
     * Read the pending human-in-the-loop interrupts persisted for a thread from the
     * checkpointer. Builds a minimal agent (model + checkpointer + HITL middleware) and
     * queries its state — no model call is made. Returns raw LangGraph interrupt objects.
     */
    getPendingInterrupts(input) {
        return __awaiter(this, void 0, void 0, function* () {
            const apiBasedTools = this.options.toolProvider.getApiBasedTools();
            const tools = yield this.options.toolProvider.getTools(apiBasedTools);
            const agent = createAgent({
                name: this.options.name,
                model: input.models.model,
                checkpointer: this.options.getCheckpointer(),
                tools,
                contextSchema,
                middleware: [
                    humanInTheLoopMiddleware({
                        interruptOn: createHumanInTheLoopInterrupts(apiBasedTools),
                        descriptionPrefix: "Tool execution pending approval",
                    }),
                ],
            });
            const state = yield agent.getState({
                configurable: { thread_id: input.sessionId },
            });
            const tasks = Array.isArray(state === null || state === void 0 ? void 0 : state.tasks) ? state.tasks : [];
            return tasks.flatMap((task) => (Array.isArray(task === null || task === void 0 ? void 0 : task.interrupts) ? task.interrupts : []));
        });
    }
    /**
     * Return the thread's latest (tip) checkpoint id, or null when the thread has no
     * persisted state. Builds a minimal agent and reads its state — no model call is
     * made. Called after a turn completes to record the turn's fork point for editing.
     */
    getLatestCheckpointId(input) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const apiBasedTools = this.options.toolProvider.getApiBasedTools();
            const tools = yield this.options.toolProvider.getTools(apiBasedTools);
            const agent = createAgent({
                name: this.options.name,
                model: input.models.model,
                checkpointer: this.options.getCheckpointer(),
                tools,
                contextSchema,
                middleware: [
                    humanInTheLoopMiddleware({
                        interruptOn: createHumanInTheLoopInterrupts(apiBasedTools),
                        descriptionPrefix: "Tool execution pending approval",
                    }),
                ],
            });
            const state = yield agent.getState({
                configurable: { thread_id: input.sessionId },
            });
            const checkpointId = (_b = (_a = state === null || state === void 0 ? void 0 : state.config) === null || _a === void 0 ? void 0 : _a.configurable) === null || _b === void 0 ? void 0 : _b.checkpoint_id;
            return checkpointId ? String(checkpointId) : null;
        });
    }
    /**
     * Drop the entire LangGraph checkpoint thread for a session. Used when editing the
     * first message, where there is no earlier checkpoint to fork from.
     */
    resetThreadCheckpoints(sessionId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.options.getCheckpointer().deleteThread(sessionId);
        });
    }
}
