type Audio = {
  /** 标题 */
  title: string;
  /** 音频的网络链接 */
  url: string;
};

type AudioWithOptionalTitle = {
  /** 标题 */
  title?: string;
  /** 音频的网络链接 */
  url: string;
};

/**
 * 播放给定的音频; 如果该音频没在播放列表中, 则会加入到播放列表.
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @param audio 要播放的音频; 如果音频没有设置标题 (`title`), 则会从链接 (`url`) 提取文件名作为标题
 *
 * @example
 * // 将给定链接作为背景音乐播放
 * playAudio('bgm', { url: 'http://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Kangaroo_MusiQue_-_The_Neverwritten_Role_Playing_Game.mp3' });
 *
 * @example
 * // 为给定链接设置标题, 并作为背景音乐播放
 * playAudio('bgm', { title: 'Kangaroo Music', url: 'http://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Kangaroo_MusiQue_-_The_Neverwritten_Role_Playing_Game.mp3' });
 */
declare function playAudio(type: 'bgm' | 'ambient', audio: AudioWithOptionalTitle): void;

/**
 * 暂停音乐
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 */
declare function pauseAudio(type: 'bgm' | 'ambient'): void;

/**
 * 获取播放列表
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @returns 播放列表
 */
declare function getAudioList(type: 'bgm' | 'ambient'): Audio[];

/**
 * 完全替换播放列表为 `audio_list`
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @param audio_list 新的播放列表; 如果其中音频没有设置标题 (`title`), 则会从链接 (`url`) 提取文件名作为标题
 */
declare function replaceAudioList(type: 'bgm' | 'ambient', audio_list: AudioWithOptionalTitle[]): void;

/**
 * 向播放列表末尾添加不存在的音频, 不会重复添加同 `title` 或 `url` 的音频
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @param audio_list 要插入的音频列表; 如果其中音频没有设置标题 (`title`), 则会从链接 (`url`) 提取文件名作为标题
 */
declare function appendAudioList(type: 'bgm' | 'ambient', audio_list: AudioWithOptionalTitle[]): void;

type AudioSettings = {
  /** 是否启用 */
  enabled: boolean;
  /**
   * 当前播放模式
   * - repeat_one: 单曲循环
   * - repeat_all: 全部循环
   * - shuffle: 随机播放
   * - play_one_and_stop: 播放一首后停止
   */
  mode: 'repeat_one' | 'repeat_all' | 'shuffle' | 'play_one_and_stop';
  /** 是否静音 */
  muted: boolean;
  /** 当前音量 (0-100) */
  volume: number;
};

/**
 * 获取音频设置
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @returns 音频设置
 */
declare function getAudioSettings(type: 'bgm' | 'ambient'): AudioSettings;

/**
 * 修改音频设置, 如果某字段不存在, 则使用原本的设置.
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @param settings 要修改的音频设置
 *
 * @example
 * // 将背景音乐设置为单曲循环
 * setAudioSettings('bgm', { mode: 'repeat_one' });
 *
 * @example
 * // 将音效设置为静音
 * setAudioSettings('ambient', { muted: true });
 *
 * @example
 * // 将背景音乐音量设置为 50%
 * setAudioSettings('bgm', { volume: 50 });
 */
declare function setAudioSettings(type: 'bgm' | 'ambient', settings: Partial<AudioSettings>): void;

type CurrentAudio = {
  /** 当前选中/正在播放的音频链接, 未选中曲目时为空字符串 */
  src: string;
  /** 当前选中音频的标题, 未匹配到时为空字符串 */
  title: string;
  /** 是否正在播放 */
  playing: boolean;
  /** 播放进度 (0-100) */
  progress: number;
};

/**
 * 获取当前播放的音频信息: 链接、标题、是否在播、进度
 *
 * @param type 背景音乐 ('bgm') 或音效 ('ambient')
 * @returns 当前音频信息
 *
 * @example
 * // 获取当前正在播放的背景音乐
 * const { title, src, playing, progress } = getCurrentAudio('bgm');
 */
declare function getCurrentAudio(type: 'bgm' | 'ambient'): CurrentAudio;
declare const builtin: {
  /**
   * 向网页添加一条楼层渲染
   *
   * @param mes 要渲染的楼层数据
   * @param options 可选选项
   * - `type`: 楼层类型; 默认为 `'normal'`
   * - `insertAfter`: 插入到指定楼层后; 默认为 `null`
   * - `scroll`: 是否滚动到新楼层; 默认为 `true`
   * - `insertBefore`: 插入到指定楼层前; 默认为 `null`
   * - `forceId`: 强制使用指定楼层号; 默认为 `null`
   * - `showSwipes`: 是否显示滑动按钮; 默认为 `true`
   */
  addOneMessage: (
    mes: Record<string, any>,
    options?: {
      type?: string;
      insertAfter?: number;
      scroll?: boolean;
      insertBefore?: number;
      forceId?: number;
      showSwipes?: boolean;
    },
  ) => void;
  /**
   * 复制文本到剪贴板
   *
   * @param text 要复制的文本
   */
  copyText: (text: string) => void;
  duringGenerating: () => boolean;
  getImageTokenCost: (data_url: string, quality: 'low' | 'auto' | 'high') => Promise<number>;
  getVideoTokenCost: (data_url: string) => Promise<number>;
  parseRegexFromString: (regex: string) => RegExp | null;
  promptManager: {
    messages: Array<{
      collection: Array<{
        identifier: string;
        role: 'user' | 'assistant' | 'system';
        content: string;
        tokens: number;
      }>;
      identifier: string;
    }>;
    getPromptCollection: () => {
      collection: Array<{
        identifier: string;
        name: string;
        enabled?: boolean;

        injection_position: 0 | 1;
        injection_depth: number;
        injection_order: number;

        role: 'user' | 'assistant' | 'system';
        content: string;

        system_prompt: boolean;
        marker?: boolean;

        extra?: Record<string, any>;

        forbid_overrides?: boolean;
      }>;
      [key: string]: any;
    };
    [key: string]: any;
  };
  /** 刷新当前聊天并触发 CHARACTER_MESSAGE_RENDERED 和 USER_MESSAGE_RENDERED 事件从而重新渲染 */
  reloadAndRenderChatWithoutEvents: () => Promise<void>;
  /** 刷新当前聊天但不触发任何事件 */
  reloadChatWithoutEvents: () => Promise<void>;
  /** 刷新世界书编辑器的显示 */
  reloadEditor: (file: string, load_if_not_selected?: boolean) => void;
  /** 刷新世界书编辑器的显示 (防抖) */
  reloadEditorDebounced: (file: string, load_if_not_selected?: boolean) => void;
  /** 将 markdown 渲染成 html */
  renderMarkdown: (string: string) => string;
  /** 刷新预设提示词列表 */
  renderPromptManager: (after_try_generate?: boolean) => void;
  /** 刷新预设提示词列表 (防抖) */
  renderPromptManagerDebounced: (after_try_generate?: boolean) => void;
  saveSettings: () => Promise<void>;
  uuidv4: () => string;
};
type Character = {
  avatar: `${string}.png` | Blob;
  version: string;
  creator: string;
  creator_notes: string;

  worldbook: string | null;
  description: string;
  first_messages: string[];

  extensions: {
    regex_scripts: TavernRegex[];
    tavern_helper: {
      scripts: ScriptTree[];
      variables: Record<string, any>;
    };
    [other: string]: any;
  };
};

/**
 * 获取角色卡名称列表
 *
 * @returns 角色卡名称列表
 */
declare function getCharacterNames(): string[];

/**
 * 获取角色卡头像 id 列表
 *
 * @returns 角色卡头像 id 列表
 */
declare function getCharacterIds(): string[];

/**
 * 获取当前角色卡名称
 *
 * @returns 当前角色卡名称, 如果当前没有角色卡, 则返回 `null`
 */
declare function getCurrentCharacterName(): string | null;

/**
 * 获取当前角色卡头像 id
 *
 * @returns 当前角色卡头像 id, 如果当前没有角色卡, 则返回 `null`
 */
declare function getCurrentCharacterId(): string | null;

/**
 * 新建 `character_name` 角色卡, 内容为 `character`
 *
 * @param character_name 角色卡名称
 * @param character 角色卡数据; 不填则使用默认数据
 *
 * @returns 是否成功创建, 如果已经存在同名角色卡或尝试创建名为 `'current'` 的角色卡会失败
 *
 * @throws 如果访问后端失败, 将会抛出异常
 */
declare function createCharacter(
  character_name: Exclude<string, 'current'>,
  character?: TypeFest.PartialDeep<Character>,
): Promise<boolean>;

/**
 * 创建或替换名为 `character_name` 的角色卡, 内容为 `character`
 *
 * @param character_name 角色卡名称
 * @param character 角色卡数据; 不填则使用默认数据
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 酒馆网页应该防抖渲染 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为性能更好的防抖渲染
 *
 * @returns 如果发生创建, 则返回 `true`; 如果发生替换, 则返回 `false`
 *
 * @throws 如果访问后端失败, 将会抛出异常
 */
declare function createOrReplaceCharacter(
  character_name: Exclude<string, 'current'>,
  character?: TypeFest.PartialDeep<Character>,
  options?: ReplaceCharacterOptions,
): Promise<boolean>;

/**
 * 删除 `character_name` 角色卡
 *
 * @param character_name 角色卡名称
 * @param options 可选选项
 *   - `delete_chats:boolean`: 是否要同时删除角色卡的聊天文件
 *
 * @returns 是否成功删除, 可能因角色卡不存在等原因而失败
 */
declare function deleteCharacter(
  character_name: TypeFest.LiteralUnion<'current', string>,
  options?: { delete_chats?: boolean },
): Promise<boolean>;

/**
 * 获取 `character_name` 角色卡的内容
 *
 * @param character_name 角色卡名称
 *
 * @returns 角色卡内容
 *
 * @throws 如果角色卡不存在, 将会抛出异常
 */
declare function getCharacter(character_name: TypeFest.LiteralUnion<'current', string>): Promise<Character>;

type ReplaceCharacterOptions = {
  /** 酒馆网页应该防抖渲染 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为性能更好的防抖渲染 */
  render?: 'debounced' | 'immediate' | 'none';
};

/**
 * 完全替换 `character_name` 角色卡的内容为 `character`
 *
 * @param character_name 角色卡名称
 * @param character 角色卡数据
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 酒馆网页应该防抖渲染 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为性能更好的防抖渲染
 *
 * @throws 如果角色卡不存在, 将会抛出异常
 * @throws 如果访问后端失败, 将会抛出异常
 *
 * @example
 * // 为角色卡更改开场白
 * const character = await getCharacter('角色卡名称');
 * character.first_messages = ['新的开场白1', '新的开场白2'];
 * await replaceCharacter('角色卡名称', character);
 *
 * @example
 * // 清空角色卡的局部正则
 * const character = await getCharacter('角色卡名称');
 * character.extensions.regex_scripts = [];
 * await replaceCharacter('角色卡名称', character);
 *
 * @example
 * // 更换角色卡头像
 * const character = await getCharacter('角色卡名称');
 * character.avatar = await fetch('https://example.com/avatar.png').then(response => response.blob());
 * await replaceCharacter('角色卡名称', character);
 */
declare function replaceCharacter(
  character_name: Exclude<string, 'current'>,
  character: TypeFest.PartialDeep<Character>,
  options?: ReplaceCharacterOptions,
): Promise<void>;

type CharacterUpdater = ((character: Character) => Character) | ((character: Character) => Promise<Character>);

/**
 * 用 `updater` 函数更新 `character_name` 角色卡
 *
 * @param character_name 角色卡名称
 * @param updater 用于更新角色卡的函数. 它应该接收角色卡内容作为参数, 并返回更新后的角色卡内容.
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 如果对角色卡进行操作, 应该防抖渲染 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为性能更好的防抖渲染
 *
 * @returns 更新后的角色卡内容
 *
 * @throws 如果角色卡不存在, 将会抛出异常
 * @throws 如果访问后端失败, 将会抛出异常
 *
 * @example
 * // 为角色卡添加一个开场白
 * await updateCharacterWith('角色卡名称', character => {
 *   character.first_messages.push('新的开场白');
 *   return character;
 * });
 *
 * @example
 * // 清空角色卡的局部正则
 * await updateCharacterWith('角色卡名称', character => {
 *   character.extensions.regex_scripts = [];
 *   return character;
 * });
 *
 * @example
 * // 更换角色卡头像
 * await updateCharacterWith('角色卡名称', async character => {
 *   character.avatar = await fetch('https://example.com/avatar.png').then(response => response.blob());
 *   return character;
 * });
 */
declare function updateCharacterWith(
  character_name: TypeFest.LiteralUnion<'current', string>,
  updater: CharacterUpdater,
): Promise<Character>;
type ChatMessage = {
  message_id: number;
  name: string;
  role: 'system' | 'assistant' | 'user';
  is_hidden: boolean;
  message: string;
  data: Record<string, any>;
  extra: Record<string, any>;
};

type ChatMessageSwiped = {
  message_id: number;
  name: string;
  role: 'system' | 'assistant' | 'user';
  is_hidden: boolean;
  swipe_id: number;
  swipes: string[];
  swipes_data: Record<string, any>[];
  swipes_info: Record<string, any>[];
};

type GetChatMessagesOption = {
  /** 按 role 筛选消息; 默认为 `'all'` */
  role?: 'all' | 'system' | 'assistant' | 'user';
  /** 按是否被隐藏筛选消息; 默认为 `'all'` */
  hide_state?: 'all' | 'hidden' | 'unhidden';
  /** 是否包含未被 AI 使用的消息页信息, 如没选择的开局、通过点击箭头重 roll 的楼层. 如果不包含则返回类型为 `ChatMessage`, 否则返回类型为 `ChatMessageSwiped`; 默认为 `false` */
  include_swipes?: boolean;
};

/**
 * 获取聊天消息, 仅获取每楼被 AI 使用的消息页
 *
 * @param range 要获取的消息楼层号或楼层范围, 如 `0`, `'0-{{lastMessageId}}'`, `-1` 等. 负数表示深度, 如 `-1` 表示最新的消息楼层, `-2` 表示倒数第二条消息楼层.
 * @param option 可选选项
 *   - `role:'all'|'system'|'assistant'|'user'`: 按 role 筛选消息; 默认为 `'all'`
 *   - `hide_state:'all'|'hidden'|'unhidden'`: 按是否被隐藏筛选消息; 默认为 `'all'`
 *   - `include_swipes:false`: 不包含未被 AI 使用的消息页信息
 *
 * @returns 一个 `ChatMessage` 数组, 包含指定楼层范围内实际存在的所有楼层, 依据 message_id 从低到高排序; 如果范围内完全不存在楼层 (如目前只有 3 楼, 但范围为 `4-5`), 则返回空数组
 *
 * @example
 * // 仅获取第 10 楼被 AI 使用的消息页
 * const chat_messages = getChatMessages(10);
 * const chat_messages = getChatMessages('10');
 * const chat_messages = getChatMessages('10', { include_swipes: false });
 *
 * @example
 * // 获取最新楼层被 AI 使用的消息页
 * const chat_message = getChatMessages(-1)[0];  // 或 getChatMessages('{{lastMessageId}}')[0]
 *
 * @example
 * // 获取所有楼层被 AI 使用的消息页
 * const chat_messages = getChatMessages('0-{{lastMessageId}}');
 */
declare function getChatMessages(
  range: string | number,
  { role, hide_state, include_swipes }?: Omit<GetChatMessagesOption, 'include_swipes'> & { include_swipes?: false },
): ChatMessage[];

/**
 * 获取聊天消息, 获取每楼所有的消息页, 包含未被 AI 使用的消息页消息
 *
 * @param range 要获取的消息楼层号或楼层范围, 如 `0`, `'0-{{lastMessageId}}'`, `-1` 等. 负数表示深度, 如 `-1` 表示最新的消息楼层, `-2` 表示倒数第二条消息楼层.
 * @param option 可选选项
 *   - `role:'all'|'system'|'assistant'|'user'`: 按 role 筛选消息; 默认为 `'all'`
 *   - `hide_state:'all'|'hidden'|'unhidden'`: 按是否被隐藏筛选消息; 默认为 `'all'`
 *   - `include_swipes:true`: 包含未被 AI 使用的消息页信息
 *
 * @returns 一个 `ChatMessage` 数组, 包含指定楼层范围内实际存在的所有楼层, 依据 message_id 从低到高排序; 如果范围内完全不存在楼层 (如目前只有 3 楼, 但范围为 `4-5`), 则返回空数组
 *
 * @example
 * // 获取第 10 楼所有的消息页
 * const chat_messages = getChatMessages(10, { include_swipes: true });
 * const chat_messages = getChatMessages('10', { include_swipes: true });
 *
 * @example
 * // 获取最新楼层所有的消息页
 * const chat_message = getChatMessages(-1, { include_swipes: true })[0];  // 或 getChatMessages('{{lastMessageId}}', { include_swipes: true })[0]
 *
 * @example
 * // 获取所有楼层所有的消息页
 * const chat_messages = getChatMessages('0-{{lastMessageId}}', { include_swipes: true });
 */
declare function getChatMessages(
  range: string | number,
  { role, hide_state, include_swipes }?: Omit<GetChatMessagesOption, 'include_swipes'> & { include_swipes?: true },
): ChatMessageSwiped[];

/**
 * 获取聊天消息
 *
 * @param range 要获取的消息楼层号或楼层范围, 如 `0`, `'0-{{lastMessageId}}'`, `-1` 等. 负数表示深度, 如 `-1` 表示最新的消息楼层, `-2` 表示倒数第二条消息楼层.
 * @param option 可选选项
 *   - `role:'all'|'system'|'assistant'|'user'`: 按 role 筛选消息; 默认为 `'all'`
 *   - `hide_state:'all'|'hidden'|'unhidden'`: 按是否被隐藏筛选消息; 默认为 `'all'`
 *   - `include_swipes:boolean`: 是否包含未被 AI 使用的消息页信息, 如没选择的开局、通过点击箭头重 roll 的楼层. 如果不包含则返回类型为 `ChatMessage`, 否则返回类型为 `ChatMessageSwiped`; 默认为 `false`
 *
 * @returns 一个数组, 数组的元素是每楼的消息, 依据 message_id 从低到高排序, 类型为 `ChatMessage` 或 `ChatMessageSwiped` (取决于 `include_swipes` 的值, 默认为 `ChatMessage`).
 */
declare function getChatMessages(
  range: string | number,
  { role, hide_state, include_swipes }?: GetChatMessagesOption,
): (ChatMessage | ChatMessageSwiped)[];

type SetChatMessagesOption = {
  /**
   * 是否更新楼层在页面上的显示; 默认为 `'affected'`
   * - `'none'`: 不更新页面的显示
   * - `'affected'`: 仅更新被影响楼层的显示, 更新显示时会发送 `tavern_events.USER_MESSAGE_RENDERED` 或 `tavern_events.CHARACTER_MESSAGE_RENDERED` 事件
   * - `'all'`: 重新载入整个聊天消息, 将会触发 `tavern_events.CHAT_CHANGED` 事件
   */
  refresh?: 'none' | 'affected' | 'all';
};

/**
 * 修改聊天消息的数据
 *
 * @param chat_messages 要修改的消息, 必须包含 `message_id` 字段
 * @param option 可选选项
 *   - `refresh:'none'|'affected'|'all'`: 是否更新楼层在页面上的显示; 默认为 `'affected'`
 *
 * @example
 * // 修改第 10 楼被 AI 使用的消息页的正文
 * await setChatMessages([{message_id: 10, message: '新的消息'}]);
 *
 * @example
 * // 设置开局
 * await setChatMessages([{message_id: 0, swipes: ['开局1', '开局2']}])
 *
 * @example
 * // 切换为开局 3
 * await setChatMessages([{message_id: 0, swipe_id: 2}]);
 *
 * @example
 * // 重新渲染第 4 楼的前端界面 (利用 `{render: 'affected'}`)
 * await setChatMessages([{message_id: 4}]);
 *
 * @example
 * // 补充倒数第二楼的楼层变量
 * const chat_message = getChatMessages(-2)[0];
 * _.set(chat_message.data, '神乐光好感度', 5);
 * await setChatMessages([{message_id: 0, data: chat_message.data}], {refresh: 'none'});
 *
 * @example
 * // 隐藏所有楼层
 * const last_message_id = getLastMessageId();
 * await setChatMessages(_.range(last_message_id + 1).map(message_id => ({message_id, is_hidden: true})));
 */
declare function setChatMessages(
  chat_messages: Array<{ message_id: number } & (Partial<ChatMessage> | Partial<ChatMessageSwiped>)>,
  { refresh }?: SetChatMessagesOption,
): Promise<void>;

type ChatMessageCreating = {
  name?: string;
  role: 'system' | 'assistant' | 'user';
  is_hidden?: boolean;
  message: string;
  data?: Record<string, any>;
  extra?: Record<string, any>;
};

type CreateChatMessagesOption = SetChatMessagesOption & {
  /** @deprecated 请使用 `insert_before` */
  insert_at?: number | 'end';

  /** 插入到指定楼层前或末尾; 默认为末尾 */
  insert_before?: number | 'end';
};

/**
 * 创建聊天消息
 *
 * @param chat_messages 要创建的消息, 必须包含 `role` 和 `message` 字段
 * @param option 可选选项
 *   - `insert_before:number|'end'`: 插入到指定楼层前或末尾; 默认为末尾
 *   - `refresh:'none'|'affected'|'all'`: 是否更新楼层在页面上的显示; 默认为 `'affected'`
 *
 * @example
 * // 在第 10 楼前插入一条消息
 * await createChatMessages([{role: 'user', message: '你好'}], {insert_at: 10});
 *
 * @example
 * // 在末尾插入一条消息
 * await createChatMessages([{role: 'user', message: '你好'}]);
 */
declare function createChatMessages(
  chat_messages: ChatMessageCreating[],
  { insert_before, refresh }?: CreateChatMessagesOption,
): Promise<void>;

/**
 * 删除聊天消息
 *
 * @param message_ids 要删除的消息楼层号数组
 * @param option 可选选项
 *   - `refresh:'none'|'affected'|'all'`: 是否更新楼层在页面上的显示; 默认为 `'affected'`
 *
 * @example
 * // 删除第 10 楼、第 15 楼、倒数第二楼和最后一楼
 * await deleteChatMessages([10, 15, -2, getLastMessageId()]);
 *
 * @example
 * // 删除所有楼层
 * await deleteChatMessages(_.range(getLastMessageId() + 1));
 */
declare function deleteChatMessages(message_ids: number[], { refresh }?: SetChatMessagesOption): Promise<void>;

/**
 * 将原本顺序是 `[begin, middle) [middle, end)` 的楼层旋转为 `[middle, end) [begin, middle)`
 *
 * @param begin 旋转前开头楼层的楼层号
 * @param middle 旋转后将会被放到最开头的楼层号
 * @param end 旋转前结尾楼层的楼层号 + 1
 * @param option 可选选项
 *   - `refresh:'none'|'affected'|'all'`: 是否更新楼层在页面上的显示; 默认为 `'affected'`
 *
 * @example
 * // 将最后一楼放到第 5 楼之前
 * await rotateChatMessages(5, getLastMessageId(), getLastMessageId() + 1);
 *
 * // 将最后 3 楼放到第 1 楼之前
 * await rotateChatMessages(1, getLastMessageId() - 2, getLastMessageId() + 1);
 *
 * // 将前 3 楼放到最后
 * await rotateChatMessages(0, 3, getLastMessageId() + 1);
 */
declare function rotateChatMessages(
  begin: number,
  middle: number,
  end: number,
  { refresh }?: SetChatMessagesOption,
): Promise<void>;
/**
 * 获取消息楼层号对应的消息内容 JQuery 实例
 *
 * 相比于一个实用函数, 这更像是一个告诉你可以用 JQuery 的示例
 *
 * @param message_id 要获取的消息楼层号, 必须要酒馆页面显示了该消息楼层才能获取到
 * @returns 如果能获取到该消息楼层的 html, 则返回对应的 JQuery; 否则返回空 JQuery
 *
 * @example
 * // 获取第 0 楼的消息内容文本
 * const text = retrieveDisplayedMessage(0).text();
 *
 * @example
 * // 修改第 0 楼的消息内容文本
 * // - 这样的修改只会影响本次显示, 不会保存到消息文件中, 因此重新加载消息或刷新网页等操作后就会回到原样;
 * // - 如果需要实际修改消息文件, 请使用 `setChatMessage`
 * retrieveDisplayedMessage(0).text("new text");
 * retrieveDisplayedMessage(0).append("<pre>new text</pre>");
 * retrieveDisplayedMessage(0).append(formatAsDisplayedMessage("{{char}} speaks in {{lastMessageId}}"));
 */
