/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var ai_employee_exports = {};
__export(ai_employee_exports, {
  AIEmployee: () => AIEmployee
});
module.exports = __toCommonJS(ai_employee_exports);
var import_database = require("@nocobase/database");
var import_provider = require("../llm-providers/provider");
var import_utils = require("../utils");
var import_prompts = require("./prompts");
var import_lodash = __toESM(require("lodash"));
var import_ai_chat_conversation = require("../manager/ai-chat-conversation");
var import_ai_feature_manager = require("../manager/ai-feature-manager");
var import_prompts2 = require("@langchain/core/prompts");
var import_middleware = require("./middleware");
var import_checkpoints = require("./checkpoints");
var import_langchain = require("langchain");
var import_langgraph = require("@langchain/langgraph");
var import_stream = require("@langchain/core/utils/stream");
var import_utils2 = require("./utils");
var import_base = require("@langchain/core/callbacks/base");
class AIEmployee {
  employee;
  aiChatConversation;
  skillSettings;
  plugin;
  db;
  sessionId;
  ctx;
  systemMessage;
  webSearch;
  model;
  legacy;
  protocol;
  constructor(ctx, employee, sessionId, systemMessage, skillSettings, webSearch, model, legacy) {
    this.employee = employee;
    this.ctx = ctx;
    this.plugin = ctx.app.pm.get("ai");
    this.db = ctx.db;
    this.sessionId = sessionId;
    this.systemMessage = systemMessage;
    this.aiChatConversation = (0, import_ai_chat_conversation.createAIChatConversation)(this.ctx, this.sessionId);
    this.skillSettings = skillSettings;
    this.model = model;
    this.legacy = legacy;
    const locale = this.ctx.getCurrentLocale();
    const builtInManager = this.plugin.builtInManager;
    builtInManager.setupBuiltInInfo(locale, this.employee);
    this.webSearch = webSearch;
    this.protocol = ChatStreamProtocol.create(ctx);
  }
  // === Chat flow ===
  buildState(messages) {
    return {
      lastMessageIndex: {
        lastHumanMessageIndex: messages.filter((m) => m.role === "user").length,
        lastAIMessageIndex: messages.filter((m) => m.role === this.employee.username).length,
        lastToolMessageIndex: messages.filter((m) => m.role === "tool").length,
        lastMessageIndex: messages.length
      }
    };
  }
  async initSession({ messageId, provider, model, providerName }) {
    const tools = await this.getAIEmployeeTools();
    if (!messageId && this.legacy !== true) {
      return {
        historyMessages: [],
        tools,
        middleware: this.getMiddleware({ tools, model, providerName }),
        config: void 0,
        state: void 0
      };
    }
    const agentThread = await this.forkCurrentThread(provider);
    const historyMessages = await this.aiChatConversation.listMessages({
      messageId
    });
    return {
      historyMessages,
      tools,
      middleware: this.getMiddleware({
        tools,
        model,
        providerName,
        messageId,
        agentThread
      }),
      config: {
        configurable: {
          thread_id: agentThread.threadId
        }
      },
      state: this.buildState(historyMessages)
    };
  }
  async buildChatContext({
    messageId,
    userMessages,
    userDecisions
  }) {
    const { provider, model, service } = await this.plugin.aiManager.getLLMService({
      ...this.model
    });
    const { historyMessages, tools, middleware, config, state } = await this.initSession({
      messageId,
      provider,
      model,
      providerName: service.provider
    });
    const chatContext = await this.aiChatConversation.getChatContext({
      userMessages: [...historyMessages, ...userMessages ?? []],
      userDecisions,
      tools,
      middleware,
      getSystemPrompt: (userMessages2) => this.getSystemPrompt(userMessages2),
      formatMessages: (messages) => this.formatMessages({ messages, provider })
    });
    return { providerName: service.provider, model, provider, chatContext, config, state };
  }
  async stream({
    messageId,
    userMessages = [],
    userDecisions
  }) {
    try {
      const { providerName, model, provider, chatContext, config, state } = await this.buildChatContext({
        messageId,
        userMessages,
        userDecisions
      });
      const responseMetadata = /* @__PURE__ */ new Map();
      const responseMetadataCollector = new ResponseMetadataCollector(provider, responseMetadata);
      const { stream, signal } = await this.prepareChatStream({
        chatContext,
        provider,
        config: { ...config, callbacks: [responseMetadataCollector] },
        state
      });
      await this.processChatStream(stream, {
        signal,
        providerName,
        model,
        provider,
        responseMetadata
      });
      return true;
    } catch (err) {
      this.ctx.log.error(err);
      this.sendErrorResponse(err.message || "Chat error warning");
      return false;
    }
  }
  async invoke({
    messageId,
    userMessages = [],
    userDecisions
  }) {
    try {
      const { provider, chatContext, config, state } = await this.buildChatContext({
        messageId,
        userMessages,
        userDecisions
      });
      const { threadId } = await this.getCurrentThread();
      const invokeConfig = {
        configurable: { thread_id: threadId },
        context: { ctx: this.ctx },
        recursionLimit: 100,
        ...config
      };
      return await this.agentInvoke(provider, chatContext, invokeConfig, state);
    } catch (err) {
      this.ctx.log.error(err);
      throw err;
    }
  }
  // === Agent wiring & execution ===
  async createAgent({
    provider,
    systemPrompt,
    tools,
    middleware
  }) {
    const model = provider.createModel();
    const toolDefinitions = (tools == null ? void 0 : tools.map(import_provider.ToolDefinition.from("ToolsEntry"))) ?? [];
    const allTools = provider.resolveTools(toolDefinitions);
    const checkpointer = new import_checkpoints.SequelizeCollectionSaver(() => this.ctx.app.mainDataSource);
    return (0, import_langchain.createAgent)({ model, tools: allTools, middleware, systemPrompt, checkpointer });
  }
  getAgentInput(context, state) {
    var _a;
    if ((_a = context.decisions) == null ? void 0 : _a.length) {
      return new import_langgraph.Command({
        resume: {
          decisions: context.decisions
        }
      });
    }
    if (context.messages) {
      return { messages: context.messages, ...state };
    }
    return null;
  }
  async agentStream(provider, context, config, state) {
    const { systemPrompt, tools, middleware } = context;
    const agent = await this.createAgent({ provider, systemPrompt, tools, middleware });
    const input = this.getAgentInput(context, state);
    return agent.stream(input, config);
  }
  async agentInvoke(provider, context, config, state) {
    const { systemPrompt, tools, middleware } = context;
    const agent = await this.createAgent({ provider, systemPrompt, tools, middleware });
    const input = this.getAgentInput(context, state);
    return agent.invoke(input, config);
  }
  async prepareChatStream({
    chatContext,
    provider,
    config,
    state
  }) {
    const controller = new AbortController();
    const { signal } = controller;
    try {
      const { threadId } = await this.getCurrentThread();
      const stream = await this.agentStream(
        provider,
        chatContext,
        {
          signal,
          streamMode: ["updates", "messages", "custom"],
          configurable: { thread_id: threadId },
          context: { ctx: this.ctx },
          recursionLimit: 100,
          ...config
        },
        state
      );
      this.plugin.aiEmployeesManager.conversationController.set(this.sessionId, controller);
      return { stream, controller, signal };
    } catch (error) {
      throw error;
    }
  }
  async processChatStream(stream, options) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
    let toolCalls;
    const { signal, providerName, model, provider, responseMetadata, allowEmpty = false } = options;
    let isReasoning = false;
    let gathered;
    signal.addEventListener("abort", async () => {
      try {
        if ((gathered == null ? void 0 : gathered.type) === "ai") {
          const values = (0, import_utils2.convertAIMessage)({
            aiEmployee: this,
            providerName,
            model,
            aiMessage: gathered
          });
          if (values) {
            values.metadata.interrupted = true;
          }
          await this.aiChatConversation.withTransaction(async (conversation, transaction) => {
            const result = await conversation.addMessages(values);
            if (toolCalls == null ? void 0 : toolCalls.length) {
              await this.initToolCall(transaction, result.messageId, toolCalls);
            }
          });
        }
      } catch (e) {
        this.logger.error("Fail to save message after conversation abort", gathered);
      }
    });
    try {
      this.protocol.startStream();
      for await (const [mode, chunks] of stream) {
        if (mode === "messages") {
          const [chunk] = chunks;
          if (chunk.type === "ai") {
            gathered = gathered !== void 0 ? (0, import_stream.concat)(gathered, chunk) : chunk;
            if (chunk.content) {
              if (isReasoning) {
                isReasoning = false;
                this.protocol.stopReasoning();
              }
              const parsedContent = provider.parseResponseChunk(chunk.content);
              if (parsedContent) {
                this.protocol.content(parsedContent);
              }
            }
            if ((_a = chunk.tool_call_chunks) == null ? void 0 : _a.length) {
              this.protocol.toolCallChunks(chunk.tool_call_chunks);
            }
            const webSearch = provider.parseWebSearchAction(chunk);
            if (webSearch == null ? void 0 : webSearch.length) {
              this.protocol.webSearch(webSearch);
            }
            const reasoningContent = provider.parseReasoningContent(chunk);
            if (reasoningContent) {
              isReasoning = true;
              this.protocol.reasoning(reasoningContent);
            }
          }
        } else if (mode === "updates") {
          if ("__interrupt__" in chunks) {
            const interruptActions = this.toInterruptActions(chunks.__interrupt__[0].value);
            if (interruptActions.size) {
              for (const toolCall of toolCalls ?? []) {
                const interruptAction = interruptActions.get(toolCall.name);
                if (!interruptAction) {
                  continue;
                }
                await this.updateToolCallInterrupted(toolCall.messageId, toolCall.id, interruptAction);
                this.protocol.toolCallStatus({
                  toolCall: {
                    messageId: toolCall.messageId,
                    id: toolCall.id,
                    name: toolCall.name,
                    willInterrupt: toolCall.willInterrupt
                  },
                  invokeStatus: "interrupted",
                  interruptAction
                });
              }
            }
          }
        } else if (mode === "custom") {
          if (chunks.action === "AfterAIMessageSaved") {
            const data = responseMetadata.get(chunks.body.id);
            if (data) {
              const savedMessage = await this.aiMessagesModel.findOne({
                where: {
                  messageId: chunks.body.messageId
                }
              });
              if (savedMessage) {
                await this.aiMessagesModel.update(
                  {
                    metadata: {
                      ...savedMessage.metadata,
                      response_metadata: {
                        ...savedMessage.metadata.response_metadata,
                        ...data
                      }
                    }
                  },
                  {
                    where: {
                      messageId: chunks.body.messageId
                    }
                  }
                );
              }
            }
          } else if (chunks.action === "initToolCalls") {
            toolCalls = ((_b = chunks.body) == null ? void 0 : _b.toolCalls) ?? [];
            this.protocol.toolCalls(chunks.body);
          } else if (chunks.action === "beforeToolCall") {
            const toolsMap = await this.getToolsMap();
            const willInterrupt = this.shouldInterruptToolCall(toolsMap.get((_d = (_c = chunks.body) == null ? void 0 : _c.toolCall) == null ? void 0 : _d.name));
            this.protocol.toolCallStatus({
              toolCall: {
                messageId: (_f = (_e = chunks.body) == null ? void 0 : _e.toolCall) == null ? void 0 : _f.messageId,
                id: (_h = (_g = chunks.body) == null ? void 0 : _g.toolCall) == null ? void 0 : _h.id,
                name: (_j = (_i = chunks.body) == null ? void 0 : _i.toolCall) == null ? void 0 : _j.name,
                willInterrupt
              },
              invokeStatus: "pending"
            });
          } else if (chunks.action === "afterToolCall") {
            const toolsMap = await this.getToolsMap();
            const willInterrupt = this.shouldInterruptToolCall(toolsMap.get((_l = (_k = chunks.body) == null ? void 0 : _k.toolCall) == null ? void 0 : _l.name));
            this.protocol.toolCallStatus({
              toolCall: {
                messageId: (_n = (_m = chunks.body) == null ? void 0 : _m.toolCall) == null ? void 0 : _n.messageId,
                id: (_p = (_o = chunks.body) == null ? void 0 : _o.toolCall) == null ? void 0 : _p.id,
                name: (_r = (_q = chunks.body) == null ? void 0 : _q.toolCall) == null ? void 0 : _r.name,
                willInterrupt
              },
              invokeStatus: "done",
              status: (_t = (_s = chunks.body) == null ? void 0 : _s.toolCallResult) == null ? void 0 : _t.status
            });
          } else if (chunks.action === "beforeSendToolMessage") {
            const { messageId, messages } = chunks.body ?? {};
            if (messages.length) {
              const toolsMap = await this.getToolsMap();
              const toolCallResultMap = await this.getToolCallResultMap(
                messageId,
                messages.map((x) => x.metadata).map((x) => x.toolCallId)
              );
              for (const { metadata } of messages) {
                const tools = toolsMap.get(metadata.toolName);
                const toolCallResult = toolCallResultMap.get(metadata.toolCallId);
                this.protocol.toolCallStatus({
                  toolCall: {
                    messageId,
                    id: metadata.toolCallId,
                    name: metadata.toolName,
                    willInterrupt: this.shouldInterruptToolCall(tools)
                  },
                  invokeStatus: "confirmed",
                  status: toolCallResult == null ? void 0 : toolCallResult.status
                });
              }
            }
            this.protocol.newMessage();
          }
        }
      }
      if (this.protocol.statistics.sent === 0 && !signal.aborted && !allowEmpty) {
        this.sendErrorResponse("Empty message");
        return;
      }
      this.protocol.endStream();
    } catch (err) {
      this.ctx.log.error(err);
      if (err.name === "GraphRecursionError") {
        this.sendSpecificError({ name: err.name, message: err.message });
      } else {
        this.sendErrorResponse(provider.parseResponseError(err));
      }
    } finally {
      this.ctx.res.end();
    }
  }
  // === Prompts & knowledge base ===
  // Notice: employee.dataSourceSettings is not used in the current version.
  getEmployeeDataSourceContext() {
    var _a;
    const dataSourceSettings = this.employee.dataSourceSettings;
    if (!dataSourceSettings) {
      return null;
    }
    const collections = dataSourceSettings.collections || [];
    const names = collections.map((collection) => collection.collection);
    let message = "";
    for (const name of names) {
      let [dataSourceName, collectionName] = name.split(":");
      let db;
      if (!collectionName) {
        collectionName = dataSourceName;
        dataSourceName = "main";
        db = this.db;
      } else {
        const dataSource = this.ctx.app.dataSourceManager.dataSources.get(dataSourceName);
        db = dataSource == null ? void 0 : dataSource.collectionManager.db;
      }
      if (!db) {
        continue;
      }
      const collection = db.getCollection(collectionName);
      if (!collection || collection.options.hidden) {
        continue;
      }
      message += `
Database type: ${db.sequelize.getDialect()}, Collection: ${collectionName}, Title: ${collection.options.title}`;
      const fields = collection.getFields();
      for (const field of fields) {
        if (field.options.hidden) {
          continue;
        }
        message += `
Field: ${field.name}, Title: ${(_a = field.options.uiSchema) == null ? void 0 : _a.title}, Type: ${field.type}, Interface: ${field.options.interface}, Options: ${JSON.stringify(field.options)}`;
      }
    }
    return message;
  }
  async getSystemPrompt(userMessages) {
    var _a;
    const userConfig = await this.db.getRepository("usersAiEmployees").findOne({
      filter: {
        userId: (_a = this.ctx.auth) == null ? void 0 : _a.user.id,
        aiEmployee: this.employee.username
      }
    });
    const about = await (0, import_utils.parseVariables)(this.ctx, this.employee.about ?? this.employee.defaultPrompt ?? "");
    let background = "";
    if (this.systemMessage) {
      background = await (0, import_utils.parseVariables)(this.ctx, this.systemMessage);
    }
    const dataSourceMessage = this.getEmployeeDataSourceContext();
    if (dataSourceMessage) {
      background = `${background}
${dataSourceMessage}`;
    }
    const aiMessages = await this.aiChatConversation.listMessages();
    const workContextBackground = await this.plugin.workContextHandler.background(this.ctx, aiMessages);
    if (workContextBackground == null ? void 0 : workContextBackground.length) {
      background = `${background}
${workContextBackground.join("\n")}`;
    }
    const addSystemPrompt = userMessages == null ? void 0 : userMessages.filter((it) => it.role == "system");
    if (addSystemPrompt.length) {
      background = `${background}
${addSystemPrompt.map((it) => it.content).join("\n")}`;
    }
    let knowledgeBase;
    if (this.isEnabledKnowledgeBase() && this.employee.knowledgeBasePrompt && (userMessages == null ? void 0 : userMessages.length)) {
      const lastUserMessage = userMessages.filter((x) => x.role === "user").at(-1);
      if (lastUserMessage) {
        const docs = await this.retrieveKnowledgeBase(lastUserMessage);
        const knowledgeBaseData = docs.map((x) => x.content).join("\n");
        const promptTemplate = import_prompts2.ChatPromptTemplate.fromTemplate(this.employee.knowledgeBasePrompt);
        knowledgeBase = import_lodash.default.isEmpty(knowledgeBaseData) ? void 0 : await promptTemplate.format({
          knowledgeBaseData
        });
      }
    }
    const systemPrompt = (0, import_prompts.getSystemPrompt)({
      aiEmployee: {
        nickname: this.employee.nickname,
        about
      },
      task: {
        background
      },
      personal: userConfig == null ? void 0 : userConfig.prompt,
      environment: {
        database: this.db.sequelize.getDialect(),
        locale: this.ctx.getCurrentLocale() || "en-US"
      },
      knowledgeBase
    });
    const { important } = this.ctx.action.params.values || {};
    if (important === "GraphRecursionError") {
      const importantPrompt = `<Important>You have already called tools multiple times and gathered sufficient information.
First, provide a summary based on the existing information. Do not call additional tools.
If information is missing, clearly state it in the summary.</Important>`;
      return importantPrompt + "\n\n" + systemPrompt;
    } else {
      return systemPrompt;
    }
  }
  async retrieveKnowledgeBase(userMessage) {
    const vectorStoreProvider = this.plugin.features.vectorStoreProvider;
    let queryResult = [];
    const queryString = userMessage.content.content;
    if (!queryString || import_lodash.default.isEmpty(queryString)) {
      return queryResult;
    }
    const { topK, score } = this.getAIEmployeeKnowledgeBaseConfig();
    const knowledgeBaseGroup = await this.getKnowledgeBaseGroup();
    for (const entry of knowledgeBaseGroup) {
      const { vectorStoreConfig, knowledgeBaseType, knowledgeBaseList } = entry;
      if (!knowledgeBaseList || import_lodash.default.isEmpty(knowledgeBaseList)) {
        continue;
      }
      if (knowledgeBaseType === "LOCAL") {
        const vectorStoreService = await vectorStoreProvider.createVectorStoreService(
          vectorStoreConfig.vectorStoreProvider,
          [
            {
              key: "vectorStoreConfigKey",
              value: vectorStoreConfig.vectorStoreConfigKey
            }
          ]
        );
        const knowledgeBaseOuterIds = knowledgeBaseList.map((x) => x.knowledgeBaseOuterId);
        const result = await vectorStoreService.search(queryString, {
          topK,
          score,
          filter: {
            knowledgeBaseOuterId: { in: knowledgeBaseOuterIds }
          }
        });
        queryResult = [...queryResult, ...result];
      } else if (knowledgeBaseType === "READONLY") {
        for (const knowledgeBase of knowledgeBaseList) {
          const vectorStoreService = await vectorStoreProvider.createVectorStoreService(
            vectorStoreConfig.vectorStoreProvider,
            [
              ...knowledgeBase.vectorStoreProps,
              {
                key: "vectorStoreConfigKey",
                value: vectorStoreConfig.vectorStoreConfigKey
              }
            ]
          );
          const result = await vectorStoreService.search(queryString, {
            topK,
            score
          });
          queryResult = [...queryResult, ...result];
        }
      } else if (knowledgeBaseType === "EXTERNAL") {
        for (const knowledgeBase of knowledgeBaseList) {
          const vectorStoreService = await vectorStoreProvider.createVectorStoreService(
            vectorStoreConfig.vectorStoreProvider,
            knowledgeBase.vectorStoreProps
          );
          const result = await vectorStoreService.search(queryString, {
            topK,
            score
          });
          queryResult = [...queryResult, ...result];
        }
      }
    }
    return queryResult;
  }
  isEnabledKnowledgeBase() {
    const featureEnabled = this.plugin.features.isFeaturesEnabled(Object.values(import_ai_feature_manager.EEFeatures));
    const knowledgeBaseEnabled = this.employee.enableKnowledgeBase;
    return featureEnabled && knowledgeBaseEnabled;
  }
  getAIEmployeeKnowledgeBaseConfig() {
    const { topK, score } = this.employee.knowledgeBase ?? {};
    return {
      topK,
      score
    };
  }
  async getKnowledgeBaseGroup() {
    const { knowledgeBaseKeys } = this.employee.knowledgeBase ?? {};
    if (!knowledgeBaseKeys || import_lodash.default.isEmpty(knowledgeBaseKeys)) {
      return [];
    }
    return await this.plugin.features.knowledgeBase.getKnowledgeBaseGroup(knowledgeBaseKeys);
  }
  // === Tool calls ===
  async initToolCall(transaction, messageId, toolCalls) {
    const nowTime = /* @__PURE__ */ new Date();
    const toolMap = await this.getToolsMap();
    return await this.aiToolMessagesRepo.create({
      values: toolCalls.map((toolCall) => {
        const toolsExisted = toolMap.has(toolCall.name);
        const tools = toolMap.get(toolCall.name);
        const auto = this.isAutoCall(tools);
        return {
          id: this.plugin.snowflake.generate(),
          sessionId: this.sessionId,
          messageId,
          toolCallId: toolCall.id,
          toolName: toolCall.name,
          status: toolsExisted ? null : "error",
          content: toolsExisted ? null : `Tool ${toolCall.name} not found`,
          invokeStatus: toolsExisted ? "init" : "done",
          invokeStartTime: toolsExisted ? null : nowTime,
          invokeEndTime: toolsExisted ? null : nowTime,
          auto,
          execution: (tools == null ? void 0 : tools.execution) ?? "backend"
        };
      }),
      transaction
    });
  }
  async updateToolCallInterrupted(messageId, toolCallId, interruptAction) {
    const [updated] = await this.aiToolMessagesModel.update(
      {
        invokeStatus: "interrupted",
        interruptActionOrder: interruptAction.order,
        interruptAction
      },
      {
        where: {
          sessionId: this.sessionId,
          messageId,
          toolCallId,
          invokeStatus: "init"
        }
      }
    );
    return updated;
  }
  async updateToolCallPending(messageId, toolCallId) {
    const [updated] = await this.aiToolMessagesModel.update(
      {
        invokeStatus: "pending",
        invokeStartTime: /* @__PURE__ */ new Date()
      },
      {
        where: {
          sessionId: this.sessionId,
          messageId,
          toolCallId,
          invokeStatus: {
            [import_database.Op.in]: ["init", "waiting"]
          }
        }
      }
    );
    return updated;
  }
  async updateToolCallDone(messageId, toolCallId, result) {
    const [updated] = await this.aiToolMessagesModel.update(
      {
        invokeStatus: "done",
        invokeEndTime: /* @__PURE__ */ new Date(),
        status: (result == null ? void 0 : result.status) ?? "success",
        content: (result == null ? void 0 : result.content) ?? result
      },
      {
        where: {
          sessionId: this.sessionId,
          messageId,
          toolCallId,
          invokeStatus: "pending"
        }
      }
    );
    return updated;
  }
  async confirmToolCall(transaction, messageId, toolCallIds) {
    const [updated] = await this.aiToolMessagesModel.update(
      {
        invokeStatus: "confirmed"
      },
      {
        where: {
          sessionId: this.sessionId,
          messageId,
          toolCallId: {
            [import_database.Op.in]: toolCallIds
          }
        },
        transaction
      }
    );
    return updated;
  }
  async getToolCallResult(messageId, toolCallId) {
    var _a;
    return (_a = await this.aiToolMessagesModel.findOne({
      where: {
        sessionId: this.sessionId,
        messageId,
        toolCallId
      }
    })) == null ? void 0 : _a.toJSON();
  }
  async getToolCallResultMap(messageId, toolCallIds) {
    const list = (await this.aiToolMessagesModel.findAll({
      where: {
        sessionId: this.sessionId,
        messageId,
        toolCallId: {
          [import_database.Op.in]: toolCallIds
        }
      }
    })).map((it) => it.toJSON());
    return new Map(list.map((it) => [it.toolCallId, it]));
  }
  async getUserDecisions(messageId) {
    const allInterruptedToolCall = await this.aiToolMessagesModel.findAll({
      where: {
        messageId,
        interruptActionOrder: { [import_database.Op.not]: null }
      },
      order: [["interruptActionOrder", "ASC"]]
    });
    if (!allInterruptedToolCall.every((t) => t.invokeStatus === "waiting")) {
      return [];
    }
    return allInterruptedToolCall.map((item) => item.userDecision);
  }
  async cancelToolCall() {
    var _a;
    let messageId;
    const historyMessages = await this.db.getRepository("aiConversations.messages", this.sessionId).find({
      sort: ["-messageId"]
    });
    const [lastMessage] = historyMessages;
    if (((_a = lastMessage == null ? void 0 : lastMessage.toolCalls) == null ? void 0 : _a.length) ?? 0 > 0) {
      messageId = lastMessage.messageId;
    } else {
      return;
    }
    const toolMessages = (await this.aiToolMessagesModel.findAll({
      where: {
        sessionId: this.sessionId,
        messageId,
        invokeStatus: {
          [import_database.Op.ne]: "confirmed"
        }
      }
    })).map((it) => it.toJSON());
    if (!toolMessages || import_lodash.default.isEmpty(toolMessages)) {
      return;
    }
    const { model, service } = await this.plugin.aiManager.getLLMService({
      ...this.model
    });
    const toolCallMap = await this.getToolCallMap(messageId);
    const now = /* @__PURE__ */ new Date();
    const toolMessageContent = "The user ignored the application for tools usage and will continued to ask questions";
    return await this.db.sequelize.transaction(async (transaction) => {
      for (const toolMessage of toolMessages) {
        await this.aiToolMessagesModel.update(
          {
            invokeStatus: "confirmed",
            status: "success",
            content: toolMessageContent,
            invokeStartTime: toolMessage.invokeStartTime ?? now,
            invokeEndTime: toolMessage.invokeEndTime ?? now
          },
          {
            where: {
              id: toolMessage.id,
              invokeStatus: toolMessage.invokeStatus
            },
            transaction
          }
        );
      }
      return await this.db.getRepository("aiConversations.messages", this.sessionId).create({
        values: toolMessages.map((toolMessage) => ({
          messageId: this.plugin.snowflake.generate(),
          role: "tool",
          content: {
            type: "text",
            content: toolMessageContent
          },
          metadata: {
            model,
            provider: service.provider,
            toolCall: toolCallMap.get(toolMessage.toolCallId),
            toolCallId: toolMessage.toolCallId,
            autoCall: toolMessage.auto
          },
          transaction
        }))
      });
    });
  }
  get logger() {
    return this.ctx.logger;
  }
  sendErrorResponse(errorMessage) {
    (0, import_utils.sendSSEError)(this.ctx, errorMessage);
  }
  sendSpecificError({ name, message }) {
    (0, import_utils.sendSSEError)(this.ctx, message, name);
  }
  // === Conversation/thread helpers ===
  async updateThread(transaction, { sessionId, thread }) {
    await this.aiConversationsRepo.update({
      values: { thread },
      filter: {
        sessionId,
        thread: {
          $lt: thread
        }
      },
      transaction
    });
  }
  removeAbortController() {
    this.plugin.aiEmployeesManager.conversationController.delete(this.sessionId);
  }
  shouldInterruptToolCall(tools) {
    return (tools == null ? void 0 : tools.execution) === "frontend" || !this.isAutoCall(tools);
  }
  isAutoCall(tools) {
    var _a;
    if (!tools) {
      return false;
    }
    const isAutoCall = tools.defaultPermission === "ALLOW";
    if (tools.scope !== "CUSTOM") {
      return isAutoCall;
    }
    const skills = ((_a = this.employee.skillSettings) == null ? void 0 : _a.skills) ?? [];
    const presetTools = skills.find((s) => s.name === tools.definition.name);
    return presetTools ? presetTools.autoCall : isAutoCall;
  }
  async formatMessages({ messages, provider }) {
    var _a, _b;
    const formattedMessages = [];
    const workContextHandler = this.plugin.workContextHandler;
    await this.hydrateAttachmentsMeta(messages);
    const truncate = (text, maxLen = 5e4) => {
      if (!text || text.length <= maxLen) return text;
      return text.slice(0, maxLen) + "\n...[truncated]";
    };
    for (const msg of messages) {
      const attachments = msg.attachments;
      const workContext = msg.workContext;
      const userContent = msg.content;
      let { content } = userContent ?? {};
      if (Array.isArray(content)) {
        const textBlocks = content.filter((block) => block.type === "text");
        content = textBlocks.map((block) => block.text).join("") || "";
      }
      if (typeof content === "string") {
        content = truncate(content);
      }
      if (msg.role === "user") {
        if (typeof content === "string") {
          content = `<user_query>${content}</user_query>`;
          if (workContext == null ? void 0 : workContext.length) {
            const workContextStr = (await workContextHandler.resolve(this.ctx, workContext)).map((x) => `<work_context>${x}</work_context>`).join("\n");
            content = workContextStr + "\n" + content;
          }
        }
        const contentBlocks = [];
        if (attachments == null ? void 0 : attachments.length) {
          for (const attachment of attachments) {
            const parsed = await provider.parseAttachment(this.ctx, attachment);
            if (parsed.placement === "system") {
              formattedMessages.push({
                role: "system",
                content: parsed.content
              });
            } else {
              contentBlocks.push(parsed.content);
            }
          }
          if (content && contentBlocks.length > 0) {
            contentBlocks.push({
              type: "text",
              text: content
            });
          }
        }
        const role = "user";
        const additional_kwargs = { userContent, attachments, workContext };
        if (contentBlocks.length) {
          formattedMessages.push({
            role,
            additional_kwargs,
            contentBlocks
          });
        } else {
          formattedMessages.push({
            role,
            additional_kwargs,
            content
          });
        }
        continue;
      }
      if (msg.role === "tool") {
        formattedMessages.push({
          role: "tool",
          content,
          tool_call_id: (_a = msg.metadata) == null ? void 0 : _a.toolCallId
        });
        continue;
      }
      formattedMessages.push({
        role: "assistant",
        content,
        tool_calls: msg.toolCalls,
        additional_kwargs: (_b = msg.metadata) == null ? void 0 : _b.additional_kwargs
      });
    }
    return formattedMessages;
  }
  async hydrateAttachmentsMeta(messages) {
    var _a, _b;
    const attachmentIds = /* @__PURE__ */ new Set();
    for (const message of messages) {
      if (!((_a = message.attachments) == null ? void 0 : _a.length)) {
        continue;
      }
      for (const attachment of message.attachments) {
        if ((attachment == null ? void 0 : attachment.id) != null) {
          attachmentIds.add(attachment.id);
        }
      }
    }
    if (!attachmentIds.size) {
      return;
    }
    const files = await this.aiFilesModel.findAll({
      where: {
        id: {
          [import_database.Op.in]: Array.from(attachmentIds)
        }
      },
      attributes: ["id", "meta"]
    });
    const metaById = new Map(files.map((file) => [file.get("id"), file.get("meta")]));
    for (const message of messages) {
      if (!((_b = message.attachments) == null ? void 0 : _b.length)) {
        continue;
      }
      for (const attachment of message.attachments) {
        if ((attachment == null ? void 0 : attachment.id) == null) {
          continue;
        }
        const meta = metaById.get(attachment.id);
        if (meta !== void 0) {
          attachment.meta = meta;
        }
      }
    }
  }
  async getToolCallMap(messageId) {
    const result = /* @__PURE__ */ new Map();
    const { toolCalls } = await this.aiMessagesRepo.findById(messageId);
    if (!toolCalls) {
      return result;
    }
    for (const toolCall of toolCalls) {
      result.set(toolCall.id, toolCall);
    }
    return result;
  }
  toInterruptActions(interrupt) {
    var _a;
    const result = /* @__PURE__ */ new Map();
    const { actionRequests = [], reviewConfigs = [] } = interrupt;
    if (!actionRequests.length) {
      return result;
    }
    let order = 0;
    const actionRequestsMap = new Map(actionRequests.map((x) => [x.name, x]));
    const reviewConfigsMap = new Map(reviewConfigs.map((x) => [x.actionName, x]));
    for (const [name, actionRequest] of actionRequestsMap.entries()) {
      result.set(name, {
        order: order++,
        description: actionRequest.description,
        allowedDecisions: (_a = reviewConfigsMap.get(name)) == null ? void 0 : _a.allowedDecisions
      });
    }
    return result;
  }
  async getAIEmployeeTools() {
    var _a, _b;
    const tools = await this.listTools({ scope: "GENERAL" });
    if (this.webSearch === true) {
      const subAgentWebSearch = await this.toolsManager.getTools("subAgentWebSearch");
      tools.push(subAgentWebSearch);
    }
    const generalToolsNameSet = new Set(tools.map((x) => x.definition.name));
    const toolMap = await this.getToolsMap();
    const skills = ((_a = this.employee.skillSettings) == null ? void 0 : _a.skills) ?? [];
    for (const skill of skills) {
      if (generalToolsNameSet.has(skill.name)) {
        continue;
      }
      const tool = toolMap.get(skill.name);
      if (!tool) {
        continue;
      }
      tools.push(tool);
    }
    const skillFilter = ((_b = this.skillSettings) == null ? void 0 : _b.skills) ?? [];
    return tools.filter((t) => skillFilter.length === 0 || skillFilter.includes(t.definition.name));
  }
  getMiddleware(options) {
    const { providerName, model, tools, messageId, agentThread } = options;
    return [
      (0, import_middleware.toolInteractionMiddleware)(this, tools),
      (0, import_middleware.toolCallStatusMiddleware)(this),
      (0, import_middleware.conversationMiddleware)(this, { providerName, model, messageId, agentThread })
    ];
  }
  async getCurrentThread() {
    const aiConversation = await this.aiConversationsRepo.findByTargetKey(this.sessionId);
    if (!aiConversation) {
      throw new Error("Conversation not existed");
    }
    return AgentThread.newThread(aiConversation.sessionId, aiConversation.thread);
  }
  async forkCurrentThread(provider) {
    var _a;
    let retTry = 3;
    const agent = await this.createAgent({ provider });
    let currentThread = await this.getCurrentThread();
    do {
      currentThread = currentThread.fork();
      const existedState = await agent.graph.getState({ configurable: { thread_id: currentThread.threadId } });
      if (!((_a = existedState.config.configurable) == null ? void 0 : _a.checkpoint_id)) {
        return currentThread;
      }
    } while (retTry-- > 0);
    throw new Error("Fail to create new agent thread");
  }
  async getToolsMap() {
    const tools = await this.listTools();
    return new Map(tools.map((tool) => [tool.definition.name, tool]));
  }
  listTools(filter) {
    return this.toolsManager.listTools(filter);
  }
  get toolsManager() {
    return this.ctx.app.aiManager.toolsManager;
  }
  get aiConversationsRepo() {
    return this.ctx.db.getRepository("aiConversations");
  }
  get aiMessagesRepo() {
    return this.ctx.db.getRepository("aiMessages");
  }
  get aiMessagesModel() {
    return this.ctx.db.getModel("aiMessages");
  }
  get aiToolMessagesRepo() {
    return this.ctx.db.getRepository("aiToolMessages");
  }
  get aiToolMessagesModel() {
    return this.ctx.db.getModel("aiToolMessages");
  }
  get aiFilesModel() {
    return this.ctx.db.getModel("aiFiles");
  }
}
class AgentThread {
  constructor(_sessionId, _thread) {
    this._sessionId = _sessionId;
    this._thread = _thread;
  }
  static newThread(sessionId, thread) {
    return new AgentThread(sessionId, thread);
  }
  get sessionId() {
    return this._sessionId;
  }
  get thread() {
    return this._thread;
  }
  get threadId() {
    return `${this._sessionId}:${this._thread}`;
  }
  fork() {
    return new AgentThread(this._sessionId, this._thread + 1);
  }
}
class ChatStreamProtocol {
  constructor(ctx) {
    this.ctx = ctx;
  }
  _statistics = {
    sent: 0,
    addSent: (s) => {
      this._statistics.sent += s;
    },
    reset: () => {
      this._statistics.sent = 0;
    }
  };
  static create(ctx) {
    return new ChatStreamProtocol(ctx);
  }
  startStream() {
    this._statistics.reset();
    this.write({ type: "stream_start" });
  }
  endStream() {
    this.write({ type: "stream_end" });
  }
  newMessage(content) {
    this.write({ type: "new_message", body: content });
  }
  content(content) {
    this.write({ type: "content", body: content });
  }
  webSearch(content) {
    this.write({ type: "web_search", body: content });
  }
  reasoning(content) {
    this.write({ type: "reasoning", body: content });
  }
  stopReasoning() {
    this.write({
      type: "reasoning",
      body: {
        status: "stop",
        content: ""
      }
    });
  }
  toolCallChunks(content) {
    this.write({ type: "tool_call_chunks", body: content });
  }
  toolCalls(content) {
    this.write({ type: "tool_calls", body: content });
  }
  toolCallStatus({
    toolCall,
    invokeStatus,
    status,
    interruptAction
  }) {
    this.write({
      type: "tool_call_status",
      body: {
        toolCall,
        invokeStatus,
        status,
        interruptAction
      }
    });
  }
  get statistics() {
    return {
      sent: this._statistics.sent
    };
  }
  write({ type, body }) {
    const data = `data: ${JSON.stringify({ type, body })}

`;
    this.ctx.res.write(data);
    this._statistics.addSent(data.length);
  }
}
class ResponseMetadataCollector extends import_base.BaseCallbackHandler {
  constructor(llmProvider, responseMetadata) {
    super();
    this.llmProvider = llmProvider;
    this.responseMetadata = responseMetadata;
  }
  name = "ResponseMetadataCollector";
  handleLLMEnd(output, runId) {
    const [id, metadata] = this.llmProvider.parseResponseMetadata(output);
    if (id && metadata) {
      this.responseMetadata.set(id, metadata);
    }
  }
  getResponseMetadata(id) {
    return this.responseMetadata.get(id);
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  AIEmployee
});
