"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppPlannerAgent = exports.PlannerAgentToolGroups = void 0;
exports.logReplacer = logReplacer;
const path_1 = __importDefault(require("path"));
const cliconfig_1 = require("../../database/cliconfig");
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
const chalk_1 = __importDefault(require("chalk"));
const baseAgent_1 = require("./baseAgent");
const projectrootpath_1 = require("../../projectrootpath");
const logger_1 = require("../../utils/logger");
const tokenCounter_1 = require("../tokenCounter");
const supabase_1 = require("./mcp-servers/supabase");
const Sentry = __importStar(require("@sentry/node"));
/**
 * We use a custom replacer for logging big strings in JSON
 */
function logReplacer(key, value) {
    if (key === "data" && typeof value === "string" && value.length > 20) {
        return value.slice(0, 20) + "...[truncated]";
    }
    return value;
}
exports.PlannerAgentToolGroups = [
    "projectCodeReadForPlanner",
    "dashboardRead",
    // "dashboardWrite",
    "promptPluginAgent",
    "codeExecution",
    "supabaseTools",
    "supabaseReadTools",
    "batchedTool"
];
class AppPlannerAgent extends baseAgent_1.BaseAgent {
    constructor() {
        super();
        this.initialize().catch((err) => {
            console.error("Failed to initialize PluginAgent", err);
        });
    }
    initialize() {
        const _super = Object.create(null, {
            initialize: { get: () => super.initialize }
        });
        return __awaiter(this, void 0, void 0, function* () {
            _super.initialize.call(this);
            if (!this.mcp) {
                this.mcp = {
                    client: new index_js_1.Client({ name: "planner-agent-client", version: "1.0.0" }),
                };
                yield this.connectToMCPServer(path_1.default.resolve(projectrootpath_1.nodejsRoot, "controllers", "agents", "mcp-servers", "index.ts"), this.mcp, [
                    "appPlannerAgent", // name of the mcp server. The rest are tools.
                    ...exports.PlannerAgentToolGroups
                ]);
            }
        });
    }
    // public prepareUserPrompt(
    //   userInput: string | {
    //     isToolResponse: boolean; 
    //     response: Array<{result: string; id: string;}>; 
    //   }, 
    //   assetName: string): UserPrompt[] {
    //   if (typeof userInput === "string") {
    //     return super.prepareUserPrompt(userInput, assetName);
    //   } else {
    //     return userInput.response.map(resp => {
    //       return {
    //         type: "tool_result",
    //         tool_use_id: resp.id,
    //         content: resp.result
    //       };
    //     });
    //   }
    // }
    /**
     * The main driver method that replaces the old 'prompt(...)' function. It orchestrates
     * the sub-steps using the class methods above.
     */
    runPrompt(appId, assetName, userPrompt, req, res, model, provider, user, temp_id) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c;
            const chalkColor = (0, baseAgent_1.cycleColor)();
            logger_1.logger.info(chalkColor(`[PlannerAgent][${user}][${appId}]`) + " runPrompt started with userPrompt: " + chalk_1.default.gray(JSON.stringify(userPrompt, null, 2)));
            let requestDropped = false;
            req.socket.on("close", () => {
                requestDropped = true;
                logger_1.logger.info(chalkColor(`[PlannerAgent][${user}][${appId}] request was closed. Tool loop will be abandoned if its still running.`));
            });
            // Prepare prompt data
            const finalizedPrompt = this.prepareUserPrompt(userPrompt, assetName);
            // ensure anthropic is ready
            if (!this.claude) {
                logger_1.logger.info(chalkColor(`[PlannerAgent][${user}]`) + " no anthropic client found!");
                return {
                    tokenCounterInstance: null,
                    message: "No anthropic client found!",
                };
            }
            if (!this.openai) {
                logger_1.logger.info(chalkColor(`[PlannerAgent][${user}]`) + " no openai client found!");
                return {
                    tokenCounterInstance: null,
                    message: "No openai client found!",
                };
            }
            if (!this.google) {
                logger_1.logger.info(chalkColor(`[PlannerAgent][${user}]`) + " no google client found!");
                return {
                    tokenCounterInstance: null,
                    message: "No google client found!",
                };
            }
            if (!this.amazon) {
                logger_1.logger.info(chalkColor(`[PlannerAgent][${user}]`) + " no amazon client found!");
                return {
                    tokenCounterInstance: null,
                    message: "No amazon client found!",
                };
            }
            // get the open app
            const openedApp = yield (0, cliconfig_1.getOpenApp)(appId);
            if (!openedApp) {
                logger_1.logger.info(chalkColor(`[PlannerAgent][${user}]`) + " no app found for appId: " + appId);
                return {
                    tokenCounterInstance: null,
                    message: "App not found!",
                };
            }
            const pluginsDir = path_1.default.resolve(openedApp.repoPath, "remoteCode", "plugins");
            // we will build a file for writing
            logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " anchoring chat to " + appId);
            const { chat, messages, systemPrompt } = yield this.retrieveOrCreateChat(appId, model, "app", provider, finalizedPrompt, path_1.default.resolve(projectrootpath_1.nodejsRoot, provider === "amazon" ? "systemPrompts/tokenoptimized/appPlanner.html" : "systemPrompts/appPlanner.html"), appId, user, temp_id);
            logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " Chat retrieved");
            // const slicedMessages = messages.length < 10 ? messages : messages.slice(messages.length - 10);
            const slicedMessages = messages;
            // Now ensure we are under token budget
            let finalMessages = yield this.fitHistoryWithinTokenBudget(slicedMessages, systemPrompt, model, provider, null, user);
            logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " messages fit inside token budget" + finalMessages.length);
            //message history array for counting tokens later
            const tokenCounterInstance = new tokenCounter_1.TokenCounter([], "app", model, provider);
            try {
                let toolRun = false;
                do {
                    logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " triggering completion");
                    //Putting the input message in chat history message
                    tokenCounterInstance.appendInMemoryInputMessages(finalMessages);
                    let llmsdk;
                    if (provider === "claude") {
                        llmsdk = this.claude;
                    }
                    else if (provider === "google") {
                        llmsdk = this.openai;
                    }
                    else if (provider === "amazon") {
                        llmsdk = this.amazon;
                    }
                    else {
                        llmsdk = this.openai;
                    }
                    const ingestedResponse = yield this.ingestResponseAndStream(llmsdk, systemPrompt, model, finalMessages, res, tokenCounterInstance, true);
                    logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " completion finished");
                    if ((ingestedResponse === null || ingestedResponse === void 0 ? void 0 : ingestedResponse.messageText) &&
                        ingestedResponse.messageText.trim()) {
                        //Pushing to chat history array
                        tokenCounterInstance.appendInMemoryOutputMessages([
                            {
                                role: "assistant",
                                tool_call_id: null,
                                tool_name: null,
                                content_type: "text",
                                content: ingestedResponse === null || ingestedResponse === void 0 ? void 0 : ingestedResponse.messageText,
                                temp_id: ingestedResponse === null || ingestedResponse === void 0 ? void 0 : ingestedResponse.uuid
                            },
                        ]);
                        yield (0, cliconfig_1.persistChatMessage)(chat.id, null, null, "text", "assistant", ingestedResponse.messageText, ingestedResponse.uuid);
                        finalMessages.push({
                            role: "assistant",
                            tool_call_id: null,
                            tool_name: null,
                            content_type: "text",
                            content: ingestedResponse.messageText,
                            temp_id: ingestedResponse.uuid
                        });
                    }
                    logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " Checking for token usage exceeeded error");
                    if ((ingestedResponse === null || ingestedResponse === void 0 ? void 0 : ingestedResponse.status) === "MaxTokensUsed") {
                        logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " Claude stopped in the middle. We'll add a line to continue and loop again.");
                        yield new Promise((resolve) => setTimeout(resolve, 20000));
                        finalMessages.push({
                            role: "user",
                            content_type: "text",
                            tool_call_id: null,
                            tool_name: null,
                            content: `You stopped in the middle of generation. Please continue. If you were in the middle of calling the tool apply_file_ops
                then you can try splitting the ops across multiple tool calls to avoid running out of tokens.`,
                            temp_id: "none"
                        });
                        finalMessages = yield this.fitHistoryWithinTokenBudget(finalMessages, systemPrompt, model, provider, null, user);
                        logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) + " Continuing after refitting and pausing");
                        continue; // back up and call again
                    }
                    // handle tool calls
                    toolRun = !!(ingestedResponse === null || ingestedResponse === void 0 ? void 0 : ingestedResponse.toolInput);
                    if (toolRun && (ingestedResponse === null || ingestedResponse === void 0 ? void 0 : ingestedResponse.toolInput)) {
                        //Putting the tool call in chat history message
                        tokenCounterInstance.appendInMemoryOutputMessages((_a = ingestedResponse.toolInput) === null || _a === void 0 ? void 0 : _a.flatMap((toolcall) => [
                            {
                                role: "assistant",
                                tool_call_id: toolcall.id,
                                tool_name: toolcall.tool,
                                content_type: "tool_use",
                                content: toolcall.partialInput || "",
                                temp_id: "none"
                            },
                            {
                                role: "user",
                                tool_call_id: toolcall.id,
                                tool_name: toolcall.tool,
                                content_type: "tool_result",
                                content: "",
                                temp_id: "none"
                            },
                        ]));
                        logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) +
                            " tool call detected. Processing.");
                        let nextStep = "ContinueChat";
                        let supabaseCreds = null;
                        for (let toolcall of ingestedResponse.toolInput) {
                            logger_1.logger.info(chalkColor(`[ArchAgent][${user}]${appId}`) +
                                " running tool: " +
                                chalk_1.default.green(toolcall.tool));
                            if (toolcall.tool.startsWith("supabase_") && !supabaseCreds) {
                                supabaseCreds = yield supabase_1.supabaseConfigManager.getCreds(appId);
                            }
                            let res = yield this.handleToolRun(toolcall, finalMessages, {
                                plugin_root: "",
                                plugins_dir: pluginsDir,
                                app_id: appId,
                                supabase_creds: supabaseCreds,
                            }, ingestedResponse.uuid, chat.id);
                            // once nextstep is set to waitforuserinput we don't unset it till user responds
                            if (nextStep !== "WaitForUserInput") {
                                nextStep = res;
                            }
                        }
                        logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) +
                            " Next step for tool: " +
                            chalk_1.default.green(nextStep));
                        if (nextStep === "WaitForUserInput") {
                            toolRun = false;
                        }
                        else if (nextStep === "ContinueChat" || nextStep === "ToolError") {
                            toolRun = true;
                        }
                    }
                    else {
                        logger_1.logger.info(chalkColor(`[ArchAgent][${user}][${appId}]`) +
                            " Stopping because no toolrun was detected");
                    }
                } while (toolRun && !requestDropped);
            }
            catch (err) {
                console.error(chalk_1.default.red(`[ArchAgent][${user}][${appId}] Failure during Claude conversation:`, (_b = err === null || err === void 0 ? void 0 : err.error) === null || _b === void 0 ? void 0 : _b.error), err);
                res.write("streamItemStart:"
                    + JSON.stringify({ type: "error", details: ((_c = err === null || err === void 0 ? void 0 : err.error) === null || _c === void 0 ? void 0 : _c.error) || ("unexpected error " + err) })
                    + ":streamItemEnd");
                if (err && process.env.ENABLE_SENTRY) {
                    Sentry.captureException(err);
                }
                // res.write("\nAn error occurred\n");
                // res.write(err?.error?.message || "\nNo error details\n");
            }
            finally {
                // close the file
                // fileStream.end();
            }
            // res.write("turn finished\n");
            res.end();
            return {
                tokenCounterInstance,
                message: "Success",
            };
        });
    }
}
exports.AppPlannerAgent = AppPlannerAgent;