declare function retrieveDisplayedMessage(message_id: number): JQuery<HTMLDivElement>;

type FormatAsDisplayedMessageOption = {
  /** 消息所在的楼层, 要求该楼层已经存在, 即在 `[0, getLastMessageId()]` 范围内; 默认为 'last' */
  message_id?: 'last' | 'last_user' | 'last_char' | number;
};

/**
 * 将字符串处理为酒馆用于显示的 html 格式. 将会,
 * 1. 替换字符串中的酒馆宏
 * 2. 对字符串应用对应的酒馆正则
 * 3. 将字符串调整为 html 格式
 *
 * @param text 要处理的字符串
 * @param option 可选选项
 *   - `message_id?:number`: 消息所在的楼层, 要求该楼层已经存在, 即在 `[0, getLastMessageId()]` 范围内; 默认为最新楼层
 *
 * @returns 处理结果
 *
 * @throws 如果提供的消息楼层号 `message_id` 不在 `[0, getLastMessageId()]` 范围内, 将会抛出错误
 *
 * @example
 * const text = formatAsDisplayedMessage("{{char}} speaks in {{lastMessageId}}");
 * => "<p>少女歌剧 speaks in 5</p>";
 */
declare function formatAsDisplayedMessage(text: string, { message_id }?: FormatAsDisplayedMessageOption): string;

/**
 * 刷新或替换单个楼层的显示, 如果该楼层并没有显示在网页上则什么也不做
 *
 * @param message_id 要刷新的消息楼层号
 * @param $mes 要刷新的消息楼层对应的 JQuery 实例, 如果未提供则自动通过 `message_id` 获取
 *
 * @example
 * // 刷新第 0 楼的显示
 * await refreshOneMessage(0);
 *
 * @example
 * // 刷新最新楼层的显示
 * await refreshOneMessage(getLastMessageId());
 *
 * @example
 * // 强行让第 5 楼显示第 0 楼的消息, 这只会影响网页显示, 不会影响实际聊天记录
 * await refreshOneMessage(0, $('#chat > .mes[mesid="5"]'));
 *
 * @example
 * // 强行让最后一楼显示第 0 楼的消息, 这只会影响网页显示, 不会影响实际聊天记录
 * await refreshOneMessage(0, $('#chat > .mes.last_mes'));
 */
declare function refreshOneMessage(message_id: number, $mes?: JQuery): Promise<void>;
/** 检查当前用户是否为管理员, 只有管理员能更新全局扩展 */
declare function isAdmin(): boolean;

/** 获取酒馆助手扩展 id */
declare function getTavernHelperExtensionId(): string;

/**
 * 获取已安装扩展的类型
 * - `'local'`: 本地扩展, 仅当前用户可用
 * - `'global'`: 全局扩展, 酒馆所有用户可用
 * - `'system'`: 酒馆内置扩展, 如正则等
 *
 * @param extension_id 扩展 id, 一般是扩展文件夹名
 */
declare function getExtensionType(extension_id: string): 'local' | 'global' | 'system' | null;

type ExtensionInstallationInfo = {
  current_branch_name: string;
  current_commit_hash: string;
  is_up_to_date: boolean;
  remote_url: string;
};

/**
 * 获取扩展安装信息
 *
 * @param extension_id 扩展 id, 一般是扩展文件夹名
 */
declare function getExtensionInstallationInfo(extension_id: string): Promise<ExtensionInstallationInfo | null>;

/**
 * 检查是否已安装某一扩展
 *
 * @param extension_id 扩展 id, 一般是扩展文件夹名
 *
 * @example
 * // 检查是否已安装酒馆助手
 * const is_installed = isInstalledExtension(getTavernHelperExtensionId());
 */
declare function isInstalledExtension(extension_id: string): boolean;

/**
 * 安装扩展; 新安装的扩展需要刷新页面 (`triggerSlash('/reload-page')`) 才生效
 *
 * @param url 扩展 URL
 * @param type 要安装成的扩展类型
 *   - `'local'`: 本地扩展, 仅当前用户可用
 *   - `'global'`: 全局扩展, 酒馆所有用户可用
 * @returns 对安装的响应情况
 *
 * @example
 * // 安装酒馆助手
 * const response = await installExtension('https://github.com/n0vi028/JS-Slash-Runner', 'local');
 * if (response.ok) {
 *   toastr.success(`成功安装酒馆助手, 准备刷新页面以生效...`);
 *   _.delay(() => triggerSlash('/reload-page'), 3000);
 * }
 */
declare function installExtension(url: string, type: 'local' | 'global'): Promise<Response>;

/**
 * 卸载扩展; 卸载后需要刷新页面 (`triggerSlash('/reload-page')`) 才生效
 *
 * @param extension_id 扩展 id, 一般是扩展文件夹名
 *
 * @example
 * // 卸载酒馆助手
 * const response = await uninstallExtension('JS-Slash-Runner');
 * if (response.ok) {
 *   toastr.success(`成功卸载酒馆助手, 准备刷新页面以生效...`);
 *   _.delay(() => triggerSlash('/reload-page'), 3000);
 * }
 */
declare function uninstallExtension(extension_id: string): Promise<Response>;

/**
 * 重新安装扩展; 重新安装后需要刷新页面 (`triggerSlash('/reload-page')`) 才生效
 *
 * @param extension_id 扩展 id, 一般是扩展文件夹名
 *
 * @example
 * // 重新安装酒馆助手
 * const response = await reinstallExtension('JS-Slash-Runner');
 * if (response.ok) {
 *   toastr.success(`成功重新安装酒馆助手, 准备刷新页面以生效...`);
 *   _.delay(() => triggerSlash('/reload-page'), 3000);
 * }
 */
declare function reinstallExtension(extension_id: string): Promise<Response>;

/**
 * 更新扩展; 更新后需要刷新页面 (`triggerSlash('/reload-page')`) 才生效
 *
 * @param extension_id 扩展 id, 一般是扩展文件夹名
 *
 * @example
 * // 更新酒馆助手
 * const response = await updateExtension('JS-Slash-Runner');
 * if (response.ok) {
 *   toastr.success(`成功更新酒馆助手, 准备刷新页面以生效...`);
 *   _.delay(() => triggerSlash('/reload-page'), 3000);
 * }
 */
declare function updateExtension(extension_id: string): Promise<Response>;
/**
 * 获取代理预设名称列表
 *
 * @returns 代理预设名称列表
 */
declare function getProxyPresetNames(): string[];

/**
 * 使用酒馆当前启用的预设, 让 AI 生成一段文本.
 *
 * 该函数在执行过程中将会发送以下事件:
 * - `iframe_events.GENERATION_STARTED`: 生成开始
 * - 若启用流式传输, `iframe_events.STREAM_TOKEN_RECEIVED_FULLY`: 监听它可以得到流式传输的当前完整文本 ("这是", "这是一条", "这是一条流式传输")
 * - 若启用流式传输, `iframe_events.STREAM_TOKEN_RECEIVED_INCREMENTALLY`: 监听它可以得到流式传输的当前增量文本 ("这是", "一条", "流式传输")
 * - `iframe_events.GENERATION_ENDED`: 生成结束, 监听它可以得到生成的最终文本 (当然也能通过函数返回值获得)
 *
 * @param config 提示词和生成方式设置
 *   - `user_input?:string`: 用户输入
 *   - `should_stream?:boolean`: 是否启用流式传输; 默认为 'false'
 *   - `should_silence?:boolean`: 是否静默生成; 默认为 'false'
 *   - `image?:File|string`: 图片输入
 *   - `overrides?:Overrides`: 覆盖选项. 若设置, 则 `overrides` 中给出的字段将会覆盖对应的提示词. 如 `overrides.char_description = '覆盖的角色描述';` 将会覆盖角色描述
 *   - `injects?:Omit<InjectionPrompt, 'id'>[]`: 要额外注入的提示词
 *   - `max_chat_history?:'all'|number`: 最多使用多少条聊天历史
 * @returns 生成的最终文本
 *
 * @example
 * // 请求生成
 * const result = await generate({ user_input: '你好' });
 * console.info('收到回复: ', result);
 *
 * @example
 * // 图片输入
 * const result = await generate({ user_input: '你好', image: 'https://example.com/image.jpg' });
 * console.info('收到回复: ', result);
 *
 * @example
 * // 注入、覆盖提示词
 * const result = await generate({
 *   user_input: '你好',
 *   injects: [{ role: 'system', content: '思维链...', position: 'in_chat', depth: 0, should_scan: true, }]
 *   overrides: {
 *     char_personality: '温柔',
 *     world_info_before: '',
 *     chat_history: {
 *       prompts: [],
 *     }
 *   }
 * });
 * console.info('收到回复: ', result);
 *
 * @example
 * // 使用自定义API
 * const result = await generate({
 *   user_input: '你好',
 *   custom_api: {
 *     apiurl: 'https://your-proxy-url.com',
 *     key: 'your-api-key',
 *     model: 'gpt-4',
 *     source: 'openai'
 *   }
 * });
 * console.info('收到回复: ', result);
 *
 * @example
 * // 使用酒馆代理预设
 * const result = await generate({
 *   user_input: '你好',
 *   custom_api: {
 *     proxy_preset: 'MyProxy',
 *     model: 'gpt-4',
 *   }
 * });
 * console.info('收到回复: ', result);
 *
 * @example
 * // 使用当前 ST 源，但切换模型
 * const result = await generate({
 *   user_input: '你好',
 *   custom_api: {
 *     model: 'gpt-4-turbo',
 *   }
 * });
 * console.info('收到回复: ', result);
 *
 * @example
 * // 流式生成
 *
 * // 需要预先监听事件来接收流式回复
 * eventOn(iframe_events.STREAM_TOKEN_RECEIVED_FULLY, text => {
 *   console.info('收到流式回复: ', text);
 * });
 *
 * // 然后进行生成
 * const result = await generate({ user_input: '你好', should_stream: true });
 * console.info('收到最终回复: ', result);
 *
 * @example
 * // 使用 tool use / function calling
 * const result = await generate({
 *   user_input: '今天北京天气怎么样？',
 *   tools: [{
 *     type: 'function',
 *     function: {
 *       name: 'get_weather',
 *       description: '获取指定城市的天气',
 *       parameters: {
 *         type: 'object',
 *         properties: {
 *           city: { type: 'string', description: '城市名称' }
 *         },
 *         required: ['city']
 *       }
 *     }
 *   }],
 *   tool_choice: 'auto'
 * });
 *
 * if (typeof result === 'object' && result.tool_calls) {
 *   for (const call of result.tool_calls) {
 *     console.info(`模型调用了工具: ${call.function.name}(${call.function.arguments})`);
 *   }
 * } else {
 *   console.info('收到文本回复: ', result);
 * }
 *
 * @example
 * // 使用 json_schema 强制结构化输出
 * const result = await generate({
 *   user_input: '描述场景',
 *   json_schema: {
 *     name: 'scene_output',
 *     value: {
 *       type: 'object',
 *       properties: {
 *         narrative: { type: 'string' },
 *         mood: { type: 'string', enum: ['happy', 'sad', 'tense'] }
 *       },
 *       required: ['narrative', 'mood']
 *     }
 *   }
 * });
 * const parsed = JSON.parse(result as string);
 * console.info(parsed.narrative, parsed.mood);
 */
declare function generate(config: GenerateConfig): Promise<string | GenerateToolCallResult>;

/**
 * 不使用酒馆当前启用的预设, 让 AI 生成一段文本.
 *
 * 该函数在执行过程中将会发送以下事件:
 * - `iframe_events.GENERATION_STARTED`: 生成开始
 * - 若启用流式传输, `iframe_events.STREAM_TOKEN_RECEIVED_FULLY`: 监听它可以得到流式传输的当前完整文本 ("这是", "这是一条", "这是一条流式传输")
 * - 若启用流式传输, `iframe_events.STREAM_TOKEN_RECEIVED_INCREMENTALLY`: 监听它可以得到流式传输的当前增量文本 ("这是", "一条", "流式传输")
 * - `iframe_events.GENERATION_ENDED`: 生成结束, 监听它可以得到生成的最终文本 (当然也能通过函数返回值获得)
 *
 * @param config 提示词和生成方式设置
 *   - `user_input?:string`: 用户输入
 *   - `should_stream?:boolean`: 是否启用流式传输; 默认为 'false'
 *   - `should_silence?:boolean`: 是否静默生成; 默认为 'false'
 *   - `image?:File|string`: 图片输入
 *   - `overrides?:Overrides`: 覆盖选项. 若设置, 则 `overrides` 中给出的字段将会覆盖对应的提示词. 如 `overrides.char_description = '覆盖的角色描述';` 将会覆盖角色描述
 *   - `injects?:Omit<InjectionPrompt, 'id'>[]`: 要额外注入的提示词
 *   - `max_chat_history?:'all'|number`: 最多使用多少条聊天历史
 *   - `ordered_prompts?:(BuiltinPrompt|RolePrompt)[]`: 一个提示词数组, 数组元素将会按顺序发给 AI, 因而相当于自定义预设
 * @returns 生成的最终文本
 *
 * @example
 * // 自定义内置提示词顺序, 未在 ordered_prompts 中给出的将不会被使用
 * const result = await generateRaw({
 *   user_input: '你好',
 *   ordered_prompts: [
 *     'char_description',
 *     { role: 'system', content: '系统提示' },
 *     'chat_history',
 *     'user_input',
 *   ]
 * })
 * console.info('收到回复: ', result);
 *
 * @example
 * // 使用自定义API和自定义提示词顺序
 * const result = await generateRaw({
 *   user_input: '你好',
 *   custom_api: {
 *     apiurl: 'https://your-proxy-url.com',
 *     key: 'your-api-key',
 *     model: 'gpt-4',
 *     source: 'openai'
 *   },
 *   ordered_prompts: [
 *     'char_description',
 *     'chat_history',
 *     'user_input',
 *   ]
 * })
 * console.info('收到回复: ', result);
 */
declare function generateRaw(config: GenerateRawConfig): Promise<string | GenerateToolCallResult>;

/**
 * 获取模型列表
 *
 * @param custom_api 自定义API配置
 * @returns Promise<string[]> 模型列表
 * @throws 获取模型列表失败
 */
declare function getModelList(custom_api: { apiurl: string; key?: string }): Promise<string[]>;

/**
 * 根据生成请求唯一标识符停止特定的生成请求
 *
 * @param generation_id 生成请求唯一标识符, 用于标识要停止的生成请求
 * @returns boolean 是否成功停止生成
 */
declare function stopGenerationById(generation_id: string): boolean;

/**
 * 停止所有正在进行的生成请求
 *
 * @returns boolean 是否成功停止所有生成
 */
declare function stopAllGeneration(): boolean;

type GenerateConfig = {
  /** 要使用的预设名称, 默认为当前加载的预设 `'in_use'`; 若设置, 则会用所选预设的提示词及参数, 但不会使用所选预设的正则、酒馆助手脚本 */
  preset_name?: 'in_use' | string;

  /**
   * 请求生成的唯一标识符, 不设置则默认生成一个随机标识符.
   *
   * 当有多个 generate/generateRaw 同时请求生成时, 可以为每个请求指定唯一标识符, 从而能用 `stopGenerationById` 停止特定生成请求, 或正确监听对应的生成事件.
   */
  generation_id?: string;

  /** 用户输入 */
  user_input?: string;

  /**
   * 图片输入，支持以下格式：
   * - File 对象：通过 input[type="file"] 获取的文件对象
   * - Base64 字符串：图片的 base64 编码
   * - URL 字符串：图片的在线地址
   */
  image?: File | string | (File | string)[];

  /**
   * 是否启用流式传输; 默认为 `false`.
   *
   * 若启用流式传输, 每次得到流式传输结果时, 函数将会发送事件:
   * - `iframe_events.STREAM_TOKEN_RECEIVED_FULLY`: 监听它可以得到流式传输的当前完整文本 ("这是", "这是一条", "这是一条流式传输")
   * - `iframe_events.STREAM_TOKEN_RECEIVED_INCREMENTALLY`: 监听它可以得到流式传输的当前增量文本 ("这是", "一条", "流式传输")
   *
   * @example
   * eventOn(iframe_events.STREAM_TOKEN_RECEIVED_FULLY, text => console.info(text));
   */
  should_stream?: boolean;

  /**
   * 是否静默生成; 默认为 `false`.
   * - `false`: 酒馆页面的发送按钮将会变为停止按钮, 点击停止按钮会中断所有非静默生成请求
   * - `true`: 不影响酒馆停止按钮状态, 点击停止按钮不会中断该生成
   *
   * 虽然静默生成不能通过停止按钮中断, 但可以在代码中使用以下方式停止生成:
   * - 使用该生成请求的 `generation_id` 调用 `stopGenerationById`
   * - 调用 `stopAllGeneration`
   */
  should_silence?: boolean;

  /**
   * 覆盖选项. 若设置, 则 `overrides` 中给出的字段将会覆盖对应的提示词.
   *   如 `overrides.char_description = '覆盖的角色描述';` 将会覆盖角色描述.
   */
  overrides?: Overrides;

  /** 要额外注入的提示词 */
  injects?: Omit<InjectionPrompt, 'id'>[];

  /** 最多使用多少条聊天历史; 默认为 'all' */
  max_chat_history?: 'all' | number;

  /** 自定义API配置 */
  custom_api?: CustomApiConfig;

  /**
   * 工具定义列表（OpenAI 格式）。
   * 传入后，模型可能返回 tool_calls 而非纯文本，此时函数返回 `GenerateToolCallResult` 对象。
   */
  tools?: ToolDefinition[];

  /**
   * 工具选择策略:
   * - `'auto'`: 模型自行决定是否调用工具（默认）
   * - `'required'`: 模型必须调用工具
   * - `'none'`: 模型不调用工具
   * - `{ type: 'function', function: { name: string } }`: 强制调用指定工具
   */
  tool_choice?: ToolChoice;

  /**
   * JSON Schema 定义，强制模型输出符合指定 schema 的 JSON。
   * 返回值为 JSON 字符串（需自行 JSON.parse）。
   *
   * ST 服务端会根据 provider 自动转换格式：
   * - OpenAI/DeepSeek/Mistral 等 → response_format.json_schema
   * - Claude → 转为 tool + forced tool_choice
   *
   * 与 tools 互斥，不要同时传入。
   */
  json_schema?: JsonSchema;
};

type GenerateRawConfig = GenerateConfig & {
  /**
   * 一个提示词数组, 数组元素将会按顺序发给 AI, 因而相当于自定义预设. 该数组允许存放两种类型:
   * - `PlaceholderPrompt`: 内置提示词. 由于不使用预设, 如果需要 "角色描述" 等提示词, 你需要自己指定要用哪些并给出顺序
   *                        如果不想自己指定, 可通过 `placeholder_prompt_default_order` 得到酒馆默认预设所使用的顺序 (但对于这种情况, 也许你更应该用 `generate`).
   * - `RolePrompt`: 要额外给定的提示词.
   */
  ordered_prompts?: (PlaceholderPrompt | RolePrompt)[];
};

/**
 * 预设为内置提示词设置的默认顺序
 */
declare const placeholder_prompt_default_order: PlaceholderPrompt[];

type PlaceholderPrompt =
  | 'world_info_before'
  | 'persona_description'
  | 'char_description'
  | 'char_personality'
  | 'scenario'
  | 'world_info_after'
  | 'dialogue_examples'
  | 'chat_history'
  | 'user_input';

type RolePrompt = {
  role: 'system' | 'assistant' | 'user';
  content: string;
  image?: File | string | (File | string)[];
};

type Overrides = {
  world_info_before?: string;
  persona_description?: string;
  char_description?: string;
  char_personality?: string;
  scenario?: string;
  world_info_after?: string;
  dialogue_examples?: string;

  /**
   * 聊天历史
   * - `with_depth_entries`: 是否启用世界书中按深度插入的条目; 默认为 `true`
   * - `author_note`: 若设置, 覆盖 "作者注释" 为给定的字符串
   * - `prompts`: 若设置, 覆盖 "聊天历史" 为给定的提示词
   */
  chat_history?: {
    with_depth_entries?: boolean;
    author_note?: string;
    prompts?: RolePrompt[];
  };
};

/**
 * 自定义API配置
 *
 * 所有字段均为可选：
 * - 指定 proxy_preset：使用酒馆代理预设的 URL 和 Key
 * - 指定 apiurl：使用自定义 API 地址
 * - 都不指定：使用当前 ST 源，但可覆盖 model 等参数
 */
type CustomApiConfig = {
  /**
   * 酒馆代理预设名称。
   * - 预设匹配成功：完全使用预设的 URL 和密码（即使为空也不回退到 custom_api 的字段）
   * - 预设未找到：回退到 custom_api.apiurl 和 custom_api.key
   * 注意：名称需完全一致（大小写敏感，前后空格会被自动去除）
   */
  proxy_preset?: string;
  /** 自定义API地址 */
  apiurl?: string;
  /** API密钥 */
  key?: string;
  /** 模型名称 */
  model?: string;
  /** API源, 默认为 'openai'. 目前支持的源请查看酒馆官方代码[`SillyTavern/src/constants.js`](https://github.com/SillyTavern/SillyTavern/blob/2e3dff73a127679f643e971801cd51173c2c34e7/src/constants.js#L164) */
  source?: string;

  /** 最大回复 tokens 度 */
  max_tokens?: 'same_as_preset' | 'unset' | number;
  /** 温度 */
  temperature?: 'same_as_preset' | 'unset' | number;
  /** 频率惩罚 */
  frequency_penalty?: 'same_as_preset' | 'unset' | number;
  /** 存在惩罚 */
  presence_penalty?: 'same_as_preset' | 'unset' | number;
  top_p?: 'same_as_preset' | 'unset' | number;
  top_k?: 'same_as_preset' | 'unset' | number;

  /** 仅 `source === 'custom'` 时有效, 在请求体中额外覆盖参数; 如 `{ max_tokens: 1024 }` */
  custom_include_body?: Record<string, any>;
  /** 仅 `source === 'custom'` 时有效, 在请求体中排除参数, 由于酒馆后端限制仅能排除根参数; 如 `['max_tokens']` */
  custom_exclude_body?: string[];
  /** 仅 `source === 'custom'` 时有效, 在请求头中额外覆盖参数; 如 `{ Content-Type: 'application/json' }` */
  custom_include_headers?: Record<string, any>;
};

/**
 * JSON Schema 定义，用于强制模型输出符合指定 schema 的 JSON。
 *
 * @example
 * const result = await generateRaw({
 *   user_input: '描述场景',
 *   json_schema: {
 *     name: 'scene_output',
 *     description: '场景描述和角色状态',
 *     value: {
 *       type: 'object',
 *       properties: {
 *         narrative: { type: 'string', description: '叙事文本' },
 *         status: { type: 'object', properties: { name: { type: 'string' } } }
 *       },
 *       required: ['narrative', 'status']
 *     }
 *   }
 * });
 * const parsed = JSON.parse(result as string);
 */
type JsonSchema = {
  /** Schema 名称 */
  name: string;
  /** Schema 描述 */
  description?: string;
  /** JSON Schema 定义 */
  value: Record<string, any>;
  /** 是否严格模式（默认 true） */
  strict?: boolean;
};

/**
 * Tool function 定义
 */
type ToolFunction = {
  /** 工具函数名称 */
  name: string;
  /** 工具函数描述 */
  description?: string;
  /** JSON Schema 格式的参数定义 */
  parameters?: Record<string, any>;
};

/**
 * Tool 定义（OpenAI 格式）
 */
type ToolDefinition = {
  type: 'function';
  function: ToolFunction;
};

/**
 * Tool choice 选项
 */
type ToolChoice = 'auto' | 'required' | 'none' | 'any' | { type: 'function'; function: { name: string } };

/**
 * 当模型返回 tool_calls 时的结构化结果。
 *
 * 仅在 `generate` / `generateRaw` 配置中传入了 `tools` 且模型决定调用工具时返回；
 * 否则函数仍返回普通的 `string`。
 */
type GenerateToolCallResult = {
  /** 模型返回的文本内容（可能为空字符串） */
  content: string;
  /** 模型请求调用的工具列表 */
  tool_calls: {
    id: string;
    type: 'function';
    function: {
      /** 工具函数名称 */
      name: string;
      /** JSON 字符串格式的参数 */
      arguments: string;
    };
    /**
     * 加密的 reasoning/thought 签名（若 provider 返回）。
     *
     * 多轮 tool call 场景下必须把签名原样回传给下一轮请求以维持推理上下文。
     *
     * **Gemini 3 强制要求**：不回传 thought_signature 会返回 4xx 校验错误
     * （Gemini 2.5 及之前是可选，3.0+ 强制，见官方 thought-signatures 文档）。
     *
     * **并行 tool call**：Gemini 只会把签名挂在**第一个** tool_call 上，
     * 后续并行 call 的 `thought_signature` 为 undefined——这一个签名代表整批，
     * 回传时只需要还原到第一个 call 对应的 functionCall part 上。
     *
     * **多轮累积**：必须把**历史所有轮次**返回过的签名一起回传，而不是只带最后一次。
     *
     * **绕过校验**（仅限手动构造 tool call 的情况）：把 thought_signature 设为
     * `"skip_thought_signature_validator"` 或 `"context_engineering_is_the_way_to_go"`
     * 可以跳过 Gemini 服务端校验。
     */
    thought_signature?: string;
  }[];
  /**
   * 顶层 reasoning 签名（非绑定到具体 tool_call 的那一份）。
   *
   * 主要出现在「模型只返回文本、没有 tool_calls」的场景：Gemini 会把签名挂在最后一个
   * text part 上（流式终帧里可能是空字符串 text + 签名）。OpenRouter 通过
   * `reasoning_details` 暴露非 tool 绑定的 encrypted 段；Claude 通过 thinking 块的
   * signature 字段提供。
   *
   * 同样用于多轮场景下把 thinking 上下文回传给下一轮请求。
   */
  reasoning_signature?: string;
};

//----------------------------------------------------------------------------------------------------------------------
/**
 * 预设为内置提示词设置的默认顺序
 * @deprecated 请使用 `placeholder_prompt_default_order`
 */
declare const builtin_prompt_default_order: PlaceholderPrompt[];

/** @deprecated 请使用 `PlaceholderPrompt` */
type BuiltinPrompt =
  | 'world_info_before'
  | 'persona_description'
  | 'char_description'
  | 'char_personality'
  | 'scenario'
  | 'world_info_after'
  | 'dialogue_examples'
  | 'chat_history'
  | 'user_input';
/**
 * 将接口共享到全局, 使其可以在其他前端界面或脚本中使用.
 *
 * 其他前端界面或脚本将能通过 `await waitGlobalInitialized(global)` 来等待初始化完毕, 从而用 `global` 为变量名访问该接口.
 *
 * @param global 要共享的接口名称
 * @param value 要共享的接口内容
 *
 * @example
 * // 共享 Mvu 接口到全局
 * initializeGlobal('Mvu', Mvu);
 * // 此后其他前端界面或脚本中可以通过 `await waitGlobalInitialized('Mvu')` 来等待初始化完毕, 从而用 `Mvu` 为变量名访问该接口
 */
declare function initializeGlobal(global: TypeFest.LiteralUnion<'Mvu', string>, value: any): void;

/**
 * 等待其他前端界面或脚本中共享出来的全局接口初始化完毕, 并使之在当前前端界面或脚本中可用.
 *
 * 这需要其他前端界面或脚本通过 `initializeGlobal(global, value)` 来共享接口.
 *
 * @param global 要初始化的全局接口名称
 *
 * @example
 * await waitGlobalInitialized('Mvu');
 * ...此后可以直接使用 Mvu 接口
 */
declare function waitGlobalInitialized<T>(global: TypeFest.LiteralUnion<'Mvu', string>): Promise<T>;
/**
 * 像酒馆界面里那样导入新角色/更新现有角色卡
 *
 * @param filename 角色卡名
 * @param content 角色卡文件内容
 *
 * @example
 * // 从网络链接导入新角色/更新现有角色卡
 * const response = await fetch(角色卡网络链接);
 * await importRawCharacter(角色卡名, await response.blob());
 */
declare function importRawCharacter(filename: string, content: Blob): Promise<Response>;

/**
 * 像酒馆界面里那样导入聊天文件, 目前仅能导入到当前选择的角色卡
 *
 * @param filename 聊天文件名, 由于酒馆限制, 它实际不会作为最终导入的聊天文件名称
 * @param content 聊天文件内容
 *
 * @throws 如果未选择角色卡, 将会抛出错误
 *
 * @example
 * // 从网络链接导入聊天文件
 * const response = await fetch(聊天文件网络链接);
 * await importRawChat(聊天文件名, await response.text());
 */
declare function importRawChat(filename: string, content: string): Promise<Response>;

/**
 * 像酒馆界面里那样导入新预设/更新现有预设
 *
 * @param filename 预设名
 * @param content 预设文件内容
 *
 * @example
 * // 从网络链接导入新预设/更新现有预设
 * const response = await fetch(预设网络链接);
 * await importRawChat(预设名, await response.text());
 */
declare function importRawPreset(filename: string, content: string): Promise<boolean>;

/**
 * 像酒馆界面里那样导入新世界书/更新现有世界书
 *
 * @param filename 世界书名
 * @param content 世界书文件内容
 *
 * @example
 * // 从网络链接导入新世界书/更新现有世界书
 * const response = await fetch(世界书网络链接);
 * await importRawChat(世界书名, await response.text());
 */
declare function importRawWorldbook(filename: string, content: string): Promise<Response>;

/**
 * 像酒馆界面里那样导入酒馆正则
 *
 * @param filename 酒馆正则名
 * @param content 酒馆正则文件内容
 *
 * @example
 * // 从网络链接导入酒馆正则
 * const response = await fetch(酒馆正则网络链接);
 * await importRawChat(酒馆正则名, await response.text());
 */
declare function importRawTavernRegex(filename: string, content: string): boolean;
interface Window {
  /**
   * 酒馆助手提供的额外功能, 具体内容见于 https://n0vi028.github.io/JS-Slash-Runner-Doc
   * 你也可以在酒馆页面按 f12, 在控制台中输入 `window.TavernHelper` 来查看当前酒馆助手所提供的接口
   */
  TavernHelper: {
    // audio
    readonly playAudio: typeof playAudio;
    readonly pauseAudio: typeof pauseAudio;
    readonly getAudioList: typeof getAudioList;
    readonly replaceAudioList: typeof replaceAudioList;
    readonly appendAudioList: typeof appendAudioList;
    readonly getAudioSettings: typeof getAudioSettings;
    readonly setAudioSettings: typeof setAudioSettings;
    readonly getCurrentAudio: typeof getCurrentAudio;

    // builtin
    readonly builtin: typeof builtin;

    // character
    readonly getCharacterNames: typeof getCharacterNames;
    readonly getCharacterIds: typeof getCharacterIds;
    readonly getCurrentCharacterName: typeof getCurrentCharacterName;
    readonly getCurrentCharacterId: typeof getCurrentCharacterId;
    readonly createCharacter: typeof createCharacter;
    readonly createOrReplaceCharacter: typeof createOrReplaceCharacter;
    readonly deleteCharacter: typeof deleteCharacter;
    readonly getCharacter: typeof getCharacter;
    readonly replaceCharacter: typeof replaceCharacter;
    readonly updateCharacterWith: typeof updateCharacterWith;

    // chat_message
    readonly getChatMessages: typeof getChatMessages;
    readonly setChatMessages: typeof setChatMessages;
    readonly createChatMessages: typeof createChatMessages;
    readonly deleteChatMessages: typeof deleteChatMessages;
    readonly rotateChatMessages: typeof rotateChatMessages;

    // displayed_message
    readonly formatAsDisplayedMessage: typeof formatAsDisplayedMessage;
    readonly retrieveDisplayedMessage: typeof retrieveDisplayedMessage;
    readonly refreshOneMessage: typeof refreshOneMessage;

    // extension
    readonly isAdmin: typeof isAdmin;
    readonly getExtensionType: typeof getExtensionType;
    readonly getExtensionStatus: typeof getExtensionInstallationInfo;
    readonly isInstalledExtension: typeof isInstalledExtension;
    readonly installExtension: typeof installExtension;
    readonly uninstallExtension: typeof uninstallExtension;
    readonly reinstallExtension: typeof reinstallExtension;
    readonly updateExtension: typeof updateExtension;

    // generate
    readonly builtin_prompt_default_order: typeof builtin_prompt_default_order;
    readonly generate: typeof generate;
    readonly generateRaw: typeof generateRaw;
    readonly getModelList: typeof getModelList;
    readonly getProxyPresetNames: typeof getProxyPresetNames;
    readonly stopGenerationById: typeof stopGenerationById;
    readonly stopAllGeneration: typeof stopAllGeneration;

    // global
    readonly initializeGlobal: typeof initializeGlobal;
    readonly waitGlobalInitialized: typeof waitGlobalInitialized;

    // import_raw
    readonly importRawCharacter: typeof importRawCharacter;
    readonly importRawChat: typeof importRawChat;
    readonly importRawPreset: typeof importRawPreset;
    readonly importRawWorldbook: typeof importRawWorldbook;
    readonly importRawTavernRegex: typeof importRawTavernRegex;

    // inject
    readonly injectPrompts: typeof injectPrompts;
    readonly uninjectPrompts: typeof uninjectPrompts;

    // lorebook_entry
    readonly getLorebookEntries: typeof getLorebookEntries;
    readonly replaceLorebookEntries: typeof replaceLorebookEntries;
    readonly updatelorebookEntriesWith: typeof updateLorebookEntriesWith;
    readonly setLorebookEntries: typeof setLorebookEntries;
    readonly createLorebookEntries: typeof createLorebookEntries;
    readonly deleteLorebookEntries: typeof deleteLorebookEntries;

    // lorebook
    readonly getLorebookSettings: typeof getLorebookSettings;
    readonly setLorebookSettings: typeof setLorebookSettings;
    readonly getLorebooks: typeof getLorebooks;
    readonly deleteLorebook: typeof deleteLorebook;
    readonly createLorebook: typeof createLorebook;
    readonly getCharLorebooks: typeof getCharLorebooks;
    readonly setCurrentCharLorebooks: typeof setCurrentCharLorebooks;
    readonly getCurrentCharPrimaryLorebook: typeof getCurrentCharPrimaryLorebook;
    readonly getOrCreateChatLorebook: typeof getOrCreateChatLorebook;

    // macrolike
    readonly registerMacroLike: typeof registerMacroLike;

    // preset
    readonly isPresetNormalPrompt: typeof isPresetNormalPrompt;
    readonly isPresetSystemPrompt: typeof isPresetSystemPrompt;
    readonly isPresetPlaceholderPrompt: typeof isPresetPlaceholderPrompt;
    readonly default_preset: typeof default_preset;
    readonly getPresetNames: typeof getPresetNames;
    readonly getLoadedPresetName: typeof getLoadedPresetName;
    readonly loadPreset: typeof loadPreset;
    readonly createPreset: typeof createPreset;
    readonly createOrReplacePreset: typeof createOrReplacePreset;
    readonly deletePreset: typeof deletePreset;
    readonly renamePreset: typeof renamePreset;
    readonly getPreset: typeof getPreset;
    readonly replacePreset: typeof replacePreset;
    readonly updatePresetWith: typeof updatePresetWith;
    readonly setPreset: typeof setPreset;

    // persona
    readonly getPersonaNames: typeof getPersonaNames;
    readonly getPersonaIds: typeof getPersonaIds;
    readonly getCurrentPersonaName: typeof getCurrentPersonaName;
    readonly getCurrentPersonaId: typeof getCurrentPersonaId;
    readonly getPersonaAvatarPath: typeof getPersonaAvatarPath;
    readonly createPersona: typeof createPersona;
    readonly createOrReplacePersona: typeof createOrReplacePersona;
    readonly deletePersona: typeof deletePersona;
    readonly getPersona: typeof getPersona;
    readonly replacePersona: typeof replacePersona;
    readonly updatePersonaWith: typeof updatePersonaWith;

    // raw_character
    readonly RawCharacter: typeof RawCharacter;
    readonly getCharData: typeof getCharData;
    readonly getCharAvatarPath: typeof getCharAvatarPath;
    readonly getChatHistoryBrief: typeof getChatHistoryBrief;
    readonly getChatHistoryDetail: typeof getChatHistoryDetail;

    // script
    readonly getAllEnabledScriptButtons: typeof getAllEnabledScriptButtons;

    // slash
    readonly triggerSlash: typeof triggerSlash;

    // tavern_regex
    readonly formatAsTavernRegexedString: typeof formatAsTavernRegexedString;
    readonly isCharacterTavernRegexesEnabled: typeof isCharacterTavernRegexesEnabled;
    readonly getTavernRegexes: typeof getTavernRegexes;
    readonly replaceTavernRegexes: typeof replaceTavernRegexes;
    readonly updateTavernRegexesWith: typeof updateTavernRegexesWith;

    // util
    readonly substitudeMacros: typeof substitudeMacros;
    readonly getLastMessageId: typeof getLastMessageId;
    readonly errorCatched: typeof errorCatched;
    readonly getMessageId: typeof getMessageId;

    // variables
    readonly getVariables: typeof getVariables;
    readonly replaceVariables: typeof replaceVariables;
    readonly updateVariablesWith: typeof updateVariablesWith;
    readonly insertOrAssignVariables: typeof insertOrAssignVariables;
    readonly insertVariables: typeof insertVariables;
    readonly deleteVariable: typeof deleteVariable;

    // version
    readonly getTavernHelperVersion: typeof getTavernHelperVersion;
    readonly getTavernHelperExtensionId: typeof getTavernHelperExtensionId;
    readonly getTavernVersion: typeof getTavernVersion;

    // worldbook
    readonly getWorldbookNames: typeof getWorldbookNames;
    readonly getGlobalWorldbookNames: typeof getGlobalWorldbookNames;
    readonly rebindGlobalWorldbooks: typeof rebindGlobalWorldbooks;
    readonly getCharWorldbookNames: typeof getCharWorldbookNames;
    readonly rebindCharWorldbooks: typeof rebindCharWorldbooks;
    readonly getChatWorldbookName: typeof getChatWorldbookName;
    readonly rebindChatWorldbook: typeof rebindChatWorldbook;
    readonly getOrCreateChatWorldbook: typeof getOrCreateChatWorldbook;
    readonly createWorldbook: typeof createWorldbook;
    readonly createOrReplaceWorldbook: typeof createOrReplaceWorldbook;
    readonly deleteWorldbook: typeof deleteWorldbook;
    readonly getWorldbook: typeof getWorldbook;
    readonly replaceWorldbook: typeof replaceWorldbook;
    readonly updateWorldbookWith: typeof updateWorldbookWith;
    readonly createWorldbookEntries: typeof createWorldbookEntries;
    readonly deleteWorldbookEntries: typeof deleteWorldbookEntries;
  };
}
type InjectionPrompt = {
  id: string;
  /**
   * 要注入的位置
   * - 'in_chat': 插入到聊天中
   * - 'none': 不会发给 AI, 但能用来激活世界书条目.
   */
  position: 'in_chat' | 'none';
  depth: number;

  role: 'system' | 'assistant' | 'user';
  content: string;

  /** 提示词在什么情况下启用; 默认为始终 */
  filter?: (() => boolean) | (() => Promise<boolean>);
  /** 是否作为欲扫描文本, 加入世界书绿灯条目扫描文本中; 默认为任意 */
  should_scan?: boolean;
};

type injectPromptsOptions = {
  /** 是否只在下一次请求生成中有效; 默认为 false */
  once?: boolean;
};

/**
 * 注入提示词
 *
 * 这样注入的提示词仅在当前聊天文件中有效,
 * - 如果需要跨聊天文件注入或在新开聊天时重新注入, 你可以监听 `tavern_events.CHAT_CHANGED` 事件.
 * - 或者, 可以监听 `tavern_events.GENERATION_AFTER_COMMANDS` 事件, 在生成前注入.
 *
 * @param prompts 要注入的提示词
 * @param options 可选选项
 *   - `once:boolean`: 是否只在下一次请求生成中有效; 默认为 false
 *
 * @returns 后续操作
 *   - `uninject`: 取消这个提示词的注入
 */
declare function injectPrompts(prompts: InjectionPrompt[], options?: injectPromptsOptions): { uninject: () => void };

/**
 * 移除注入的提示词
 *
 * @param ids 要移除的提示词的 id 列表
 */
declare function uninjectPrompts(ids: string[]): void;
/** @deprecated 请使用内置库 "世界书强制用推荐的全局设置" */
type LorebookSettings = {
  selected_global_lorebooks: string[];
  scan_depth: number;
  context_percentage: number;
  budget_cap: number;
  min_activations: number;
  max_depth: number;
  max_recursion_steps: number;
  insertion_strategy: 'evenly' | 'character_first' | 'global_first';
  include_names: boolean;
  recursive: boolean;
  case_sensitive: boolean;
  match_whole_words: boolean;
  use_group_scoring: boolean;
  overflow_alert: boolean;
}

/** @deprecated 请使用内置库 "世界书强制用推荐的全局设置" */
declare function getLorebookSettings(): LorebookSettings;
/** @deprecated 请使用内置库 "世界书强制用推荐的全局设置" */
declare function setLorebookSettings(settings: Partial<LorebookSettings>): void;

/** @deprecated 请使用 `getWorldbookNames` */
declare function getLorebooks(): string[];

/** @deprecated 请使用 `deleteWorldbook` */
declare function deleteLorebook(lorebook: string): Promise<boolean>;

/** @deprecated 请使用 `createWorldbook` */
declare function createLorebook(lorebook: string): Promise<boolean>;

/** @deprecated 请使用 `getCharWorldbookNames` */
type CharLorebooks = {
  primary: string | null;
  additional: string[];
}

/** @deprecated 请使用 `getCharWorldbookNames` */
type GetCharLorebooksOption = {
  name?: string;
  type?: 'all' | 'primary' | 'additional';
}

/** @deprecated 请使用 `getCharWorldbookNames` */
declare function getCharLorebooks({ name, type }?: GetCharLorebooksOption): CharLorebooks;

/** @deprecated 请使用 `getCharWorldbookNames` */
declare function getCurrentCharPrimaryLorebook(): string | null;

/** @deprecated 请使用 `rebindCharWorldbook` */
declare function setCurrentCharLorebooks(lorebooks: Partial<CharLorebooks>): Promise<void>;

/** @deprecated 请使用 `getChatWorldbook` */
declare function getChatLorebook(): string | null;

/** @deprecated 请使用 `rebindChatWorldbook` */
declare function setChatLorebook(lorebook: string | null): Promise<void>;

/** @deprecated 请使用 `getOrCreateChatWorldbook` */
declare function getOrCreateChatLorebook(lorebook?: string): Promise<string>;
/** @deprecated 请使用 `WolrdbookEntry` */
type LorebookEntry = {
  uid: number;
  display_index: number;
  comment: string;
  enabled: boolean;
  type: 'constant' | 'selective' | 'vectorized';
  position:
    | 'before_character_definition'
    | 'after_character_definition'
    | 'before_example_messages'
    | 'after_example_messages'
    | 'before_author_note'
    | 'after_author_note'
    | 'at_depth_as_system'
    | 'at_depth_as_assistant'
    | 'at_depth_as_user';
  depth: number | null;
  order: number;
  probability: number;
  keys: string[];
  logic: 'and_any' | 'and_all' | 'not_all' | 'not_any';
  filters: string[];
  scan_depth: 'same_as_global' | number;
  case_sensitive: 'same_as_global' | boolean;
  match_whole_words: 'same_as_global' | boolean;
  use_group_scoring: 'same_as_global' | boolean;
  automation_id: string | null;
  exclude_recursion: boolean;
  prevent_recursion: boolean;
  delay_until_recursion: boolean | number;
  content: string;
  group: string;
  group_prioritized: boolean;
  group_weight: number;
  sticky: number | null;
  cooldown: number | null;
  delay: number | null;
};

/** @deprecated 请使用 `getWorldbook` */
type GetLorebookEntriesOption = {
  filter?: 'none' | Partial<LorebookEntry>;
};

/** @deprecated 请使用 `getWorldbook` */
declare function getLorebookEntries(lorebook: string): Promise<LorebookEntry[]>;

/** @deprecated 请使用 `replaceWorldbook` */
declare function replaceLorebookEntries(lorebook: string, entries: Partial<LorebookEntry>[]): Promise<void>;

/** @deprecated 请使用 `updateWorldbookWith` */
type LorebookEntriesUpdater =
  | ((entries: LorebookEntry[]) => Partial<LorebookEntry>[])
  | ((entries: LorebookEntry[]) => Promise<Partial<LorebookEntry>[]>);

/** @deprecated 请使用 `updateWorldbookWith` */
declare function updateLorebookEntriesWith(lorebook: string, updater: LorebookEntriesUpdater): Promise<LorebookEntry[]>;

/** @deprecated 请使用 `replaceWorldbook` */
declare function setLorebookEntries(
  lorebook: string,
  entries: Array<Pick<LorebookEntry, 'uid'> & Partial<LorebookEntry>>,
): Promise<LorebookEntry[]>;

/** @deprecated 请使用 `createWorldbookEntries` */
declare function createLorebookEntries(
  lorebook: string,
  entries: Partial<LorebookEntry>[],
): Promise<{ entries: LorebookEntry[]; new_uids: number[] }>;

/** @deprecated 请使用 `deleteWorldbookEntries` */
declare function deleteLorebookEntries(
  lorebook: string,
  uids: number[],
): Promise<{ entries: LorebookEntry[]; delete_occurred: boolean }>;
type MacroLikeContext = {
  message_id?: number;
  role?: 'user' | 'assistant' | 'system';
};

type RegisterMacroLikeReturn = {
  /** 取消注册 */
  unregister: () => void;
};

/**
 * 注册一个新的助手宏
 *
 * @param regex 匹配的正则表达式
 * @param replace 针对匹配到的文本所要进行的替换
 *
 * @example
 * // 注册一个统计行数的宏
 * registerMacros(
 *   /<count_lines>(.*?)<count_lines>/gi,
 *   context => content.split('\n').length
 * );
 *
 * @returns 后续操作
 *   - `unregister`: 取消注册
 */
declare function registerMacroLike(
  regex: RegExp,
  replace: (context: MacroLikeContext, substring: string, ...args: any[]) => string,
): RegisterMacroLikeReturn;

/**
 * 取消注册一个助手宏
 *
 * @param regex 助手宏的正则表达式
 */
declare function unregisterMacroLike(regex: RegExp): void;
type PersonaConnection = {
  type: 'character' | 'group';
  id: string;
};

type Persona = {
  avatar_id: string;
  avatar: `${string}.png` | Blob;
  name: string;
  title: string;
  description: string;
  position: number;
  depth: number;
  role: number;
  lorebook: string;
  connections: PersonaConnection[];
  is_default: boolean;
};

type ReplacePersonaOptions = {
  /** 酒馆网页应该防抖渲染 persona 管理列表 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为防抖渲染 */
  render?: 'debounced' | 'immediate' | 'none';
};

/**
 * 获取 persona 名称列表
 *
 * @returns persona 名称列表
 */
declare function getPersonaNames(): string[];

/**
 * 获取 persona 头像 id 列表
 *
 * @returns persona 头像 id 列表
 */
declare function getPersonaIds(): string[];

/**
 * 获取当前 persona 名称
 *
 * @returns 当前 persona 名称, 如果当前没有 persona, 则返回 `null`
 */
declare function getCurrentPersonaName(): string | null;

/**
 * 获取当前 persona 头像 id
 *
 * @returns 当前 persona 头像 id, 如果当前没有 persona, 则返回 `null`
 */
declare function getCurrentPersonaId(): string | null;

/**
 * 获取 persona 的头像路径
 *
 * @param persona_id persona 名称、头像 id 或 `'current'`
 *
 * @returns persona 头像路径, 如果 persona 不存在或名称不唯一, 则返回 `null`
 */
declare function getPersonaAvatarPath(persona_id?: TypeFest.LiteralUnion<'current', string>): string | null;

/**
 * 获取 persona 的内容
 *
 * @param persona_id persona 名称、头像 id 或 `'current'`
 *
 * @returns persona 内容
 *
 * @throws 如果 persona 不存在或名称不唯一, 将会抛出异常
 */
declare function getPersona(persona_id: TypeFest.LiteralUnion<'current', string>): Persona;

/**
 * 新建 persona
 *
 * @param persona_name persona 名称
 * @param persona persona 数据; 不填则使用默认头像和默认描述设置
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 酒馆网页应该防抖渲染、立即渲染还是不刷新前端显示
 *
 * @returns 是否成功创建, 如果已经存在同名 persona、同头像 id persona 或尝试创建名为 `'current'` 的 persona 会失败
 *
 * @throws 如果访问后端失败, 将会抛出异常
 */
declare function createPersona(
  persona_name: Exclude<string, 'current'>,
  persona?: TypeFest.PartialDeep<Persona>,
  options?: ReplacePersonaOptions,
): Promise<boolean>;

/**
 * 创建或替换名为 `persona_name` 的 persona
 *
 * @param persona_name persona 名称或头像 id
 * @param persona persona 数据; 不填则使用默认头像和默认描述设置
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 酒馆网页应该防抖渲染、立即渲染还是不刷新前端显示
 *
 * @returns 如果发生创建, 则返回 `true`; 如果发生替换, 则返回 `false`
 *
 * @throws 如果 persona 名称不唯一, 将会抛出异常
 * @throws 如果访问后端失败, 将会抛出异常
 */
declare function createOrReplacePersona(
  persona_name: Exclude<string, 'current'>,
  persona?: TypeFest.PartialDeep<Persona>,
  options?: ReplacePersonaOptions,
): Promise<boolean>;

/**
 * 删除 persona
 *
 * @param persona_id persona 名称、头像 id 或 `'current'`
 *
 * @returns 是否成功删除, 可能因 persona 不存在或名称不唯一等原因而失败
 *
 * @throws 如果访问后端失败, 将会抛出异常
 */
declare function deletePersona(persona_id: TypeFest.LiteralUnion<'current', string>): Promise<boolean>;

/**
 * 完全替换 persona 的内容
 *
 * @param persona_id persona 名称、头像 id 或 `'current'`
 * @param persona persona 数据
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 酒馆网页应该防抖渲染、立即渲染还是不刷新前端显示
 *
 * @throws 如果 persona 不存在或名称不唯一, 将会抛出异常
 * @throws 如果访问后端失败, 将会抛出异常
 *
 * @example
 * const persona = getPersona('玩家');
 * persona.description = '新的玩家描述';
 * await replacePersona('玩家', persona);
 */
declare function replacePersona(
  persona_id: TypeFest.LiteralUnion<'current', string>,
  persona: TypeFest.PartialDeep<Persona>,
  options?: ReplacePersonaOptions,
): Promise<void>;

type PersonaUpdater = ((persona: Persona) => Persona) | ((persona: Persona) => Promise<Persona>);

/**
 * 用 `updater` 函数更新 persona
 *
 * @param persona_id persona 名称、头像 id 或 `'current'`
 * @param updater 用于更新 persona 的函数. 它应该接收 persona 内容作为参数, 并返回更新后的 persona 内容.
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 酒馆网页应该防抖渲染、立即渲染还是不刷新前端显示
 *
 * @returns 更新后的 persona 内容
 *
 * @throws 如果 persona 不存在或名称不唯一, 将会抛出异常
 * @throws 如果访问后端失败, 将会抛出异常
 *
 * @example
 * await updatePersonaWith('玩家', persona => {
 *   persona.title = '旁白显示标题';
 *   return persona;
 * });
 */
declare function updatePersonaWith(
  persona_id: TypeFest.LiteralUnion<'current', string>,
  updater: PersonaUpdater,
  options?: ReplacePersonaOptions,
): Promise<Persona>;
type Preset = {
  settings: {
    /** 最大上下文 token 数 */
    max_context: number;
    /** 最大回复 token 数 */
    max_completion_tokens: number;
    /** 每次生成几个回复 */
    reply_count: number;

    /** 是否流式传输 */
    should_stream: boolean;

    /** 温度 */
    temperature: number;
    /** 频率惩罚 */
    frequency_penalty: number;
    /** 存在惩罚 */
    presence_penalty: number;
    top_p: number;
    /** 重复惩罚 */
    repetition_penalty: number;
    min_p: number;
    top_k: number;
    top_a: number;

    /** 种子, -1 表示随机 */
    seed: number;

    /** 压缩系统消息: 将连续的系统消息合并为一条消息 */
    squash_system_messages: boolean;

    /** 推理强度, 即内置思维链的投入程度. 例如, 如果酒馆直连 gemini-2.5-flash, 则 `min` 将会不使用内置思维链 */
    reasoning_effort: 'auto' | 'min' | 'low' | 'medium' | 'high' | 'max';
    /** 请求思维链: 允许模型返回内置思维链的思考过程; 注意这只影响内置思维链显不显示, 不决定模型是否使用内置思维链 */
    request_thoughts: boolean;
    /** 请求图片: 允许模型在回复中返回图片 */
    request_images: boolean;
    /** 启用函数调用: 允许模型使用函数调用功能; 比如 cursor 借此在回复中读写文件、运行命令 */
    enable_function_calling: boolean;
    /** 启用网络搜索: 允许模型使用网络搜索功能 */
    enable_web_search: boolean;

    /** 是否允许发送图片作为提示词 */
    allow_sending_images: 'disabled' | 'auto' | 'low' | 'high';
    /** 是否允许发送视频作为提示词 */
    allow_sending_videos: boolean;

    /**
     * 角色名称前缀: 是否要为消息添加角色名称前缀, 以及怎么添加
     * - `none`: 不添加
     * - `default`: 为与角色卡不同名的消息添加角色名称前缀, 添加到 `content` 字段开头 (即发送的消息内容是 `角色名: 消息内容`)
     * - `content`: 为所有消息添加角色名称前缀, 添加到 `content` 字段开头 (即发送的消息内容是 `角色名: 消息内容`)
     * - `completion`: 在发送给模型时, 将角色名称写入到 `name` 字段; 仅支持字母数字和下划线, 不适用于 Claude、Google 等模型
     */
    character_name_prefix: 'none' | 'default' | 'content' | 'completion';
    /** 用引号包裹用户消息: 在发送给模型之前, 将所有用户消息用引号包裹 */
    wrap_user_messages_in_quotes: boolean;
  };

  /** 提示词列表里已经添加的提示词 */
  prompts: PresetPrompt[];
  /** 下拉框里的, 没有添加进提示词列表的提示词 */
  prompts_unused: PresetPrompt[];

  /** 额外字段, 用于为预设绑定额外数据 */
  extensions: {
    regex_scripts?: TavernRegex[];
    tavern_helper?: {
      scripts: Record<string, any>[];
      variales: Record<string, any>;
    };
    [other: string]: any;
  };
};

type PresetPrompt = {
  /**
   * 根据 id, 预设提示词分为以下三类:
   * - 普通提示词 (`isPresetNormalPrompt`): 预设界面上可以手动添加的提示词
   * - 系统提示词 (`isPresetSystemPrompt`): 酒馆所设置的系统提示词, 但其实相比于手动添加的提示词没有任何优势, 分为 `main`、`nsfw`、`jailbreak`、`enhance_definitions`
   * - 占位符提示词 (`isPresetPlaceholderPrompt`): 用于表示世界书条目、角色卡、玩家角色、聊天记录等提示词的插入位置, 分为 `world_info_before`、`persona_description`、`char_description`、`char_personality`、`scenario`、`world_info_after`、`dialogue_examples`、`chat_history`
   */
  id: TypeFest.LiteralUnion<
    | 'main'
    | 'nsfw'
    | 'jailbreak'
    | 'enhanceDefinitions'
    | 'worldInfoBefore'
    | 'personaDescription'
    | 'charDescription'
    | 'charPersonality'
    | 'scenario'
    | 'worldInfoAfter'
    | 'dialogueExamples'
    | 'chatHistory',
    string
  >;
  name: string;
  enabled: boolean;

  /**
   * 插入位置, 仅用于普通和占位符提示词
   *   - `'relative'`: 按提示词相对位置插入
   *   - `'in_chat'`: 插入到聊天记录的对应深度, 需要设置对应的深度 `depth` 和顺序 `order`
   */
  position?:
    | {
        type: 'relative';
        depth?: never;
        order?: never;
      }
    | { type: 'in_chat'; depth: number; order: number };
  role: 'system' | 'user' | 'assistant';
  /** 仅用于普通和系统提示词 */
  content?: string;

  /** 额外字段, 用于为预设提示词绑定额外数据 */
  extra?: Record<string, any>;
};
type PresetNormalPrompt = TypeFest.SetRequired<{ id: string } & Omit<PresetPrompt, 'id'>, 'position' | 'content'>;
type PresetSystemPrompt = TypeFest.SetRequired<
  { id: 'main' | 'nsfw' | 'jailbreak' | 'enhanceDefinitions' } & Omit<PresetPrompt, 'id' | 'position'>,
  'content'
>;
type PresetPlaceholderPrompt = TypeFest.SetRequired<
  {
    id:
      | 'worldInfoBefore'
      | 'personaDescription'
      | 'charDescription'
      | 'charPersonality'
      | 'scenario'
      | 'worldInfoAfter'
      | 'dialogueExamples'
      | 'chatHistory';
  } & Omit<PresetPrompt, 'id' | 'content'>,
  'position'
>;
declare function isPresetNormalPrompt(prompt: PresetPrompt): prompt is PresetNormalPrompt;
declare function isPresetSystemPrompt(prompt: PresetPrompt): prompt is PresetSystemPrompt;
declare function isPresetPlaceholderPrompt(prompt: PresetPrompt): prompt is PresetPlaceholderPrompt;

declare const default_preset: Preset;

/**
 * 获取预设名称列表
 *
 * @returns 预设名称列表
 */
declare function getPresetNames(): string[];

/**
 * 获取酒馆正在使用的预设 (`'in_use'`) 是从哪个预设加载来的.
 *
 * 请务必注意这个说法, `'in_use'` 预设虽然是从 `getLoadedPresetName()` 预设加载而来, 但它的预设内容可能与 `getLoadedPresetName()` 预设不同.
 *   请回忆一下: 在酒馆中编辑预设后, 编辑结果会立即在聊天中生效 (`'in_use'` 预设被更改),
 *   但我们没有点击保存按钮 (将 `'in_use'` 预设内容保存回 `getLoadedPresetName()` 预设), 一旦切换预设, 编辑结果就会丢失.
 *
 * @returns 预设名称
 */
declare function getLoadedPresetName(): string;

/**
 * 加载 `preset_name` 预设作为酒馆正在使用的预设 (`'in_use'`)
 *
 * @param preset_name 预设名称
 * @returns 是否成功切换, 可能因预设不存在等原因而失败
 */
declare function loadPreset(preset_name: Exclude<string, 'in_use'>): boolean;

/**
 * 获取 `preset_name` 预设的内容
 *
 * @param preset_name 预设名称
 *
 * @returns 预设内容
 *
 * @throws 如果预设不存在, 将会抛出异常
 */
declare function getPreset(preset_name: TypeFest.LiteralUnion<'in_use', string>): Preset;

/**
 * 新建 `preset_name` 预设, 内容为 `preset`
 *
 * @param preset_name 预设名称
 * @param preset 预设内容; 不填则使用默认内容
 *
 * @returns 是否成功创建, 如果已经存在同名预设或尝试创建名为 `'in_use'` 的预设会失败
 *
 * @throws 如果创建的预设内容中存在重复的系统/占位提示词, 将会抛出异常
 */
declare function createPreset(preset_name: Exclude<string, 'in_use'>, preset?: Preset): Promise<boolean>;

/**
 * 创建或替换名为 `preset_name` 的预设, 内容为 `preset`
 *
 * @param preset_name 预设名称
 * @param preset 预设内容; 不填则使用默认内容
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 如果对 `'in_use'` 预设进行操作, 应该防抖重新渲染 (debounced) 还是立即重新渲染 (immediate) 预设界面? 默认为性能更好的防抖渲染
 *
 * @returns 如果发生创建, 则返回 `true`; 如果发生替换, 则返回 `false`
 */
declare function createOrReplacePreset(
  preset_name: TypeFest.LiteralUnion<'in_use', string>,
  preset?: Preset,
  { render }?: ReplacePresetOptions,
): Promise<boolean>;

/**
 * 删除 `preset_name` 预设
 *
 * @param preset_name 预设名称
 *
 * @returns 是否成功删除, 可能因预设不存在等原因而失败
 */
declare function deletePreset(preset_name: Exclude<string, 'in_use'>): Promise<boolean>;

/**
 * 重命名 `preset_name` 预设为 `new_name`
 *
 * @param preset_name 预设名称
 * @param new_name 新名称
 *
 * @returns 是否成功重命名, 可能因预设不存在等原因而失败
 */
declare function renamePreset(preset_name: Exclude<string, 'in_use'>, new_name: string): Promise<boolean>;

type ReplacePresetOptions = {
  /** 如果对 `'in_use'` 预设进行操作, 应该防抖渲染 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为性能更好的防抖渲染 */
  render?: 'debounced' | 'immediate' | 'none';
};
/**
 * 完全替换 `preset_name` 预设的内容为 `preset`
 *
 * @param preset_name 预设名称
 * @param preset 预设内容
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 如果对 `'in_use'` 预设进行操作, 应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @throws 如果预设不存在, 将会抛出异常
 * @throws 如果替换的预设内容中存在重复的系统/占位提示词, 将会抛出异常
 *
 * @example
 * // 为酒馆正在使用的预设开启流式传输
 * const preset = getPreset('in_use');
 * preset.settings.should_stream = true;
 * await replacePreset('in_use', preset);
 *
 * @example
 * // 关闭酒馆正在使用的预设中名字包含 "COT" 的条目
 * const preset = getPreset('in_use');
 * preset.prompts.filter(prompt => prompt.name.includes('COT')).forEach(prompt => prompt.enabled = false);
 * await replacePreset('in_use', preset);
 *
 * @example
 * // 为酒馆正在使用的预设添加一个提示词条目
 * const preset = getPreset('in_use');
 * preset.prompts.push({
 *   id: 'new_prompt',
 *   name: '新提示词',
 *   enabled: true,
 *   position: { type: 'relative' },
 *   role: 'user',
 *   content: '新提示词内容',
 * });
 * await replacePreset('in_use', preset);
 *
 * @example
 * // 将 '预设A' 的条目按顺序复制到 '预设B' 开头
 * const preset_a = getPreset('预设A');
 * const preset_b = getPreset('预设B');
 * preset_b.prompts = [...preset_a.prompts, ...preset_b.prompts];
 * await replacePreset('预设B', preset_b);
 */
declare function replacePreset(
  preset_name: TypeFest.LiteralUnion<'in_use', string>,
  preset: Preset,
  { render }?: ReplacePresetOptions,
): Promise<void>;

type PresetUpdater = ((preset: Preset) => Preset) | ((preset: Preset) => Promise<Preset>);
/**
 * 用 `updater` 函数更新 `preset_name` 预设
 *
 * @param preset_name 预设名称
 * @param updater 用于更新预设的函数. 它应该接收预设内容作为参数, 并返回更新后的预设内容.
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 如果对 `'in_use'` 预设进行操作, 应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @returns 更新后的预设内容
 *
 * @throws 如果预设不存在, 将会抛出异常
 * @throws 如果替换的预设内容中存在重复的系统/占位提示词, 将会抛出异常
 *
 * @example
 * // 为酒馆正在使用的预设开启流式传输
 * await updatePresetWith('in_use', preset => {
 *   preset.settings.should_stream = true;
 *   return preset;
 * });
 *
 * @example
 * // 关闭酒馆正在使用的预设中名字包含 "COT" 的条目
 * await updatePresetWith('in_use', preset => {
 *   preset.prompts.filter(prompt => prompt.name.includes('COT')).forEach(prompt => prompt.enabled = false);
 *   return preset;
 * });
 *
 * @example
 * // 为酒馆正在使用的预设添加一个提示词条目
 * await updatePresetWith('in_use', preset => {
 *   preset.prompts.push({
 *     id: 'new_prompt',
 *     name: '新提示词',
 *     enabled: true,
 *     position: { type: 'relative' },
 *     role: 'user',
 *     content: '新提示词内容',
 *   });
 *   return preset;
 * });
 *
 * @example
 * // 将 '预设A' 的条目按顺序复制到 '预设B' 开头
 * await updatePresetWith('预设B', preset => {
 *   const another_preset = getPreset('预设A');
 *   preset.prompts = [...another_preset.prompts, ...preset.prompts];
 *   return preset;
 * });
 */
declare function updatePresetWith(
  preset_name: TypeFest.LiteralUnion<'in_use', string>,
  updater: PresetUpdater,
  { render }?: ReplacePresetOptions,
): Promise<Preset>;

/**
 * 将预设内容修改回预设中, 如果某个内容不存在, 则该内容将会采用原来的值
 *
 * @param preset_name 预设名称
 * @param preset 预设内容
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 如果对 `'in_use'` 预设进行操作, 应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @returns 更新后的预设内容
 *
 * @throws 如果预设不存在, 将会抛出异常
 * @throws 如果替换的预设内容中存在重复的系统/占位提示词, 将会抛出异常
 *
 * @example
 * // 为酒馆正在使用的预设开启流式传输
 * await setPreset('in_use', { settings: { should_stream: true } });
 *
 * @example
 * // 将 '预设A' 的条目按顺序复制到 '预设B' 开头
 * await setPreset('预设B', {
 *   prompts: [...getPreset('预设A').prompts, ...getPreset('预设B').prompts],
 * });
 */
declare function setPreset(
  preset_name: TypeFest.LiteralUnion<'in_use', string>,
  preset: TypeFest.PartialDeep<Preset>,
  { render }?: ReplacePresetOptions,
): Promise<Preset>;
/**
 * 角色卡管理类
 * 用于封装角色卡数据操作和提供便捷的访问方法
 */
declare class RawCharacter {
  constructor(characterData: SillyTavern.v1CharData);

  /**
   * 根据名称或头像id查找角色卡数据
   * @param options 查找选项
   * @returns 找到的角色卡数据，找不到为null
   */
  static find({
    name,
    allowAvatar,
  }?: {
    name: TypeFest.LiteralUnion<'current', string>;
    allowAvatar?: boolean;
  }): SillyTavern.v1CharData;

  /**
   * 根据名称查找角色卡数据在characters数组中的索引（类似this_chid）
   * @param name 角色名称
   * @returns 角色卡数据在characters数组中的索引，未找到返回-1
   */
  static findCharacterIndex(name: string): any;

  /**
   * 从服务器获取每个聊天文件的聊天内容，并将其编译成字典。
   * 该函数遍历提供的聊天元数据列表，并请求每个聊天的实际聊天内容，
   *
   * @param {Array} data - 包含每个聊天的元数据的数组，例如文件名。
   * @param {boolean} isGroupChat - 一个标志，指示聊天是否为群组聊天。
   * @returns {Promise<Object>} chat_dict - 一个字典，其中每个键是文件名，值是
   * 从服务器获取的相应聊天内容。
   */
  static getChatsFromFiles(data: any[], isGroupChat: boolean): Promise<Record<string, any>>;

  /**
   * 获取角色管理内的数据
   * @returns 完整的角色管理内的数据对象
   */
  getCardData(): SillyTavern.v1CharData;

  /**
   * 获取角色头像ID
   * @returns 头像ID/文件名
   */
  getAvatarId(): string;

  /**
   * 获取正则脚本
   * @returns 正则脚本数组
   */
  getRegexScripts(): Array<{
    id: string;
    scriptName: string;
    findRegex: string;
    replaceString: string;
    trimStrings: string[];
    placement: number[];
    disabled: boolean;
    markdownOnly: boolean;
    promptOnly: boolean;
    runOnEdit: boolean;
    substituteRegex: number | boolean;
    minDepth: number;
    maxDepth: number;
  }>;

  /**
   * 获取角色书
   * @returns 角色书数据对象或null
   */
  getCharacterBook(): {
    name: string;
    entries: Array<{
      keys: string[];
      secondary_keys?: string[];
      comment: string;
      content: string;
      constant: boolean;
      selective: boolean;
      insertion_order: number;
      enabled: boolean;
      position: string;
      extensions: any;
      id: number;
    }>;
  } | null;

  /**
   * 获取角色世界名称
   * @returns 世界名称
   */
  getWorldName(): string;
}

/**
 * 获取角色卡数据
 * @param name 角色名称或头像ID
 * @param allowAvatar 是否允许通过头像ID查找
 * @returns 角色卡数据
 */
declare function getCharData(name: TypeFest.LiteralUnion<'current', string>): SillyTavern.v1CharData | null;

/**
 * 获取角色头像路径
 * @param name 角色名称或头像ID
 * @param allowAvatar 是否允许通过头像ID查找
 * @returns 角色头像路径
 */
declare function getCharAvatarPath(name: TypeFest.LiteralUnion<'current', string>): string | null;

/**
 * 获取角色聊天历史摘要
 * @param name 角色名称或头像ID
 * @param allowAvatar 是否允许通过头像ID查找
 * @returns 聊天历史摘要数组
 */
declare function getChatHistoryBrief(
  name: TypeFest.LiteralUnion<'current', string>,
  allowAvatar?: boolean,
): Promise<any[] | null>;

/**
 * 获取聊天历史详情
 * @param data 聊天数据数组
 * @param isGroupChat 是否为群组聊天
 * @returns 聊天历史详情
 */
declare function getChatHistoryDetail(data: any[], isGroupChat?: boolean): Promise<Record<string, any> | null>;
/**
 * 获取所有处于启用状态的酒馆助手脚本按钮, 主要是方便 QR 助手等兼容脚本按钮
 */
declare function getAllEnabledScriptButtons(): { [script_id: string]: { button_id: string; button_name: string }[] };

type ScriptButton = {
  name: string;
  visible: boolean;
};

type Script = {
  type: 'script';
  enabled: boolean;
  name: string;
  id: string;
  content: string;
  info: string;
  button: {
    enabled: boolean;
    buttons: Array<ScriptButton>;
  };
  data: Record<string, any>;
  export_with: {
    data: boolean;
    button: boolean;
  };
};
type ScriptFolder = {
  type: 'folder';
  enabled: boolean;
  name: string;
  id: string;
  icon: string;
  color: string;
  scripts: Script[];
};
type ScriptTree = Script | ScriptFolder;

type ScriptTreesOptions = {
  /** 对全局脚本 (`'chat'`)、当前预设脚本 (`'preset'`) 或当前角色卡脚本 (`'global'`) 进行操作 */
  type: 'global' | 'preset' | 'character';
};

/**
 * 获取酒馆助手脚本列表
 *
 * @param option 要操作的酒馆助手脚本类型
 *
 * @returns 酒馆助手脚本列表
 */
declare function getScriptTrees(option: ScriptTreesOptions): ScriptTree[];

/**
 * 完全替换酒馆助手列表为 `script_trees`
 *
 * @param script_trees 要用于替换的酒馆助手列表
 * @param option 要操作的酒馆助手脚本类型
 */
declare function replaceScriptTrees(script_trees: TypeFest.PartialDeep<ScriptTree>[], option: ScriptTreesOptions): void;

/**
 * 用 `updater` 函数更新酒馆助手列表
 *
 * @param updater 用于更新酒馆助手列表的函数. 它应该接收酒馆助手列表作为参数, 并返回更新后的酒馆助手列表.
 * @param option 要操作的酒馆助手脚本类型
 *
 * @returns 更新后的酒馆助手列表
 */
declare function updateScriptTreesWith(
  updater: (script_trees: ScriptTree[]) => TypeFest.PartialDeep<ScriptTree>[],
  option: ScriptTreesOptions,
): ScriptTree[];

/**
 * 用 `updater` 函数更新酒馆助手列表
 *
 * @param updater 用于更新酒馆助手列表的函数. 它应该接收酒馆助手列表作为参数, 并返回更新后的酒馆助手列表.
 * @param option 要操作的酒馆助手脚本类型
 *
 * @returns 更新后的酒馆助手列表
 */
declare function updateScriptTreesWith(
  updater: (script_trees: ScriptTree[]) => Promise<TypeFest.PartialDeep<ScriptTree>[]>,
  option: ScriptTreesOptions,
): Promise<ScriptTree[]>;
/**
 * 运行 Slash 命令, 注意如果命令写错了将不会有任何反馈
 *
 * 能使用的命令请参考[编写模板](https://stagedog.github.io/青空莉/工具经验/实时编写前端界面或脚本/)的 `slash_command.txt` 或[命令手册](https://rentry.org/sillytavern-script-book).
 *
 * @param command 要运行的 Slash 命令
 * @returns Slash 管道结果, 如果命令出错或执行了 `/abort` 则返回 `undefined`
 *
 * @throws Slash 命令出错时, 将会抛出错误
 *
 * @example
 * // 在酒馆界面弹出提示语 `运行成功!`
 * triggerSlash('/echo severity=success 运行成功!');
 * // 但更建议你直接用 toastr 弹出提示
 * toastr.success('运行成功!');
 *
 * @example
 * // 获取当前聊天消息最后一条消息对应的 id
 * const last_message_id = await triggerSlash('/pass {{lastMessageId}}');
 * // 但更建议你用酒馆助手函数
 * const last_message = getLastMessageId();
 *
 * @example
 * // 创建一条用户输入到消息楼层末尾
 * await createChatMessages([{ role: 'user', content: '你好' }]);
 * // 触发 AI 回复
 * await triggerSlash('/trigger');
 */
declare function triggerSlash(command: string): Promise<string>;
type FormatAsTavernRegexedStringOption = {
  /** 文本所在的深度; 不填则不考虑酒馆正则的`深度`选项: 无论该深度是否在酒馆正则的`最小深度`和`最大深度`范围内都生效 */
  depth?: number;
  /** 角色卡名称; 不填则使用当前角色卡名称 */
  character_name?: string;
};

/**
 * 对 `text` 应用酒馆正则
 *
 * @param text 要应用酒馆正则的文本
 * @param source 文本来源, 例如来自用户输入或 AI 输出. 对应于酒馆正则的`作用范围`选项.
 * @param destination 文本将作为什么而使用, 例如用于显示或作为提示词. 对应于酒馆正则的`仅格式显示`和`仅格式提示词`选项.
 * @param option 可选选项
 *   - `depth?:number`: 文本所在的深度; 不填则不考虑酒馆正则的`深度`选项: 无论该深度是否在酒馆正则的`最小深度`和`最大深度`范围内都生效
 *   - `character_name?:string`: 角色卡名称; 不填则使用当前角色卡名称
 *
 * @example
 * // 获取最后一楼文本, 将它视为将会作为显示的 AI 输出, 对它应用酒馆正则
 * const message = getChatMessages(-1)[0];
 * const result = formatAsTavernRegexedString(message.message, 'ai_output', 'display', { depth: 0 });
 */
declare function formatAsTavernRegexedString(
  text: string,
  source: 'user_input' | 'ai_output' | 'slash_command' | 'world_info' | 'reasoning',
  destination: 'display' | 'prompt',
  { depth, character_name }?: FormatAsTavernRegexedStringOption,
): string;

type TavernRegex = {
  id: string;
  script_name: string;
  enabled: boolean;
  /** @deprecated 使用新 API 时不再返回此字段，仅在使用旧 scope 参数时返回 */
  scope?: 'global' | 'character';

  find_regex: string;
  replace_string: string;
  trim_strings: string[];

  source: {
    user_input: boolean;
    ai_output: boolean;
    slash_command: boolean;
    world_info: boolean;
    reasoning: boolean;
  };

  destination: {
    display: boolean;
    prompt: boolean;
  };
  run_on_edit: boolean;

  min_depth: number | null;
  max_depth: number | null;
};

/**
 * 判断局部正则是否启用
 */
declare function isCharacterTavernRegexesEnabled(): boolean;

type TavernRegexOptionGlobal = {
  /** 对全局正则 (`'global'`) 进行操作 */
  type: 'global';
};
type TavernRegexOptionCharacter = {
  /** 对角色卡局部 (`'character'`) 进行操作 */
  type: 'character';
  name?: string | 'current';
};
type TavernRegexOptionPreset = {
  /** 对预设正则 (`'preset'`) 进行操作 */
  type: 'preset';
  name?: string | 'in_use';
};
type TavernRegexOption = TavernRegexOptionGlobal | TavernRegexOptionCharacter | TavernRegexOptionPreset;

/**
 * 获取酒馆正则
 *
 * @param option 要操作的酒馆正则类型
 *
 * @returns 一个数组, 数组的元素是酒馆正则 `TavernRegex`. 该数组依据正则作用于文本的顺序排序, 也就是酒馆显示正则的地方从上到下排列.
 */
declare function getTavernRegexes(option: TavernRegexOption): TavernRegex[];

type ReplaceTavernRegexesOption = {
  scope?: 'all' | 'global' | 'character';
};

/**
 * 完全替换酒馆正则为 `regexes`.
 * - **这是一个很慢的操作!** 尽量对正则做完所有事后再一次性 replaceTavernRegexes.
 * - **为了重新应用正则, 它会重新载入整个聊天消息**, 将会触发 `tavern_events.CHAT_CHANGED` 进而重新加载楼层消息.
 *
 * 之所以提供这么直接的函数, 是因为你可能需要调换正则顺序等.
 *
 * @param regexes 要用于替换的酒馆正则
 * @param option 要操作的酒馆正则类型
 */
declare function replaceTavernRegexes(regexes: TavernRegex[], option: TavernRegexOption): Promise<void>;

type TavernRegexUpdater =
  | ((regexes: TavernRegex[]) => TavernRegex[])
  | ((regexes: TavernRegex[]) => Promise<TavernRegex[]>);

/**
 * 用 `updater` 函数更新酒馆正则
 *
 * @param updater 用于更新酒馆正则的函数. 它应该接收酒馆正则作为参数, 并返回更新后的酒馆正则.
 * @param option 要操作的酒馆正则类型
 *
 * @returns 更新后的酒馆正则
 *
 * @example
 * // 开启所有名字里带 "舞台少女" 的全局正则
 * await updateTavernRegexesWith(
 *   regexes => {
 *     regexes.forEach(regex => {
 *       if (regex.script_name.includes('舞台少女')) {
 *         regex.enabled = true;
 *       }
 *     });
 *     return regexes;
 *   },
 *   { type: 'global' },
 * );
 */
declare function updateTavernRegexesWith(
  updater: TavernRegexUpdater,
  option: TavernRegexOption,
): Promise<TavernRegex[]>;
/**
 * 替换字符串中的酒馆宏
 *
 * @param text 要替换的字符串
 * @returns 替换结果
 *
 * @example
 * const text = substitudeMacros("{{char}} speaks in {{lastMessageId}}");
 * text == "少女歌剧 speaks in 5";
 */
declare function substitudeMacros(text: string): string;

/**
 * 获取最新楼层 id
 *
 * @returns 最新楼层id
 */
declare function getLastMessageId(): number;

/**
 * 包装任意函数，返回一个会将报错消息通过酒馆通知显示出来的同功能函数
 *
 * @param fn 要包装的函数
 * @returns 包装后的函数
 *
 * @example
 * // 包装 `test` 函数从而在酒馆通知中显示 'test' 文本
 * function test() {
 *   throw Error(`test`);
 * }
 * errorCatched(test)();
 */
declare function errorCatched<T extends any[], U>(fn: (...args: T) => U): (...args: T) => U;

/**
 * 从前端界面的 iframe 标识名称 `iframe_name` 获取它所在楼层的楼层号, **只能对前端界面 iframe 标识名称使用**
 *
 * @param iframe_name 前端界面的 iframe 标识名称
 * @returns 楼层号
 *
 * @throws 如果提供的 `iframe_name` 不是前端界面 iframe 标识名称, 将会抛出错误
 */
declare function getMessageId(iframe_name: string): number;
type VariableOptionNormal = {
  /** 对聊天变量 (`'chat'`)、当前预设 (`'preset'`) 或全局变量 (`'global'`) 进行操作 */
  type: 'chat' | 'preset' | 'global';
};
type VariableOptionCharacter = {
  /**
   * 对当前角色卡 (`'character'`) 进行操作
   *
   * @throws 如果没有打开角色卡, 将会抛出错误
   */
  type: 'character';
};
type VariableOptionMessage = {
  /** 对消息楼层变量 (`message`) 进行操作 */
  type: 'message';
  /**
   * 指定要获取变量的消息楼层号, 如果为负数则为深度索引, 例如 `-1` 表示获取最新的消息楼层; 默认为 `'latest'`
   *
   * @throws 如果提供的消息楼层号 `message_id` 超出了范围 `[-chat.length, chat.length)`, 将会抛出错误
   */
  message_id?: number | 'latest';
};
type VariableOptionScript = {
  /** 对脚本变量 (`'script'`) 进行操作 */
  type: 'script';
  /** 指定要操作变量的脚本 ID; 如果在脚本内调用, 则无须指定, 当然你也可以用 `getScriptId()` 获取该脚本 ID */
  script_id?: string;
};
type VariableOptionExtension = {
  /** 对扩展变量 (`'extension'`) 进行操作 */
  type: 'extension';
  /** 指定要操作变量的扩展 ID */
  extension_id: string;
};
type VariableOption = VariableOptionNormal | VariableOptionCharacter | VariableOptionMessage | VariableOptionScript | VariableOptionExtension;

/**
 * 获取变量表
 *
 * @param option 要操作的变量类型
 *
 * @returns 变量表
 *
 * @example
 * // 获取所有聊天变量并弹窗输出结果
 * const variables = getVariables({type: 'chat'});
 * alert(variables);
 *
 * @example
 * // 获取所有全局变量
 * const variables = getVariables({type: 'global'});
 * // 酒馆助手内置了 lodash 库, 你能用它做很多事, 比如查询某个变量是否存在
 * if (_.has(variables, "神乐光.好感度")) {
 *   ...
 * }
 *
 * @example
 * // 获取倒数第二楼层的聊天变量
 * const variables = getVariables({type: 'message', message_id: -2});
 *
 * @example
 * // 在脚本内获取该脚本绑定的变量
 * const variables = getVariables({type: 'script'});
 */
declare function getVariables(option: VariableOption): Record<string, any>;

/**
 * 完全替换变量表为 `variables`
 *
 * 之所以提供这么直接的函数, 是因为酒馆助手内置了 lodash 库:
 *   `insertOrAssignVariables` 等函数其实就是先 `getVariables` 获取变量表, 用 lodash 库处理, 再 `replaceVariables` 替换变量表.
 *
 * @param variables 要用于替换的变量表
 * @param option 要操作的变量类型
 *
 * @example
 * // 执行前的聊天变量: `{爱城华恋: {好感度: 5}}`
 * replaceVariables({神乐光: {好感度: 5, 认知度: 0}});
 * // 执行后的聊天变量: `{神乐光: {好感度: 5, 认知度: 0}}`
 *
 * @example
 * // 删除 `{神乐光: {好感度: 5}}` 变量
 * let variables = getVariables();
 * _.unset(variables, "神乐光.好感度");
 * replaceVariables(variables);
 *
 * @example
 * // 在脚本内替换该脚本绑定的变量
 * replaceVariables({神乐光: {好感度: 5, 认知度: 0}}, {type: 'script'});
 */
declare function replaceVariables(variables: Record<string, any>, option: VariableOption): void;

/**
 * 用 `updater` 函数更新变量表
 *
 * @param updater 用于更新变量表的函数. 它应该接收变量表作为参数, 并返回更新后的变量表.
 * @param option 要操作的变量类型
 *
 * @returns 更新后的变量表
 *
 * @example
 * // 删除 `{神乐光: {好感度: 5}}` 变量
 * updateVariablesWith(variables => {
 *   _.unset(variables, "神乐光.好感度");
 *   return variables;
 * });
 *
 * @example
 * // 更新 "爱城华恋.好感度" 为原来的 2 倍, 如果该变量不存在则设置为 0
 * updateVariablesWith(variables => _.update(variables, "爱城华恋.好感度", value => value ? value * 2 : 0), {type: 'chat'});
 */
declare function updateVariablesWith(
  updater: (variables: Record<string, any>) => Record<string, any>,
  option: VariableOption,
): Record<string, any>;

/**
 * 用 `updater` 函数更新变量表
 *
 * @param updater 用于更新变量表的函数. 它应该接收变量表作为参数, 并返回更新后的变量表.
 * @param option 要操作的变量类型
 *
 * @returns 更新后的变量表
 *
 * @example
 * await updateVariablesWith(async variables => {await update(variables); return variables;}, {type: 'chat'});
 */
declare function updateVariablesWith(
  updater: (variables: Record<string, any>) => Promise<Record<string, any>>,
  option: VariableOption,
): Promise<Record<string, any>>;

/**
 * 插入或修改变量值, 取决于变量是否存在.
 *
 * @param variables 要更新的变量
 *   - 如果变量不存在, 则新增该变量
 *   - 如果变量已经存在, 则修改该变量的值
 * @param option 要操作的变量类型
 *
 * @returns 更新后的变量表
 *
 * @example
 * // 执行前变量: `{爱城华恋: {好感度: 5}}`
 * await insertOrAssignVariables({爱城华恋: {好感度: 10}, 神乐光: {好感度: 5, 认知度: 0}}, {type: 'chat'});
 * // 执行后变量: `{爱城华恋: {好感度: 10}, 神乐光: {好感度: 5, 认知度: 0}}`
 */
declare function insertOrAssignVariables(variables: Record<string, any>, option: VariableOption): Record<string, any>;

/**
 * 插入新变量, 如果变量已经存在则什么也不做
 *
 * @param variables 要插入的变量
 *   - 如果变量不存在, 则新增该变量
 *   - 如果变量已经存在, 则什么也不做
 * @param option 要操作的变量类型
 *
 * @returns 更新后的变量表
 *
 * @example
 * // 执行前变量: `{爱城华恋: {好感度: 5}}`
 * await insertVariables({爱城华恋: {好感度: 10}, 神乐光: {好感度: 5, 认知度: 0}}, {type: 'chat'});
 * // 执行后变量: `{爱城华恋: {好感度: 5}, 神乐光: {好感度: 5, 认知度: 0}}`
 */
declare function insertVariables(variables: Record<string, any>, option: VariableOption): Record<string, any>;

/**
 * 删除变量, 如果变量不存在则什么也不做
 *
 * @param variable_path 要删除的变量路径
 *   - 如果变量不存在, 则什么也不做
 *   - 如果变量已经存在, 则删除该变量
 * @param option 要操作的变量类型
 *
 * @returns 更新后的变量表, 以及是否成功删除变量
 *
 * @example
 * // 执行前变量: `{爱城华恋: {好感度: 5}}`
 * await deleteVariable("爱城华恋.好感度", {type: 'chat'});
 * // 执行后变量: `{爱城华恋: {}}`
 */
declare function deleteVariable(
  variable_path: string,
  option: VariableOption,
): { variables: Record<string, any>; delete_occurred: boolean };

/**
 * 为变量管理器注册一个变量结构. 注册后, 变量管理器上将会按变量结构对变量进行校验
 *
 * **这只是方便使用变量管理器这一 UI 查看和管理变量, 对于代码层面没有任何影响**
 *
 * @param schema zod 库表示的变量结构
 * @param option 要注册变量结构的变量类型
 *
 * @example
 * // 注册消息楼层变量的结构为 stat_data 内有一个 好感度 数值变量
 * registerVariableSchema(z.object({
 *   stat_data: z.object({
 *     好感度: z.number(),
 *   }),
 * }), {type: 'message'});
 */
declare function registerVariableSchema(
  schema: z.ZodType<any>,
  option: { type: 'global' | 'preset' | 'character' | 'chat' | 'message' },
): void;
/**
 * 获取酒馆助手版本号
 */
declare function getTavernHelperVersion(): string;

/**
 * 获取酒馆版本号
 */
declare function getTavernVersion(): string;
/**
 * 获取世界书名称列表
 *
 * @returns 世界书名称列表
 */
declare function getWorldbookNames(): string[];

/**
 * 获取当前全局开启的世界书名称列表
 *
 * @returns 全局世界书名称列表
 */
declare function getGlobalWorldbookNames(): string[];
/**
 * 重新绑定全局世界书
 *
 * @param worldbook_names 要全局开启的世界书
 */
declare function rebindGlobalWorldbooks(worldbook_names: string[]): Promise<void>;

type CharWorldbooks = {
  primary: string | null;
  additional: string[];
};
/**
 * 获取角色卡绑定的世界书
 *
 * @param character_name 要查询的角色卡名称, 'current' 表示当前打开的角色卡
 *
 * @returns 角色卡绑定的世界书
 */
declare function getCharWorldbookNames(character_name: TypeFest.LiteralUnion<'current' | string>): CharWorldbooks;
/**
 * 重新绑定角色卡世界书
 *
 * @param character_name 角色卡名称, 'current' 表示当前打开的角色卡
 * @param char_worldbooks 要对该角色卡绑定的世界书
 */
declare function rebindCharWorldbooks(character_name: 'current', char_worldbooks: CharWorldbooks): Promise<void>;

/**
 * 获取聊天文件绑定的世界书
 *
 * @param chat_name 聊天文件名称
 *
 * @returns 聊天文件绑定的世界书, 如果没有则为 `null`
 */
declare function getChatWorldbookName(chat_name: 'current'): string | null;
/**
 * 重新绑定聊天文件世界书
 *
 * @param character_name 聊天文件名称, 'current' 表示当前打开的聊天
 * @param char_worldbooks 要对该聊天文件绑定的世界书
 */
declare function rebindChatWorldbook(chat_name: 'current', worldbook_name: string): Promise<void>;
/**
 * 获取或新建聊天文件世界书
 *
 * @param chat_name 聊天文件名称, 'current' 表示当前打开的聊天
 * @param worldbook_name 世界书名称; 不填则根据当前时间创建
 */
declare function getOrCreateChatWorldbook(chat_name: 'current', worldbook_name?: string): Promise<string>;

type WorldbookEntry = {
  /** uid 是相对于世界书内部的, 不要跨世界书使用 */
  uid: number;
  name: string;
  enabled: boolean;

  /** 激活策略: 条目应该何时激活 */
  strategy: {
    /**
     * 激活策略类型:
     * - `'constant'`: 常量🔵, 俗称蓝灯. 只需要满足 "启用"、"激活概率%" 等别的要求即可.
     * - `'selective'`: 可选项🟢, 俗称绿灯. 除了蓝灯条件, 还需要满足 `keys` 扫描条件
     * - `'vectorized'`: 向量化🔗. 一般不使用
     */
    type: 'constant' | 'selective' | 'vectorized';
    /** 主要关键字. 绿灯条目必须在欲扫描文本中扫描到其中任意一个关键字才能激活 */
    keys: (string | RegExp)[];
    /**
     * 次要关键字. 如果次要关键字的 `keys` 数组不为空, 则条目除了在主要关键字中匹配到任意一个关键字外, 还需要满足 `logic`:
     * - `'and_any'`: 次要关键字中任意一个关键字能在欲扫描文本中匹配到
     * - `'and_all'`: 次要关键字中所有关键字都能在欲扫描文本中匹配到
     * - `'not_all'`: 次要关键字中至少有一个关键字没能在欲扫描文本中匹配到
     * - `'not_any'`: 次要关键字中所有关键字都没能欲扫描文本中匹配到
     */
    keys_secondary: { logic: 'and_any' | 'and_all' | 'not_all' | 'not_any'; keys: (string | RegExp)[] };
    /** 扫描深度: 1 为仅扫描最后一个楼层, 2 为扫描最后两个楼层, 以此类推 */
    scan_depth: 'same_as_global' | number;
  };
  /** 插入位置: 如果条目激活应该插入到什么地方 */
  position: {
    /**
     * 位置类型:
     * - `'before_character_definition'`: 角色定义之前
     * - `'after_character_definition'`: 角色定义之后
     * - `'before_example_messages'`: 示例消息之前
     * - `'after_example_messages'`: 示例消息之后
     * - `'before_author_note'`: 作者注释之前
     * - `'after_author_note'`: 作者注释之后
     * - `'at_depth'`: 插入到指定深度
     */
    type:
      | 'before_character_definition'
      | 'after_character_definition'
      | 'before_example_messages'
      | 'after_example_messages'
      | 'before_author_note'
      | 'after_author_note'
      | 'at_depth'
      | 'outlet';
    /** 该条目的消息身份, 仅位置类型为 `'at_depth'` 时有效 */
    role: 'system' | 'assistant' | 'user';
    /** 该条目要插入的深度, 仅位置类型为 `'at_depth'` 时有效 */
    depth: number;
    // TODO: 世界书条目的插入: 文档链接
    order: number;
  };

  content: string;

  probability: number;
  /** 递归表示某世界书条目被激活后, 该条目的提示词又激活了其他条目 */
  recursion: {
    /** 禁止其他条目递归激活本条目 */
    prevent_incoming: boolean;
    /** 禁止本条目递归激活其他条目 */
    prevent_outgoing: boolean;
    /** 延迟到第 n 级递归检查时才能激活本条目 */
    delay_until: null | number;
  };
  effect: {
    /** 黏性: 条目激活后, 在之后 n 条消息内始终激活, 无视激活策略、激活概率% */
    sticky: null | number;
    /** 冷却: 条目激活后, 在之后 n 条消息内不能再激活 */
    cooldown: null | number;
    /** 延迟: 聊天中至少有 n 楼消息时, 才能激活条目 */
    delay: null | number;
  };

  /** 额外字段, 用于为世界书条目绑定额外数据 */
  extra?: Record<string, any>;
};

/**
 * 获取 `worldbook_name` 世界书的内容
 *
 * @param worldbook_name 世界书名称
 *
 * @returns 世界书内容
 *
 * @throws 如果世界书不存在, 将会抛出错误
 */
declare function getWorldbook(worldbook_name: string): Promise<WorldbookEntry[]>;

/**
 * 创建新的世界书
 *
 * @param worldbook_name 世界书名称
 * @param worldbook 世界书内容; 不填则没有任何条目
 *
 * @returns 如果发生创建, 则返回 `true`; 如果发生替换, 则返回 `false`
 */
declare function createWorldbook(worldbook_name: string, worldbook?: WorldbookEntry[]): Promise<boolean>;

/**
 * 创建或替换名为 `worldbook_name` 的世界书, 内容为 `worldbook`
 *
 * @param worldbook_name 世界书名称
 * @param worldbook 世界书内容; 不填则没有任何条目
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'|'none'`: 对于对世界书的更改, 世界书编辑器应该防抖渲染 (debounced)、立即渲染 (immediate) 还是不刷新前端显示 (none)? 默认为性能更好的防抖渲染
 *
 * @returns 如果发生创建, 则返回 `true`; 如果发生替换, 则返回 `false`
 */
declare function createOrReplaceWorldbook(
  worldbook_name: string,
  worldbook?: TypeFest.PartialDeep<WorldbookEntry>[],
  { render }?: ReplaceWorldbookOptions,
): Promise<boolean>;

/**
 * 删除 `worldbook_name` 世界书
 *
 * @param worldbook_name 世界书名称
 *
 * @returns 是否成功删除, 可能因世界书不存在等原因而失败
 */
declare function deleteWorldbook(worldbook_name: string): Promise<boolean>;

// TODO: rename 需要处理世界书绑定
// export function renameWorldbook(old_name: string, new_name: string): boolean;

interface ReplaceWorldbookOptions {
  /** 对于对世界书的更改, 世界书编辑器应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染 */
  render?: 'debounced' | 'immediate';
}
/**
 * 完全替换 `worldbook_name` 世界书的内容为 `worldbook`
 *
 * @param worldbook_name 世界书名称
 * @param worldbook 世界书内容
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 对于对世界书的更改, 世界书编辑器应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @throws 如果世界书不存在, 将会抛出错误
 *
 * @example
 * // 禁止所有条目递归, 保持其他设置不变
 * const worldbook = await getWorldbook("eramgt少女歌剧");
 * await replaceWorldbook(
 *   'eramgt少女歌剧',
 *   worldbook.map(entry => ({
 *     ...entry,
 *     recursion: { prevent_incoming: true, prevent_outgoing: true, delay_until: null },
 *   })),
 * );
 *
 * @example
 * // 删除所有名字中包含 `'神乐光'` 的条目
 * const worldbook = await getWorldbook("eramgt少女歌剧");
 * _.remove(worldbook, entry => entry.name.includes('神乐光'));
 * await replaceWorldbook("eramgt少女歌剧", worldbook);
 */
declare function replaceWorldbook(
  worldbook_name: string,
  worldbook: TypeFest.PartialDeep<WorldbookEntry>[],
  { render }?: ReplaceWorldbookOptions,
): Promise<void>;

type WorldbookUpdater =
  | ((worldbook: WorldbookEntry[]) => TypeFest.PartialDeep<WorldbookEntry>[])
  | ((worldbook: WorldbookEntry[]) => Promise<TypeFest.PartialDeep<WorldbookEntry>[]>);
/**
 * 用 `updater` 函数更新世界书 `worldbook_name`
 *
 * @param worldbook_name 世界书名称
 * @param updater 用于更新世界书的函数. 它应该接收世界书条目作为参数, 并返回更新后的世界书条目
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 对于对世界书的更改, 世界书编辑器应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @returns 更新后的世界书条目
 *
 * @throws 如果世界书不存在, 将会抛出错误
 *
 * @example
 * // 禁止所有条目递归, 保持其他设置不变
 * await updateWorldbookWith('eramgt少女歌剧', worldbook => {
 *   return worldbook.map(entry => ({
 *     ...entry,
 *     recursion: { prevent_incoming: true, prevent_outgoing: true, delay_until: null },
 *   }));
 * });
 *
 * @example
 * // 删除所有名字中包含 "神乐光" 的条目
 * await updateWorldbookWith('eramgt少女歌剧', worldbook => {
 *   _.remove(worldbook, entry => entry.name.includes('神乐光'));
 *   return worldbook;
 * });
 */
declare function updateWorldbookWith(
  worldbook_name: string,
  updater: WorldbookUpdater,
  { render }?: ReplaceWorldbookOptions,
): Promise<WorldbookEntry[]>;

/**
 * 向世界书中新增条目
 *
 * @param worldbook_name 世界书名称
 * @param new_entries 要新增的条目, 对于不设置的字段将会采用酒馆给的默认值
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 对于对世界书的更改, 世界书编辑器应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @returns 更新后的世界书条目, 以及新增条目补全字段后的结果
 *
 * @throws 如果世界书不存在, 将会抛出错误
 *
 * @example
 * // 创建两个条目, 一个标题叫 `'神乐光'`, 一个留白
 * const { worldbook, new_entries } = await createWorldbookEntries('eramgt少女歌剧', [{ name: '神乐光' }, {}]);
 */
declare function createWorldbookEntries(
  worldbook_name: string,
  new_entries: TypeFest.PartialDeep<WorldbookEntry>[],
  { render }?: ReplaceWorldbookOptions,
): Promise<{ worldbook: WorldbookEntry[]; new_entries: WorldbookEntry[] }>;

/**
 * 删除世界书中的条目
 *
 * @param worldbook_name 世界书名称
 * @param predicate 判断函数, 如果返回 `true` 则删除该条目
 * @param options 可选选项
 *   - `render:'debounced'|'immediate'`: 对于对世界书的更改, 世界书编辑器应该防抖渲染 (debounced) 还是立即渲染 (immediate)? 默认为性能更好的防抖渲染
 *
 * @returns 更新后的世界书条目, 以及被删除的条目
 *
 * @throws 如果世界书不存在, 将会抛出错误
 *
 * @example
 * // 删除所有名字中包含 `'神乐光'` 的条目
 * const { worldbook, deleted_entries } = await deleteWorldbookEntries('eramgt少女歌剧', entry => entry.name.includes('神乐光'));
 */
declare function deleteWorldbookEntries(
  worldbook_name: string,
  predicate: (entry: WorldbookEntry) => boolean,
  { render }?: ReplaceWorldbookOptions,
): Promise<{ worldbook: WorldbookEntry[]; deleted_entries: WorldbookEntry[] }>;
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
/**
 * 事件可以是
 * - `iframe_events` 中的 iframe 事件
 * - `tavern_events` 中的酒馆事件
 * - 自定义的字符串事件
 */
type EventType = IframeEventType | TavernEventType | string;

type EventOnReturn = {
  /** 取消监听 */
  stop: () => void;
};

/**
 * 让 `listener` 监听 `event_type`, 当事件发生时自动运行 `listener`;
 * 如果 `listener` 已经在监听 `event_type`, 则调用本函数不会有任何效果.
 *
 * 当 `eventOn` 所在的前端界面/脚本关闭时, 监听将会自动卸载.
 *
 * @param event_type 要监听的事件
 * @param listener 要注册的函数
 *
 * @example
 * function hello() { alert("hello"); }
 * eventOn(要监听的事件, hello);
 *
 * @example
 * // 监听消息接收并弹出 `'hello'`
 * eventOn(tavern_events.MESSAGE_RECEIVED, () => alert('hello'));
 *
 * @example
 * // 消息被修改时监听是哪一条消息被修改
 * // 酒馆事件 tavern_events.MESSAGE_UPDATED 会传递被更新的楼层 id
 * eventOn(tavern_events.MESSAGE_UPDATED, message_id => {
 *   alert(`你刚刚更新了第 ${message_id} 条聊天消息对吧😡`);
 * });
 *
 * @returns 后续操作
 *   - `stop`: 取消这个监听
 */
declare function eventOn<T extends EventType>(event_type: T, listener: ListenerType[T]): EventOnReturn;

/** @deprecated 请使用 `eventOn(getButtonEvent('按钮名称'), 函数)` 代替 */
declare function eventOnButton<T extends EventType>(event_type: T, listener: ListenerType[T]): void;

/**
 * 让 `listener` 监听 `event_type`, 当事件发生时自动在最后运行 `listener`;
 * 如果 `listener` 已经在监听 `event_type`, 则调用本函数会将 `listener` 调整为最后运行.
 *
 * 当 `eventMakeLast` 所在的前端界面/脚本关闭时, 监听将会自动卸载.
 *
 * @param event_type 要监听的事件
 * @param listener 要注册/调整到最后运行的函数
 *
 * @example
 * eventMakeLast(要监听的事件, 要注册的函数);
 *
 * @returns 后续操作
 *   - `stop`: 取消这个监听
 */
declare function eventMakeLast<T extends EventType>(event_type: T, listener: ListenerType[T]): EventOnReturn;

/**
 * 让 `listener` 监听 `event_type`, 当事件发生时自动在最先运行 `listener`;
 * 如果 `listener` 已经在监听 `event_type`, 则调用本函数会将 `listener` 调整为最先运行.
 *
 * 当 `eventMakeFirst` 所在的前端界面/脚本关闭时, 监听将会自动卸载.
 *
 * @param event_type 要监听的事件
 * @param listener 要注册/调整为最先运行的函数
 *
 * @example
 * eventMakeFirst(要监听的事件, 要注册的函数);
 *
 * @returns 后续操作
 *   - `stop`: 取消这个监听
 */
declare function eventMakeFirst<T extends EventType>(event_type: T, listener: ListenerType[T]): EventOnReturn;

/**
 * 让 `listener` 仅监听下一次 `event_type`, 当该次事件发生时运行 `listener`, 此后取消监听;
 * 如果 `listener` 已经在监听 `event_type`, 则调用本函数不会有任何效果.
 *
 * 当 `eventOnce` 所在的前端界面/脚本关闭时, 监听将会自动卸载.
 *
 * @param event_type 要监听的事件
 * @param listener 要注册的函数
 *
 * @example
 * eventOnce(要监听的事件, 要注册的函数);
 *
 * @returns 后续操作
 *   - `stop`: 取消这个监听
 */
declare function eventOnce<T extends EventType>(event_type: T, listener: ListenerType[T]): EventOnReturn;

/**
 * 发送 `event_type` 事件, 同时可以发送一些数据 `data`.
 *
 * 所有正在监听 `event_type` 消息频道的都会收到该消息并接收到 `data`.
 *
 * @param event_type 要发送的事件
 * @param data 要随着事件发送的数据
 *
 * @example
 * // 发送 "角色阶段更新完成" 事件, 所有监听该事件的 `listener` 都会被运行
 * eventEmit("角色阶段更新完成");
 *
 * @example
 * // 发送 "存档" 事件, 并等待所有 `listener` (也许是负责存档的函数) 执行完毕后才继续
 * await eventEmit("存档");
 *
 * @example
 * // 发送时携带数据 ["你好", 0]
 * eventEmit("事件", "你好", 0);
 */
declare function eventEmit<T extends EventType>(event_type: T, ...data: Parameters<ListenerType[T]>): Promise<void>;

/**
 * 携带 `data` 而发送 `event_type` 事件并等待事件处理结束.
 *
 * @param event_type 要发送的事件
 * @param data 要随着事件发送的数据
 */
declare function eventEmitAndWait<T extends EventType>(event_type: T, ...data: Parameters<ListenerType[T]>): void;

/**
 * 让 `listener` 取消对 `event_type` 的监听; 如果 `listener` 没有监听 `event_type`, 则调用本函数不会有任何效果.
 *
 * 前端界面/脚本关闭时会自动卸载所有的事件监听, 你不必手动调用 `eventRemoveListener` 来移除.
 *
 * @param event_type 要监听的事件
 * @param listener 要取消注册的函数
 *
 * @example
 * eventRemoveListener(要监听的事件, 要取消注册的函数);
 */
declare function eventRemoveListener<T extends EventType>(event_type: T, listener: ListenerType[T]): void;

/**
 * 取消本 iframe 中对 `event_type` 的所有监听
 *
 * 前端界面/脚本关闭时会自动卸载所有的事件监听, 你不必手动调用 `eventClearEvent` 来移除.
 *
 * @param event_type 要取消监听的事件
 */
declare function eventClearEvent(event_type: EventType): void;

/**
 * 取消本 iframe 中 `listener` 的的所有监听
 *
 * 前端界面/脚本关闭时会自动卸载所有的事件监听, 你不必手动调用 `eventClearListener` 来移除.
 *
 * @param listener 要取消注册的函数
 */
declare function eventClearListener(listener: Function): void;

/**
 * 取消本 iframe 中对所有事件的所有监听
 *
 * 前端界面/脚本关闭时会自动卸载所有的事件监听, 你不必手动调用 `eventClearAll` 来移除.
 */
declare function eventClearAll(): void;

//------------------------------------------------------------------------------------------------------------------------
// 以下是可用的事件, 你可以发送和监听它们

type IframeEventType = (typeof iframe_events)[keyof typeof iframe_events];

// iframe 事件
declare const iframe_events: {
  MESSAGE_IFRAME_RENDER_STARTED: 'message_iframe_render_started';
  MESSAGE_IFRAME_RENDER_ENDED: 'message_iframe_render_ended';
  /** `generate` 函数开始生成 */
  GENERATION_STARTED: 'js_generation_started';
  /** 启用流式传输的 `generate` 函数传输当前完整文本: "这是", "这是一条", "这是一条流式传输" */
  STREAM_TOKEN_RECEIVED_FULLY: 'js_stream_token_received_fully';
  /** 启用流式传输的 `generate` 函数传输当前增量文本: "这是", "一条", "流式传输" */
  STREAM_TOKEN_RECEIVED_INCREMENTALLY: 'js_stream_token_received_incrementally';
  /** `generate` 函数完成生成 */
  GENERATION_ENDED: 'js_generation_ended';
};

type TavernEventType = (typeof tavern_events)[keyof typeof tavern_events];

// 酒馆事件. **不建议自己发送酒馆事件, 因为你并不清楚它需要发送什么数据**
declare const tavern_events: {
  APP_READY: 'app_ready';
  EXTRAS_CONNECTED: 'extras_connected';
  MESSAGE_SWIPED: 'message_swiped';
  MESSAGE_SENT: 'message_sent';
  MESSAGE_RECEIVED: 'message_received';
  MESSAGE_EDITED: 'message_edited';
  MESSAGE_DELETED: 'message_deleted';
  MESSAGE_UPDATED: 'message_updated';
  MESSAGE_FILE_EMBEDDED: 'message_file_embedded';
  MESSAGE_REASONING_EDITED: 'message_reasoning_edited';
  MESSAGE_REASONING_DELETED: 'message_reasoning_deleted';
  /** since SillyTavern v1.13.5 */
  MESSAGE_SWIPE_DELETED: 'message_swipe_deleted';
  MORE_MESSAGES_LOADED: 'more_messages_loaded';
  IMPERSONATE_READY: 'impersonate_ready';
  CHAT_CHANGED: 'chat_id_changed';
  GENERATION_AFTER_COMMANDS: 'GENERATION_AFTER_COMMANDS';
  GENERATION_STARTED: 'generation_started';
  GENERATION_STOPPED: 'generation_stopped';
  GENERATION_ENDED: 'generation_ended';
  SD_PROMPT_PROCESSING: 'sd_prompt_processing';
  EXTENSIONS_FIRST_LOAD: 'extensions_first_load';
  EXTENSION_SETTINGS_LOADED: 'extension_settings_loaded';
  SETTINGS_LOADED: 'settings_loaded';
  SETTINGS_UPDATED: 'settings_updated';
  MOVABLE_PANELS_RESET: 'movable_panels_reset';
  SETTINGS_LOADED_BEFORE: 'settings_loaded_before';
  SETTINGS_LOADED_AFTER: 'settings_loaded_after';
  CHATCOMPLETION_SOURCE_CHANGED: 'chatcompletion_source_changed';
  CHATCOMPLETION_MODEL_CHANGED: 'chatcompletion_model_changed';
  OAI_PRESET_CHANGED_BEFORE: 'oai_preset_changed_before';
  OAI_PRESET_CHANGED_AFTER: 'oai_preset_changed_after';
  OAI_PRESET_EXPORT_READY: 'oai_preset_export_ready';
  OAI_PRESET_IMPORT_READY: 'oai_preset_import_ready';
  WORLDINFO_SETTINGS_UPDATED: 'worldinfo_settings_updated';
  WORLDINFO_UPDATED: 'worldinfo_updated';
  /** since SillyTavern v1.13.5 */
  CHARACTER_EDITOR_OPENED: 'character_editor_opened';
  CHARACTER_EDITED: 'character_edited';
  CHARACTER_PAGE_LOADED: 'character_page_loaded';
  USER_MESSAGE_RENDERED: 'user_message_rendered';
  CHARACTER_MESSAGE_RENDERED: 'character_message_rendered';
  FORCE_SET_BACKGROUND: 'force_set_background';
  CHAT_DELETED: 'chat_deleted';
  CHAT_CREATED: 'chat_created';
  GENERATE_BEFORE_COMBINE_PROMPTS: 'generate_before_combine_prompts';
  GENERATE_AFTER_COMBINE_PROMPTS: 'generate_after_combine_prompts';
  GENERATE_AFTER_DATA: 'generate_after_data';
  WORLD_INFO_ACTIVATED: 'world_info_activated';
  TEXT_COMPLETION_SETTINGS_READY: 'text_completion_settings_ready';
  CHAT_COMPLETION_SETTINGS_READY: 'chat_completion_settings_ready';
  CHAT_COMPLETION_PROMPT_READY: 'chat_completion_prompt_ready';
  CHARACTER_FIRST_MESSAGE_SELECTED: 'character_first_message_selected';
  CHARACTER_DELETED: 'characterDeleted';
  CHARACTER_DUPLICATED: 'character_duplicated';
  CHARACTER_RENAMED: 'character_renamed';
  CHARACTER_RENAMED_IN_PAST_CHAT: 'character_renamed_in_past_chat';
  SMOOTH_STREAM_TOKEN_RECEIVED: 'stream_token_received';
  STREAM_TOKEN_RECEIVED: 'stream_token_received';
  STREAM_REASONING_DONE: 'stream_reasoning_done';
  FILE_ATTACHMENT_DELETED: 'file_attachment_deleted';
  WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate';
  OPEN_CHARACTER_LIBRARY: 'open_character_library';
  ONLINE_STATUS_CHANGED: 'online_status_changed';
  IMAGE_SWIPED: 'image_swiped';
  CONNECTION_PROFILE_LOADED: 'connection_profile_loaded';
  CONNECTION_PROFILE_CREATED: 'connection_profile_created';
  CONNECTION_PROFILE_DELETED: 'connection_profile_deleted';
  CONNECTION_PROFILE_UPDATED: 'connection_profile_updated';
  TOOL_CALLS_PERFORMED: 'tool_calls_performed';
  TOOL_CALLS_RENDERED: 'tool_calls_rendered';
  CHARACTER_MANAGEMENT_DROPDOWN: 'charManagementDropdown';
  SECRET_WRITTEN: 'secret_written';
  SECRET_DELETED: 'secret_deleted';
  SECRET_ROTATED: 'secret_rotated';
  SECRET_EDITED: 'secret_edited';
  PRESET_CHANGED: 'preset_changed';
  PRESET_DELETED: 'preset_deleted';
  /** since SillyTavern v1.13.5 */
  PRESET_RENAMED: 'preset_renamed';
  /** since SillyTavern v1.13.5 */
  PRESET_RENAMED_BEFORE: 'preset_renamed_before';
  MAIN_API_CHANGED: 'main_api_changed';
  WORLDINFO_ENTRIES_LOADED: 'worldinfo_entries_loaded';
  WORLDINFO_SCAN_DONE: 'worldinfo_scan_done';
  /** since SillyTavern v1.14.0 */
  MEDIA_ATTACHMENT_DELETED: 'media_attachment_deleted';
};

interface ListenerType {
  [iframe_events.MESSAGE_IFRAME_RENDER_STARTED]: (iframe_name: string) => void;
  [iframe_events.MESSAGE_IFRAME_RENDER_ENDED]: (iframe_name: string) => void;
  [iframe_events.GENERATION_STARTED]: (generation_id: string) => void;
  [iframe_events.STREAM_TOKEN_RECEIVED_FULLY]: (full_text: string, generation_id: string) => void;
  [iframe_events.STREAM_TOKEN_RECEIVED_INCREMENTALLY]: (incremental_text: string, generation_id: string) => void;
  [iframe_events.GENERATION_ENDED]: (text: string, generation_id: string) => void;

  [tavern_events.APP_READY]: () => void;
  [tavern_events.EXTRAS_CONNECTED]: (modules: any) => void;
  [tavern_events.MESSAGE_SWIPED]: (message_id: number) => void;
  [tavern_events.MESSAGE_SENT]: (message_id: number) => void;
  [tavern_events.MESSAGE_RECEIVED]: (
    message_id: number,
    type: TypeFest.LiteralUnion<
      | 'normal'
      | 'quiet'
      | 'regenerate'
      | 'impersonate'
      | 'continue'
      | 'swipe'
      | 'append'
      | 'appendFinal'
      | 'first_message'
      | 'command'
      | 'extension',
      string
    >,
  ) => void;
  [tavern_events.MESSAGE_EDITED]: (message_id: number) => void;
  [tavern_events.MESSAGE_DELETED]: (message_id: number) => void;
  [tavern_events.MESSAGE_UPDATED]: (message_id: number) => void;
  [tavern_events.MESSAGE_FILE_EMBEDDED]: (message_id: number) => void;
  [tavern_events.MESSAGE_REASONING_EDITED]: (message_id: number) => void;
  [tavern_events.MESSAGE_REASONING_DELETED]: (message_id: number) => void;
  [tavern_events.MESSAGE_SWIPE_DELETED]: (event_data: {
    messageId: number;
    swipeId: number;
    newSwipeId: number;
  }) => void;
  [tavern_events.MORE_MESSAGES_LOADED]: () => void;
  [tavern_events.IMPERSONATE_READY]: (message: string) => void;
  [tavern_events.CHAT_CHANGED]: (chat_file_name: string) => void;
  [tavern_events.GENERATION_AFTER_COMMANDS]: (
    type: string,
    option: {
      automatic_trigger?: boolean;
      force_name2?: boolean;
      quiet_prompt?: string;
      quietToLoud?: boolean;
      skipWIAN?: boolean;
      force_chid?: number;
      signal?: AbortSignal;
      quietImage?: string;
      quietName?: string;
      depth?: number;
    },
    dry_run: boolean,
  ) => void;
  [tavern_events.GENERATION_STARTED]: (
    type: string,
    option: {
      automatic_trigger?: boolean;
      force_name2?: boolean;
      quiet_prompt?: string;
      quietToLoud?: boolean;
      skipWIAN?: boolean;
      force_chid?: number;
      signal?: AbortSignal;
      quietImage?: string;
      quietName?: string;
      depth?: number;
    },
    dry_run: boolean,
  ) => void;
  [tavern_events.GENERATION_STOPPED]: () => void;
  [tavern_events.GENERATION_ENDED]: (message_id: number) => void;
  [tavern_events.SD_PROMPT_PROCESSING]: (event_data: {
    prompt: string;
    generationType: number;
    message: string;
    trigger: string;
  }) => void;
  [tavern_events.EXTENSIONS_FIRST_LOAD]: () => void;
  [tavern_events.EXTENSION_SETTINGS_LOADED]: () => void;
  [tavern_events.SETTINGS_LOADED]: () => void;
  [tavern_events.SETTINGS_UPDATED]: () => void;
  [tavern_events.MOVABLE_PANELS_RESET]: () => void;
  [tavern_events.SETTINGS_LOADED_BEFORE]: (settings: object) => void;
  [tavern_events.SETTINGS_LOADED_AFTER]: (settings: object) => void;
  [tavern_events.CHATCOMPLETION_SOURCE_CHANGED]: (source: string) => void;
  [tavern_events.CHATCOMPLETION_MODEL_CHANGED]: (model: string) => void;
  [tavern_events.OAI_PRESET_CHANGED_BEFORE]: (result: {
    preset: object;
    presetName: string;
    settingsToUpdate: object;
    settings: object;
    savePreset: (name: string, settings: Record<string, any>, trigger_ui?: boolean) => Promise<void>;
    presetNameBefore: string;
  }) => void;
  [tavern_events.OAI_PRESET_CHANGED_AFTER]: () => void;
  [tavern_events.OAI_PRESET_EXPORT_READY]: (preset: object) => void;
  [tavern_events.OAI_PRESET_IMPORT_READY]: (result: { data: object; presetName: string }) => void;
  [tavern_events.WORLDINFO_SETTINGS_UPDATED]: () => void;
  [tavern_events.WORLDINFO_UPDATED]: (
    name: string,
    data: { entries: { [uid: number]: SillyTavern.FlattenedWorldInfoEntry } },
  ) => void;
  [tavern_events.CHARACTER_EDITOR_OPENED]: (chid: string) => void;
  [tavern_events.CHARACTER_EDITED]: (result: { detail: { id: string; character: SillyTavern.v1CharData } }) => void;
  [tavern_events.CHARACTER_PAGE_LOADED]: () => void;
  [tavern_events.USER_MESSAGE_RENDERED]: (message_id: number) => void;
  [tavern_events.CHARACTER_MESSAGE_RENDERED]: (message_id: number, type: string) => void;
  [tavern_events.FORCE_SET_BACKGROUND]: (background: { url: string; path: string }) => void;
  [tavern_events.CHAT_DELETED]: (chat_file_name: string) => void;
  [tavern_events.CHAT_CREATED]: () => void;
  [tavern_events.GENERATE_BEFORE_COMBINE_PROMPTS]: () => void;
  [tavern_events.GENERATE_AFTER_COMBINE_PROMPTS]: (result: { prompt: string; dryRun: boolean }) => void;
  /** dry_run 只在 SillyTavern 1.13.15 及以后有 */
  [tavern_events.GENERATE_AFTER_DATA]: (
    generate_data: {
      prompt: SillyTavern.SendingMessage[];
    },
    dry_run: boolean,
  ) => void;
  [tavern_events.WORLD_INFO_ACTIVATED]: (entries: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[]) => void;
  [tavern_events.TEXT_COMPLETION_SETTINGS_READY]: () => void;
  [tavern_events.CHAT_COMPLETION_SETTINGS_READY]: (generate_data: {
    messages: SillyTavern.SendingMessage[];
    model: string;
    temprature: number;
    frequency_penalty: number;
    presence_penalty: number;
    top_p: number;
    max_tokens: number;
    stream: boolean;
    logit_bias: object;
    stop: string[];
    chat_comletion_source: string;
    n?: number;
    user_name: string;
    char_name: string;
    group_names: string[];
    include_reasoning: boolean;
    reasoning_effort: string;
    json_schema: {
      name: string;
      value: Record<string, any>;
      description?: string;
      strict?: boolean;
    };
    [others: string]: any;
  }) => void;
  [tavern_events.CHAT_COMPLETION_PROMPT_READY]: (event_data: {
    chat: SillyTavern.SendingMessage[];
    dryRun: boolean;
  }) => void;
  [tavern_events.CHARACTER_FIRST_MESSAGE_SELECTED]: (event_args: {
    input: string;
    output: string;
    character: object;
  }) => void;
  [tavern_events.CHARACTER_DELETED]: (result: { id: string; character: SillyTavern.v1CharData }) => void;
  [tavern_events.CHARACTER_DUPLICATED]: (result: { oldAvatar: string; newAvatar: string }) => void;
  [tavern_events.CHARACTER_RENAMED]: (old_avatar: string, new_avatar: string) => void;
  [tavern_events.CHARACTER_RENAMED_IN_PAST_CHAT]: (
    current_chat: Record<string, any>,
    old_avatar: string,
    new_avatar: string,
  ) => void;
  [tavern_events.STREAM_TOKEN_RECEIVED]: (text: string) => void;
  [tavern_events.STREAM_REASONING_DONE]: (
    reasoning: string,
    duration: number | null,
    message_id: number,
    state: 'none' | 'thinking' | 'done' | 'hidden',
  ) => void;
  [tavern_events.FILE_ATTACHMENT_DELETED]: (url: string) => void;
  [tavern_events.WORLDINFO_FORCE_ACTIVATE]: (entries: object[]) => void;
  [tavern_events.OPEN_CHARACTER_LIBRARY]: () => void;
  [tavern_events.ONLINE_STATUS_CHANGED]: () => void;
  [tavern_events.IMAGE_SWIPED]: (result: {
    message: object;
    element: JQuery<HTMLElement>;
    direction: 'left' | 'right';
  }) => void;
  [tavern_events.CONNECTION_PROFILE_LOADED]: (profile_name: string) => void;
  [tavern_events.CONNECTION_PROFILE_CREATED]: (profile: Record<string, any>) => void;
  [tavern_events.CONNECTION_PROFILE_DELETED]: (profile: Record<string, any>) => void;
  [tavern_events.CONNECTION_PROFILE_UPDATED]: (
    old_profile: Record<string, any>,
    new_profile: Record<string, any>,
  ) => void;
  [tavern_events.TOOL_CALLS_PERFORMED]: (tool_invocations: object[]) => void;
  [tavern_events.TOOL_CALLS_RENDERED]: (tool_invocations: object[]) => void;
  [tavern_events.WORLDINFO_ENTRIES_LOADED]: (lores: {
    globalLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
    characterLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
    chatLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
    personaLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
  }) => void;
  [tavern_events.CHARACTER_MANAGEMENT_DROPDOWN]: (target: JQuery) => void;
  [tavern_events.SECRET_WRITTEN]: (secret: string) => void;
  [tavern_events.SECRET_DELETED]: (secret: string) => void;
  [tavern_events.SECRET_ROTATED]: (secret: string) => void;
  [tavern_events.SECRET_EDITED]: (secret: string) => void;
  [tavern_events.PRESET_CHANGED]: (data: { apiId: string; name: string }) => void;
  [tavern_events.PRESET_DELETED]: (data: { apiId: string; name: string }) => void;
  [tavern_events.PRESET_RENAMED]: (data: { apiId: string; oldName: string; newName: string }) => void;
  [tavern_events.PRESET_RENAMED_BEFORE]: (data: { apiId: string; oldName: string; newName: string }) => void;
  [tavern_events.MAIN_API_CHANGED]: (data: { apiId: string }) => void;
  [tavern_events.WORLDINFO_ENTRIES_LOADED]: (lores: {
    globalLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
    characterLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
    chatLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
    personaLore: ({ world: string } & SillyTavern.FlattenedWorldInfoEntry)[];
  }) => void;
  [tavern_events.WORLDINFO_SCAN_DONE]: (event_data: {
    state: {
      current: number;
      next: number;
      loopCount: number;
    };
    new: {
      all: SillyTavern.FlattenedWorldInfoEntry[];
      successful: SillyTavern.FlattenedWorldInfoEntry[];
    };
    activated: {
      entries: Map<`${string}.${string}`, SillyTavern.FlattenedWorldInfoEntry>;
      text: string;
    };
    sortedEntries: SillyTavern.FlattenedWorldInfoEntry[];
    recursionDelay: {
      availableLevels: number[];
      currentLevel: number;
    };
    budget: {
      current: number;
      overflowed: boolean;
    };
    timedEffects: Record<string, any>;
  }) => void;
  [custom_event: string]: (...args: any) => any;
}
declare namespace EjsTemplate {
  type Features = {
    /** 是否启用扩展 */
    enabled: boolean;

    /** 处理生成内容 */
    generate_enabled: boolean;
    /** 生成时注入 [GENERATE] 世界书条目 */
    generate_loader_enabled: boolean;
    /** 生成时注入 @INJECT 世界书条目 */
    inject_loader_enabled: boolean;

    /** 处理楼层消息 */
    render_enabled: boolean;
    /** 渲染楼层时注入 [RENDER] 世界书条目 */
    render_loader_enabled: boolean;
    /** 处理代码块 */
    code_blocks_enabled: boolean;
    /** 处理原始消息内容 */
    raw_message_evaluation_enabled: boolean;
    /** 生成时忽略楼层消息处理 */
    filter_message_enabled: boolean;
    /** 处理楼层深度限制 (-1=无限制) */
    depth_limit: number;

    /** 自动保存变量更新 */
    autosave_enabled: boolean;
    /** 立即加载世界书 */
    preload_worldinfo_enabled: boolean;
    /** 禁用 with 语句块 */
    with_context_disabled: boolean;
    /** 控制台显示详细信息 */
    debug_enabled: boolean;
    /** 旧设定兼容模式，世界书中的 GENERATE/RENDER/INJECT 条目禁用时视为启用 */
    invert_enabled: boolean;
    /** 是否启用后台编译 (用 Web Workers 编译) */
    compile_workers: boolean;
    /** 是否启用沙盒执行代码 (性能下降, 提升安全性) */
    sandbox: boolean;

    /** 缓存 (实验性) (0=禁用, 1=全部, 2=仅世界书) */
    cache_enabled: number;
    /** 缓存大小 */
    cache_size: number;
    /** 缓存 Hash 函数 */
    cache_hasher: 'h32ToString' | 'h64ToString';
  };
}

/**
 * 提示词模板语法插件所提供的额外功能, 必须额外安装提示词模板语法插件, 具体内容见于 https://github.com/zonde306/ST-Prompt-Template
 * 你也可以在酒馆页面按 f12, 在控制台中输入 `window.EjsTemplate` 来查看当前提示词模板语法所提供的接口
 */
declare const EjsTemplate: {
  /**
   * 对文本进行模板语法处理
   * @note `context` 一般从 `prepareContext` 获取, 若要修改则应直接修改原始对象
   *
   * @param code 模板代码
   * @param context 执行环境 (上下文)
   * @param options ejs 参数
   * @returns 对模板进行计算后的内容
   *
   * @example
   * // 使用提示词模板语法插件提供的函数创建一个临时的酒馆正则, 对消息楼层进行一次处理
   * await EjsTemplate.evalTemplate('<%_ await activateRegex(/<thinking>.*?<\/thinking>/gs, '') _%>')
   *
   * @example
   * const env    = await EjsTemplate.prepareContext({ a: 1 });
   * const result = await EjsTemplate.evalTemplate('a is <%= a _%>', env);
   * => result === 'a is 1'
   * // 但这种用法更推荐用 _.template 来做, 具体见于 https://lodash.com/docs/4.17.15#template
   * const compiled = _.template('hello <%= user %>!');
   * const result   = compiled({ 'user': 'fred' });;
   * => result === 'hello user!'
   */
  evaltemplate: (code: string, context?: Record<string, any>, options?: Record<string, any>) => Promise<string>;

  /**
   * 创建模板语法处理使用的执行环境 (上下文)
   *
   * @param additional_context 附加的执行环境 (上下文)
   * @param last_message_id 合并消息变量的最大 ID; 默认为所有
   * @returns 执行环境 (上下文)
   */
  prepareContext: (additional_context?: Record<string, any>, last_message_id?: number) => Promise<Record<string, any>>;

  /**
   * 检查模板是否存在语法错误
   * 并不会实际执行
   *
   * @param content 模板代码
   * @param output_line_count 发生错误时输出的附近行数; 默认为 4
   * @returns 语法错误信息, 无错误返回空字符串
   */
  getSyntaxErrorInfo: (code: string, output_line_count?: number) => Promise<string>;

  /**
   * 获取全局变量、聊天变量、消息楼层变量的并集
   *
   * @param end_message_id 要合并的消息楼层变量最大楼层数
   * @returns 合并后的变量
   */
  allVariables: (end_message_id?: number) => Record<string, any>;

  /**
   * 获取提示词模板语法插件的设置
   *
   * @returns 设置情况
   */
  getFeatures: () => EjsTemplate.Features;

  /**
   * 设置提示词模板语法插件的设置
   *
   * @param features 设置
   */
  setFeatures: (features: Partial<EjsTemplate.Features>) => void;

  /**
   * 重置提示词模板语法插件的设置
   */
  resetFeatures: () => void;
};
declare namespace Mvu {
  type MvuData = {
    /** 已被 mvu 初始化 initvar 条目的世界书列表 */
    initialized_lorebooks: Record<string, any[]>;

    /** 实际的变量数据 */
    stat_data: Record<string, any>;

    [key: string]: any;
  };

  type CommandInfo = SetCommandInfo | InsertCommandInfo | DeleteCommandInfo | AddCommandInfo | MoveCommandInfo;
  type SetCommandInfo = {
    type: 'set';
    full_match: string;
    args:
      | [path: string, new_value_literal: string]
      | [path: string, expected_old_value_literal: string, new_value_literal: string];
    reason: string;
  };
  type InsertCommandInfo = {
    type: 'insert';
    full_match: string;
    args:
      | [path: string, value_literal: string] // 在尾部追加值
      | [path: string, index_or_key_literal: string, value_literal: string]; // 在指定索引/键处插入值
    reason: string;
  };
  type DeleteCommandInfo = {
    type: 'delete';
    full_match: string;
    args: [path: string] | [path: string, index_or_key_or_value_literal: string];
    reason: string;
  };
  type AddCommandInfo = {
    type: 'add';
    full_match: string;
    args: [path: string, delta_or_toggle_literal: string];
    reason: string;
  };
  type MoveCommandInfo = {
    type: 'move';
    full_match: string;
    args: [from: string, to: string];
    reason: string;
  };
}

/**
 * mvu 变量框架脚本提供的额外功能, 必须额外安装 mvu 变量框架脚本, 具体内容见于 https://github.com/MagicalAstrogy/MagVarUpdate/blob/master/src/export_globals.ts
 * **在使用它之前, 你应该先通过 `await waitGlobalInitialized('Mvu')` 来等待 Mvu 初始化完毕**
 * 你也可以在酒馆页面按 f12, 在控制台中输入 `window.Mvu` 来查看当前 Mvu 变量框架所提供的接口
 */
declare const Mvu: {
  events: {
    /** 新开聊天对变量初始化时触发的事件  */
    VARIABLE_INITIALIZED: 'mag_variable_initiailized';

    /** 某轮变量更新开始时触发的事件 */
    VARIABLE_UPDATE_STARTED: 'mag_variable_update_started';

    /**
     * 某轮变量更新过程中, 对文本成功解析了所有更新命令时触发的事件
     *
     * @example
     * // 修复 gemini 在中文间加入的 '-'', 如将 '角色.络-络' 修复为 '角色.络络'
     * eventOn(Mvu.events.COMMAND_PARSED, commands => {
     *   commands.forEach(command => {
     *     command.args[0] = command.args[0].replace(/-/g, '');
     *   });
     * });
     *
     * @example
     * // 修复繁体字, 如将 '絡絡' 修复为 '络络'
     * eventOn(Mvu.events.COMMAND_PARSED, commands => {
     *   commands.forEach(command => {
     *     command.args[0] = command.args[0].replaceAll('絡絡', '络络');
     *   });
     * });
     *
     * @example
     * // 添加新的更新命令
     * eventOn(Mvu.events.COMMAND_PARSED, commands => {
     *   commands.push({
     *     type: 'set',
     *     full_match: `_.set('络络.好感度', 5)`,
     *     args: ['络络.好感度', 5],
     *     reason: '脚本强行更新',
     *   });
     * });
     */
    COMMAND_PARSED: 'mag_command_parsed';

    /**
     * 某轮变量更新结束时触发的事件
     *
     * @example
     * // 保持好感度不低于 0
     * eventOn(Mvu.events.VARIABLE_UPDATE_ENDED, variables => {
     *   if (_.get(variables, 'stat_data.角色.络络.好感度') < 0) {
     *     _.set(variables, 'stat_data.角色.络络.好感度', 0);
     *   }
     * })
     *
     * @example
     * // 保持好感度增幅不超过 3
     * eventOn(Mvu.events.VARIABLE_UPDATE_ENDED, (variables, variables_before_update) => {
     *   const old_value = _.get(variables_before_update, 'stat_data.角色.络络.好感度');
     *   const new_value = _.get(variables, 'stat_data.角色.络络.好感度');
     *
     *   // 新的好感度必须在 旧好感度-3 和 旧好感度+3 之间
     *   _.set(variables, 'stat_data.角色.络络.好感度', _.clamp(new_value, old_value - 3, old_value + 3));
     * });
     */
    VARIABLE_UPDATE_ENDED: 'mag_variable_update_ended';

    /** 即将用更新后的变量更新楼层时触发的事件  */
    BEFORE_MESSAGE_UPDATE: 'mag_before_message_update';
  };

  /**
   * 获取变量表, 并将其视为包含 mvu 数据的 MvuData
   *
   * @param  可选选项
   *   - `type?:'message'|'chat'|'character'|'global'`: 对某一楼层的聊天变量 (`message`)、聊天变量表 (`'chat'`)、角色卡变量 (`'character'`) 或全局变量表 (`'global'`) 进行操作, 默认为 `'chat'`
   *   - `message_id?:number|'latest'`: 当 `type` 为 `'message'` 时, 该参数指定要获取的消息楼层号, 如果为负数则为深度索引, 例如 `-1` 表示获取最新的消息楼层; 默认为 `'latest'`
   *   - `script_id?:string`: 当 `type` 为 `'script'` 时, 该参数指定要获取的脚本 ID; 如果在脚本内调用, 则你可以用 `getScriptId()` 获取该脚本 ID
   *
   * @returns MvuData 数据表
   *
   * @example
   * // 获取最新消息楼层的 mvu 数据
   * const message_data = Mvu.getMvuData({ type: 'message', message_id: 'latest' });
   *
   * // 在消息楼层 iframe 内获取该 iframe 所在楼层的 mvu 数据
   * const message_data = Mvu.getMvuData({ type: 'message', message_id: getCurrentMessageId() });
   */
  getMvuData: (options: VariableOption) => Mvu.MvuData;

  /**
   * 完全替换变量表为包含 mvu 数据的 `mvu_data` (但如果没用 `parseMessages` 自行处理变量, 则更建议监听 mvu 事件来修改 mvu 数据!)
   *
   * @param variables 要用于替换的变量表
   * @param option 可选选项
   *   - `type?:'message'|'chat'|'character'|'global'`: 对某一楼层的聊天变量 (`message`)、聊天变量表 (`'chat'`)、角色卡变量 (`'character'`) 或全局变量表 (`'global'`) 进行操作, 默认为 `'chat'`
   *   - `message_id?:number|'latest'`: 当 `type` 为 `'message'` 时, 该参数指定要获取的消息楼层号, 如果为负数则为深度索引, 例如 `-1` 表示获取最新的消息楼层; 默认为 `'latest'`
   *   - `script_id?:string`: 当 `type` 为 `'script'` 时, 该参数指定要获取的脚本 ID; 如果在脚本内调用, 则你可以用 `getScriptId()` 获取该脚本 ID
   *
   * @example
   * // 修改络络好感度为 30
   * const mvu_data = Mvu.getMvuData({ type: 'message', message_id: 'latest' });
   * _.set(mvu_data, 'stat_data.角色.络络.好感度', 30);
   * await Mvu.replaceMvuData(mvu_data, { type: 'message', message_id: 'latest' });
   */
  replaceMvuData: (mvu_data: Mvu.MvuData, options: VariableOption) => Promise<void>;

  /**
   * 解析包含变量更新命令 (`_.set`) 的消息 `message`, 根据它更新 `old_data` 中的 mvu 变量数据
   *
   * @param message 包含 _.set() 命令的消息字符串
   * @param old_data 当前的 MvuData 数据
   *
   * @returns 如果有变量被更新则返回新的 MvuData, 否则返回 `undefined`
   *
   * @example
   * // 修改络络好感度为 30
   * const old_data = Mvu.getMvuData({ type: 'message', message_id: 'latest' });
   * const new_data = await Mvu.parseMessage("_.set('角色.络络.好感度', 30); // 强制修改", old_data);
   * await Mvu.replaceMvuData(new_data, { type: 'message', message_id: 'latest' });
   */
  parseMessage: (message: string, old_data: Mvu.MvuData) => Promise<Mvu.MvuData>;

  /**
   * 酒馆是否正在进行额外模型解析
   */
  isDuringExtraAnalysis: () => boolean;
};

interface ListenerType {
  [Mvu.events.VARIABLE_INITIALIZED]: (variables: Mvu.MvuData, swipe_id: number) => void;

  [Mvu.events.VARIABLE_UPDATE_STARTED]: (variables: Mvu.MvuData) => void;

  [Mvu.events.COMMAND_PARSED]: (variables: Mvu.MvuData, commands: Mvu.CommandInfo[], message_content: string) => void;

  [Mvu.events.VARIABLE_UPDATE_ENDED]: (variables: Mvu.MvuData, variables_before_update: Mvu.MvuData) => void;

  [Mvu.events.BEFORE_MESSAGE_UPDATE]: (context: { variables: Mvu.MvuData; message_content: string }) => void;
}
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
declare namespace SillyTavern {
  type ChatMessage = {
    name: string;
    /**
     * 实际的 role 为:
     * - 'system': extra?.type === 'narrator' && !is_user
     * - 'user': extra?.type !== 'narrator' && is_user
     * - 'assistant': extra?.type !== 'narrator' && !is_user
     */
    is_user: boolean;
    /**
     * 实际是表示消息是否被隐藏不会发给 llm
     */
    is_system: boolean;

    mes: string;

    swipe_id?: number;
    swipes?: string[];

    swipe_info?: Record<string, any>[];
    extra?: Record<string, any>;

    variables?: Record<string, any>[] | { [swipe_id: number]: Record<string, any> };
  };

  type SendingMessage = {
    role: 'user' | 'assistant' | 'system' | 'tool';

    content?:
      | string
      | Array<
          | { type: 'text'; text: string }
          | { type: 'image_url'; image_url: { url: string; detail: 'auto' | 'low' | 'high' } }
          | { type: 'video_url'; video_url: { url: string } }
        >;

    tool_call_id?: string;
    tool_calls?: Array<{
      id: string;
      type: 'function';
      function: {
        name: string;
        arguments: string;
      };
    }>;
  };

  type FlattenedWorldInfoEntry = {
    uid: number;
    displayIndex: number;
    comment?: string;
    disable: boolean;

    constant: boolean;
    selective: boolean;
    key: string[];
    /** 0: and_any, 1: not_all, 2: not_any, 3: and_all */
    selectiveLogic: 0 | 1 | 2 | 3;
    keysecondary: string[];
    scanDepth: number | null;
    vectorized: boolean;
    position: 0 | 1 | 2 | 3 | 4 | 5 | 6;
    /** 0: system, 1: user, 2: assistant */
    role: 0 | 1 | 2 | null;
    depth: number;
    order: number;

    content: string;

    useProbability: boolean;
    probability: number;
    excludeRecursion: boolean;
    preventRecursion: boolean;
    delayUntilRecursion: boolean | number;
    sticky: number | null;
    cooldown: number | null;
    delay: number | null;

    extra?: Record<string, any>;
  };

  /**
   * V1 character data structure.
   */
  type v1CharData = {
    /** the name of the character */
    name: string;
    /** the description of the character */
    description: string;
    /** a short personality description of the character */
    personality: string;
    /** a scenario description of the character */
    scenario: string;
    /** the first message in the conversation */
    first_mes: string;
    /** the example message in the conversation */
    mes_example: string;
    /** creator's notes of the character */
    creatorcomment: string;
    /** the tags of the character */
    tags: string[];
    /** talkativeness */
    talkativeness: number;
    /** fav */
    fav: boolean | string;
    /** create_date */
    create_date: string;
    /** v2 data extension */
    data: v2CharData;
    // Non-standard extensions added by the ST server (not part of the original data)
    /** name of the current chat file chat */
    chat: string;
    /** file name of the avatar image (acts as a unique identifier) */
    avatar: string;
    /** the full raw JSON data of the character */
    json_data: string;
    /** if the data is shallow (lazy-loaded) */
    shallow?: boolean;
  };

  /**
   * V2 character data structure.
   */
  type v2CharData = {
    /** The character's name. */
    name: string;
    /** A brief description of the character. */
    description: string;
    /** The character's data version. */
    character_version: string;
    /** A short summary of the character's personality traits. */
    personality: string;
    /** A description of the character's background or setting. */
    scenario: string;
    /** The character's opening message in a conversation. */
    first_mes: string;
    /** An example message demonstrating the character's conversation style. */
    mes_example: string;
    /** Internal notes or comments left by the character's creator. */
    creator_notes: string;
    /** A list of keywords or labels associated with the character. */
    tags: string[];
    /** The system prompt used to interact with the character. */
    system_prompt: string;
    /** Instructions for handling the character's conversation history. */
    post_history_instructions: string;
    /** The name of the person who created the character. */
    creator: string;
    /** Additional greeting messages the character can use. */
    alternate_greetings: string[];
    /** Data about the character's world or story (if applicable). */
    character_book: v2WorldInfoBook;
    /** Additional details specific to the character. */
    extensions: v2CharDataExtensionInfos;
  };

  /**
   * A world info book containing entries.
   */
  type v2WorldInfoBook = {
    /** the name of the book */
    name: string;
    /** the entries of the book */
    entries: Record<number, v2DataWorldInfoEntry>;
  };

  /**
   * A world info entry object.
   */
  type v2DataWorldInfoEntry = {
    /** An array of primary keys associated with the entry. */
    keys: string[];
    /** An array of secondary keys associated with the entry (optional). */
    secondary_keys: string[];
    /** A human-readable description or explanation for the entry. */
    comment: string;
    /** The main content or data associated with the entry. */
    content: string;
    /** Indicates if the entry's content is fixed and unchangeable. */
    constant: boolean;
    /** Indicates if the entry's inclusion is controlled by specific conditions. */
    selective: boolean;
    /** Defines the order in which the entry is inserted during processing. */
    insertion_order: number;
    /** Controls whether the entry is currently active and used. */
    enabled: boolean;
    /** Specifies the location or context where the entry applies. */
    position: string;
    /** An object containing additional details for extensions associated with the entry. */
    extensions: v2DataWorldInfoEntryExtensionInfos;
    /** A unique identifier assigned to the entry. */
    id: number;
  };

  /**
   * An object containing additional details for extensions associated with the entry.
   */
  type v2DataWorldInfoEntryExtensionInfos = {
    /** The order in which the extension is applied relative to other extensions. */
    position: number;
    /** Prevents the extension from being applied recursively. */
    exclude_recursion: boolean;
    /** The chance (between 0 and 1) of the extension being applied. */
    probability: number;
    /** Determines if the `probability` property is used. */
    useProbability: boolean;
    /** The maximum level of nesting allowed for recursive application of the extension. */
    depth: number;
    /** Defines the logic used to determine if the extension is applied selectively. */
    selectiveLogic: number;
    /** A category or grouping for the extension. */
    group: string;
    /** Overrides any existing group assignment for the extension. */
    group_override: boolean;
    /** A value used for prioritizing extensions within the same group. */
    group_weight: number;
    /** Completely disallows recursive application of the extension. */
    prevent_recursion: boolean;
    /** Will only be checked during recursion. */
    delay_until_recursion: boolean;
    /** The maximum depth to search for matches when applying the extension. */
    scan_depth: number;
    /** Specifies if only entire words should be matched during extension application. */
    match_whole_words: boolean;
    /** Indicates if group weight is considered when selecting extensions. */
    use_group_scoring: boolean;
    /** Controls whether case sensitivity is applied during matching for the extension. */
    case_sensitive: boolean;
    /** An identifier used for automation purposes related to the extension. */
    automation_id: string;
    /** The specific function or purpose of the extension. */
    role: number;
    /** Indicates if the extension is optimized for vectorized processing. */
    vectorized: boolean;
    /** The order in which the extension should be displayed for user interfaces. */
    display_index: number;
    /** Wether to match against the persona description. */
    match_persona_description: boolean;
    /** Wether to match against the persona description. */
    match_character_description: boolean;
    /** Wether to match against the character personality. */
    match_character_personality: boolean;
    /** Wether to match against the character depth prompt. */
    match_character_depth_prompt: boolean;
    /** Wether to match against the character scenario. */
    match_scenario: boolean;
    /** Wether to match against the character creator notes. */
    match_creator_notes: boolean;
  };

  /**
   * Additional details specific to the character.
   */
  type v2CharDataExtensionInfos = {
    /** A numerical value indicating the character's propensity to talk. */
    talkativeness: number;
    /** A flag indicating whether the character is a favorite. */
    fav: boolean;
    /** The fictional world or setting where the character exists (if applicable). */
    world: string;
    /** Prompts used to explore the character's depth and complexity. */
    depth_prompt: {
      /** The level of detail or nuance targeted by the prompt. */
      depth: number;
      /** The actual prompt text used for deeper character interaction. */
      prompt: string;
      /** The role the character takes on during the prompted interaction (system, user, or assistant). */
      role: 'system' | 'user' | 'assistant';
    };
    /** Custom regex scripts for the character. */
    regex_scripts: RegexScriptData[];
    // Non-standard extensions added by external tools
    /** The unique identifier assigned to the character by the Pygmalion.chat. */
    pygmalion_id?: string;
    /** The gitHub repository associated with the character. */
    github_repo?: string;
    /** The source URL associated with the character. */
    source_url?: string;
    /** The Chub-specific data associated with the character. */
    chub?: { full_path: string };
    /** The RisuAI-specific data associated with the character. */
    risuai?: { source: string[] };
    /** SD-specific data associated with the character. */
    sd_character_prompt?: { positive: string; negative: string };
  };

  /**
   * Regex script data for character processing.
   */
  type RegexScriptData = {
    /** UUID of the script */
    id: string;
    /** The name of the script */
    scriptName: string;
    /** The regex to find */
    findRegex: string;
    /** The string to replace */
    replaceString: string;
    /** The strings to trim */
    trimStrings: string[];
    /** The placement of the script */
    placement: number[];
    /** Whether the script is disabled */
    disabled: boolean;
    /** Whether the script only applies to Markdown */
    markdownOnly: boolean;
    /** Whether the script only applies to prompts */
    promptOnly: boolean;
    /** Whether the script runs on edit */
    runOnEdit: boolean;
    /** Whether the regex should be substituted */
    substituteRegex: number;
    /** The minimum depth */
    minDepth: number;
    /** The maximum depth */
    maxDepth: number;
  };

  type PopupOptions = {
    /** Custom text for the OK button, or `true` to use the default (If set, the button will always be displayed, no matter the type of popup) */
    okButton?: string | boolean;
    /** Custom text for the Cancel button, or `true` to use the default (If set, the button will always be displayed, no matter the type of popup) */
    cancelButton?: string | boolean;
    /** The number of rows for the input field */
    rows?: number;
    /** Whether to display the popup in wide mode (wide screen, 1/1 aspect ratio) */
    wide?: boolean;
    /** Whether to display the popup in wider mode (just wider, no height scaling) */
    wider?: boolean;
    /** Whether to display the popup in large mode (90% of screen) */
    large?: boolean;
    /** Whether to display the popup in transparent mode (no background, border, shadow or anything, only its content) */
    transparent?: boolean;
    /** Whether to allow horizontal scrolling in the popup */
    allowHorizontalScrolling?: boolean;
    /** Whether to allow vertical scrolling in the popup */
    allowVerticalScrolling?: boolean;
    /** Whether the popup content should be left-aligned by default */
    leftAlign?: boolean;
    /** Animation speed for the popup (opening, closing, ...) */
    animation?: 'slow' | 'fast' | 'none';
    /** The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`. */
    defaultResult?: number;
    /** Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward. */
    customButtons?: CustomPopupButton[] | string[];
    /** Custom inputs to add to the popup. The display below the content and the input box, one by one. */
    customInputs?: CustomPopupInput[];
    /** Handler called before the popup closes, return `false` to cancel the close */
    onClosing?: (popup: InstanceType<typeof SillyTavern.Popup>) => Promise<boolean | void>;
    /** Handler called after the popup closes, but before the DOM is cleaned up */
    onClose?: (popup: InstanceType<typeof SillyTavern.Popup>) => Promise<void>;
    /** Handler called after the popup opens */
    onOpen?: (popup: InstanceType<typeof SillyTavern.Popup>) => Promise<void>;
    /** Aspect ratio for the crop popup */
    cropAspect?: number;
    /** Image URL to display in the crop popup */
    cropImage?: string;
  };

  type CustomPopupButton = {
    /** The text of the button */
    text: string;
    /** The result of the button - can also be a custom result value to make be able to find out that this button was clicked. If no result is specified, this button will **not** close the popup. */
    result?: number;
    /** Optional custom CSS classes applied to the button */
    classes?: string[] | string;
    /** Optional action to perform when the button is clicked */
    action?: () => void;
    /** Whether to append the button to the end of the popup - by default it will be prepended */
    appendAtEnd?: boolean;
  };

  type CustomPopupInput = {
    /** The id for the html element */
    id: string;
    /** The label text for the input */
    label: string;
    /** Optional tooltip icon displayed behind the label */
    tooltip?: string;
    /** The default state when opening the popup (false if not set) */
    defaultState?: boolean;
    /** The type of the input (default is checkbox) */
    type?: string;
  };
}

/**
 * 酒馆提供给插件的稳定接口, 具体内容见于 SillyTavern/public/scripts/st-context.js 或 https://github.com/SillyTavern/SillyTavern/blob/release/public/scripts/st-context.js
 * 你也可以在酒馆页面按 f12, 在控制台中输入 `window.SillyTavern.getContext()` 来查看当前酒馆所提供的接口
 */
declare const SillyTavern: {
  readonly accountStorage: any;
  readonly chat: Array<SillyTavern.ChatMessage>;
  readonly characters: SillyTavern.v1CharData[];
  readonly groups: any;
  readonly name1: string;
  readonly name2: string;
  /* this_chid */
  readonly characterId: string;
  readonly groupId: string;
  readonly chatId: string;
  readonly getCurrentChatId: () => string;
  readonly getRequestHeaders: () => {
    'Content-Type': string;
    'X-CSRF-TOKEN': string;
  };
  readonly reloadCurrentChat: () => Promise<void>;
  readonly renameChat: (old_name: string, new_name: string) => Promise<void>;
  readonly saveSettingsDebounced: () => Promise<void>;
  readonly onlineStatus: string;
  readonly maxContext: number;
  /** chat_metadata */
  readonly chatMetadata: Record<string, any>;
  readonly streamingProcessor: any;
  readonly eventSource: {
    on: typeof eventOn;
    makeLast: typeof eventMakeLast;
    makeFirst: typeof eventMakeFirst;
    removeListener: typeof eventRemoveListener;
    emit: typeof eventEmit;
    emitAndWait: typeof eventEmitAndWait;
    once: typeof eventOnce;
  };
  readonly eventTypes: typeof tavern_events;
  readonly addOneMessage: (
    mes: SillyTavern.ChatMessage,
    options?: {
      type?: 'swipe';
      insertAfter?: number;
      scroll?: true;
      insertBefore?: number;
      forceId?: number;
      showSwipes?: boolean;
    },
  ) => JQuery<HTMLElement>;
  readonly deleteLastMessage: () => Promise<void>;
  readonly generate: Function;
  readonly sendStreamingRequest: (type: string, data: object) => Promise<void>;
  readonly sendGenerationRequest: (type: string, data: object) => Promise<void>;
  readonly stopGeneration: () => boolean;
  readonly tokenizers: any;
  readonly getTextTokens: (tokenizer_type: number, string: string) => Promise<number>;
  readonly getTokenCountAsync: (string: string, padding?: number | undefined) => Promise<number>;
  /**  `/inject`、`setExtensionPrompt` 等注入的所有额外提示词 */
  readonly extensionPrompts: Record<
    string,
    {
      value: string;
      position: number;
      depth: number;
      scan: boolean;
      role: number;
      filter: () => Promise<boolean> | boolean;
    }
  >;
  /**
   * 注入一段额外的提示词
   *
   * @param prompt_id id, 重复则会替换原本的内容
   * @param content 内容
   * @param position 位置. -1 为不注入 (配合 scan=true 来仅用于激活绿灯), 1 为插入到聊天中
   * @param depth 深度
   * @param scan 是否作为欲扫描文本, 加入世界书绿灯条目扫描文本中
   * @param role 消息角色. 0 为 system, 1 为 user, 2 为 assistant
   * @param filter 提示词在什么情况下启用
   */
  readonly setExtensionPrompt: (
    prompt_id: string,
    content: string,
    position: -1 | 1,
    depth: number,
    scan?: boolean,
    role?: number,
    filter?: () => Promise<boolean> | boolean,
  ) => Promise<void>;
  readonly updateChatMetadata: (new_values: any, reset: boolean) => void;
  readonly saveChat: () => Promise<void>;
  readonly openCharacterChat: (file_name: any) => Promise<void>;
  readonly openGroupChat: (group_id: any, chat_id: any) => Promise<void>;
  readonly saveMetadata: () => Promise<void>;
  readonly sendSystemMessage: (type: any, text: any, extra?: any) => Promise<void>;
  readonly activateSendButtons: () => void;
  readonly deactivateSendButtons: () => void;
  readonly saveReply: (options: any, ...args: any[]) => Promise<void>;
  readonly substituteParams: (
    content: string,
    name1?: string,
    name2?: string,
    original?: string,
    group?: string,
    replace_character_card?: boolean,
    additional_macro?: Record<string, any>,
    post_process_function?: (text: string) => string,
  ) => Promise<void>;
  readonly substituteParamsExtended: (
    content: string,
    additional_macro?: Record<string, any>,
    post_process_function?: (text: string) => string,
  ) => Promise<void>;
  readonly SlashCommandParser: any;
  readonly SlashCommand: any;
  readonly SlashCommandArgument: any;
  readonly SlashCommandNamedArgument: any;
  readonly ARGUMENT_TYPE: {
    STRING: string;
    NUMBER: string;
    RANGE: string;
    BOOLEAN: string;
    VARIABLE_NAME: string;
    CLOSURE: string;
    SUBCOMMAND: string;
    LIST: string;
    DICTIONARY: string;
  };
  readonly executeSlashCommandsWithOptions: (
    text: string,
    options?: any,
  ) => Promise<{
    interrupt: boolean;
    pipe: string;
    isBreak: boolean;
    isAborted: boolean;
    isQuietlyAborted: boolean;
    abortReason: string;
    isError: boolean;
    errorMessage: string;
  }>;
  readonly timestampToMoment: (timestamp: string | number) => any;
  readonly registerMacro: (key: string, value: string | ((uid: string) => string), description?: string) => void;
  readonly unregisterMacro: (key: string) => void;
  readonly registerFunctionTool: (tool: {
    /** 工具名称 */
    name: string;
    /** 工具显示名称 */
    displayName: string;
    /** 工具描述 */
    description: string;
    /** 对函数参数的 JSON schema 定义, 可以通过 zod 的 z.toJSONSchema 来得到 */
    parameters: Record<string, any>;
    /** 要注册的函数调用工具 */
    action: ((args: any) => string) | ((args: any) => Promise<string>);

    /** 要如何格式化函数调用结果消息; 默认不进行任何操作, 显示为 `'Invoking tool: 工具显示名称'` */
    formatMessage?: (args: any) => string;
    /** 在下次聊天补全请求时是否注册本工具; 默认为始终注册 */
    shouldRegister?: (() => boolean) | (() => Promise<boolean>);
    /** 是否不在楼层中用一层楼显示函数调用结果, `true` 则不显示且将不会触发生成; 默认为 false */
    stealth?: boolean;
  }) => void;
  readonly unregisterFunctionTool: (name: string) => void;
  readonly isToolCallingSupported: () => boolean;
  readonly canPerformToolCalls: (type: string) => boolean;
  readonly ToolManager: any;
  readonly registerDebugFunction: (function_id: string, name: string, description: string, fn: Function) => void;
  readonly renderExtensionTemplateAsync: (
    extension_name: string,
    template_id: string,
    template_data?: object,
    sanitize?: boolean,
    localize?: boolean,
  ) => Promise<string>;
  readonly registerDataBankScraper: (scraper: any) => Promise<void>;
  readonly showLoader: () => void;
  readonly hideLoader: () => Promise<any>;
  readonly mainApi: any;
  /** extension_settings */
  readonly extensionSettings: Record<string, any>;
  readonly ModuleWorkerWrapper: any;
  readonly getTokenizerModel: () => string;
  readonly generateQuietPrompt: () => (
    quiet_prompt: string,
    quiet_to_loud: boolean,
    skip_wian: boolean,
    quiet_iamge?: string,
    quiet_name?: string,
    response_length?: number,
    force_chid?: number,
  ) => Promise<string>;
  /** 严禁使用本方法修改角色卡的 `extensions` 字段, 它会合并原有值和新值而不是替换; 应该使用 `updateCharacterWith` */
  readonly writeExtensionField: (character_id: number, key: string, value: any) => Promise<void>;
  readonly getThumbnailUrl: (type: any, file: any) => string;
  readonly selectCharacterById: (id: number, { switchMenu }?: { switchMenu?: boolean }) => Promise<void>;
  readonly messageFormatting: (
    message: string,
    ch_name: string,
    is_system: boolean,
    is_user: boolean,
    message_id: number,
    sanitizerOverrides?: object,
    isReasoning?: boolean,
  ) => string;
  readonly shouldSendOnEnter: () => boolean;
  readonly isMobile: () => boolean;
  readonly t: (strings: TemplateStringsArray, ...values: any[]) => string;
  readonly translate: (text: string, key?: string | null) => string;
  readonly getCurrentLocale: () => string;
  readonly addLocaleData: (localeId: string, data: Record<string, string>) => void;
  readonly tags: any[];
  readonly tagMap: {
    [identifier: string]: string[];
  };
  readonly menuType: any;
  readonly createCharacterData: Record<string, any>;
  readonly Popup: {
    new (
      content: JQuery<HTMLElement> | string | Element,
      type: number,
      inputValue?: string,
      popupOptions?: SillyTavern.PopupOptions,
    ): {
      dlg: HTMLDialogElement;

      show: () => Promise<void>;
      complete: (result: number) => Promise<void>;
      completeAffirmative: () => Promise<void>;
      completeNegative: () => Promise<void>;
      completeCancelled: () => Promise<void>;
    };
  };
  readonly POPUP_TYPE: {
    TEXT: number;
    CONFIRM: number;
    INPUT: number;
    DISPLAY: number;
    CROP: number;
  };
  readonly POPUP_RESULT: {
    AFFIRMATIVE: number;
    NEGATIVE: number;
    CANCELLED: number;
    CUSTOM1: number;
    CUSTOM2: number;
    CUSTOM3: number;
    CUSTOM4: number;
    CUSTOM5: number;
    CUSTOM6: number;
    CUSTOM7: number;
    CUSTOM8: number;
    CUSTOM9: number;
  };
  readonly callGenericPopup: (
    content: JQuery<HTMLElement> | string | Element,
    type: number,
    inputValue?: string,
    popupOptions?: SillyTavern.PopupOptions,
  ) => Promise<number | string | boolean | undefined>;
  /** oai_settings */
  readonly chatCompletionSettings: any;
  /** textgenerationwebui_settings */
  readonly textCompletionSettings: any;
  /** power_user */
  readonly powerUserSettings: any;
  readonly getCharacters: () => Promise<void>;
  readonly getCharacterCardFields: ({ chid }?: { chid?: number }) => any;
  readonly uuidv4: () => string;
  readonly humanizedDateTime: () => string;
  readonly updateMessageBlock: (
    message_id: number,
    message: object,
    { rerenderMessage }?: { rerenderMessage?: boolean },
  ) => void;
  readonly appendMediaToMessage: (mes: object, messageElement: JQuery<HTMLElement>, adjust_scroll?: boolean) => void;

  readonly loadWorldInfo: (name: string) => Promise<any | null>;
  readonly saveWorldInfo: (name: string, data: any, immediately?: boolean) => Promise<void>;
  /** reloadEditor */
  readonly reloadWorldInfoEditor: (file: string, loadIfNotSelected?: boolean) => void;
  readonly updateWorldInfoList: () => Promise<void>;
  readonly convertCharacterBook: (character_book: any) => {
    entries: Record<string, any>;
    originalData: Record<string, any>;
  };
  readonly getWorldInfoPrompt: (
    chat: string[],
    max_context: number,
    is_dry_run: boolean,
  ) => Promise<{
    worldInfoString: string;
    worldInfoBefore: string;
    worldInfoAfter: string;
    worldInfoExamples: any[];
    worldInfoDepth: any[];
    anBefore: any[];
    anAfter: any[];
  }>;
  readonly CONNECT_API_MAP: Record<string, any>;
  readonly getTextGenServer: (type?: string) => string;
  readonly extractMessageFromData: (data: object, activateApi?: string) => string;
  readonly getPresetManager: (apiId?: string) => any;
  readonly getChatCompletionModel: (source?: string) => string;
  readonly printMessages: () => Promise<void>;
  readonly clearChat: () => Promise<void>;
  readonly ChatCompletionService: any;
  readonly TextCompletionService: any;
  readonly ConnectionManagerRequestService: any;
  readonly updateReasoningUI: (
    message_id_or_element: number | JQuery<HTMLElement> | HTMLElement,
    { reset }?: { reset?: boolean },
  ) => void;
  readonly parseReasoningFromString: (string: string, { strict }?: { strict?: boolean }) => any | null;
  readonly unshallowCharacter: (character_id?: string) => Promise<void>;
  readonly unshallowGroupMembers: (group_id: string) => Promise<void>;
  readonly symbols: {
    ignore: any;
  };
};
/**
 * 酒馆助手提供的额外功能, 具体内容见于 https://n0vi028.github.io/JS-Slash-Runner-Doc
 * 你也可以在酒馆页面按 f12, 在控制台中输入 `window.TavernHelper` 来查看当前酒馆助手所提供的接口
 */
declare const TavernHelper: typeof window.TavernHelper;
/**
 * 获取按钮对应的事件类型, **只能在脚本中使用**
 *
 * @param button_name 按钮名
 * @returns 事件类型
 *
 * @example
 * const event_type = getButtonEvent('按钮名');
 * eventOn(event_type, () => {
 *   console.log('按钮被点击了');
 * });
 */
declare function getButtonEvent(button_name: string): string;

/**
 * 获取脚本的按钮列表, **只能在脚本中使用**
 *
 * @returns 按钮数组
 *
 * @example
 * // 在脚本内获取当前脚本的按钮设置
 * const buttons = getScriptButtons();
 */
declare function getScriptButtons(): ScriptButton[];

/**
 * 完全替换脚本的按钮列表, **只能在脚本中使用**
 *
 * @param buttons 按钮数组
 *
 * @example
 * // 在脚本内设置脚本按钮为一个"开始游戏"按钮
 * replaceScriptButtons([{name: '开始游戏', visible: true}])
 *
 * @example
 * // 点击"前往地点"按钮后，切换为地点选项按钮
 * eventOnButton("前往地点" () => {
 *   replaceScriptButtons([{name: '学校', visible: true}, {name: '商店', visible: true}])
 * })
 */
declare function replaceScriptButtons(buttons: ScriptButton[]): void;

/**
 * 用 `updater` 函数更新脚本按钮列表, **只能在脚本中使用**
 *
 * @param updater 用于更新脚本按钮列表的函数. 它应该接收脚本按钮列表作为参数, 并返回更新后的脚本按钮列表.
 *
 * @returns 更新后的脚本按钮列表
 *
 * @example
 * // 新增一个按钮到末尾
 * updateVariablesWith(buttons => [...buttons, { name: '新按钮', visible: true }]);
 */
declare function updateScriptButtonsWith(updater: (buttons: ScriptButton[]) => ScriptButton[]): ScriptButton[];

/**
 * 用 `updater` 函数更新脚本按钮列表, **只能在脚本中使用**
 *
 * @param updater 用于更新脚本按钮列表的函数. 它应该接收脚本按钮列表作为参数, 并返回更新后的脚本按钮列表.
 *
 * @returns 更新后的脚本按钮列表
 */
declare function updateScriptButtonsWith(
  updater: (buttons: ScriptButton[]) => Promise<ScriptButton[]>,
): Promise<ScriptButton[]>;

/**
 * 为脚本按钮列表末尾添加不存在的按钮, 不会重复添加同名按钮, **只能在脚本中使用**
 *
 * @param buttons
 *
 * @exmaple
 * // 新增 "重新开始" 按钮
 * appendInexistentScriptButtons([{name: '重新开始', visible: true}]);
 */
declare function appendInexistentScriptButtons(buttons: ScriptButton[]): void;

/** 获取脚本名称 */
declare function getScriptName(): string;

/** 获取脚本作者注释 */
declare function getScriptInfo(): string;

/**
 * 替换脚本作者注释
 *
 * @param info 新的作者注释
 */
declare function replaceScriptInfo(info: string): void;
/**
 * 在前端界面或脚本内使用, 从而重新加载前端界面或脚本
 *
 * 这相当于调用 `window.location.reload()`, 会让分享到全局的接口失效;
 *   如果有需要在重新加载前端界面后沿用的数据, 你应该自行编写重新加载方式而不是使用这个函数
 *
 * @example
 * // 当聊天文件变更时, 重新加载前端界面或脚本
 * let current_chat_id = SillyTavern.getCurrentChatId();
 * eventOn(tavern_events.CHAT_CHANGED, chat_id => {
 *   if (current_chat_id !== chat_id) {
 *     current_chat_id = chat_id;
 *     reloadIframe();
 *   }
 * })
 *
 * @example
 * // 自行编写重新加载方式
 * function initailzie() { ... }
 * $(initialize);
 *
 * function destroy() { eventClearAll(); ... }
 * $(window).on('pagehide', destroy);
 *
 * function reload() {
 *   destory();
 *   initialize();
 * }
 */
declare function reloadIframe(): void;

/**
 * 获取前端界面或脚本的标识名称
 *
 * @returns 对于前端界面是 `TH-message--楼层号--前端界面是该楼层第几个界面`, 对于脚本库是 `TH-script--脚本名称--脚本id`
 */
declare function getIframeName(): string;

/**
 * 获取本消息楼层 iframe 所在楼层的楼层 id, **只能对楼层消息 iframe** 使用
 *
 * @returns 楼层 id
 *
 * @throws 如果不在楼层消息 iframe 内使用, 将会抛出错误
 */
declare function getCurrentMessageId(): number;

/**
 * 获取脚本的脚本库 id, **只能在脚本内使用**
 *
 * @returns 脚本库的 id
 *
 * @throws 如果不在脚本内使用, 将会抛出错误
 */
declare function getScriptId(): string;
/**
 * 获取合并后的变量表
 * - 如果在消息楼层 iframe 中调用本函数, 则获取 全局→角色卡→聊天→0号消息楼层→中间所有消息楼层→当前消息楼层 的合并结果
 * - 如果在全局变量 iframe 中调用本函数, 则获取 全局→角色卡→脚本→聊天→0号消息楼层→中间所有消息楼层→最新消息楼层 的合并结果
 *
 * @example
 * const variables = getAllVariables();
 */
declare function getAllVariables(): Record<string, any>;
