Skip to content

fix: 提升打包版桌面端启动稳定性并优化插件依赖处理#5031

Merged
Soulter merged 19 commits intoAstrBotDevs:masterfrom
zouyonghe:master
Feb 11, 2026
Merged

fix: 提升打包版桌面端启动稳定性并优化插件依赖处理#5031
Soulter merged 19 commits intoAstrBotDevs:masterfrom
zouyonghe:master

Conversation

@zouyonghe
Copy link
Member

@zouyonghe zouyonghe commented Feb 11, 2026

变更概述

本 PR 主要修复了打包版 Electron 启动/退出阶段的稳定性问题,并优化了插件依赖加载逻辑。

具体改动

  • 优化插件依赖恢复流程(打包运行时):
    • 插件导入失败时,先尝试从 ~/.astrbot/data/site-packages 恢复已安装依赖;
    • 仅在恢复失败时才执行 pip install,避免不必要的重复重装。
  • 升级桌面端 Electron 版本:
    • electron30.5.1 升级到 40.3.0
  • 调整 CI Node 配置:
    • GitHub Actions 中 setup-node 统一为 node-version: 'latest'
  • 修复退出时 pending task 警告:
    • webchat 增加可优雅终止机制;
    • PlatformManager 增加平台任务跟踪与取消,避免出现:
      • Task was destroyed but it is pending!
  • 打包版后端默认监听本机:
    • Electron 自动拉起后端时(且未显式覆盖),默认注入:
      • DASHBOARD_HOST=127.0.0.1
      • DASHBOARD_PORT=6185
  • 统一日志时间格式:
    • Electron 侧时间戳改为本地时区带偏移格式,便于与后端日志对齐排查。

变更原因

  • 减少插件依赖冲突场景下的重复安装和启动噪音日志;
  • 解决应用退出/重启阶段异步任务未清理导致的告警;
  • 提升打包版默认安全性(本机监听);
  • 提升跨端日志可读性与排障效率。

验证情况

  • uv run ruff format .
  • uv run ruff check .
  • 修改后 Python 文件编译检查通过
  • 修改后桌面端 JS 模块加载检查通过
  • 启动日志验证:依赖恢复成功时可跳过重装,应用可正常启动

兼容性说明

  • 若用户显式设置了 DASHBOARD_HOST/PORT(或 ASTRBOT_DASHBOARD_HOST/PORT),仍以显式配置为准。
  • 改动主要影响打包版自动拉起后端及平台任务生命周期管理逻辑。

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.

新特性(New Features):

  • 允许通过环境变量覆盖打包后后端仪表盘的主机和端口,并为打包运行提供合理的默认值。

缺陷修复(Bug Fixes):

  • 在关闭时跟踪并取消平台和 webchat 任务,避免未完成任务警告并确保干净退出。
  • 当插件导入失败时,先尝试复用打包环境中 site-packages 里已安装的依赖,再考虑重新安装。
  • 确保在平台终止时,webchat 队列监听器和任务能够被正确清理。

改进(Enhancements):

  • 统一 Electron 和后端日志时间戳,使用带显式时区偏移的本地时间格式,以便更容易进行跨组件日志关联。
  • 优化插件加载流程,更一致地计算和复用插件目录路径以及依赖(requirements)文件。
  • 在 pip 安装器中新增辅助工具,以在打包的 Electron 运行时中优先使用已安装依赖。

构建(Build):

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 发布工作流,通过 setup-node 使用最新的 Node.js 版本。
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.

新特性(New Features):

  • 允许通过环境变量覆盖打包后后端仪表盘的主机和端口,并为打包运行提供合理的默认值。

缺陷修复(Bug Fixes):

  • 在关闭时跟踪并取消平台和 webchat 任务,避免未完成任务警告并确保干净退出。
  • 当插件导入失败时,先尝试复用打包环境中 site-packages 里已安装的依赖,再考虑重新安装。
  • 确保在平台终止时,webchat 队列监听器和任务能够被正确清理。

改进(Enhancements):

  • 统一 Electron 和后端日志时间戳,使用带显式时区偏移的本地时间格式,以便更容易进行跨组件日志关联。
  • 优化插件加载流程,更一致地计算和复用插件目录路径以及依赖(requirements)文件。
  • 在 pip 安装器中新增辅助工具,以在打包的 Electron 运行时中优先使用已安装依赖。

构建(Build):

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 发布工作流,通过 setup-node 使用最新的 Node.js 版本。
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.

新功能:

  • 为 Electron 和后端日志添加基于大小的日志轮转机制,可配置大小上限和备份数量。
  • 为打包后的后端进程引入缓冲日志机制,以减少 I/O,并支持在退出时优雅刷新日志。
  • 允许通过环境变量覆盖打包后端的仪表盘主机和端口,并提供合理的默认值。

错误修复:

  • 在关闭时跟踪并取消平台和网页聊天任务,避免待处理任务警告,确保干净退出。
  • 当插件导入失败时,先尝试重用打包的 site-packages 中已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,网页聊天监听器和队列能够被正确清理。

改进:

  • 统一 Electron 端日志时间戳格式,使用带明确偏移的本地时间格式,便于日志关联分析。
  • 重构日志工具,将其抽象为可复用的轮转日志辅助工具,在 Electron 和后端日志之间共享。

构建:

  • 将桌面应用使用的 Electron 依赖从 30.x 系列升级到 40.3.0。
  • 更新 GitHub Actions 发布流程,使用 setup-node 配置最新的 Node.js 版本。

文档:

  • 记录桌面运行时日志轮转行为,以及用于控制日志大小、备份数量和调试输出的环境变量。
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.

新特性(New Features):

  • 允许通过环境变量覆盖打包后后端仪表盘的主机和端口,并为打包运行提供合理的默认值。

缺陷修复(Bug Fixes):

  • 在关闭时跟踪并取消平台和 webchat 任务,避免未完成任务警告并确保干净退出。
  • 当插件导入失败时,先尝试复用打包环境中 site-packages 里已安装的依赖,再考虑重新安装。
  • 确保在平台终止时,webchat 队列监听器和任务能够被正确清理。

改进(Enhancements):

  • 统一 Electron 和后端日志时间戳,使用带显式时区偏移的本地时间格式,以便更容易进行跨组件日志关联。
  • 优化插件加载流程,更一致地计算和复用插件目录路径以及依赖(requirements)文件。
  • 在 pip 安装器中新增辅助工具,以在打包的 Electron 运行时中优先使用已安装依赖。

构建(Build):

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 发布工作流,通过 setup-node 使用最新的 Node.js 版本。
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.

新特性(New Features):

  • 允许通过环境变量覆盖打包后后端仪表盘的主机和端口,并为打包运行提供合理的默认值。

缺陷修复(Bug Fixes):

  • 在关闭时跟踪并取消平台和 webchat 任务,避免未完成任务警告并确保干净退出。
  • 当插件导入失败时,先尝试复用打包环境中 site-packages 里已安装的依赖,再考虑重新安装。
  • 确保在平台终止时,webchat 队列监听器和任务能够被正确清理。

改进(Enhancements):

  • 统一 Electron 和后端日志时间戳,使用带显式时区偏移的本地时间格式,以便更容易进行跨组件日志关联。
  • 优化插件加载流程,更一致地计算和复用插件目录路径以及依赖(requirements)文件。
  • 在 pip 安装器中新增辅助工具,以在打包的 Electron 运行时中优先使用已安装依赖。

构建(Build):

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 发布工作流,通过 setup-node 使用最新的 Node.js 版本。
Original summary in English

由 Sourcery 提供的摘要

通过强化平台 / webchat 的关闭流程、优化插件依赖加载,以及统一 Electron 端日志与后端的默认设置,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量覆盖打包后的后端仪表盘(dashboard)主机和端口;当从 Electron 启动时,如果未配置则安全地默认指向 localhost。

缺陷修复:

  • 在关闭时跟踪并取消各平台和 webchat 的任务,避免挂起任务的警告,确保应用干净退出。
  • 当插件导入失败时,优先尝试复用打包环境中 site-packages 里已安装的插件依赖,而不是直接重新安装。
  • 确保在平台终止时,webchat 的队列监听器和任务能够被正确停止并清理。

改进:

  • 引入共享的时间戳格式化工具,并将 Electron 与后端启动日志统一切换为带有明确时区偏移的本地化时间戳,方便跨组件日志对齐与排查。
  • 优化插件加载逻辑,以一致的方式推导插件目录路径和依赖文件,并在 pip 安装器中新增辅助函数,在打包运行时优先使用已安装的依赖。

构建:

  • 将桌面端的 Electron 依赖从 30.x 升级到 40.3.0。

CI:

  • 更新发布用的 GitHub Actions 工作流,对 dashboard 和桌面构件使用 Node.js 24.13.0 进行构建。
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.
Original summary in English

由 Sourcery 提供的摘要

通过在关闭时跟踪并取消平台 / webchat 任务、优化插件依赖恢复机制,以及收紧后端仪表盘的默认配置和日志记录,提升打包桌面运行时的稳定性。

新功能:

  • 允许通过环境变量配置仪表盘的主机与端口;当从打包的 Electron 应用启动时,使用安全的 localhost 默认值。

缺陷修复:

  • 在关闭过程中跟踪并取消各平台和 webchat 的任务,以避免“待处理任务”警告并确保干净退出。
  • 在平台终止时确保停止并清理 webchat 队列监听器,防止残留的后台工作。
  • 在导入失败时,通过优先使用已安装的 site-packages 依赖来恢复插件导入,而不是立即触发重新安装。

改进与增强:

  • 优化插件加载逻辑,以一致的方式推导插件目录和依赖文件路径,并在打包运行时复用已安装依赖。
  • 统一 Electron 侧日志时间戳为带明确时区偏移的本地时间格式,以便更容易与后端日志进行对照。

构建相关:

  • 将桌面端 Electron 依赖从 30.x 升级到 40.3.0。
  • 更新 GitHub Actions 工作流,在构建仪表盘与桌面产物时使用 Node.js 24.13.0。
Original summary in English

Summary by Sourcery

Improve packaged desktop runtime stability by tracking and cancelling platform/webchat tasks on shutdown, optimizing plugin dependency recovery, and tightening backend dashboard defaults and logging.

New Features:

  • Allow configuring the dashboard host and port via environment variables with safe localhost defaults when launched from the packaged Electron app.

Bug Fixes:

  • Track and cancel per-platform and webchat tasks during shutdown to avoid pending-task warnings and ensure clean exit.
  • Ensure webchat queue listeners are stopped and cleared when the platform is terminated to prevent lingering background work.
  • Recover plugin imports by preferring already-installed site-packages dependencies before triggering a fresh installation when imports fail.

Enhancements:

  • Refine plugin loading to consistently derive plugin directories and requirement paths and reuse installed dependencies in the packaged runtime.
  • Unify Electron-side log timestamps to a local time format with explicit timezone offset for easier correlation with backend logs.

Build:

  • Upgrade the desktop Electron dependency from 30.x to 40.3.0.
  • Update GitHub Actions workflows to build dashboard and desktop artifacts with Node.js 24.13.0.

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Feb 11, 2026
@dosubot
Copy link

dosubot bot commented Feb 11, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我们未能获取拉取请求 #5031 的差异内容。

你可以在此拉取请求中评论 @sourcery-ai review 来重试,或者联系我们以获取帮助。

Original comment in English

We failed to fetch the diff for pull request #5031

You can try again by commenting this pull request with @sourcery-ai review, or contact us for help.

@dosubot dosubot bot added area:core The bug / feature is about astrbot's core, backend area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. labels Feb 11, 2026
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Feb 11, 2026
@zouyonghe
Copy link
Member Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 3 个问题,并给出了一些高层次的反馈:

  • PlatformManager.terminate 中,每个 platform 实例似乎被终止了两次(一次通过 terminate_platform,一次在 platform_insts 的循环中);建议将每个实例的关闭流程集中到单一路径上,避免重复调用 terminate(),并减少对各个平台实现幂等性的依赖。
  • WebChatQueueMgr.clear_listener 中,任务被取消但从未被 await;为避免出现 Task was destroyed but it is pending! 之类的警告,建议在清空字典前,使用 gather 收集已取消的 _listener_tasksreturn_exceptions=True)。
给 AI Agents 的提示
请根据以下代码审查意见进行修改:

## 总体评论
-`PlatformManager.terminate` 中,每个 platform 实例似乎被终止了两次(一次通过 `terminate_platform`,一次在 `platform_insts` 的循环中);建议将每个实例的关闭流程集中到单一路径上,避免重复调用 `terminate()`,并减少对各个平台实现幂等性的依赖。
-`WebChatQueueMgr.clear_listener` 中,任务被取消但从未被 `await`;为避免出现 `Task was destroyed but it is pending!` 之类的警告,建议在清空字典前,使用 `gather` 收集已取消的 `_listener_tasks``return_exceptions=True`)。

## 单独评论

### 评论 1
<location> `astrbot/core/platform/sources/webchat/webchat_queue_mgr.py:90-96` </location>
<code_context>
         for conversation_id in list(self.queues.keys()):
             self._start_listener_if_needed(conversation_id)

+    def clear_listener(self):
+        self._listener_callback = None
+        for close_event in self._queue_close_events.values():
+            close_event.set()
+        for task in self._listener_tasks.values():
+            task.cancel()
+        self._listener_tasks.clear()
+
     def _start_listener_if_needed(self, conversation_id: str):
</code_context>

<issue_to_address>
**suggestion (performance):** 清理 listener 时并没有重置 `_queue_close_events`,这可能会在多次生命周期中导致资源堆积。

目前 `clear_listener()` 只是对 `_queue_close_events` 中的事件调用 `set`,但从不移除它们。在一个长时间运行的进程中,如果在清理后再次创建新的会话,就会不断遗留旧的事件对象。请清空 `_queue_close_events`(或至少清理与已取消 listener 相关的条目),以避免该映射无限增长。

```suggestion
    def clear_listener(self):
        self._listener_callback = None
        for close_event in self._queue_close_events.values():
            close_event.set()
        self._queue_close_events.clear()
        for task in self._listener_tasks.values():
            task.cancel()
        self._listener_tasks.clear()
```
</issue_to_address>

### 评论 2
<location> `.github/workflows/release.yml:60` </location>
<code_context>
         uses: actions/setup-node@v6
         with:
-          node-version: 20
+          node-version: 'latest'
           cache: "pnpm"
           cache-dependency-path: dashboard/pnpm-lock.yaml
</code_context>

<issue_to_address>
**suggestion (bug_risk):** 在 CI 中使用 `node-version: 'latest'` 会让发布过程不可复现,并且可能不稳定。

这里使用 `latest` 意味着 CI 会在新的 Node.js 主版本发布时静默切换到新版本,这可能在代码未变化的情况下导致构建或打包失败。更推荐固定到某个主版本(或精确版本),如 `20.x``22.x`,并在你决定支持更高版本运行时时再有计划地升级。

建议实现:

```
      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '20.x'
          cache: "pnpm"
          cache-dependency-path: dashboard/pnpm-lock.yaml

```

```
      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '20.x'
          cache: "pnpm"
          cache-dependency-path: |
            dashboard/pnpm-lock.yaml

```
</issue_to_address>

### 评论 3
<location> `astrbot/core/platform/manager.py:244` </location>
<code_context>
                 await inst.terminate()
+            await self._stop_platform_task(client_id)

     async def terminate(self) -> None:
-        for inst in self.platform_insts:
-            if getattr(inst, "terminate", None):
</code_context>

<issue_to_address>
**issue (complexity):** 建议通过单一路径来处理所有 platform 的终止逻辑,并将 `_platform_tasks` 封装在辅助方法之后,从而简化关闭流程。

你可以保留当前的任务跟踪能力,但通过一个统一、一致的终止路径来完成关闭,同时把 `_platform_tasks` 视为实现细节。

当前 `terminate()` 的行为:

1. 遍历 `_inst_map` 并调用 `terminate_platform()`(该方法内部已经调用 `_stop_platform_task()`)。
2. 遍历 `platform_insts`,再次对每个实例调用 `inst.terminate()``_stop_platform_task()`3. 遍历 `_platform_tasks`,第三次调用 `_stop_platform_task()`。

由于 `_stop_platform_task()` 本身就是幂等的,其实不需要三次遍历。你可以:

-`terminate_platform()` 成为所有存在于 `_inst_map` 中的平台实例的唯一关闭入口。
-`platform_insts` 中不在 `_inst_map` 中的实例(例如 webchat)进行第二轮处理。
- 完全避免单独遍历 `_platform_tasks`;在按实例停止任务之后直接清空它即可。

例如,可以将 `terminate()` 简化为类似下面这样:

```python
async def terminate(self) -> None:
    # 1. Terminate all platforms managed via _inst_map
    #    terminate_platform() already calls _stop_platform_task()
    for platform_id in list(self._inst_map.keys()):
        await self.terminate_platform(platform_id)

    # 2. Terminate any remaining instances (e.g. ones not in _inst_map like webchat)
    handled_client_ids = {
        info["client_id"] for info in self._inst_map.values()
    }
    for inst in list(self.platform_insts):
        client_id = inst.client_self_id
        if client_id in handled_client_ids:
            continue
        try:
            if getattr(inst, "terminate", None):
                await inst.terminate()
        finally:
            await self._stop_platform_task(client_id)

    # 3. Clear state; tasks should already be cancelled at this point
    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()
```

这样可以让 `_platform_tasks` 完全作为 `_start_platform_task/_stop_platform_task` 的内部细节存在,同时移除三重关闭路径,并保持当前行为不变(每个平台的 `terminate` 调用 + 任务取消)。
</issue_to_address>

Sourcery 对开源项目免费使用——如果你觉得这些评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • In PlatformManager.terminate, each platform instance appears to be terminated twice (once via terminate_platform and again in the platform_insts loop); consider centralizing shutdown per instance to avoid redundant terminate() calls and relying on idempotency of platform implementations.
  • In WebChatQueueMgr.clear_listener, tasks are cancelled but never awaited; to avoid potential Task was destroyed but it is pending! warnings, consider gathering the cancelled _listener_tasks (with return_exceptions=True) before clearing the dict.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `PlatformManager.terminate`, each platform instance appears to be terminated twice (once via `terminate_platform` and again in the `platform_insts` loop); consider centralizing shutdown per instance to avoid redundant `terminate()` calls and relying on idempotency of platform implementations.
- In `WebChatQueueMgr.clear_listener`, tasks are cancelled but never awaited; to avoid potential `Task was destroyed but it is pending!` warnings, consider gathering the cancelled `_listener_tasks` (with `return_exceptions=True`) before clearing the dict.

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/sources/webchat/webchat_queue_mgr.py:90-96` </location>
<code_context>
         for conversation_id in list(self.queues.keys()):
             self._start_listener_if_needed(conversation_id)

+    def clear_listener(self):
+        self._listener_callback = None
+        for close_event in self._queue_close_events.values():
+            close_event.set()
+        for task in self._listener_tasks.values():
+            task.cancel()
+        self._listener_tasks.clear()
+
     def _start_listener_if_needed(self, conversation_id: str):
</code_context>

<issue_to_address>
**suggestion (performance):** Clearing the listener does not reset `_queue_close_events`, which may cause resource buildup across lifecycles.

Right now, `clear_listener()` only sets the events in `_queue_close_events` but never removes them. In a long‑running process where new conversations are created after clearing, this can leave stale events around indefinitely. Please clear `_queue_close_events` (or prune entries tied to cancelled listeners) to prevent unbounded growth of this mapping.

```suggestion
    def clear_listener(self):
        self._listener_callback = None
        for close_event in self._queue_close_events.values():
            close_event.set()
        self._queue_close_events.clear()
        for task in self._listener_tasks.values():
            task.cancel()
        self._listener_tasks.clear()
```
</issue_to_address>

### Comment 2
<location> `.github/workflows/release.yml:60` </location>
<code_context>
         uses: actions/setup-node@v6
         with:
-          node-version: 20
+          node-version: 'latest'
           cache: "pnpm"
           cache-dependency-path: dashboard/pnpm-lock.yaml
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Using `node-version: 'latest'` in CI makes releases non-reproducible and potentially unstable.

Using `latest` here means CI will silently switch to new major Node.js versions as they’re released, which can break builds or packaging without any code change. Prefer pinning to a major (or exact) version like `20.x` or `22.x` and updating it deliberately when you choose to support a newer runtime.

Suggested implementation:

```
      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '20.x'
          cache: "pnpm"
          cache-dependency-path: dashboard/pnpm-lock.yaml

```

```
      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '20.x'
          cache: "pnpm"
          cache-dependency-path: |
            dashboard/pnpm-lock.yaml

```
</issue_to_address>

### Comment 3
<location> `astrbot/core/platform/manager.py:244` </location>
<code_context>
                 await inst.terminate()
+            await self._stop_platform_task(client_id)

     async def terminate(self) -> None:
-        for inst in self.platform_insts:
-            if getattr(inst, "terminate", None):
</code_context>

<issue_to_address>
**issue (complexity):** Consider simplifying the shutdown flow by routing all platform termination through a single code path and hiding `_platform_tasks` behind helper methods.

You can keep the task-tracking feature but simplify shutdown by making a single, consistent termination path and treating `_platform_tasks` as an implementation detail.

Right now `terminate()`:

1. Iterates over `_inst_map` and calls `terminate_platform()` (which already calls `_stop_platform_task()`).
2. Iterates over `platform_insts`, calls `inst.terminate()` and `_stop_platform_task()` again.
3. Iterates over `_platform_tasks` and calls `_stop_platform_task()` a third time.

Because `_stop_platform_task()` is already idempotent, you don’t need three passes. You can:

- Let `terminate_platform()` be the sole entry point for platforms that exist in `_inst_map`.
- Handle any remaining instances in `platform_insts` that are not in `_inst_map` (e.g. webchat) in a second pass.
- Avoid iterating over `_platform_tasks` entirely; just clear it after you’ve stopped tasks per instance.

For example, `terminate()` can be reduced to something like:

```python
async def terminate(self) -> None:
    # 1. Terminate all platforms managed via _inst_map
    #    terminate_platform() already calls _stop_platform_task()
    for platform_id in list(self._inst_map.keys()):
        await self.terminate_platform(platform_id)

    # 2. Terminate any remaining instances (e.g. ones not in _inst_map like webchat)
    handled_client_ids = {
        info["client_id"] for info in self._inst_map.values()
    }
    for inst in list(self.platform_insts):
        client_id = inst.client_self_id
        if client_id in handled_client_ids:
            continue
        try:
            if getattr(inst, "terminate", None):
                await inst.terminate()
        finally:
            await self._stop_platform_task(client_id)

    # 3. Clear state; tasks should already be cancelled at this point
    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()
```

This keeps `_platform_tasks` as an internal detail of `_start_platform_task/_stop_platform_task` and removes the triple-path shutdown, while preserving all existing behavior (per-platform terminate + task cancellation).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

@zouyonghe
Copy link
Member Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 3 个问题,并给出了一些整体性的反馈:

  • WebChatAdapter.terminate 中,webchat_queue_mgr 被引用但没有在方法作用域内定义;可以考虑在 run 中将队列管理器保存为实例属性,然后在这里使用,以避免触发 NameError。
  • 在发布工作流中使用 node-version: 'latest' 会导致构建结果不可重现,并且在发布新的 Node.js 版本时变得脆弱;建议改为固定到某个特定的主版本或 LTS 版本。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
-`WebChatAdapter.terminate` 中,`webchat_queue_mgr` 被引用但没有在方法作用域内定义;可以考虑在 `run` 中将队列管理器保存为实例属性,然后在这里使用,以避免触发 NameError。
- 在发布工作流中使用 `node-version: 'latest'` 会导致构建结果不可重现,并且在发布新的 Node.js 版本时变得脆弱;建议改为固定到某个特定的主版本或 LTS 版本。

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/sources/webchat/webchat_queue_mgr.py:90-94` </location>
<code_context>
         for conversation_id in list(self.queues.keys()):
             self._start_listener_if_needed(conversation_id)

+    async def clear_listener(self) -> None:
+        self._listener_callback = None
+        for close_event in self._queue_close_events.values():
+            close_event.set()
+        self._queue_close_events.clear()
+
+        listener_tasks = list(self._listener_tasks.values())
</code_context>

<issue_to_address>
**issue (bug_risk):** 在遍历 `_queue_close_events` 的值时修改它,可能会在并发调用 `clear_listener` 时触发 `RuntimeError`。

在 `clear_listener` 中,你遍历 `self._queue_close_events.values()`,然后清空该字典。由于 `clear_listener` 可能被并发调用(例如从 `terminate()``QueueListener.run()``finally` 中),这可能会触发 `RuntimeError: dictionary changed size during iteration`。和处理 `_listener_tasks` 一样,应该遍历一个快照:`for close_event in list(self._queue_close_events.values()): ...`,这样重复/并发调用就能保持安全且幂等。
</issue_to_address>

### Comment 2
<location> `.github/workflows/release.yml:60` </location>
<code_context>
         uses: actions/setup-node@v6
         with:
-          node-version: 20
+          node-version: 'latest'
           cache: "pnpm"
           cache-dependency-path: dashboard/pnpm-lock.yaml
</code_context>

<issue_to_address>
**issue (bug_risk):** 在 CI 中使用 `node-version: 'latest'` 会损害可重现性,并引入意料之外的构建失败。

这样配置会在新的 Node 主版本刚发布时就自动采用,这可能会在没有任何提示的情况下导致构建或工具链(包括 pnpm 和打包器)出问题。更推荐固定到某个特定的 LTS 主版本(例如 `20.x`)或一个经过验证的版本范围,并在需要时有计划地更新。
</issue_to_address>

### Comment 3
<location> `astrbot/core/star/star_manager.py:408` </location>
<code_context>
+            client_id = inst.client_self_id
+            if client_id in terminated_client_ids:
+                continue
+            try:
+                if getattr(inst, "terminate", None):
+                    await inst.terminate()
</code_context>

<issue_to_address>
**issue (complexity):** 建议将插件导入和依赖恢复逻辑提取到一个专门的辅助函数中,以线性化控制流,避免使用标志位并减少重复导入。

你可以通过把“导入 + 依赖恢复”逻辑抽到一个小的辅助函数中,并采用线性结构,来降低新增的复杂度,这样可以去掉可变标志位以及重复的 `__import__` 调用。

例如:

```python
async def _import_plugin_with_dependency_recovery(
    self,
    path: str,
    module_str: str,
    root_dir_name: str,
    requirements_path: str,
) -> Any:
    # 1. Try direct import
    try:
        return __import__(path, fromlist=[module_str])
    except (ModuleNotFoundError, ImportError) as import_exc:
        # 2. Try prefer_installed_dependencies if requirements exist
        if os.path.exists(requirements_path):
            try:
                logger.info(
                    f"插件 {root_dir_name} 导入失败,尝试从已安装依赖恢复: {import_exc!s}"
                )
                pip_installer.prefer_installed_dependencies(
                    requirements_path=requirements_path
                )
                module = __import__(path, fromlist=[module_str])
                logger.info(
                    f"插件 {root_dir_name} 已从 site-packages 恢复依赖,跳过重新安装。"
                )
                return module
            except Exception as recover_exc:
                logger.info(
                    f"插件 {root_dir_name} 已安装依赖恢复失败,将重新安装依赖: {recover_exc!s}"
                )

        # 3. Fallback: reinstall dependencies and re-import
        await self._check_plugin_dept_update(target_plugin=root_dir_name)
        return __import__(path, fromlist=[module_str])
```

然后 `load()` 会更简单,主要关注遍历/记录逻辑:

```python
logger.info(f"正在载入插件 {root_dir_name} ...")
try:
    module = await self._import_plugin_with_dependency_recovery(
        path=path,
        module_str=module_str,
        root_dir_name=root_dir_name,
        requirements_path=requirements_path,
    )
except Exception as e:
    logger.error(traceback.format_exc())
    logger.error(f"插件 {root_dir_name} 导入失败。原因:{e!s}")
    continue
```

这样可以保持现有的所有行为(包括日志和恢复顺序),但消除嵌套的 `try/except``recovered_by_preferred_deps` 标志位以及重复的 `__import__` 调用。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得这些 Review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈改进后续的 Review。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • In WebChatAdapter.terminate, webchat_queue_mgr is referenced but not defined in the method scope; consider storing the queue manager as an instance attribute in run and using that here to avoid a NameError.
  • Using node-version: 'latest' in the release workflow can make builds non-reproducible and fragile when new Node.js versions are released; consider pinning to a specific major or LTS version instead.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `WebChatAdapter.terminate`, `webchat_queue_mgr` is referenced but not defined in the method scope; consider storing the queue manager as an instance attribute in `run` and using that here to avoid a NameError.
- Using `node-version: 'latest'` in the release workflow can make builds non-reproducible and fragile when new Node.js versions are released; consider pinning to a specific major or LTS version instead.

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/sources/webchat/webchat_queue_mgr.py:90-94` </location>
<code_context>
         for conversation_id in list(self.queues.keys()):
             self._start_listener_if_needed(conversation_id)

+    async def clear_listener(self) -> None:
+        self._listener_callback = None
+        for close_event in self._queue_close_events.values():
+            close_event.set()
+        self._queue_close_events.clear()
+
+        listener_tasks = list(self._listener_tasks.values())
</code_context>

<issue_to_address>
**issue (bug_risk):** Mutating `_queue_close_events` while iterating over its values can raise `RuntimeError` under concurrent calls to `clear_listener`.

In `clear_listener`, you iterate over `self._queue_close_events.values()` and then clear the dict. Since `clear_listener` may be invoked concurrently (e.g., from `terminate()` and `QueueListener.run()`’s `finally`), this can trigger `RuntimeError: dictionary changed size during iteration`. As with `_listener_tasks`, iterate over a snapshot instead: `for close_event in list(self._queue_close_events.values()): ...` so repeated/concurrent calls remain safe and idempotent.
</issue_to_address>

### Comment 2
<location> `.github/workflows/release.yml:60` </location>
<code_context>
         uses: actions/setup-node@v6
         with:
-          node-version: 20
+          node-version: 'latest'
           cache: "pnpm"
           cache-dependency-path: dashboard/pnpm-lock.yaml
</code_context>

<issue_to_address>
**issue (bug_risk):** Using `node-version: 'latest'` in CI can hurt reproducibility and introduce unexpected breakages.

This will automatically adopt new major Node versions as soon as they’re released, which can silently break builds or tooling (including pnpm and bundlers). Prefer pinning to a specific LTS major (e.g. `20.x`) or a tested version range and updating it deliberately.
</issue_to_address>

### Comment 3
<location> `astrbot/core/star/star_manager.py:408` </location>
<code_context>
+            client_id = inst.client_self_id
+            if client_id in terminated_client_ids:
+                continue
+            try:
+                if getattr(inst, "terminate", None):
+                    await inst.terminate()
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting the plugin import and dependency-recovery logic into a dedicated helper to linearize the control flow and avoid flags and repeated imports.

You can reduce the new complexity by extracting the import + dependency-recovery logic into a small helper and structuring it linearly, which removes the mutable flag and repeated `__import__` calls.

For example:

```python
async def _import_plugin_with_dependency_recovery(
    self,
    path: str,
    module_str: str,
    root_dir_name: str,
    requirements_path: str,
) -> Any:
    # 1. Try direct import
    try:
        return __import__(path, fromlist=[module_str])
    except (ModuleNotFoundError, ImportError) as import_exc:
        # 2. Try prefer_installed_dependencies if requirements exist
        if os.path.exists(requirements_path):
            try:
                logger.info(
                    f"插件 {root_dir_name} 导入失败,尝试从已安装依赖恢复: {import_exc!s}"
                )
                pip_installer.prefer_installed_dependencies(
                    requirements_path=requirements_path
                )
                module = __import__(path, fromlist=[module_str])
                logger.info(
                    f"插件 {root_dir_name} 已从 site-packages 恢复依赖,跳过重新安装。"
                )
                return module
            except Exception as recover_exc:
                logger.info(
                    f"插件 {root_dir_name} 已安装依赖恢复失败,将重新安装依赖: {recover_exc!s}"
                )

        # 3. Fallback: reinstall dependencies and re-import
        await self._check_plugin_dept_update(target_plugin=root_dir_name)
        return __import__(path, fromlist=[module_str])
```

Then `load()` becomes simpler and focuses on traversal/bookkeeping:

```python
logger.info(f"正在载入插件 {root_dir_name} ...")
try:
    module = await self._import_plugin_with_dependency_recovery(
        path=path,
        module_str=module_str,
        root_dir_name=root_dir_name,
        requirements_path=requirements_path,
    )
except Exception as e:
    logger.error(traceback.format_exc())
    logger.error(f"插件 {root_dir_name} 导入失败。原因:{e!s}")
    continue
```

This keeps all existing behavior (including logging and recovery order) but eliminates the nested `try/except`, the `recovered_by_preferred_deps` flag, and duplicate `__import__` calls.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@zouyonghe
Copy link
Member Author

@sourcery-ai review

@dosubot dosubot bot removed the size:L This PR changes 100-499 lines, ignoring generated files. label Feb 11, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题

给 AI 代理的提示
请根据本次代码审查中的评论进行修改:

## 单条评论

### 评论 1
<location> `astrbot/core/platform/sources/webchat/webchat_adapter.py:68-69` </location>
<code_context>
             id="webchat",
             support_proactive_message=False,
         )
+        self._shutdown_event = asyncio.Event()
+        self._webchat_queue_mgr = webchat_queue_mgr

     async def send_by_session(
</code_context>

<issue_to_address>
**suggestion (bug_risk):** 建议在关闭时避免重复调用 `clear_listener``terminate()` 会设置 `_shutdown_event`,然后等待 `clear_listener()`,而 `QueueListener.run()` 现在也会在 `stop_event.wait()` 之后的 `finally` 块中等待 `clear_listener()`。这会导致 `clear_listener()` 被调用两次,并且可能是并发调用。即便当前它是幂等的,也会增加关闭流程的复杂度和未来产生竞态条件的风险。建议让 `QueueListener.run()` 成为唯一负责调用 `clear_listener()` 的地方(通过关机事件来触发),而 `terminate()` 只负责发出事件信号,并可选地等待运行任务完成。

建议的实现方式:

```python
            await self.webchat_queue_mgr.clear_listener()

```

```python

```

我已经移除了第二处对 `clear_listener()` 的调用(针对 `_webchat_queue_mgr` 的那一处),这也是最有可能从你的适配器的 `terminate()` 方法中被调用的那一处。这样就只剩下 `QueueListener.run()` 在其 `finally` 块中调用 `clear_listener()`,从而避免了重复/并发的关闭逻辑。

要完全实现你所描述的关闭设计,你还需要:

1. 确保适配器的 `terminate()` 方法只做以下两件事:
   - 设置 `self._shutdown_event` 来通知监听器停止。
   - 可选地等待由 `QueueListener.run()` 创建的监听任务(例如,通过将 `bot.run()` 返回的任务存到一个字段,如 `self._listener_task`,并在 `terminate()` 中等待它)。

2. 确认在关闭期间没有其他代码路径会直接调用 `clear_listener()``QueueListener.run()` 应该是该职责的唯一持有者。

由于我目前看不到完整的 `terminate()` 实现以及 `QueueListener.run()` 任务是在哪里存储/等待的,你可能需要根据本地的命名和生命周期模式来调整这些想法,但关键的不变点是:**只有 `QueueListener.run()` 应该调用 `clear_listener()`**。
</issue_to_address>

Sourcery 对开源项目永久免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/sources/webchat/webchat_adapter.py:68-69` </location>
<code_context>
             id="webchat",
             support_proactive_message=False,
         )
+        self._shutdown_event = asyncio.Event()
+        self._webchat_queue_mgr = webchat_queue_mgr

     async def send_by_session(
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider avoiding duplicated `clear_listener` calls on shutdown.

`terminate()` sets `_shutdown_event` and then awaits `clear_listener()`, while `QueueListener.run()` now also awaits `clear_listener()` in its `finally` block after `stop_event.wait()`. This leads to `clear_listener()` being invoked twice, potentially concurrently. Even if it’s currently idempotent, it increases shutdown complexity and future race risk. Consider letting `QueueListener.run()` be solely responsible for `clear_listener()` (triggered via the shutdown event), and have `terminate()` only signal the event and optionally wait for the run task to complete.

Suggested implementation:

```python
            await self.webchat_queue_mgr.clear_listener()

```

```python

```

I have removed the second `clear_listener()` call (the one on `_webchat_queue_mgr`), which is the one most likely invoked from your adapter’s `terminate()` method. This leaves `QueueListener.run()` as the only place that calls `clear_listener()` (in its `finally` block), avoiding duplicate/concurrent shutdown logic.

To fully implement the shutdown design you described, you will also want to:

1. Ensure that the adapter’s `terminate()` method only:
   - Sets `self._shutdown_event` to signal the listener to stop.
   - Optionally awaits the listener task created by `QueueListener.run()` (e.g., by storing the task returned from `bot.run()` into a field such as `self._listener_task` and awaiting it in `terminate()`).

2. Confirm that no other code paths call `clear_listener()` directly during shutdown; `QueueListener.run()` should be the single owner of that responsibility.

Because I cannot see the full `terminate()` implementation or where the `QueueListener.run()` task is stored/awaited, you may need to adapt these ideas to your local naming and lifecycle patterns, but the key invariant is: **only `QueueListener.run()` should call `clear_listener()`**.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dosubot dosubot bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Feb 11, 2026
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Feb 11, 2026
@zouyonghe
Copy link
Member Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题

面向 AI Agents 的提示
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/manager.py:21` </location>
<code_context>
         """加载的 Platform 的实例"""

         self._inst_map: dict[str, dict] = {}
+        self._platform_tasks: dict[str, tuple[asyncio.Task, asyncio.Task]] = {}

         self.astrbot_config = config
</code_context>

<issue_to_address>
**issue (complexity):** 可以考虑引入一个小的 PlatformTasks 抽象,以及一个共享的终止辅助函数,使任务记录和关闭逻辑更清晰、更不易出错。

你可以在不改变行为的前提下,降低新增的复杂度:

1.`_platform_tasks` 自解释(更易读)。
2. 将“terminate + 停止任务”的生命周期封装到一个辅助函数中,并在多处复用。

### 1. 使用小抽象替换裸元组

与其使用 `tuple[asyncio.Task, asyncio.Task]`,不如引入一个 dataclass:

```python
from dataclasses import dataclass

@dataclass
class PlatformTasks:
    run: asyncio.Task
    wrapper: asyncio.Task
```

然后更新 manager 的字段和辅助方法:

```python
class PlatformManager:
    def __init__(...):
        ...
        self._platform_tasks: dict[str, PlatformTasks] = {}

    def _start_platform_task(self, task_name: str, inst: Platform) -> None:
        run_task = asyncio.create_task(inst.run(), name=task_name)
        wrapper_task = asyncio.create_task(
            self._task_wrapper(run_task, platform=inst),
            name=f"{task_name}_wrapper",
        )
        self._platform_tasks[inst.client_self_id] = PlatformTasks(
            run=run_task,
            wrapper=wrapper_task,
        )

    async def _stop_platform_task(self, client_id: str) -> None:
        tasks = self._platform_tasks.pop(client_id, None)
        if not tasks:
            return
        for task in (tasks.run, tasks.wrapper):
            if not task.done():
                task.cancel()
        await asyncio.gather(tasks.run, tasks.wrapper, return_exceptions=True)
```

这样可以保持行为完全一致,但移除了位置元组的脆弱性,也让任务记录逻辑更易理解、更便于扩展。

### 2. 封装“terminate + 停止任务”的逻辑并复用

下面这个模式:

- `if getattr(inst, "terminate", None): await inst.terminate()`
- `await self._stop_platform_task(client_id)`

同时出现在 `terminate_platform``terminate` 中。可以把这段生命周期逻辑封装到一个地方,使 `terminate` 中的控制流更容易阅读:

```python
class PlatformManager:
    ...

    async def _terminate_inst_and_tasks(self, inst: Platform) -> None:
        client_id = inst.client_self_id
        try:
            if getattr(inst, "terminate", None):
                await inst.terminate()
        finally:
            await self._stop_platform_task(client_id)
```

然后:

```python
async def terminate_platform(self, platform_id: str) -> None:
    ...
    inst = ...
    ...
    await self._terminate_inst_and_tasks(inst)
````terminate` 中:

```python
async def terminate(self) -> None:
    terminated_client_ids: set[str] = set()
    for platform_id in list(self._inst_map.keys()):
        info = self._inst_map.get(platform_id)
        if info:
            terminated_client_ids.add(info["client_id"])
        await self.terminate_platform(platform_id)

    for inst in list(self.platform_insts):
        if inst.client_self_id in terminated_client_ids:
            continue
        await self._terminate_inst_and_tasks(inst)

    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()
```

行为保持不变,但:

- “terminate + 停止任务”的不变式被集中管理。
- `terminate` 的控制流可以直接读作“先按 platform_id 终止所有实例,然后处理剩余实例”,而具体细节被隐藏在一个辅助函数中。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得这些评审有帮助,欢迎帮我们分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/manager.py:21` </location>
<code_context>
         """加载的 Platform 的实例"""

         self._inst_map: dict[str, dict] = {}
+        self._platform_tasks: dict[str, tuple[asyncio.Task, asyncio.Task]] = {}

         self.astrbot_config = config
</code_context>

<issue_to_address>
**issue (complexity):** Consider introducing a small PlatformTasks abstraction and a shared termination helper to make task bookkeeping and shutdown logic clearer and less brittle.

You can reduce the new complexity without changing behavior by:

1. Making `_platform_tasks` self-documenting.
2. Encapsulating the “terminate + stop task” lifecycle in a helper and reusing it.

### 1. Replace bare tuple with a small abstraction

Instead of `tuple[asyncio.Task, asyncio.Task]`, introduce a dataclass:

```python
from dataclasses import dataclass

@dataclass
class PlatformTasks:
    run: asyncio.Task
    wrapper: asyncio.Task
```

Update the manager field and the helper methods:

```python
class PlatformManager:
    def __init__(...):
        ...
        self._platform_tasks: dict[str, PlatformTasks] = {}

    def _start_platform_task(self, task_name: str, inst: Platform) -> None:
        run_task = asyncio.create_task(inst.run(), name=task_name)
        wrapper_task = asyncio.create_task(
            self._task_wrapper(run_task, platform=inst),
            name=f"{task_name}_wrapper",
        )
        self._platform_tasks[inst.client_self_id] = PlatformTasks(
            run=run_task,
            wrapper=wrapper_task,
        )

    async def _stop_platform_task(self, client_id: str) -> None:
        tasks = self._platform_tasks.pop(client_id, None)
        if not tasks:
            return
        for task in (tasks.run, tasks.wrapper):
            if not task.done():
                task.cancel()
        await asyncio.gather(tasks.run, tasks.wrapper, return_exceptions=True)
```

This keeps behavior identical but removes the positional-tuple brittleness and makes the bookkeeping easier to understand and extend.

### 2. Encapsulate “terminate + stop tasks” and reuse it

The pattern:

- `if getattr(inst, "terminate", None): await inst.terminate()`
- `await self._stop_platform_task(client_id)`

appears in both `terminate_platform` and `terminate`. Encapsulate this so the lifecycle is in one place and the control flow in `terminate` is easier to follow:

```python
class PlatformManager:
    ...

    async def _terminate_inst_and_tasks(self, inst: Platform) -> None:
        client_id = inst.client_self_id
        try:
            if getattr(inst, "terminate", None):
                await inst.terminate()
        finally:
            await self._stop_platform_task(client_id)
```

Then:

```python
async def terminate_platform(self, platform_id: str) -> None:
    ...
    inst = ...
    ...
    await self._terminate_inst_and_tasks(inst)
```

and in `terminate`:

```python
async def terminate(self) -> None:
    terminated_client_ids: set[str] = set()
    for platform_id in list(self._inst_map.keys()):
        info = self._inst_map.get(platform_id)
        if info:
            terminated_client_ids.add(info["client_id"])
        await self.terminate_platform(platform_id)

    for inst in list(self.platform_insts):
        if inst.client_self_id in terminated_client_ids:
            continue
        await self._terminate_inst_and_tasks(inst)

    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()
```

Behavior stays the same, but:

- The “terminate + stop tasks” invariant is centralized.
- `terminate`’s control flow reads as “terminate all by platform_id, then handle any remaining instances,” with the details hidden in a single helper.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@zouyonghe
Copy link
Member Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体性的反馈:

  • PlatformManager._terminate_inst_and_tasks 中,建议捕获并记录 inst.terminate() 抛出的异常,而不是让其向上传播,这样单个平台的失败就不会中断整个关闭流程的剩余部分。
  • PlatformManager 中的 _platform_tasks 映射是以 inst.client_self_id 作为 key 的;如果该 ID 有可能在不同适配器之间发生碰撞,或者在实例生命周期内变化,那么使用稳定的标识符(例如 inst 对象本身或生成的 UUID)来作为 key 会更安全,以避免任务被覆盖。
给 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- In `PlatformManager._terminate_inst_and_tasks`, consider catching and logging exceptions from `inst.terminate()` instead of letting them propagate so that a single platform failure does not abort the rest of the shutdown sequence.
- The `_platform_tasks` map in `PlatformManager` is keyed by `inst.client_self_id`; if there is any chance this ID can collide between adapters or change during the lifetime of an instance, it may be safer to key by a stable identifier (e.g., the `inst` object or a generated UUID) to avoid overwriting tasks.

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/manager.py:16` </location>
<code_context>
 from .sources.webchat.webchat_adapter import WebChatAdapter


+@dataclass
+class PlatformTasks:
+    run: asyncio.Task
</code_context>

<issue_to_address>
**issue (complexity):** 考虑通过每个平台只跟踪一个包装任务,并让所有平台终止逻辑走一条统一的代码路径,以简化任务管理。

你可以在保留新功能(显式任务跟踪与取消)的同时,通过下面方式减少“活动部件”:

1. **每个平台只跟踪一个包装任务(wrapper task)**

你不需要在 `PlatformTasks` 中同时维护 `run``wrapper`。可以让 wrapper 全权管理 `run` 任务的生命周期,并且在 `_platform_tasks` 中只存储 wrapper。

```python
# replace PlatformTasks / _start_platform_task / _stop_platform_task with:

self._platform_tasks: dict[str, asyncio.Task] = {}

def _start_platform_task(self, task_name: str, inst: Platform) -> None:
    async def wrapper():
        run_task = asyncio.create_task(inst.run(), name=task_name)
        try:
            await self._task_wrapper(run_task, platform=inst)
        finally:
            # ensure run_task is cancelled/awaited here
            if not run_task.done():
                run_task.cancel()
            await asyncio.gather(run_task, return_exceptions=True)

    wrapper_task = asyncio.create_task(wrapper(), name=f"{task_name}_wrapper")
    self._platform_tasks[inst.client_self_id] = wrapper_task

async def _stop_platform_task(self, client_id: str) -> None:
    task = self._platform_tasks.pop(client_id, None)
    if not task:
        return
    if not task.done():
        task.cancel()
    await asyncio.gather(task, return_exceptions=True)
```

这样既保留了当前行为(wrapper 管理 `run`,你仍然可以按平台取消),又去掉了 `PlatformTasks` 数据类以及一层索引。

2. **使用单一的终止路径和单一事实来源(single source of truth)**

`terminate` 目前会对 `_inst_map``platform_insts` 做两次遍历,而且在 `_terminate_inst_and_tasks` 中混合了多种职责。你可以:

-`platform_insts` 视为所有活跃实例的事实来源;
-`terminate_platform` 成为唯一的入口,它:
  -`_inst_map` 中移除(如果存在),
  -`platform_insts` 中移除,
  - 通过 `_stop_platform_task` 停止任务。

这样就不再需要 `terminated_client_ids``_terminate_inst_and_tasks` 了。

示例重构:

```python
async def terminate_platform(self, platform_id: str) -> None:
    info = self._inst_map.pop(platform_id, None)
    if not info:
        return

    inst: Platform = info["inst"]
    client_id = info["client_id"]

    # remove from platform_insts
    try:
        self.platform_insts.remove(inst)
    except ValueError:
        logger.warning(f"可能未完全移除 {platform_id} 平台适配器")

    try:
        if getattr(inst, "terminate", None):
            await inst.terminate()
    finally:
        await self._stop_platform_task(client_id)

async def terminate(self) -> None:
    # single pass over the source of truth (inst_map keys)
    for platform_id in list(self._inst_map.keys()):
        await self.terminate_platform(platform_id)

    # clear any leftovers (e.g., webchat without inst_map entry)
    for inst in list(self.platform_insts):
        await self._stop_platform_task(inst.client_self_id)

    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()
```

这能保持当前所有保证(按平台终止、任务取消、映射被清理),同时去掉:

- 额外的辅助函数 `_terminate_inst_and_tasks`- 双重遍历以及 `terminated_client_ids` 集合,
- 在终止过程中需要同时推理三个交叉索引结构的复杂性。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得这些 Review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的 Review。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In PlatformManager._terminate_inst_and_tasks, consider catching and logging exceptions from inst.terminate() instead of letting them propagate so that a single platform failure does not abort the rest of the shutdown sequence.
  • The _platform_tasks map in PlatformManager is keyed by inst.client_self_id; if there is any chance this ID can collide between adapters or change during the lifetime of an instance, it may be safer to key by a stable identifier (e.g., the inst object or a generated UUID) to avoid overwriting tasks.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `PlatformManager._terminate_inst_and_tasks`, consider catching and logging exceptions from `inst.terminate()` instead of letting them propagate so that a single platform failure does not abort the rest of the shutdown sequence.
- The `_platform_tasks` map in `PlatformManager` is keyed by `inst.client_self_id`; if there is any chance this ID can collide between adapters or change during the lifetime of an instance, it may be safer to key by a stable identifier (e.g., the `inst` object or a generated UUID) to avoid overwriting tasks.

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/manager.py:16` </location>
<code_context>
 from .sources.webchat.webchat_adapter import WebChatAdapter


+@dataclass
+class PlatformTasks:
+    run: asyncio.Task
</code_context>

<issue_to_address>
**issue (complexity):** Consider simplifying task management by tracking only a single wrapper task per platform and routing all platform termination through one unified code path.

You can keep the new functionality (explicit task tracking & cancellation) while reducing moving parts by:

1. **Track only a single wrapper task per platform**

You don’t need both `run` and `wrapper` in `PlatformTasks`. Let the wrapper own the `run` task lifecycle and only store the wrapper in `_platform_tasks`.

```python
# replace PlatformTasks / _start_platform_task / _stop_platform_task with:

self._platform_tasks: dict[str, asyncio.Task] = {}

def _start_platform_task(self, task_name: str, inst: Platform) -> None:
    async def wrapper():
        run_task = asyncio.create_task(inst.run(), name=task_name)
        try:
            await self._task_wrapper(run_task, platform=inst)
        finally:
            # ensure run_task is cancelled/awaited here
            if not run_task.done():
                run_task.cancel()
            await asyncio.gather(run_task, return_exceptions=True)

    wrapper_task = asyncio.create_task(wrapper(), name=f"{task_name}_wrapper")
    self._platform_tasks[inst.client_self_id] = wrapper_task

async def _stop_platform_task(self, client_id: str) -> None:
    task = self._platform_tasks.pop(client_id, None)
    if not task:
        return
    if not task.done():
        task.cancel()
    await asyncio.gather(task, return_exceptions=True)
```

This preserves the behavior (wrapper manages `run`, and you can cancel per-platform) while removing the `PlatformTasks` dataclass and one index level.

2. **Use a single termination path and a single source of truth**

`terminate` currently does a two-pass sweep across `_inst_map` and `platform_insts` and mixes concerns in `_terminate_inst_and_tasks`. You can:

- Treat `platform_insts` as the source of truth for active instances.
- Make `terminate_platform` the single entry point that:
  - removes from `_inst_map` (if present),
  - removes from `platform_insts`,
  - stops tasks via `_stop_platform_task`.

That removes the need for `terminated_client_ids` and `_terminate_inst_and_tasks`.

Example refactor:

```python
async def terminate_platform(self, platform_id: str) -> None:
    info = self._inst_map.pop(platform_id, None)
    if not info:
        return

    inst: Platform = info["inst"]
    client_id = info["client_id"]

    # remove from platform_insts
    try:
        self.platform_insts.remove(inst)
    except ValueError:
        logger.warning(f"可能未完全移除 {platform_id} 平台适配器")

    try:
        if getattr(inst, "terminate", None):
            await inst.terminate()
    finally:
        await self._stop_platform_task(client_id)

async def terminate(self) -> None:
    # single pass over the source of truth (inst_map keys)
    for platform_id in list(self._inst_map.keys()):
        await self.terminate_platform(platform_id)

    # clear any leftovers (e.g., webchat without inst_map entry)
    for inst in list(self.platform_insts):
        await self._stop_platform_task(inst.client_self_id)

    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()
```

This keeps all current guarantees (per-platform termination, task cancellation, maps cleared) but removes:

- the extra helper `_terminate_inst_and_tasks`,
- the double iteration and `terminated_client_ids` set,
- the need to reason about three cross-indexed structures in termination.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

from .sources.webchat.webchat_adapter import WebChatAdapter


@dataclass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): 考虑通过每个平台只跟踪一个包装任务,并让所有平台终止逻辑走一条统一的代码路径,以简化任务管理。

你可以在保留新功能(显式任务跟踪与取消)的同时,通过下面方式减少“活动部件”:

  1. 每个平台只跟踪一个包装任务(wrapper task)

你不需要在 PlatformTasks 中同时维护 runwrapper。可以让 wrapper 全权管理 run 任务的生命周期,并且在 _platform_tasks 中只存储 wrapper。

# replace PlatformTasks / _start_platform_task / _stop_platform_task with:

self._platform_tasks: dict[str, asyncio.Task] = {}

def _start_platform_task(self, task_name: str, inst: Platform) -> None:
    async def wrapper():
        run_task = asyncio.create_task(inst.run(), name=task_name)
        try:
            await self._task_wrapper(run_task, platform=inst)
        finally:
            # ensure run_task is cancelled/awaited here
            if not run_task.done():
                run_task.cancel()
            await asyncio.gather(run_task, return_exceptions=True)

    wrapper_task = asyncio.create_task(wrapper(), name=f"{task_name}_wrapper")
    self._platform_tasks[inst.client_self_id] = wrapper_task

async def _stop_platform_task(self, client_id: str) -> None:
    task = self._platform_tasks.pop(client_id, None)
    if not task:
        return
    if not task.done():
        task.cancel()
    await asyncio.gather(task, return_exceptions=True)

这样既保留了当前行为(wrapper 管理 run,你仍然可以按平台取消),又去掉了 PlatformTasks 数据类以及一层索引。

  1. 使用单一的终止路径和单一事实来源(single source of truth)

terminate 目前会对 _inst_mapplatform_insts 做两次遍历,而且在 _terminate_inst_and_tasks 中混合了多种职责。你可以:

  • platform_insts 视为所有活跃实例的事实来源;
  • terminate_platform 成为唯一的入口,它:
    • _inst_map 中移除(如果存在),
    • platform_insts 中移除,
    • 通过 _stop_platform_task 停止任务。

这样就不再需要 terminated_client_ids_terminate_inst_and_tasks 了。

示例重构:

async def terminate_platform(self, platform_id: str) -> None:
    info = self._inst_map.pop(platform_id, None)
    if not info:
        return

    inst: Platform = info["inst"]
    client_id = info["client_id"]

    # remove from platform_insts
    try:
        self.platform_insts.remove(inst)
    except ValueError:
        logger.warning(f"可能未完全移除 {platform_id} 平台适配器")

    try:
        if getattr(inst, "terminate", None):
            await inst.terminate()
    finally:
        await self._stop_platform_task(client_id)

async def terminate(self) -> None:
    # single pass over the source of truth (inst_map keys)
    for platform_id in list(self._inst_map.keys()):
        await self.terminate_platform(platform_id)

    # clear any leftovers (e.g., webchat without inst_map entry)
    for inst in list(self.platform_insts):
        await self._stop_platform_task(inst.client_self_id)

    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()

这能保持当前所有保证(按平台终止、任务取消、映射被清理),同时去掉:

  • 额外的辅助函数 _terminate_inst_and_tasks
  • 双重遍历以及 terminated_client_ids 集合,
  • 在终止过程中需要同时推理三个交叉索引结构的复杂性。
Original comment in English

issue (complexity): Consider simplifying task management by tracking only a single wrapper task per platform and routing all platform termination through one unified code path.

You can keep the new functionality (explicit task tracking & cancellation) while reducing moving parts by:

  1. Track only a single wrapper task per platform

You don’t need both run and wrapper in PlatformTasks. Let the wrapper own the run task lifecycle and only store the wrapper in _platform_tasks.

# replace PlatformTasks / _start_platform_task / _stop_platform_task with:

self._platform_tasks: dict[str, asyncio.Task] = {}

def _start_platform_task(self, task_name: str, inst: Platform) -> None:
    async def wrapper():
        run_task = asyncio.create_task(inst.run(), name=task_name)
        try:
            await self._task_wrapper(run_task, platform=inst)
        finally:
            # ensure run_task is cancelled/awaited here
            if not run_task.done():
                run_task.cancel()
            await asyncio.gather(run_task, return_exceptions=True)

    wrapper_task = asyncio.create_task(wrapper(), name=f"{task_name}_wrapper")
    self._platform_tasks[inst.client_self_id] = wrapper_task

async def _stop_platform_task(self, client_id: str) -> None:
    task = self._platform_tasks.pop(client_id, None)
    if not task:
        return
    if not task.done():
        task.cancel()
    await asyncio.gather(task, return_exceptions=True)

This preserves the behavior (wrapper manages run, and you can cancel per-platform) while removing the PlatformTasks dataclass and one index level.

  1. Use a single termination path and a single source of truth

terminate currently does a two-pass sweep across _inst_map and platform_insts and mixes concerns in _terminate_inst_and_tasks. You can:

  • Treat platform_insts as the source of truth for active instances.
  • Make terminate_platform the single entry point that:
    • removes from _inst_map (if present),
    • removes from platform_insts,
    • stops tasks via _stop_platform_task.

That removes the need for terminated_client_ids and _terminate_inst_and_tasks.

Example refactor:

async def terminate_platform(self, platform_id: str) -> None:
    info = self._inst_map.pop(platform_id, None)
    if not info:
        return

    inst: Platform = info["inst"]
    client_id = info["client_id"]

    # remove from platform_insts
    try:
        self.platform_insts.remove(inst)
    except ValueError:
        logger.warning(f"可能未完全移除 {platform_id} 平台适配器")

    try:
        if getattr(inst, "terminate", None):
            await inst.terminate()
    finally:
        await self._stop_platform_task(client_id)

async def terminate(self) -> None:
    # single pass over the source of truth (inst_map keys)
    for platform_id in list(self._inst_map.keys()):
        await self.terminate_platform(platform_id)

    # clear any leftovers (e.g., webchat without inst_map entry)
    for inst in list(self.platform_insts):
        await self._stop_platform_task(inst.client_self_id)

    self.platform_insts.clear()
    self._inst_map.clear()
    self._platform_tasks.clear()

This keeps all current guarantees (per-platform termination, task cancellation, maps cleared) but removes:

  • the extra helper _terminate_inst_and_tasks,
  • the double iteration and terminated_client_ids set,
  • the need to reason about three cross-indexed structures in termination.

Copy link

@SourceryAI SourceryAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我已经审查了你的更改,看起来很棒!


Hi @zouyonghe! 👋

感谢你通过评论 @sourcery-ai review 来试用 Sourcery!🚀

安装 sourcery-ai bot,即可在每个拉取请求上自动获得代码审查 ✨

帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进我的审查。
Original comment in English

Hey - I've reviewed your changes and they look great!


Hi @zouyonghe! 👋

Thanks for trying out Sourcery by commenting with @sourcery-ai review! 🚀

Install the sourcery-ai bot to get automatic code reviews on every pull request ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@zouyonghe zouyonghe requested a review from SourceryAI February 11, 2026 15:56
@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Feb 11, 2026
@Soulter Soulter merged commit a8dda20 into AstrBotDevs:master Feb 11, 2026
6 checks passed
united-pooh pushed a commit to united-pooh/AstrBot that referenced this pull request Feb 19, 2026
* fix(desktop): rotate electron and backend logs

* refactor(desktop): centralize log rotation defaults and debug fs errors

* fix(desktop): harden rotation fs ops and buffer backend log writes

* refactor(desktop): extract buffered logger and reduce sync stat calls

* refactor(desktop): simplify rotation flow and harden logger config

* fix(desktop): make app logging async and flush-safe

* fix: harden app log path switching and debug-gated rotation errors

* fix: cap buffered log chunk size during path switch

* fix: avoid redundant plugin reinstall and upgrade electron

* fix: stop webchat tasks cleanly and bind packaged backend to localhost

* fix: unify platform shutdown and await webchat listener cleanup

* fix: improve startup logs for dashboard and onebot listeners

* fix: revert extra startup service logs

* fix: harden plugin import recovery and webchat listener cleanup

* fix: pin dashboard ci node version to 24.13.0

* fix: avoid duplicate webchat listener cleanup on terminate

* refactor: clarify platform task lifecycle management

* fix: continue platform shutdown when terminate fails
LIghtJUNction added a commit that referenced this pull request Feb 27, 2026
* feat: add bocha web search tool (#4902)

* add bocha web search tool

* Revert "add bocha web search tool"

This reverts commit 1b36d75a17b4c4751828f31f6759357cd2d4000a.

* add bocha web search tool

* fix: correct temporary_cache spelling and update supported tools for web search

* ruff

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: messages[x] assistant content must contain at least one part (#4928)

* fix: messages[x] assistant content must contain at least one part

fixes: #4876

* ruff format

* chore: bump version to 4.14.5 (#4930)

* feat: implement feishu / lark media file handling utilities for file, audio and video processing (#4938)

* feat: implement media file handling utilities for audio and video processing

* feat: refactor file upload handling for audio and video in LarkMessageEvent

* feat: add cleanup for failed audio and video conversion outputs in media_utils

* feat: add utility methods for sending messages and uploading files in LarkMessageEvent

* fix: correct spelling of 'temporary' in SharedPreferences class

* perf: optimize webchat and wecom ai queue lifecycle (#4941)

* perf: optimize webchat and wecom ai queue lifecycle

* perf: enhance webchat back queue management with conversation ID support

* fix: localize provider source config UI (#4933)

* fix: localize provider source ui

* feat: localize provider metadata keys

* chore: add provider metadata translations

* chore: format provider i18n changes

* fix: preserve metadata fields in i18n conversion

* fix: internationalize platform config and dialog

* fix: add Weixin official account platform icon

---------

Co-authored-by: Soulter <905617992@qq.com>

* chore: bump version to 4.14.6

* feat: add provider-souce-level proxy (#4949)

* feat: 添加 Provider 级别代理支持及请求失败日志

* refactor: simplify provider source configuration structure

* refactor: move env proxy fallback logic to log_connection_failure

* refactor: update client proxy handling and add terminate method for cleanup

* refactor: update no_proxy configuration to remove redundant subnet

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat(ComponentPanel):  implement permission management for dashboard (#4887)

* feat(backend): add permission update api

* feat(useCommandActions): add updatePermission action and translations

* feat(dashboard): implement permission editing ui

* style: fix import sorting in command.py

* refactor(backend): extract permission update logic to service

* feat(i18n): add success and failure messages for command updates

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat: 允许 LLM 预览工具返回的图片并自主决定是否发送 (#4895)

* feat: 允许 LLM 预览工具返回的图片并自主决定是否发送

* 复用 send_message_to_user 替代独立的图片发送工具

* feat: implement _HandleFunctionToolsResult class for improved tool response handling

* docs: add path handling guidelines to AGENTS.md

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat(telegram): 添加媒体组(相册)支持 / add media group (album) support (#4893)

* feat(telegram): 添加媒体组(相册)支持 / add media group (album) support

## 功能说明
支持 Telegram 的媒体组消息(相册),将多张图片/视频合并为一条消息处理,而不是分散成多条消息。

## 主要改动

### 1. 初始化媒体组缓存 (__init__)
- 添加 `media_group_cache` 字典存储待处理的媒体组消息
- 使用 2.5 秒超时收集媒体组消息(基于社区最佳实践)
- 最大等待时间 10 秒(防止永久等待)

### 2. 消息处理流程 (message_handler)
- 检测 `media_group_id` 判断是否为媒体组消息
- 媒体组消息走特殊处理流程,避免分散处理

### 3. 媒体组消息缓存 (handle_media_group_message)
- 缓存收到的媒体组消息
- 使用 APScheduler 实现防抖(debounce)机制
- 每收到新消息时重置超时计时器
- 超时后触发统一处理

### 4. 媒体组合并处理 (process_media_group)
- 从缓存中取出所有媒体项
- 使用第一条消息作为基础(保留文本、回复等信息)
- 依次添加所有图片、视频、文档到消息链
- 将合并后的消息发送到处理流程

## 技术方案论证

Telegram Bot API 在处理媒体组时的设计限制:
1. 将媒体组的每个消息作为独立的 update 发送
2. 每个 update 带有相同的 `media_group_id`
3. **不提供**组的总数、结束标志或一次性完整组的机制

因此,bot 必须自行收集消息,并通过硬编码超时(timeout/delay)等待可能延迟到达的消息。
这是目前唯一可靠的方案,被官方实现、主流框架和开发者社区广泛采用。

### 官方和社区证据:
- **Telegram Bot API 服务器实现(tdlib)**:明确指出缺少结束标志或总数信息
  https://github.com/tdlib/telegram-bot-api/issues/643

- **Telegram Bot API 服务器 issue**:讨论媒体组处理的不便性,推荐使用超时机制
  https://github.com/tdlib/telegram-bot-api/issues/339

- **Telegraf(Node.js 框架)**:专用媒体组中间件使用 timeout 控制等待时间
  https://github.com/DieTime/telegraf-media-group

- **StackOverflow 讨论**:无法一次性获取媒体组所有文件,必须手动收集
  https://stackoverflow.com/questions/50180048/telegram-api-get-all-uploaded-photos-by-media-group-id

- **python-telegram-bot 社区**:确认媒体组消息单独到达,需手动处理
  https://github.com/python-telegram-bot/python-telegram-bot/discussions/3143

- **Telegram Bot API 官方文档**:仅定义 `media_group_id` 为可选字段,不提供获取完整组的接口
  https://core.telegram.org/bots/api#message

## 实现细节
- 使用 2.5 秒超时收集媒体组消息(基于社区最佳实践)
- 最大等待时间 10 秒(防止永久等待)
- 采用防抖(debounce)机制:每收到新消息重置计时器
- 利用 APScheduler 实现延迟处理和任务调度

## 测试验证
- ✅ 发送 5 张图片相册,成功合并为一条消息
- ✅ 保留原始文本说明和回复信息
- ✅ 支持图片、视频、文档混合的媒体组
- ✅ 日志显示 Processing media group <media_group_id> with 5 items

## 代码变更
- 文件:astrbot/core/platform/sources/telegram/tg_adapter.py
- 新增代码:124 行
- 新增方法:handle_media_group_message(), process_media_group()

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* refactor(telegram): 优化媒体组处理性能和可靠性

根据代码审查反馈改进:

1. 实现 media_group_max_wait 防止无限延迟
   - 跟踪媒体组创建时间,超过最大等待时间立即处理
   - 最坏情况下 10 秒内必定处理,防止消息持续到达导致无限延迟

2. 移除手动 job 查找优化性能
   - 删除 O(N) 的 get_jobs() 循环扫描
   - 依赖 replace_existing=True 自动替换任务

3. 重用 convert_message 减少代码重复
   - 统一所有媒体类型转换逻辑
   - 未来添加新媒体类型只需修改一处

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(telegram): handle missing message in media group processing and improve logging messages

---------

Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Soulter <905617992@qq.com>

* feat: add welcome feature with localized content and onboarding steps

* fix: correct height attribute to max-height for dialog component

* feat: supports electron app (#4952)

* feat: add desktop wrapper with frontend-only packaging

* docs: add desktop build docs and track dashboard lockfile

* fix: track desktop lockfile for npm ci

* fix: allow custom install directory for windows installer

* chore: migrate desktop workflow to pnpm

* fix(desktop): build AppImage only on Linux

* fix(desktop): harden packaged startup and backend bundling

* fix(desktop): adapt packaged restart and plugin dependency flow

* fix(desktop): prevent backend respawn race on quit

* fix(desktop): prefer pyproject version for desktop packaging

* fix(desktop): improve startup loading UX and reduce flicker

* ci: add desktop multi-platform release workflow

* ci: fix desktop release build and mac runner labels

* ci: disable electron-builder auto publish in desktop build

* ci: avoid electron-builder publish path in build matrix

* ci: normalize desktop release artifact names

* ci: exclude blockmap files from desktop release assets

* ci: prefix desktop release assets with AstrBot and purge blockmaps

* feat: add electron bridge types and expose backend control methods in preload script

* Update startup screen assets and styles

- Changed the icon from PNG to SVG format for better scalability.
- Updated the border color from #d0d0d0 to #eeeeee for a softer appearance.
- Adjusted the width of the startup screen from 460px to 360px for improved responsiveness.

* Update .gitignore to include package.json

* chore: remove desktop gitkeep ignore exceptions

* docs: update desktop troubleshooting for current runtime behavior

* refactor(desktop): modularize runtime and harden startup flow

---------

Co-authored-by: Soulter <905617992@qq.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: dedupe preset messages (#4961)

* feat: enhance package.json with resource filters and compression settings

* chore: update Python version requirements to 3.12 (#4963)

* chore: bump version to 4.14.7

* feat: refactor release workflow and add special update handling for electron app (#4969)

* chore: bump version to 4.14.8 and bump faiss-cpu version up to date

* chore: auto ann fix by ruff (#4903)

* chore: auto fix by ruff

* refactor: 统一修正返回类型注解为 None/bool 以匹配实现

* refactor: 将 _get_next_page 改为异步并移除多余的请求错误抛出

* refactor: 将 get_client 的返回类型改为 object

* style: 为 LarkMessageEvent 的相关方法添加返回类型注解 None

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: prepare OpenSSL via vcpkg for Windows ARM64

* ci: change ghcr namespace

* chore: update pydantic dependency version (#4980)

* feat: add delete button to persona management dialog (#4978)

* Initial plan

* feat: add delete button to persona management dialog

- Added delete button to PersonaForm dialog (only visible when editing)
- Implemented deletePersona method with confirmation dialog
- Connected delete event to PersonaManager for proper handling
- Button positioned on left side of dialog actions for clear separation
- Uses existing i18n translations for delete button and messages

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: use finally block to ensure saving state is reset

- Moved `this.saving = false` to finally block in deletePersona
- Ensures UI doesn't stay in saving state after errors
- Follows best practices for state management

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* feat: enhance Dingtalk adapter with active push message and image, video, audio message type (#4986)

* fix: handle pip install execution in frozen runtime (#4985)

* fix: handle pip install execution in frozen runtime

* fix: harden pip subprocess fallback handling

* fix: collect certifi data in desktop backend build (#4995)

* feat: 企业微信应用 支持主动消息推送,并优化企微应用、微信公众号、微信客服音频相关的处理 (#4998)

* feat: 企业微信智能机器人支持主动消息推送以及发送视频、文件等消息类型支持 (#4999)

* feat: enhance WecomAIBotAdapter and WecomAIBotMessageEvent for improved streaming message handling (#5000)

fixes: #3965

* feat: enhance persona tool management and update UI localization for subagent orchestration (#4990)

* feat: enhance persona tool management and update UI localization for subagent orchestration

* fix: remove debug logging for final ProviderRequest in build_main_agent function

* perf: 稳定源码与 Electron 打包环境下的 pip 安装行为,并修复非 Electron 环境下点击 WebUI 更新按钮时出现跳转对话框的问题 (#4996)

* fix: handle pip install execution in frozen runtime

* fix: harden pip subprocess fallback handling

* fix: scope global data root to packaged electron runtime

* refactor: inline frozen runtime check for electron guard

* fix: prefer current interpreter for source pip installs

* fix: avoid resolving venv python symlink for pip

* refactor: share runtime environment detection utilities

* fix: improve error message when pip module is unavailable

* fix: raise ImportError when pip module is unavailable

* fix: preserve ImportError semantics for missing pip

* fix: 修复非electron app环境更新时仍然显示electron更新对话框的问题

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 'HandoffTool' object has no attribute 'agent' (#5005)

* fix: 移动agent的位置到super().__init__之后

* add: 添加一行注释

* chore(deps): bump the github-actions group with 2 updates (#5006)

Bumps the github-actions group with 2 updates: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `astral-sh/setup-uv` from 6 to 7
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v6...v7)

Updates `actions/download-artifact` from 6 to 7
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: stabilize packaged runtime pip/ssl behavior and mac font fallback (#5007)

* fix: patch pip distlib finder for frozen electron runtime

* fix: use certifi CA bundle for runtime SSL requests

* fix: configure certifi CA before core imports

* fix: improve mac font fallback for dashboard text

* fix: harden frozen pip patch and unify TLS connector

* refactor: centralize dashboard CJK font fallback stacks

* perf: reuse TLS context and avoid repeated frozen pip patch

* refactor: bootstrap TLS setup before core imports

* fix: use async confirm dialog for provider deletions

* fix: replace native confirm dialogs in dashboard

- Add shared confirm helper in dashboard/src/utils/confirmDialog.ts for async dialog usage with safe fallback.

- Migrate provider, chat, config, session, platform, persona, MCP, backup, and knowledge-base delete/close confirmations to use the shared helper.

- Remove scattered inline confirm handling to keep behavior consistent and avoid native blocking dialog focus/caret issues in Electron.

* fix: capture runtime bootstrap logs after logger init

- Add bootstrap record buffer in runtime_bootstrap for early TLS patch logs before logger is ready.

- Flush buffered bootstrap logs to astrbot logger at process startup in main.py.

- Include concrete exception details for TLS bootstrap failures to improve diagnosis.

* fix: harden runtime bootstrap and unify confirm handling

- Simplify bootstrap log buffering and add a public initialize hook for non-main startup paths.

- Guard aiohttp TLS patching with feature/type checks and keep graceful fallback when internals are unavailable.

- Standardize dashboard confirmation flow via shared confirm helpers across composition and options API components.

* refactor: simplify runtime tls bootstrap and tighten confirm typing

* refactor: align ssl helper namespace and confirm usage

* fix: 修复 Windows 打包版后端重启失败问题 (#5009)

* fix: patch pip distlib finder for frozen electron runtime

* fix: use certifi CA bundle for runtime SSL requests

* fix: configure certifi CA before core imports

* fix: improve mac font fallback for dashboard text

* fix: harden frozen pip patch and unify TLS connector

* refactor: centralize dashboard CJK font fallback stacks

* perf: reuse TLS context and avoid repeated frozen pip patch

* refactor: bootstrap TLS setup before core imports

* fix: use async confirm dialog for provider deletions

* fix: replace native confirm dialogs in dashboard

- Add shared confirm helper in dashboard/src/utils/confirmDialog.ts for async dialog usage with safe fallback.

- Migrate provider, chat, config, session, platform, persona, MCP, backup, and knowledge-base delete/close confirmations to use the shared helper.

- Remove scattered inline confirm handling to keep behavior consistent and avoid native blocking dialog focus/caret issues in Electron.

* fix: capture runtime bootstrap logs after logger init

- Add bootstrap record buffer in runtime_bootstrap for early TLS patch logs before logger is ready.

- Flush buffered bootstrap logs to astrbot logger at process startup in main.py.

- Include concrete exception details for TLS bootstrap failures to improve diagnosis.

* fix: harden runtime bootstrap and unify confirm handling

- Simplify bootstrap log buffering and add a public initialize hook for non-main startup paths.

- Guard aiohttp TLS patching with feature/type checks and keep graceful fallback when internals are unavailable.

- Standardize dashboard confirmation flow via shared confirm helpers across composition and options API components.

* refactor: simplify runtime tls bootstrap and tighten confirm typing

* refactor: align ssl helper namespace and confirm usage

* fix: avoid frozen restart crash from multiprocessing import

* fix: include missing frozen dependencies for windows backend

* fix: use execv for stable backend reboot args

* Revert "fix: use execv for stable backend reboot args"

This reverts commit 9cc27becffeba0e117fea26aa5c2e1fe7afc6e36.

* Revert "fix: include missing frozen dependencies for windows backend"

This reverts commit 52554bea1fa61045451600c64447b7bf38cf6c92.

* Revert "fix: avoid frozen restart crash from multiprocessing import"

This reverts commit 10548645b0ba1e19b64194878ece478a48067959.

* fix: reset pyinstaller onefile env before reboot

* fix: unify electron restart path and tray-exit backend cleanup

* fix: stabilize desktop restart detection and frozen reboot args

* fix: make dashboard restart wait detection robust

* fix: revert dashboard restart waiting interaction tweaks

* fix: pass auth token for desktop graceful restart

* fix: avoid false failure during graceful restart wait

* fix: start restart waiting before electron restart call

* fix: harden restart waiting and reboot arg parsing

* fix: parse start_time as numeric timestamp

* fix: 修复app内重启异常,修复app内点击重启不能立刻提示重启,以及在后端就绪时及时刷新界面的问题 (#5013)

* fix: patch pip distlib finder for frozen electron runtime

* fix: use certifi CA bundle for runtime SSL requests

* fix: configure certifi CA before core imports

* fix: improve mac font fallback for dashboard text

* fix: harden frozen pip patch and unify TLS connector

* refactor: centralize dashboard CJK font fallback stacks

* perf: reuse TLS context and avoid repeated frozen pip patch

* refactor: bootstrap TLS setup before core imports

* fix: use async confirm dialog for provider deletions

* fix: replace native confirm dialogs in dashboard

- Add shared confirm helper in dashboard/src/utils/confirmDialog.ts for async dialog usage with safe fallback.

- Migrate provider, chat, config, session, platform, persona, MCP, backup, and knowledge-base delete/close confirmations to use the shared helper.

- Remove scattered inline confirm handling to keep behavior consistent and avoid native blocking dialog focus/caret issues in Electron.

* fix: capture runtime bootstrap logs after logger init

- Add bootstrap record buffer in runtime_bootstrap for early TLS patch logs before logger is ready.

- Flush buffered bootstrap logs to astrbot logger at process startup in main.py.

- Include concrete exception details for TLS bootstrap failures to improve diagnosis.

* fix: harden runtime bootstrap and unify confirm handling

- Simplify bootstrap log buffering and add a public initialize hook for non-main startup paths.

- Guard aiohttp TLS patching with feature/type checks and keep graceful fallback when internals are unavailable.

- Standardize dashboard confirmation flow via shared confirm helpers across composition and options API components.

* refactor: simplify runtime tls bootstrap and tighten confirm typing

* refactor: align ssl helper namespace and confirm usage

* fix: avoid frozen restart crash from multiprocessing import

* fix: include missing frozen dependencies for windows backend

* fix: use execv for stable backend reboot args

* Revert "fix: use execv for stable backend reboot args"

This reverts commit 9cc27becffeba0e117fea26aa5c2e1fe7afc6e36.

* Revert "fix: include missing frozen dependencies for windows backend"

This reverts commit 52554bea1fa61045451600c64447b7bf38cf6c92.

* Revert "fix: avoid frozen restart crash from multiprocessing import"

This reverts commit 10548645b0ba1e19b64194878ece478a48067959.

* fix: reset pyinstaller onefile env before reboot

* fix: unify electron restart path and tray-exit backend cleanup

* fix: stabilize desktop restart detection and frozen reboot args

* fix: make dashboard restart wait detection robust

* fix: revert dashboard restart waiting interaction tweaks

* fix: pass auth token for desktop graceful restart

* fix: avoid false failure during graceful restart wait

* fix: start restart waiting before electron restart call

* fix: harden restart waiting and reboot arg parsing

* fix: parse start_time as numeric timestamp

* fix: preserve windows frozen reboot argv quoting

* fix: align restart waiting with electron restart timing

* fix: tighten graceful restart and unmanaged kill safety

* chore: bump version to 4.15.0 (#5003)

* fix: add reminder for v4.14.8 users regarding manual redeployment due to a bug

* fix: harden plugin dependency loading in frozen app runtime (#5015)

* fix: compare plugin versions semantically in market updates

* fix: prioritize plugin site-packages for in-process pip

* fix: reload starlette from plugin target site-packages

* fix: harden plugin dependency import precedence in frozen runtime

* fix: improve plugin dependency conflict handling

* refactor: simplify plugin conflict checks and version utils

* fix: expand transitive plugin dependencies for conflict checks

* fix: recover conflicting plugin dependencies during module prefer

* fix: reuse renderer restart flow for tray backend restart

* fix: add recoverable plugin dependency conflict handling

* revert: remove plugin version comparison changes

* fix: add missing tray restart backend labels

* feat: adding support for media and quoted message attachments for feishu (#5018)

* docs: add AUR installation method (#4879)

* docs: sync system package manager installation instructions to all languages

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update README.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix/typo

* refactor: update system package manager installation instructions for Arch Linux across multiple language README files

* feat: add installation command for AstrBot in multiple language README files

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>
Co-authored-by: Soulter <905617992@qq.com>

* fix(desktop): 为 Electron 与后端日志增加按大小轮转 (#5029)

* fix(desktop): rotate electron and backend logs

* refactor(desktop): centralize log rotation defaults and debug fs errors

* fix(desktop): harden rotation fs ops and buffer backend log writes

* refactor(desktop): extract buffered logger and reduce sync stat calls

* refactor(desktop): simplify rotation flow and harden logger config

* fix(desktop): make app logging async and flush-safe

* fix: harden app log path switching and debug-gated rotation errors

* fix: cap buffered log chunk size during path switch

* feat: add first notice feature with multilingual support and UI integration

* fix: 提升打包版桌面端启动稳定性并优化插件依赖处理 (#5031)

* fix(desktop): rotate electron and backend logs

* refactor(desktop): centralize log rotation defaults and debug fs errors

* fix(desktop): harden rotation fs ops and buffer backend log writes

* refactor(desktop): extract buffered logger and reduce sync stat calls

* refactor(desktop): simplify rotation flow and harden logger config

* fix(desktop): make app logging async and flush-safe

* fix: harden app log path switching and debug-gated rotation errors

* fix: cap buffered log chunk size during path switch

* fix: avoid redundant plugin reinstall and upgrade electron

* fix: stop webchat tasks cleanly and bind packaged backend to localhost

* fix: unify platform shutdown and await webchat listener cleanup

* fix: improve startup logs for dashboard and onebot listeners

* fix: revert extra startup service logs

* fix: harden plugin import recovery and webchat listener cleanup

* fix: pin dashboard ci node version to 24.13.0

* fix: avoid duplicate webchat listener cleanup on terminate

* refactor: clarify platform task lifecycle management

* fix: continue platform shutdown when terminate fails

* feat: temporary file handling and introduce TempDirCleaner (#5026)

* feat: temporary file handling and introduce TempDirCleaner

- Updated various modules to use `get_astrbot_temp_path()` instead of `get_astrbot_data_path()` for temporary file storage.
- Renamed temporary files for better identification and organization.
- Introduced `TempDirCleaner` to manage the size of the temporary directory, ensuring it does not exceed a specified limit by deleting the oldest files.
- Added configuration option for maximum temporary directory size in the dashboard.
- Implemented tests for `TempDirCleaner` to verify cleanup functionality and size management.

* ruff

* fix: close unawaited reset coroutine on early return (#5033)

When an OnLLMRequestEvent hook stops event propagation, the
reset_coro created by build_main_agent was never awaited, causing
a RuntimeWarning. Close the coroutine explicitly before returning.

Fixes #5032

Co-authored-by: Limitless2023 <limitless@users.noreply.github.com>

* fix: update error logging message for connection failures

* docs: clean and sync README (#5014)

* fix: close missing div in README

* fix: sync README_zh-TW with README

* fix: sync README

* fix: correct typo

correct url in README_en README_fr README_ru

* docs: sync README_en with README

* Update README_en.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: provider extra param dialog key display error

* chore: ruff format

* feat: add send_chat_action for Telegram platform adapter (#5037)

* feat: add send_chat_action for Telegram platform adapter

Add typing/upload indicator when sending messages via Telegram.
- Added _send_chat_action helper method for sending chat actions
- Send appropriate action (typing, upload_photo, upload_document, upload_voice)
  before sending different message types
- Support streaming mode with typing indicator
- Support supergroup with message_thread_id

* refactor(telegram): extract chat action helpers and add throttling

- Add ACTION_BY_TYPE mapping for message type to action priority
- Add _get_chat_action_for_chain() to determine action from message chain
- Add _send_media_with_action() for upload → send → restore typing pattern
- Add _ensure_typing() helper for typing status
- Add chat action throttling (0.5s) in streaming mode to avoid rate limits
- Update type annotation to ChatAction | str for better static checking

* feat(telegram): implement send_typing method for Telegram platform

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 修复更新日志、官方文档弹窗双滚动条问题 (#5060)

* docs: sync and fix readme typo (#5055)

* docs: fix index typo

* docs: fix typo in README_en.md

- 移除英文README中意外出现的俄语,并替换为英语

* docs: fix html typo

- remove unused '</p>'

* docs: sync table with README

* docs: sync README header format

- keep the README header format consistent

* doc: sync key features

* style: format files

- Fix formatting issues from previous PR

* fix: correct md anchor link

* docs: correct typo in README_fr.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* docs: correct typo in README_zh-TW.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix: 修复备份时缺失的人格文件夹映射 (#5042)

* feat: QQ 官方机器人平台支持主动推送消息、私聊场景下支持接收文件 (#5066)

* feat: QQ 官方机器人平台支持主动推送消息、私聊场景下支持接收文件

* feat: enhance QQOfficialWebhook to remember session scenes for group, channel, and friend messages

* perf: 优化分段回复间隔时间的初始化逻辑 (#5068)

fixes: #5059

* fix: chunk err when using openrouter deepseek (#5069)

* feat: add i18n supports for custom platform adapters (#5045)

* Feat: 为插件提供的适配器的元数据&i18n提供数据通路

* chore: update docstrings with pull request references

Added references to pull request 5045 in docstrings.

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: 完善转发引用解析与图片回退并支持配置化控制 (#5054)

* feat: support fallback image parsing for quoted messages

* fix: fallback parse quoted images when reply chain has placeholders

* style: format network utils with ruff

* test: expand quoted parser coverage and improve fallback diagnostics

* fix: fallback to text-only retry when image requests fail

* fix: tighten image fallback and resolve nested quoted forwards

* refactor: simplify quoted message extraction and dedupe images

* fix: harden quoted parsing and openai error candidates

* fix: harden quoted image ref normalization

* refactor: organize quoted parser settings and logging

* fix: cap quoted fallback images and avoid retry loops

* refactor: split quoted message parser into focused modules

* refactor: share onebot segment parsing logic

* refactor: unify quoted message parsing flow

* feat: move quoted parser tuning to provider settings

* fix: add missing i18n metadata for quoted parser settings

* chore: refine forwarded message setting labels

* fix: add config tabs and routing for normal and system configurations

* chore: bump version to 4.16.0 (#5074)

* feat: add LINE platform support with adapter and configuration (#5085)

* fix-correct-FIRST_NOTICE.md-locale-path-resolution (#5083) (#5082)

* fix:修改配置文件目录

* fix:添加备选的FIRST_NOTICE.zh-CN.md用于兼容

* fix: remove unnecessary frozen flag from requirements export in Dockerfile

fixes: #5089

* fix #5089: add uv lock step in Dockerfile before export (#5091)

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* feat: support hot reload after plugin load failure (#5043)

* add :Support hot reload after plugin load failure

* Apply suggestions from code review

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix:reformat code

* fix:reformat code

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* feat: add fallback chat model chain in tool loop runner (#5109)

* feat: implement fallback provider support for chat models and update configuration

* feat: enhance provider selection display with count and chips for selected providers

* feat: update fallback chat providers to use provider settings and add warning for non-list fallback models

* feat: add Afdian support card to resources section in WelcomePage

* feat: replace colorlog with loguru for enhanced logging support (#5115)

* feat: add SSL configuration options for WebUI and update related logging (#5117)

* chore: bump version to 4.17.0

* fix: handle list format content from OpenAI-compatible APIs (#5128)

* fix: handle list format content from OpenAI-compatible APIs

Some LLM providers (e.g., GLM-4.5V via SiliconFlow) return content as
list[dict] format like [{'type': 'text', 'text': '...'}] instead of
plain string. This causes the raw list representation to be displayed
to users.

Changes:
- Add _normalize_content() helper to extract text from various content formats
- Use json.loads instead of ast.literal_eval for safer parsing
- Add size limit check (8KB) before attempting JSON parsing
- Only convert lists that match OpenAI content-part schema (has 'type': 'text')
  to avoid collapsing legitimate list-literal replies like ['foo', 'bar']
- Add strip parameter to preserve whitespace in streaming chunks
- Clean up orphan </think> tags that may leak from some models

Fixes #5124

* fix: improve content normalization safety

- Try json.loads first, fallback to ast.literal_eval for single-quoted
  Python literals to avoid corrupting apostrophes (e.g., "don't")
- Coerce text values to str to handle null or non-string text fields

* fix: update retention logic in LogManager to handle backup count correctly

* chore: bump version to 4.17.1

* docs: Added instructions for deploying AstrBot using AstrBot Launcher. (#5136)

Added instructions for deploying AstrBot using AstrBot Launcher.

* fix: add MCP tools to function tool set in _plugin_tool_fix (#5144)

* fix: add support for collecting data from builtin stars in electron pyinstaller build (#5145)

* chore: bump version to 4.17.1

* chore: ruff format

* fix: prevent updates for AstrBot launched via launcher

* fix(desktop): include runtime deps for builtin plugins in backend build (#5146)

* fix: 'Plain' object has no attribute 'text' when using python 3.14 (#5154)

* fix: enhance plugin metadata handling by injecting attributes before instantiation (#5155)

* fix: enhance handle_result to support event context and webchat image sending

* chore: bump version to 4.17.3

* chore: ruff format

* feat: add NVIDIA provider template (#5157)

fixes: #5156

* feat: enhance provider sources panel with styled menu and mobile support

* fix: improve permission denied message for local execution in Python and shell tools

* feat: enhance PersonaForm component with responsive design and improved styling (#5162)

fix: #5159

* ui(CronJobPage): fix action column buttons overlapping in CronJobPage (#5163)

- 修改前:操作列容器仅使用 `d-flex`,在页面宽度变窄时,子元素(开关和删除按钮)会因为宽度挤压而发生视觉重叠,甚至堆叠在一起。
- 修改后:
    1. 为容器添加了 `flex-nowrap`,强制禁止子元素换行。
    2. 设置了 `min-width: 140px`,确保该列拥有固定的保护空间,防止被其他长文本列挤压。
    3. 增加了 `gap: 12px` 间距,提升了操作辨识度并优化了点击体验。

* feat: add unsaved changes notice to configuration page and update messages

* feat: implement search functionality in configuration components and update UI (#5168)

* feat: add FAQ link to vertical sidebar and update navigation for localization

* feat: add announcement section to WelcomePage and localize announcement title

* chore: bump version to 4.17.4

* feat: supports send markdown message in qqofficial (#5173)

* feat: supports send markdown message in qqofficial

closes: #1093 #918 #4180 #4264

* ruff format

* fix: prevent duplicate error message when all LLM providers fail (#5183)

* fix: 修复选择配置文件进入配置文件管理弹窗直接关闭弹窗显示的配置文件不正确 (#5174)

* feat: add MarketPluginCard component and integrate random plugin feature in ExtensionPage (#5190)

* feat: add MarketPluginCard component and integrate random plugin feature in ExtensionPage

* feat: update random plugin selection logic to use pluginMarketData and refresh on relevant events

* feat: supports aihubmix

* docs: update readme

* chore: ruff format

* feat: add LINE support to multiple language README files

* feat(core): add plugin error hook for custom error routing (#5192)

* feat(core): add plugin error hook for custom error routing

* fix(core): align plugin error suppression with event stop state

* refactor: extract Voice_messages_forbidden fallback into shared helper with typed BadRequest exception (#5204)

- Add _send_voice_with_fallback helper to deduplicate voice forbidden handling
- Catch telegram.error.BadRequest instead of bare Exception with string matching
- Add text field to Record component to preserve TTS source text
- Store original text in Record during TTS conversion for use as document caption
- Skip _send_chat_action when chat_id is empty to avoid unnecessary warnings

* chore: bump version to 4.17.5

* feat: add admin permission checks for Python and Shell execution (#5214)

* fix: 改进微信公众号被动回复处理机制,引入缓冲与分片回复,并优化超时行为 (#5224)

* 修复wechat official 被动回复功能

* ruff format

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 修复仅发送 JSON 消息段时的空消息回复报错 (#5208)

* Fix Register_Stage

· 补全 JSON 消息判断,修复发送 JSON 消息时遇到 “消息为空,跳过发送阶段” 的问题。
· 顺带补全其它消息类型判断。
Co-authored-by: Pizero <zhaory200707@outlook.com>

* Fix formatting and comments in stage.py

* Format stage.py

---------

Co-authored-by: Pizero <zhaory200707@outlook.com>

* docs: update related repo links

* fix(core): terminate active events on reset/new/del to prevent stale responses (#5225)

* fix(core): terminate active events on reset/new/del to prevent stale responses

Closes #5222

* style: fix import sorting in scheduler.py

* chore: remove Electron desktop pipeline and switch to tauri repo (#5226)

* ci: remove Electron desktop build from release pipeline

* chore: remove electron desktop and switch to tauri release trigger

* ci: remove desktop workflow dispatch trigger

* refactor: migrate data paths to astrbot_path helpers

* fix: point desktop update prompt to AstrBot-desktop releases

* fix: update feature request template for clarity and consistency in English and Chinese

* Feat/config leave confirm (#5249)

* feat: 配置文件增加未保存提示弹窗

* fix: 移除unsavedChangesDialog插件使用组件方式实现弹窗

* feat: add support for plugin astrbot-version and platform requirement checks (#5235)

* feat: add support for plugin astrbot-version and platform requirement checks

* fix: remove unsupported platform and version constraints from metadata.yaml

* fix: remove restriction on 'v' in astrbot_version specification format

* ruff format

* feat: add password confirmation when changing password (#5247)

* feat: add password confirmation when changing password

Fixes #5177

Adds a password confirmation field to prevent accidental password typos.

Changes:
- Backend: validate confirm_password matches new_password
- Frontend: add confirmation input with validation
- i18n: add labels and error messages for password mismatch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(auth): improve error message for password confirmation mismatch

* fix(auth): update password hashing logic and improve confirmation validation

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(provider): 修复 dict 格式 content 导致的 JSON 残留问题 (#5250)

* fix(provider): 修复 dict 格式 content 导致的 JSON 残留问题

修复 _normalize_content 函数未处理 dict 类型 content 的问题。
当 LLM 返回 {"type": "text", "text": "..."} 格式的 content 时,
现在会正确提取 text 字段而非直接转为字符串。

同时改进 fallback 行为,对 None 值返回空字符串。

Fixes #5244

* Update warning message for unexpected dict format

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* chore: remove outdated heihe.md documentation file

* fix: all mcp tools exposed to main agent (#5252)

* fix: enhance PersonaForm layout and improve tool selection display

* fix: update tool status display and add localization for inactive tools

* fix: remove additionalProperties from tool schema properties (#5253)

fixes: #5217

* fix: simplify error messages for account edit validation

* fix: streamline error response for empty new username and password in account edit

* chore: bump vertion to 4.17.6

* feat: add OpenRouter provider support and icon

* chore: ruff format

* refactor(dashboard): replace legacy isElectron bridge fields with isDesktop (#5269)

* refactor dashboard desktop bridge fields from isElectron to isDesktop

* refactor dashboard runtime detection into shared helper

* fix: update contributor avatar image URL to include max size and columns (#5268)

* feat: astrbot http api (#5280)

* feat: astrbot http api

* Potential fix for code scanning alert no. 34: Use of a broken or weak cryptographic hashing algorithm on sensitive data

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fix: improve error handling for missing attachment path in file upload

* feat: implement paginated retrieval of platform sessions for creators

* feat: refactor attachment directory handling in ChatRoute

* feat: update API endpoint paths for file and message handling

* feat: add documentation link to API key management section in settings

* feat: update API key scopes and related configurations in API routes and tests

* feat: enhance API key expiration options and add warning for permanent keys

* feat: add UTC normalization and serialization for API key timestamps

* feat: implement chat session management and validation for usernames

* feat: ignore session_id type chunks in message processing

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* feat(dashboard): improve plugin platform support display and mobile accessibility (#5271)

* feat(dashboard): improve plugin platform support display and mobile accessibility

- Replace hover-based tooltips with interactive click menus for platform support information.
- Fix mobile touch issues by introducing explicit state control for status capsules.
- Enhance UI aesthetics with platform-specific icons and a structured vertical list layout.
- Add dynamic chevron icons to provide clear visual cues for expandable content.

* refactor(dashboard): refactor market card with computed properties for performance

* refactor(dashboard): unify plugin platform support UI with new reusable chip component

- Create shared 'PluginPlatformChip' component to encapsulate platform meta display.
- Fix mobile interaction bugs by simplifying menu triggers and event handling.
- Add stacked platform icon previews and dynamic chevron indicators within capsules.
- Improve information hierarchy using structured vertical lists for platform details.
- Optimize rendering efficiency with computed properties across both card views.

* fix: qq official guild message send error (#5287)

* fix: qq official guild message send error

* Update astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* 更新readme文档,补充桌面app说明,并向前移动位置 (#5297)

* docs: update desktop deployment section in README

* docs: refine desktop and launcher deployment descriptions

* Update README.md

* feat: add Anthropic Claude Code OAuth provider and adaptive thinking support (#5209)

* feat: add Anthropic Claude Code OAuth provider and adaptive thinking support

* fix: add defensive guard for metadata overrides and align budget condition with docs

* refactor: adopt sourcery-ai suggestions for OAuth provider

- Use use_api_key=False in OAuth subclass to avoid redundant
  API-key client construction before replacing with auth_token client
- Generalize metadata override helper to merge all dict keys
  instead of only handling 'limit', improving extensibility

* Feat/telegram command alias register  #5233 (#5234)

* feat: support registering command aliases for Telegram

Now when registering commands with aliases, all aliases will be
registered as Telegram bot commands in addition to the main command.

Example:
    @register_command(command_name="draw", alias={"画", "gen"})
Now /draw, /画, and /gen will all appear in the Telegram command menu.

* feat(telegram): add duplicate command name warning when registering commands

Log a warning when duplicate command names are detected during Telegram
command registration to help identify configuration conflicts.

* refactor: remove Anthropic OAuth provider implementation and related metadata overrides

* fix: 修复新建对话时因缺少会话ID导致配置绑定失败的问题 (#5292)

* fix:尝试修改

* fix:添加详细日志

* fix:进行详细修改,并添加日志

* fix:删除所有日志

* fix: 增加安全访问函数

- 给 localStorage 访问加了 try/catch + 可用性判断:dashboard/src/utils/chatConfigBinding.ts:13
- 新增 getFromLocalStorage/setToLocalStorage(在受限存储/无痕模式下异常时回退/忽略)
- getStoredDashboardUsername() / getStoredSelectedChatConfigId() 改为走安全读取:dashboard/src/utils/chatConfigBinding.ts:36       - 新增 setStoredSelectedChatConfigId(),写入失败静默忽略:dashboard/src/utils/chatConfigBinding.ts:44
- 把 ConfigSelector.vue 里直接 localStorage.getItem/setItem 全部替换为上述安全方法:dashboard/src/components/chat/ConfigSelector.vue:81
- 已重新跑过 pnpm run typecheck,通过。

* rm:删除个人用的文档文件

* Revert "rm:删除个人用的文档文件"

This reverts commit 0fceee05434cfbcb11e45bb967a77d5fa93196bf.

* rm:删除个人用的文档文件

* rm:删除个人用的文档文件

* chore: bump version to 4.18.0

* fix(SubAgentPage): 当中间的介绍文本非常长时,Flex 布局会自动挤压右侧的控制按钮区域 (#5306)

* fix: 修复新版本插件市场出现插件显示为空白的 bug;纠正已安装插件卡片的排版,统一大小 (#5309)

* fix(ExtensionCard): 解决插件卡片大小不统一的问题

* fix(MarketPluginCard): 解决插件市场不加载插件的问题 (#5303)

* feat: supports spawn subagent as a background task that not block the main agent workflow (#5081)

* feat:为subagent添加后台任务参数

* ruff

* fix: update terminology from 'handoff mission' to 'background task' and refactor related logic

* fix: update terminology from 'background_mission' to 'background_task' in HandoffTool and related logic

* fix(HandoffTool): update background_task description for clarity on usage

---------

Co-authored-by: Soulter <905617992@qq.com>

* cho

* fix: 修复 aiohttp 版本过新导致 qq-botpy 报错的问题 (#5316)

* chore: ruff format

* fix: remove hard-coded 6s timeout from tavily request

* fix: remove changelogs directory from .dockerignore

* feat(dashboard): make release redirect base URL configurable (#5330)

* feat(dashboard): make desktop release base URL configurable

* refactor(dashboard): use generic release base URL env with upstream default

* fix(dashboard): guard release base URL normalization when env is unset

* refactor(dashboard): use generic release URL helpers and avoid latest suffix duplication

* feat: add stop functionality for active agent sessions and improve handling of stop requests (#5380)

* feat: add stop functionality for active agent sessions and improve handling of stop requests

* feat: update stop button icon and tooltip in ChatInput component

* fix: correct indentation in tool call handling within ChatRoute class

* fix: chatui cannot persist file segment (#5386)

* fix(plugin): update plugin directory handling for reserved plugins (#5369)

* fix(plugin): update plugin directory handling for reserved plugins

* fix(plugin): add warning logs for missing plugin name, object, directory, and changelog

* chore(README): updated with README.md (#5375)

* chore(README): updated with README.md

* Update README_fr.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Update README_zh-TW.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* feat: add image urls / paths supports for subagent (#5348)

* fix: 修复5081号PR在子代理执行后台任务时,未正确使用系统配置的流式/非流请求的问题(#5081)

* feat:为子代理增加远程图片URL参数支持

* fix: update description for image_urls parameter in HandoffTool to clarify usage in multimodal tasks

* ruff format

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat: add hot reload when failed to load plugins (#5334)

* feat:add hot reload when failed to load plugins

* apply bot suggestions

* fix(chatui): add copy rollback path and error message. (#5352)

* fix(chatui): add copy rollback path and error message.

* fix(chatui): fixed textarea leak in the copy button.

* fix(chatui): use color styles from the component library.

* fix: 处理配置文件中的 UTF-8 BOM 编码问题 (#5376)

* fix(config): handle UTF-8 BOM in configuration file loading

Problem:
On Windows, some text editors (like Notepad) automatically add UTF-8 BOM
to JSON files when saving. This causes json.decoder.JSONDecodeError:
"Unexpected UTF-8 BOM" and AstrBot fails to start when cmd_config.json
contains BOM.

Solution:
Add defensive check to strip UTF-8 BOM (\ufeff) if present before
parsing JSON configuration file.

Impact:
- Improves robustness and cross-platform compatibility
- No breaking changes to existing functionality
- Fixes startup failure when configuration file has UTF-8 BOM encoding

Relates-to: Windows editor compatibility issues

* style: fix code formatting with ruff

Fix single quote to double quote to comply with project code style.

* feat: add plugin load&unload hook (#5331)

* 添加了插件的加载完成和卸载完成的钩子事件

* 添加了插件的加载完成和卸载完成的钩子事件

* format code with ruff

* ruff format

---------

Co-authored-by: Soulter <905617992@qq.com>

* test: enhance test framework with comprehensive fixtures and mocks (#5354)

* test: enhance test framework with comprehensive fixtures and mocks

- Add shared mock builders for aiocqhttp, discord, telegram
- Add test helpers for platform configs and mock objects
- Expand conftest.py with test profile support
- Update coverage test workflow configuration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(tests): 移动并重构模拟 LLM 响应和消息组件函数

* fix(tests): 优化 pytest_runtest_setup 中的标记检查逻辑

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add comprehensive tests for message event handling (#5355)

* test: add comprehensive tests for message event handling

- Add AstrMessageEvent unit tests (688 lines)
- Add AstrBotMessage unit tests
- Enhance smoke tests with message event scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: improve message type handling and add defensive tests

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add support for showing tool call results in agent execution (#5388)

closes: #5329

* fix: resolve pipeline and star import cycles (#5353)

* fix: resolve pipeline and star import cycles

- Add bootstrap.py and stage_order.py to break circular dependencies
- Export Context, PluginManager, StarTools from star module
- Update pipeline __init__ to defer imports
- Split pipeline initialization into separate bootstrap module

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add logging for get_config() failure in Star class

* fix: reorder logger initialization in base.py

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: enable computer-use tools for subagent handoff (#5399)

* fix: enforce admin guard for sandbox file transfer tools (#5402)

* fix: enforce admin guard for sandbox file transfer tools

* refactor: deduplicate computer tools admin permission checks

* fix: add missing space in permission error message

* fix(core): 优化 File 组件处理逻辑并增强 OneBot 驱动层路径兼容性 (#5391)

* fix(core): 优化 File 组件处理逻辑并增强 OneBot 驱动层路径兼容性

原因 (Necessity):
1. 内核一致性:AstrBot 内核的 Record 和 Video 组件均具备识别 `file:///` 协议头的逻辑,但 File 组件此前缺失此功能,导致行为不统一。
2. OneBot 协议合规:OneBot 11 标准要求本地文件路径必须使用 `file:///` 协议头。此前驱动层未对裸路径进行自动转换,导致发送本地文件时常触发 retcode 1200 (识别URL失败) 错误。
3. 容器环境适配:在 Docker 等路径隔离环境下,裸路径更容易因驱动或协议端的解析歧义而失效。

更改 (Changes):
- [astrbot/core/message/components.py]:
  - 在 File.get_file() 中增加对 `file:///` 前缀的识别与剥离逻辑,使其与 Record/Video 组件行为对齐。
- [astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py]:
  - 在发送文件前增加自动修正逻辑:若路径为绝对路径且未包含协议头,驱动层将自动补全 `file:///` 前缀。
  - 对 http、base64 及已有协议头,确保不干扰原有的正常传输逻辑。

影响 (Impact):
- 以完全兼容的方式增强了文件发送的鲁棒性。
- 解决了插件在发送日志等本地生成的压缩包时,因路径格式不规范导致的发送失败问题。

* refactor(core): 根据 cr 建议,规范化文件 URI 生成与解析逻辑,优化跨平台兼容性

原因 (Necessity):
1. 修复原生路径与 URI 转换在 Windows 下的不对称问题。
2. 规范化 file: 协议头处理,确保符合 RFC 标准并能在 Linux/Windows 间稳健切换。
3. 增强协议判定准确度,防止对普通绝对路径的误处理。

更改 (Changes):
- [astrbot/core/platform/sources/aiocqhttp]:
  - 弃用手动拼接,改用 `pathlib.Path.as_uri()` 生成标准 URI。
  - 将协议检测逻辑从前缀匹配优化为包含性检测 ("://")。
- [astrbot/core/message/components]:
  - 重构 `File.get_file` 解析逻辑,支持对称处理 2/3 斜杠格式。
  - 针对 Windows 环境增加了对 `file:///C:/` 格式的自动修正,避免 `os.path` 识别失效。
- [data/plugins/astrbot_plugin_logplus]:
  - 在直接 API 调用中同步应用 URI 规范化处理。

影响 (Impact):
- 解决 Docker 环境中因路径不规范导致的 "识别URL失败" 报错。
- 提升了本体框架在 Windows 系统下的文件操作鲁棒性。

* i18n(SubAgentPage): complete internationalization for subagent orchestration page (#5400)

* i18n: complete internationalization for subagent orchestration page

- Replace hardcoded English strings in [SubAgentPage.vue] with i18n keys.
- Update `en-US` and `zh-CN` locales with missing hints, validation messages, and empty state translations.
- Fix translation typos and improve consistency across the SubAgent orchestration UI.

* fix(bug_risk): 避免在模板中的翻译调用上使用 || 'Close' 作为回退值。

* fix(aiocqhttp): enhance shutdown process for aiocqhttp adapter (#5412)

* fix: pass embedding dimensions to provider apis (#5411)

* fix(context): log warning when platform not found for session

* fix(context): improve logging for platform not found in session

* chore: bump version to 4.18.2

* chore: bump version to 4.18.2

* chore: bump version to 4.18.2

* fix: Telegram voice message format (OGG instead of WAV) causing issues with OpenAI STT API (#5389)

* chore: ruff format

* feat(dashboard): add generic desktop app updater bridge (#5424)

* feat(dashboard): add generic desktop app updater bridge

* fix(dashboard): address updater bridge review feedback

* fix(dashboard): unify updater bridge types and error logging

* fix(dashboard): consolidate updater bridge typings

* fix(conversation): retain existing persona_id when updating conversation

* fix(dashboard): 修复设置页新建 API Key 后复制失败问题 (#5439)

* Fix: GitHub proxy not displaying correctly in WebUI (#5438)

* fix(dashboard): preserve custom GitHub proxy setting on reload

* fix(dashboard): keep github proxy selection persisted in settings

* fix(persona): enhance persona resolution logic for conversations and sessions

* fix: ensure tool call/response pairing in context truncation (#5417)

* fix: ensure tool call/response pairing in context truncation

* refactor: simplify fix_messages to single-pass state machine

* perf(cron): enhance future task session isolation

fixes: #5392

* feat: add useExtensionPage composable for managing plugin extensions

- Implemented a new composable `useExtensionPage` to handle various functionalities related to plugin management, including fetching extensions, handling updates, and managing UI states.
- Added support for conflict checking, plugin installation, and custom source management.
- Integrated search and filtering capabilities for plugins in the market.
- Enhanced user experience with dialogs for confirmations and notifications.
- Included pagination and sorting features for better plugin visibility.

* fix: clear markdown field when sending media messages via QQ Official Platform (#5445)

* fix: clear markdown field when sending media messages via QQ Official API

* refactor: use pop() to remove markdown key instead of setting None

* fix: cannot automatically get embedding dim when create embedding provider (#5442)

* fix(dashboard): 强化 API Key 复制临时节点清理逻辑

* fix(embedding): 自动检测改为探测 OpenAI embedding 最大可用维度

* fix: normalize openai embedding base url and add hint key

* i18n: add embedding_api_base hint translations

* i18n: localize provider embedding/proxy metadata hints

* fix: show provider-specific embedding API Base URL hint as field subtitle

* fix(embedding): cap OpenAI detect_dim probes with early short-circuit

* fix(dashboard): return generic error on provider adapter import failure

* 回退检测逻辑

* fix: 修复Pyright静态类型检查报错 (#5437)

* refactor: 修正 Sqlite 查询、下载回调、接口重构与类型调整

* feat: 为 OneBotClient 增加 CallAction 协议与异步调用支持

* fix(telegram): avoid duplicate message_thread_id in streaming (#5430)

* perf: batch metadata query in KB retrieval to fix N+1 problem (#5463)

* perf: batch metadata query in KB retrieval to fix N+1 problem

Replace N sequential get_document_with_metadata() calls with a single
get_documents_with_metadata_batch() call using SQL IN clause.

Benchmark results (local SQLite):
- 10 docs: 10.67ms → 1.47ms (7.3x faster)
- 20 docs: 26.00ms → 2.68ms (9.7x faster)
- 50 docs: 63.87ms → 2.79ms (22.9x faster)

* refactor: use set[str] param type and chunk IN clause for SQLite safety

Address review feedback:
- Change doc_ids param from list[str] to set[str] to avoid unnecessary conversion
- Chunk IN clause into batches of 900 to stay under SQLite's 999 parameter limit
- Remove list() wrapping at call site, pass set directly

* fix:fix the issue where incomplete cleanup of residual plugins occurs… (#5462)

* fix:fix the issue where incomplete cleanup of residual plugins occurs in the failed loading of plugins

* fix:ruff format,apply bot suggestions

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* chore: 为类型检查添加 TYPE_CHECKING 的导入与阶段类型引用 (#5474)

* fix(line): line adapter does not appear in the add platform dialog

fixes: #5477

* [bug]查看介入教程line前往错误界面的问题 (#5479)

Fixes #5478

* chore: bump version to 4.18.3

* feat: implement follow-up message handling in ToolLoopAgentRunner (#5484)

* feat: implement follow-up message handling in ToolLoopAgentRunner

* fix: correct import path for follow-up module in InternalAgentSubStage

* feat: implement websockets transport mode selection for chat (#5410)

* feat: implement websockets transport mode selection for chat

- Added transport mode selection (SSE/WebSocket) in the chat component.
- Updated conversation sidebar to include transport mode options.
- Integrated transport mode handling in message sending logic.
- Refactored message sending functions to support both SSE and WebSocket.
- Enhanced WebSocket connection management and message handling.
- Updated localization files for transport mode labels.
- Configured Vite to support WebSocket proxying.

* feat(webchat): refactor message parsing logic and integrate new parsing function

* feat(chat): add websocket API key extraction and scope validation

* Revert "可选后端,实现前后端分离" (#5536)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: can <51474963+weijintaocode@users.noreply.github.com>
Co-authored-by: Soulter <905617992@qq.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>
Co-authored-by: letr <123731298+letr007@users.noreply.github.com>
Co-authored-by: 搁浅 <id6543156918@gmail.com>
Co-authored-by: Helian Nuits <sxp20061207@163.com>
Co-authored-by: Gao Jinzhe <2968474907@qq.com>
Co-authored-by: DD斩首 <155905740+DDZS987@users.noreply.github.com>
Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: エイカク <62183434+zouyonghe@users.noreply.github.com>
Co-authored-by: 鸦羽 <Raven95676@gmail.com>
Co-authored-by: Dt8333 <25431943+Dt8333@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Li-shi-ling <114913764+Li-shi-ling@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Limitless <127183162+Limitless2023@users.noreply.github.com>
Co-authored-by: Limitless2023 <limitless@users.noreply.github.com>
Co-authored-by: evpeople <54983536+evpeople@users.noreply.github.com>
Co-authored-by: SnowNightt <127504703+SnowNightt@users.noreply.github.com>
Co-authored-by: xzj0898 <62733743+xzj0898@users.noreply.github.com>
Co-authored-by: stevessr <89645372+stevessr@users.noreply.github.com>
Co-authored-by: Waterwzy <2916963017@qq.com>
Co-authored-by: NayukiMeko <MekoNayuki@outlook.com>
Co-authored-by: 時壹 <137363396+KBVsent@users.noreply.github.com>
Co-authored-by: sanyekana <Clhikari@qq.com>
Co-authored-by: Chiu Chun-Hsien <95356121+911218sky@users.noreply.github.com>
Co-authored-by: Dream Tokenizer <60459821+Trance-0@users.noreply.github.com>
Co-authored-by: NanoRocky <76585834+NanoRocky@users.noreply.github.com>
Co-authored-by: Pizero <zhaory200707@outlook.com>
Co-authored-by: 雪語 <167516635+YukiRa1n@users.noreply.github.com>
Co-authored-by: whatevertogo <1879483647@qq.com>
Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-…
LIghtJUNction added a commit that referenced this pull request Feb 27, 2026
* feat: add bocha web search tool (#4902)

* add bocha web search tool

* Revert "add bocha web search tool"

This reverts commit 1b36d75a17b4c4751828f31f6759357cd2d4000a.

* add bocha web search tool

* fix: correct temporary_cache spelling and update supported tools for web search

* ruff

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: messages[x] assistant content must contain at least one part (#4928)

* fix: messages[x] assistant content must contain at least one part

fixes: #4876

* ruff format

* chore: bump version to 4.14.5 (#4930)

* feat: implement feishu / lark media file handling utilities for file, audio and video processing (#4938)

* feat: implement media file handling utilities for audio and video processing

* feat: refactor file upload handling for audio and video in LarkMessageEvent

* feat: add cleanup for failed audio and video conversion outputs in media_utils

* feat: add utility methods for sending messages and uploading files in LarkMessageEvent

* fix: correct spelling of 'temporary' in SharedPreferences class

* perf: optimize webchat and wecom ai queue lifecycle (#4941)

* perf: optimize webchat and wecom ai queue lifecycle

* perf: enhance webchat back queue management with conversation ID support

* fix: localize provider source config UI (#4933)

* fix: localize provider source ui

* feat: localize provider metadata keys

* chore: add provider metadata translations

* chore: format provider i18n changes

* fix: preserve metadata fields in i18n conversion

* fix: internationalize platform config and dialog

* fix: add Weixin official account platform icon

---------

Co-authored-by: Soulter <905617992@qq.com>

* chore: bump version to 4.14.6

* feat: add provider-souce-level proxy (#4949)

* feat: 添加 Provider 级别代理支持及请求失败日志

* refactor: simplify provider source configuration structure

* refactor: move env proxy fallback logic to log_connection_failure

* refactor: update client proxy handling and add terminate method for cleanup

* refactor: update no_proxy configuration to remove redundant subnet

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat(ComponentPanel):  implement permission management for dashboard (#4887)

* feat(backend): add permission update api

* feat(useCommandActions): add updatePermission action and translations

* feat(dashboard): implement permission editing ui

* style: fix import sorting in command.py

* refactor(backend): extract permission update logic to service

* feat(i18n): add success and failure messages for command updates

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat: 允许 LLM 预览工具返回的图片并自主决定是否发送 (#4895)

* feat: 允许 LLM 预览工具返回的图片并自主决定是否发送

* 复用 send_message_to_user 替代独立的图片发送工具

* feat: implement _HandleFunctionToolsResult class for improved tool response handling

* docs: add path handling guidelines to AGENTS.md

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat(telegram): 添加媒体组(相册)支持 / add media group (album) support (#4893)

* feat(telegram): 添加媒体组(相册)支持 / add media group (album) support

## 功能说明
支持 Telegram 的媒体组消息(相册),将多张图片/视频合并为一条消息处理,而不是分散成多条消息。

## 主要改动

### 1. 初始化媒体组缓存 (__init__)
- 添加 `media_group_cache` 字典存储待处理的媒体组消息
- 使用 2.5 秒超时收集媒体组消息(基于社区最佳实践)
- 最大等待时间 10 秒(防止永久等待)

### 2. 消息处理流程 (message_handler)
- 检测 `media_group_id` 判断是否为媒体组消息
- 媒体组消息走特殊处理流程,避免分散处理

### 3. 媒体组消息缓存 (handle_media_group_message)
- 缓存收到的媒体组消息
- 使用 APScheduler 实现防抖(debounce)机制
- 每收到新消息时重置超时计时器
- 超时后触发统一处理

### 4. 媒体组合并处理 (process_media_group)
- 从缓存中取出所有媒体项
- 使用第一条消息作为基础(保留文本、回复等信息)
- 依次添加所有图片、视频、文档到消息链
- 将合并后的消息发送到处理流程

## 技术方案论证

Telegram Bot API 在处理媒体组时的设计限制:
1. 将媒体组的每个消息作为独立的 update 发送
2. 每个 update 带有相同的 `media_group_id`
3. **不提供**组的总数、结束标志或一次性完整组的机制

因此,bot 必须自行收集消息,并通过硬编码超时(timeout/delay)等待可能延迟到达的消息。
这是目前唯一可靠的方案,被官方实现、主流框架和开发者社区广泛采用。

### 官方和社区证据:
- **Telegram Bot API 服务器实现(tdlib)**:明确指出缺少结束标志或总数信息
  https://github.com/tdlib/telegram-bot-api/issues/643

- **Telegram Bot API 服务器 issue**:讨论媒体组处理的不便性,推荐使用超时机制
  https://github.com/tdlib/telegram-bot-api/issues/339

- **Telegraf(Node.js 框架)**:专用媒体组中间件使用 timeout 控制等待时间
  https://github.com/DieTime/telegraf-media-group

- **StackOverflow 讨论**:无法一次性获取媒体组所有文件,必须手动收集
  https://stackoverflow.com/questions/50180048/telegram-api-get-all-uploaded-photos-by-media-group-id

- **python-telegram-bot 社区**:确认媒体组消息单独到达,需手动处理
  https://github.com/python-telegram-bot/python-telegram-bot/discussions/3143

- **Telegram Bot API 官方文档**:仅定义 `media_group_id` 为可选字段,不提供获取完整组的接口
  https://core.telegram.org/bots/api#message

## 实现细节
- 使用 2.5 秒超时收集媒体组消息(基于社区最佳实践)
- 最大等待时间 10 秒(防止永久等待)
- 采用防抖(debounce)机制:每收到新消息重置计时器
- 利用 APScheduler 实现延迟处理和任务调度

## 测试验证
- ✅ 发送 5 张图片相册,成功合并为一条消息
- ✅ 保留原始文本说明和回复信息
- ✅ 支持图片、视频、文档混合的媒体组
- ✅ 日志显示 Processing media group <media_group_id> with 5 items

## 代码变更
- 文件:astrbot/core/platform/sources/telegram/tg_adapter.py
- 新增代码:124 行
- 新增方法:handle_media_group_message(), process_media_group()

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* refactor(telegram): 优化媒体组处理性能和可靠性

根据代码审查反馈改进:

1. 实现 media_group_max_wait 防止无限延迟
   - 跟踪媒体组创建时间,超过最大等待时间立即处理
   - 最坏情况下 10 秒内必定处理,防止消息持续到达导致无限延迟

2. 移除手动 job 查找优化性能
   - 删除 O(N) 的 get_jobs() 循环扫描
   - 依赖 replace_existing=True 自动替换任务

3. 重用 convert_message 减少代码重复
   - 统一所有媒体类型转换逻辑
   - 未来添加新媒体类型只需修改一处

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(telegram): handle missing message in media group processing and improve logging messages

---------

Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Soulter <905617992@qq.com>

* feat: add welcome feature with localized content and onboarding steps

* fix: correct height attribute to max-height for dialog component

* feat: supports electron app (#4952)

* feat: add desktop wrapper with frontend-only packaging

* docs: add desktop build docs and track dashboard lockfile

* fix: track desktop lockfile for npm ci

* fix: allow custom install directory for windows installer

* chore: migrate desktop workflow to pnpm

* fix(desktop): build AppImage only on Linux

* fix(desktop): harden packaged startup and backend bundling

* fix(desktop): adapt packaged restart and plugin dependency flow

* fix(desktop): prevent backend respawn race on quit

* fix(desktop): prefer pyproject version for desktop packaging

* fix(desktop): improve startup loading UX and reduce flicker

* ci: add desktop multi-platform release workflow

* ci: fix desktop release build and mac runner labels

* ci: disable electron-builder auto publish in desktop build

* ci: avoid electron-builder publish path in build matrix

* ci: normalize desktop release artifact names

* ci: exclude blockmap files from desktop release assets

* ci: prefix desktop release assets with AstrBot and purge blockmaps

* feat: add electron bridge types and expose backend control methods in preload script

* Update startup screen assets and styles

- Changed the icon from PNG to SVG format for better scalability.
- Updated the border color from #d0d0d0 to #eeeeee for a softer appearance.
- Adjusted the width of the startup screen from 460px to 360px for improved responsiveness.

* Update .gitignore to include package.json

* chore: remove desktop gitkeep ignore exceptions

* docs: update desktop troubleshooting for current runtime behavior

* refactor(desktop): modularize runtime and harden startup flow

---------

Co-authored-by: Soulter <905617992@qq.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: dedupe preset messages (#4961)

* feat: enhance package.json with resource filters and compression settings

* chore: update Python version requirements to 3.12 (#4963)

* chore: bump version to 4.14.7

* feat: refactor release workflow and add special update handling for electron app (#4969)

* chore: bump version to 4.14.8 and bump faiss-cpu version up to date

* chore: auto ann fix by ruff (#4903)

* chore: auto fix by ruff

* refactor: 统一修正返回类型注解为 None/bool 以匹配实现

* refactor: 将 _get_next_page 改为异步并移除多余的请求错误抛出

* refactor: 将 get_client 的返回类型改为 object

* style: 为 LarkMessageEvent 的相关方法添加返回类型注解 None

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: prepare OpenSSL via vcpkg for Windows ARM64

* ci: change ghcr namespace

* chore: update pydantic dependency version (#4980)

* feat: add delete button to persona management dialog (#4978)

* Initial plan

* feat: add delete button to persona management dialog

- Added delete button to PersonaForm dialog (only visible when editing)
- Implemented deletePersona method with confirmation dialog
- Connected delete event to PersonaManager for proper handling
- Button positioned on left side of dialog actions for clear separation
- Uses existing i18n translations for delete button and messages

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: use finally block to ensure saving state is reset

- Moved `this.saving = false` to finally block in deletePersona
- Ensures UI doesn't stay in saving state after errors
- Follows best practices for state management

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* feat: enhance Dingtalk adapter with active push message and image, video, audio message type (#4986)

* fix: handle pip install execution in frozen runtime (#4985)

* fix: handle pip install execution in frozen runtime

* fix: harden pip subprocess fallback handling

* fix: collect certifi data in desktop backend build (#4995)

* feat: 企业微信应用 支持主动消息推送,并优化企微应用、微信公众号、微信客服音频相关的处理 (#4998)

* feat: 企业微信智能机器人支持主动消息推送以及发送视频、文件等消息类型支持 (#4999)

* feat: enhance WecomAIBotAdapter and WecomAIBotMessageEvent for improved streaming message handling (#5000)

fixes: #3965

* feat: enhance persona tool management and update UI localization for subagent orchestration (#4990)

* feat: enhance persona tool management and update UI localization for subagent orchestration

* fix: remove debug logging for final ProviderRequest in build_main_agent function

* perf: 稳定源码与 Electron 打包环境下的 pip 安装行为,并修复非 Electron 环境下点击 WebUI 更新按钮时出现跳转对话框的问题 (#4996)

* fix: handle pip install execution in frozen runtime

* fix: harden pip subprocess fallback handling

* fix: scope global data root to packaged electron runtime

* refactor: inline frozen runtime check for electron guard

* fix: prefer current interpreter for source pip installs

* fix: avoid resolving venv python symlink for pip

* refactor: share runtime environment detection utilities

* fix: improve error message when pip module is unavailable

* fix: raise ImportError when pip module is unavailable

* fix: preserve ImportError semantics for missing pip

* fix: 修复非electron app环境更新时仍然显示electron更新对话框的问题

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 'HandoffTool' object has no attribute 'agent' (#5005)

* fix: 移动agent的位置到super().__init__之后

* add: 添加一行注释

* chore(deps): bump the github-actions group with 2 updates (#5006)

Bumps the github-actions group with 2 updates: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `astral-sh/setup-uv` from 6 to 7
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v6...v7)

Updates `actions/download-artifact` from 6 to 7
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: stabilize packaged runtime pip/ssl behavior and mac font fallback (#5007)

* fix: patch pip distlib finder for frozen electron runtime

* fix: use certifi CA bundle for runtime SSL requests

* fix: configure certifi CA before core imports

* fix: improve mac font fallback for dashboard text

* fix: harden frozen pip patch and unify TLS connector

* refactor: centralize dashboard CJK font fallback stacks

* perf: reuse TLS context and avoid repeated frozen pip patch

* refactor: bootstrap TLS setup before core imports

* fix: use async confirm dialog for provider deletions

* fix: replace native confirm dialogs in dashboard

- Add shared confirm helper in dashboard/src/utils/confirmDialog.ts for async dialog usage with safe fallback.

- Migrate provider, chat, config, session, platform, persona, MCP, backup, and knowledge-base delete/close confirmations to use the shared helper.

- Remove scattered inline confirm handling to keep behavior consistent and avoid native blocking dialog focus/caret issues in Electron.

* fix: capture runtime bootstrap logs after logger init

- Add bootstrap record buffer in runtime_bootstrap for early TLS patch logs before logger is ready.

- Flush buffered bootstrap logs to astrbot logger at process startup in main.py.

- Include concrete exception details for TLS bootstrap failures to improve diagnosis.

* fix: harden runtime bootstrap and unify confirm handling

- Simplify bootstrap log buffering and add a public initialize hook for non-main startup paths.

- Guard aiohttp TLS patching with feature/type checks and keep graceful fallback when internals are unavailable.

- Standardize dashboard confirmation flow via shared confirm helpers across composition and options API components.

* refactor: simplify runtime tls bootstrap and tighten confirm typing

* refactor: align ssl helper namespace and confirm usage

* fix: 修复 Windows 打包版后端重启失败问题 (#5009)

* fix: patch pip distlib finder for frozen electron runtime

* fix: use certifi CA bundle for runtime SSL requests

* fix: configure certifi CA before core imports

* fix: improve mac font fallback for dashboard text

* fix: harden frozen pip patch and unify TLS connector

* refactor: centralize dashboard CJK font fallback stacks

* perf: reuse TLS context and avoid repeated frozen pip patch

* refactor: bootstrap TLS setup before core imports

* fix: use async confirm dialog for provider deletions

* fix: replace native confirm dialogs in dashboard

- Add shared confirm helper in dashboard/src/utils/confirmDialog.ts for async dialog usage with safe fallback.

- Migrate provider, chat, config, session, platform, persona, MCP, backup, and knowledge-base delete/close confirmations to use the shared helper.

- Remove scattered inline confirm handling to keep behavior consistent and avoid native blocking dialog focus/caret issues in Electron.

* fix: capture runtime bootstrap logs after logger init

- Add bootstrap record buffer in runtime_bootstrap for early TLS patch logs before logger is ready.

- Flush buffered bootstrap logs to astrbot logger at process startup in main.py.

- Include concrete exception details for TLS bootstrap failures to improve diagnosis.

* fix: harden runtime bootstrap and unify confirm handling

- Simplify bootstrap log buffering and add a public initialize hook for non-main startup paths.

- Guard aiohttp TLS patching with feature/type checks and keep graceful fallback when internals are unavailable.

- Standardize dashboard confirmation flow via shared confirm helpers across composition and options API components.

* refactor: simplify runtime tls bootstrap and tighten confirm typing

* refactor: align ssl helper namespace and confirm usage

* fix: avoid frozen restart crash from multiprocessing import

* fix: include missing frozen dependencies for windows backend

* fix: use execv for stable backend reboot args

* Revert "fix: use execv for stable backend reboot args"

This reverts commit 9cc27becffeba0e117fea26aa5c2e1fe7afc6e36.

* Revert "fix: include missing frozen dependencies for windows backend"

This reverts commit 52554bea1fa61045451600c64447b7bf38cf6c92.

* Revert "fix: avoid frozen restart crash from multiprocessing import"

This reverts commit 10548645b0ba1e19b64194878ece478a48067959.

* fix: reset pyinstaller onefile env before reboot

* fix: unify electron restart path and tray-exit backend cleanup

* fix: stabilize desktop restart detection and frozen reboot args

* fix: make dashboard restart wait detection robust

* fix: revert dashboard restart waiting interaction tweaks

* fix: pass auth token for desktop graceful restart

* fix: avoid false failure during graceful restart wait

* fix: start restart waiting before electron restart call

* fix: harden restart waiting and reboot arg parsing

* fix: parse start_time as numeric timestamp

* fix: 修复app内重启异常,修复app内点击重启不能立刻提示重启,以及在后端就绪时及时刷新界面的问题 (#5013)

* fix: patch pip distlib finder for frozen electron runtime

* fix: use certifi CA bundle for runtime SSL requests

* fix: configure certifi CA before core imports

* fix: improve mac font fallback for dashboard text

* fix: harden frozen pip patch and unify TLS connector

* refactor: centralize dashboard CJK font fallback stacks

* perf: reuse TLS context and avoid repeated frozen pip patch

* refactor: bootstrap TLS setup before core imports

* fix: use async confirm dialog for provider deletions

* fix: replace native confirm dialogs in dashboard

- Add shared confirm helper in dashboard/src/utils/confirmDialog.ts for async dialog usage with safe fallback.

- Migrate provider, chat, config, session, platform, persona, MCP, backup, and knowledge-base delete/close confirmations to use the shared helper.

- Remove scattered inline confirm handling to keep behavior consistent and avoid native blocking dialog focus/caret issues in Electron.

* fix: capture runtime bootstrap logs after logger init

- Add bootstrap record buffer in runtime_bootstrap for early TLS patch logs before logger is ready.

- Flush buffered bootstrap logs to astrbot logger at process startup in main.py.

- Include concrete exception details for TLS bootstrap failures to improve diagnosis.

* fix: harden runtime bootstrap and unify confirm handling

- Simplify bootstrap log buffering and add a public initialize hook for non-main startup paths.

- Guard aiohttp TLS patching with feature/type checks and keep graceful fallback when internals are unavailable.

- Standardize dashboard confirmation flow via shared confirm helpers across composition and options API components.

* refactor: simplify runtime tls bootstrap and tighten confirm typing

* refactor: align ssl helper namespace and confirm usage

* fix: avoid frozen restart crash from multiprocessing import

* fix: include missing frozen dependencies for windows backend

* fix: use execv for stable backend reboot args

* Revert "fix: use execv for stable backend reboot args"

This reverts commit 9cc27becffeba0e117fea26aa5c2e1fe7afc6e36.

* Revert "fix: include missing frozen dependencies for windows backend"

This reverts commit 52554bea1fa61045451600c64447b7bf38cf6c92.

* Revert "fix: avoid frozen restart crash from multiprocessing import"

This reverts commit 10548645b0ba1e19b64194878ece478a48067959.

* fix: reset pyinstaller onefile env before reboot

* fix: unify electron restart path and tray-exit backend cleanup

* fix: stabilize desktop restart detection and frozen reboot args

* fix: make dashboard restart wait detection robust

* fix: revert dashboard restart waiting interaction tweaks

* fix: pass auth token for desktop graceful restart

* fix: avoid false failure during graceful restart wait

* fix: start restart waiting before electron restart call

* fix: harden restart waiting and reboot arg parsing

* fix: parse start_time as numeric timestamp

* fix: preserve windows frozen reboot argv quoting

* fix: align restart waiting with electron restart timing

* fix: tighten graceful restart and unmanaged kill safety

* chore: bump version to 4.15.0 (#5003)

* fix: add reminder for v4.14.8 users regarding manual redeployment due to a bug

* fix: harden plugin dependency loading in frozen app runtime (#5015)

* fix: compare plugin versions semantically in market updates

* fix: prioritize plugin site-packages for in-process pip

* fix: reload starlette from plugin target site-packages

* fix: harden plugin dependency import precedence in frozen runtime

* fix: improve plugin dependency conflict handling

* refactor: simplify plugin conflict checks and version utils

* fix: expand transitive plugin dependencies for conflict checks

* fix: recover conflicting plugin dependencies during module prefer

* fix: reuse renderer restart flow for tray backend restart

* fix: add recoverable plugin dependency conflict handling

* revert: remove plugin version comparison changes

* fix: add missing tray restart backend labels

* feat: adding support for media and quoted message attachments for feishu (#5018)

* docs: add AUR installation method (#4879)

* docs: sync system package manager installation instructions to all languages

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update README.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix/typo

* refactor: update system package manager installation instructions for Arch Linux across multiple language README files

* feat: add installation command for AstrBot in multiple language README files

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>
Co-authored-by: Soulter <905617992@qq.com>

* fix(desktop): 为 Electron 与后端日志增加按大小轮转 (#5029)

* fix(desktop): rotate electron and backend logs

* refactor(desktop): centralize log rotation defaults and debug fs errors

* fix(desktop): harden rotation fs ops and buffer backend log writes

* refactor(desktop): extract buffered logger and reduce sync stat calls

* refactor(desktop): simplify rotation flow and harden logger config

* fix(desktop): make app logging async and flush-safe

* fix: harden app log path switching and debug-gated rotation errors

* fix: cap buffered log chunk size during path switch

* feat: add first notice feature with multilingual support and UI integration

* fix: 提升打包版桌面端启动稳定性并优化插件依赖处理 (#5031)

* fix(desktop): rotate electron and backend logs

* refactor(desktop): centralize log rotation defaults and debug fs errors

* fix(desktop): harden rotation fs ops and buffer backend log writes

* refactor(desktop): extract buffered logger and reduce sync stat calls

* refactor(desktop): simplify rotation flow and harden logger config

* fix(desktop): make app logging async and flush-safe

* fix: harden app log path switching and debug-gated rotation errors

* fix: cap buffered log chunk size during path switch

* fix: avoid redundant plugin reinstall and upgrade electron

* fix: stop webchat tasks cleanly and bind packaged backend to localhost

* fix: unify platform shutdown and await webchat listener cleanup

* fix: improve startup logs for dashboard and onebot listeners

* fix: revert extra startup service logs

* fix: harden plugin import recovery and webchat listener cleanup

* fix: pin dashboard ci node version to 24.13.0

* fix: avoid duplicate webchat listener cleanup on terminate

* refactor: clarify platform task lifecycle management

* fix: continue platform shutdown when terminate fails

* feat: temporary file handling and introduce TempDirCleaner (#5026)

* feat: temporary file handling and introduce TempDirCleaner

- Updated various modules to use `get_astrbot_temp_path()` instead of `get_astrbot_data_path()` for temporary file storage.
- Renamed temporary files for better identification and organization.
- Introduced `TempDirCleaner` to manage the size of the temporary directory, ensuring it does not exceed a specified limit by deleting the oldest files.
- Added configuration option for maximum temporary directory size in the dashboard.
- Implemented tests for `TempDirCleaner` to verify cleanup functionality and size management.

* ruff

* fix: close unawaited reset coroutine on early return (#5033)

When an OnLLMRequestEvent hook stops event propagation, the
reset_coro created by build_main_agent was never awaited, causing
a RuntimeWarning. Close the coroutine explicitly before returning.

Fixes #5032

Co-authored-by: Limitless2023 <limitless@users.noreply.github.com>

* fix: update error logging message for connection failures

* docs: clean and sync README (#5014)

* fix: close missing div in README

* fix: sync README_zh-TW with README

* fix: sync README

* fix: correct typo

correct url in README_en README_fr README_ru

* docs: sync README_en with README

* Update README_en.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: provider extra param dialog key display error

* chore: ruff format

* feat: add send_chat_action for Telegram platform adapter (#5037)

* feat: add send_chat_action for Telegram platform adapter

Add typing/upload indicator when sending messages via Telegram.
- Added _send_chat_action helper method for sending chat actions
- Send appropriate action (typing, upload_photo, upload_document, upload_voice)
  before sending different message types
- Support streaming mode with typing indicator
- Support supergroup with message_thread_id

* refactor(telegram): extract chat action helpers and add throttling

- Add ACTION_BY_TYPE mapping for message type to action priority
- Add _get_chat_action_for_chain() to determine action from message chain
- Add _send_media_with_action() for upload → send → restore typing pattern
- Add _ensure_typing() helper for typing status
- Add chat action throttling (0.5s) in streaming mode to avoid rate limits
- Update type annotation to ChatAction | str for better static checking

* feat(telegram): implement send_typing method for Telegram platform

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 修复更新日志、官方文档弹窗双滚动条问题 (#5060)

* docs: sync and fix readme typo (#5055)

* docs: fix index typo

* docs: fix typo in README_en.md

- 移除英文README中意外出现的俄语,并替换为英语

* docs: fix html typo

- remove unused '</p>'

* docs: sync table with README

* docs: sync README header format

- keep the README header format consistent

* doc: sync key features

* style: format files

- Fix formatting issues from previous PR

* fix: correct md anchor link

* docs: correct typo in README_fr.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* docs: correct typo in README_zh-TW.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix: 修复备份时缺失的人格文件夹映射 (#5042)

* feat: QQ 官方机器人平台支持主动推送消息、私聊场景下支持接收文件 (#5066)

* feat: QQ 官方机器人平台支持主动推送消息、私聊场景下支持接收文件

* feat: enhance QQOfficialWebhook to remember session scenes for group, channel, and friend messages

* perf: 优化分段回复间隔时间的初始化逻辑 (#5068)

fixes: #5059

* fix: chunk err when using openrouter deepseek (#5069)

* feat: add i18n supports for custom platform adapters (#5045)

* Feat: 为插件提供的适配器的元数据&i18n提供数据通路

* chore: update docstrings with pull request references

Added references to pull request 5045 in docstrings.

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* fix: 完善转发引用解析与图片回退并支持配置化控制 (#5054)

* feat: support fallback image parsing for quoted messages

* fix: fallback parse quoted images when reply chain has placeholders

* style: format network utils with ruff

* test: expand quoted parser coverage and improve fallback diagnostics

* fix: fallback to text-only retry when image requests fail

* fix: tighten image fallback and resolve nested quoted forwards

* refactor: simplify quoted message extraction and dedupe images

* fix: harden quoted parsing and openai error candidates

* fix: harden quoted image ref normalization

* refactor: organize quoted parser settings and logging

* fix: cap quoted fallback images and avoid retry loops

* refactor: split quoted message parser into focused modules

* refactor: share onebot segment parsing logic

* refactor: unify quoted message parsing flow

* feat: move quoted parser tuning to provider settings

* fix: add missing i18n metadata for quoted parser settings

* chore: refine forwarded message setting labels

* fix: add config tabs and routing for normal and system configurations

* chore: bump version to 4.16.0 (#5074)

* feat: add LINE platform support with adapter and configuration (#5085)

* fix-correct-FIRST_NOTICE.md-locale-path-resolution (#5083) (#5082)

* fix:修改配置文件目录

* fix:添加备选的FIRST_NOTICE.zh-CN.md用于兼容

* fix: remove unnecessary frozen flag from requirements export in Dockerfile

fixes: #5089

* fix #5089: add uv lock step in Dockerfile before export (#5091)

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* feat: support hot reload after plugin load failure (#5043)

* add :Support hot reload after plugin load failure

* Apply suggestions from code review

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix:reformat code

* fix:reformat code

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* feat: add fallback chat model chain in tool loop runner (#5109)

* feat: implement fallback provider support for chat models and update configuration

* feat: enhance provider selection display with count and chips for selected providers

* feat: update fallback chat providers to use provider settings and add warning for non-list fallback models

* feat: add Afdian support card to resources section in WelcomePage

* feat: replace colorlog with loguru for enhanced logging support (#5115)

* feat: add SSL configuration options for WebUI and update related logging (#5117)

* chore: bump version to 4.17.0

* fix: handle list format content from OpenAI-compatible APIs (#5128)

* fix: handle list format content from OpenAI-compatible APIs

Some LLM providers (e.g., GLM-4.5V via SiliconFlow) return content as
list[dict] format like [{'type': 'text', 'text': '...'}] instead of
plain string. This causes the raw list representation to be displayed
to users.

Changes:
- Add _normalize_content() helper to extract text from various content formats
- Use json.loads instead of ast.literal_eval for safer parsing
- Add size limit check (8KB) before attempting JSON parsing
- Only convert lists that match OpenAI content-part schema (has 'type': 'text')
  to avoid collapsing legitimate list-literal replies like ['foo', 'bar']
- Add strip parameter to preserve whitespace in streaming chunks
- Clean up orphan </think> tags that may leak from some models

Fixes #5124

* fix: improve content normalization safety

- Try json.loads first, fallback to ast.literal_eval for single-quoted
  Python literals to avoid corrupting apostrophes (e.g., "don't")
- Coerce text values to str to handle null or non-string text fields

* fix: update retention logic in LogManager to handle backup count correctly

* chore: bump version to 4.17.1

* docs: Added instructions for deploying AstrBot using AstrBot Launcher. (#5136)

Added instructions for deploying AstrBot using AstrBot Launcher.

* fix: add MCP tools to function tool set in _plugin_tool_fix (#5144)

* fix: add support for collecting data from builtin stars in electron pyinstaller build (#5145)

* chore: bump version to 4.17.1

* chore: ruff format

* fix: prevent updates for AstrBot launched via launcher

* fix(desktop): include runtime deps for builtin plugins in backend build (#5146)

* fix: 'Plain' object has no attribute 'text' when using python 3.14 (#5154)

* fix: enhance plugin metadata handling by injecting attributes before instantiation (#5155)

* fix: enhance handle_result to support event context and webchat image sending

* chore: bump version to 4.17.3

* chore: ruff format

* feat: add NVIDIA provider template (#5157)

fixes: #5156

* feat: enhance provider sources panel with styled menu and mobile support

* fix: improve permission denied message for local execution in Python and shell tools

* feat: enhance PersonaForm component with responsive design and improved styling (#5162)

fix: #5159

* ui(CronJobPage): fix action column buttons overlapping in CronJobPage (#5163)

- 修改前:操作列容器仅使用 `d-flex`,在页面宽度变窄时,子元素(开关和删除按钮)会因为宽度挤压而发生视觉重叠,甚至堆叠在一起。
- 修改后:
    1. 为容器添加了 `flex-nowrap`,强制禁止子元素换行。
    2. 设置了 `min-width: 140px`,确保该列拥有固定的保护空间,防止被其他长文本列挤压。
    3. 增加了 `gap: 12px` 间距,提升了操作辨识度并优化了点击体验。

* feat: add unsaved changes notice to configuration page and update messages

* feat: implement search functionality in configuration components and update UI (#5168)

* feat: add FAQ link to vertical sidebar and update navigation for localization

* feat: add announcement section to WelcomePage and localize announcement title

* chore: bump version to 4.17.4

* feat: supports send markdown message in qqofficial (#5173)

* feat: supports send markdown message in qqofficial

closes: #1093 #918 #4180 #4264

* ruff format

* fix: prevent duplicate error message when all LLM providers fail (#5183)

* fix: 修复选择配置文件进入配置文件管理弹窗直接关闭弹窗显示的配置文件不正确 (#5174)

* feat: add MarketPluginCard component and integrate random plugin feature in ExtensionPage (#5190)

* feat: add MarketPluginCard component and integrate random plugin feature in ExtensionPage

* feat: update random plugin selection logic to use pluginMarketData and refresh on relevant events

* feat: supports aihubmix

* docs: update readme

* chore: ruff format

* feat: add LINE support to multiple language README files

* feat(core): add plugin error hook for custom error routing (#5192)

* feat(core): add plugin error hook for custom error routing

* fix(core): align plugin error suppression with event stop state

* refactor: extract Voice_messages_forbidden fallback into shared helper with typed BadRequest exception (#5204)

- Add _send_voice_with_fallback helper to deduplicate voice forbidden handling
- Catch telegram.error.BadRequest instead of bare Exception with string matching
- Add text field to Record component to preserve TTS source text
- Store original text in Record during TTS conversion for use as document caption
- Skip _send_chat_action when chat_id is empty to avoid unnecessary warnings

* chore: bump version to 4.17.5

* feat: add admin permission checks for Python and Shell execution (#5214)

* fix: 改进微信公众号被动回复处理机制,引入缓冲与分片回复,并优化超时行为 (#5224)

* 修复wechat official 被动回复功能

* ruff format

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 修复仅发送 JSON 消息段时的空消息回复报错 (#5208)

* Fix Register_Stage

· 补全 JSON 消息判断,修复发送 JSON 消息时遇到 “消息为空,跳过发送阶段” 的问题。
· 顺带补全其它消息类型判断。
Co-authored-by: Pizero <zhaory200707@outlook.com>

* Fix formatting and comments in stage.py

* Format stage.py

---------

Co-authored-by: Pizero <zhaory200707@outlook.com>

* docs: update related repo links

* fix(core): terminate active events on reset/new/del to prevent stale responses (#5225)

* fix(core): terminate active events on reset/new/del to prevent stale responses

Closes #5222

* style: fix import sorting in scheduler.py

* chore: remove Electron desktop pipeline and switch to tauri repo (#5226)

* ci: remove Electron desktop build from release pipeline

* chore: remove electron desktop and switch to tauri release trigger

* ci: remove desktop workflow dispatch trigger

* refactor: migrate data paths to astrbot_path helpers

* fix: point desktop update prompt to AstrBot-desktop releases

* fix: update feature request template for clarity and consistency in English and Chinese

* Feat/config leave confirm (#5249)

* feat: 配置文件增加未保存提示弹窗

* fix: 移除unsavedChangesDialog插件使用组件方式实现弹窗

* feat: add support for plugin astrbot-version and platform requirement checks (#5235)

* feat: add support for plugin astrbot-version and platform requirement checks

* fix: remove unsupported platform and version constraints from metadata.yaml

* fix: remove restriction on 'v' in astrbot_version specification format

* ruff format

* feat: add password confirmation when changing password (#5247)

* feat: add password confirmation when changing password

Fixes #5177

Adds a password confirmation field to prevent accidental password typos.

Changes:
- Backend: validate confirm_password matches new_password
- Frontend: add confirmation input with validation
- i18n: add labels and error messages for password mismatch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(auth): improve error message for password confirmation mismatch

* fix(auth): update password hashing logic and improve confirmation validation

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(provider): 修复 dict 格式 content 导致的 JSON 残留问题 (#5250)

* fix(provider): 修复 dict 格式 content 导致的 JSON 残留问题

修复 _normalize_content 函数未处理 dict 类型 content 的问题。
当 LLM 返回 {"type": "text", "text": "..."} 格式的 content 时,
现在会正确提取 text 字段而非直接转为字符串。

同时改进 fallback 行为,对 None 值返回空字符串。

Fixes #5244

* Update warning message for unexpected dict format

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>

* chore: remove outdated heihe.md documentation file

* fix: all mcp tools exposed to main agent (#5252)

* fix: enhance PersonaForm layout and improve tool selection display

* fix: update tool status display and add localization for inactive tools

* fix: remove additionalProperties from tool schema properties (#5253)

fixes: #5217

* fix: simplify error messages for account edit validation

* fix: streamline error response for empty new username and password in account edit

* chore: bump vertion to 4.17.6

* feat: add OpenRouter provider support and icon

* chore: ruff format

* refactor(dashboard): replace legacy isElectron bridge fields with isDesktop (#5269)

* refactor dashboard desktop bridge fields from isElectron to isDesktop

* refactor dashboard runtime detection into shared helper

* fix: update contributor avatar image URL to include max size and columns (#5268)

* feat: astrbot http api (#5280)

* feat: astrbot http api

* Potential fix for code scanning alert no. 34: Use of a broken or weak cryptographic hashing algorithm on sensitive data

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fix: improve error handling for missing attachment path in file upload

* feat: implement paginated retrieval of platform sessions for creators

* feat: refactor attachment directory handling in ChatRoute

* feat: update API endpoint paths for file and message handling

* feat: add documentation link to API key management section in settings

* feat: update API key scopes and related configurations in API routes and tests

* feat: enhance API key expiration options and add warning for permanent keys

* feat: add UTC normalization and serialization for API key timestamps

* feat: implement chat session management and validation for usernames

* feat: ignore session_id type chunks in message processing

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* feat(dashboard): improve plugin platform support display and mobile accessibility (#5271)

* feat(dashboard): improve plugin platform support display and mobile accessibility

- Replace hover-based tooltips with interactive click menus for platform support information.
- Fix mobile touch issues by introducing explicit state control for status capsules.
- Enhance UI aesthetics with platform-specific icons and a structured vertical list layout.
- Add dynamic chevron icons to provide clear visual cues for expandable content.

* refactor(dashboard): refactor market card with computed properties for performance

* refactor(dashboard): unify plugin platform support UI with new reusable chip component

- Create shared 'PluginPlatformChip' component to encapsulate platform meta display.
- Fix mobile interaction bugs by simplifying menu triggers and event handling.
- Add stacked platform icon previews and dynamic chevron indicators within capsules.
- Improve information hierarchy using structured vertical lists for platform details.
- Optimize rendering efficiency with computed properties across both card views.

* fix: qq official guild message send error (#5287)

* fix: qq official guild message send error

* Update astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* 更新readme文档,补充桌面app说明,并向前移动位置 (#5297)

* docs: update desktop deployment section in README

* docs: refine desktop and launcher deployment descriptions

* Update README.md

* feat: add Anthropic Claude Code OAuth provider and adaptive thinking support (#5209)

* feat: add Anthropic Claude Code OAuth provider and adaptive thinking support

* fix: add defensive guard for metadata overrides and align budget condition with docs

* refactor: adopt sourcery-ai suggestions for OAuth provider

- Use use_api_key=False in OAuth subclass to avoid redundant
  API-key client construction before replacing with auth_token client
- Generalize metadata override helper to merge all dict keys
  instead of only handling 'limit', improving extensibility

* Feat/telegram command alias register  #5233 (#5234)

* feat: support registering command aliases for Telegram

Now when registering commands with aliases, all aliases will be
registered as Telegram bot commands in addition to the main command.

Example:
    @register_command(command_name="draw", alias={"画", "gen"})
Now /draw, /画, and /gen will all appear in the Telegram command menu.

* feat(telegram): add duplicate command name warning when registering commands

Log a warning when duplicate command names are detected during Telegram
command registration to help identify configuration conflicts.

* refactor: remove Anthropic OAuth provider implementation and related metadata overrides

* fix: 修复新建对话时因缺少会话ID导致配置绑定失败的问题 (#5292)

* fix:尝试修改

* fix:添加详细日志

* fix:进行详细修改,并添加日志

* fix:删除所有日志

* fix: 增加安全访问函数

- 给 localStorage 访问加了 try/catch + 可用性判断:dashboard/src/utils/chatConfigBinding.ts:13
- 新增 getFromLocalStorage/setToLocalStorage(在受限存储/无痕模式下异常时回退/忽略)
- getStoredDashboardUsername() / getStoredSelectedChatConfigId() 改为走安全读取:dashboard/src/utils/chatConfigBinding.ts:36       - 新增 setStoredSelectedChatConfigId(),写入失败静默忽略:dashboard/src/utils/chatConfigBinding.ts:44
- 把 ConfigSelector.vue 里直接 localStorage.getItem/setItem 全部替换为上述安全方法:dashboard/src/components/chat/ConfigSelector.vue:81
- 已重新跑过 pnpm run typecheck,通过。

* rm:删除个人用的文档文件

* Revert "rm:删除个人用的文档文件"

This reverts commit 0fceee05434cfbcb11e45bb967a77d5fa93196bf.

* rm:删除个人用的文档文件

* rm:删除个人用的文档文件

* chore: bump version to 4.18.0

* fix(SubAgentPage): 当中间的介绍文本非常长时,Flex 布局会自动挤压右侧的控制按钮区域 (#5306)

* fix: 修复新版本插件市场出现插件显示为空白的 bug;纠正已安装插件卡片的排版,统一大小 (#5309)

* fix(ExtensionCard): 解决插件卡片大小不统一的问题

* fix(MarketPluginCard): 解决插件市场不加载插件的问题 (#5303)

* feat: supports spawn subagent as a background task that not block the main agent workflow (#5081)

* feat:为subagent添加后台任务参数

* ruff

* fix: update terminology from 'handoff mission' to 'background task' and refactor related logic

* fix: update terminology from 'background_mission' to 'background_task' in HandoffTool and related logic

* fix(HandoffTool): update background_task description for clarity on usage

---------

Co-authored-by: Soulter <905617992@qq.com>

* cho

* fix: 修复 aiohttp 版本过新导致 qq-botpy 报错的问题 (#5316)

* chore: ruff format

* fix: remove hard-coded 6s timeout from tavily request

* fix: remove changelogs directory from .dockerignore

* feat(dashboard): make release redirect base URL configurable (#5330)

* feat(dashboard): make desktop release base URL configurable

* refactor(dashboard): use generic release base URL env with upstream default

* fix(dashboard): guard release base URL normalization when env is unset

* refactor(dashboard): use generic release URL helpers and avoid latest suffix duplication

* feat: add stop functionality for active agent sessions and improve handling of stop requests (#5380)

* feat: add stop functionality for active agent sessions and improve handling of stop requests

* feat: update stop button icon and tooltip in ChatInput component

* fix: correct indentation in tool call handling within ChatRoute class

* fix: chatui cannot persist file segment (#5386)

* fix(plugin): update plugin directory handling for reserved plugins (#5369)

* fix(plugin): update plugin directory handling for reserved plugins

* fix(plugin): add warning logs for missing plugin name, object, directory, and changelog

* chore(README): updated with README.md (#5375)

* chore(README): updated with README.md

* Update README_fr.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Update README_zh-TW.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* feat: add image urls / paths supports for subagent (#5348)

* fix: 修复5081号PR在子代理执行后台任务时,未正确使用系统配置的流式/非流请求的问题(#5081)

* feat:为子代理增加远程图片URL参数支持

* fix: update description for image_urls parameter in HandoffTool to clarify usage in multimodal tasks

* ruff format

---------

Co-authored-by: Soulter <905617992@qq.com>

* feat: add hot reload when failed to load plugins (#5334)

* feat:add hot reload when failed to load plugins

* apply bot suggestions

* fix(chatui): add copy rollback path and error message. (#5352)

* fix(chatui): add copy rollback path and error message.

* fix(chatui): fixed textarea leak in the copy button.

* fix(chatui): use color styles from the component library.

* fix: 处理配置文件中的 UTF-8 BOM 编码问题 (#5376)

* fix(config): handle UTF-8 BOM in configuration file loading

Problem:
On Windows, some text editors (like Notepad) automatically add UTF-8 BOM
to JSON files when saving. This causes json.decoder.JSONDecodeError:
"Unexpected UTF-8 BOM" and AstrBot fails to start when cmd_config.json
contains BOM.

Solution:
Add defensive check to strip UTF-8 BOM (\ufeff) if present before
parsing JSON configuration file.

Impact:
- Improves robustness and cross-platform compatibility
- No breaking changes to existing functionality
- Fixes startup failure when configuration file has UTF-8 BOM encoding

Relates-to: Windows editor compatibility issues

* style: fix code formatting with ruff

Fix single quote to double quote to comply with project code style.

* feat: add plugin load&unload hook (#5331)

* 添加了插件的加载完成和卸载完成的钩子事件

* 添加了插件的加载完成和卸载完成的钩子事件

* format code with ruff

* ruff format

---------

Co-authored-by: Soulter <905617992@qq.com>

* test: enhance test framework with comprehensive fixtures and mocks (#5354)

* test: enhance test framework with comprehensive fixtures and mocks

- Add shared mock builders for aiocqhttp, discord, telegram
- Add test helpers for platform configs and mock objects
- Expand conftest.py with test profile support
- Update coverage test workflow configuration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(tests): 移动并重构模拟 LLM 响应和消息组件函数

* fix(tests): 优化 pytest_runtest_setup 中的标记检查逻辑

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add comprehensive tests for message event handling (#5355)

* test: add comprehensive tests for message event handling

- Add AstrMessageEvent unit tests (688 lines)
- Add AstrBotMessage unit tests
- Enhance smoke tests with message event scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: improve message type handling and add defensive tests

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add support for showing tool call results in agent execution (#5388)

closes: #5329

* fix: resolve pipeline and star import cycles (#5353)

* fix: resolve pipeline and star import cycles

- Add bootstrap.py and stage_order.py to break circular dependencies
- Export Context, PluginManager, StarTools from star module
- Update pipeline __init__ to defer imports
- Split pipeline initialization into separate bootstrap module

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add logging for get_config() failure in Star class

* fix: reorder logger initialization in base.py

---------

Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: enable computer-use tools for subagent handoff (#5399)

* fix: enforce admin guard for sandbox file transfer tools (#5402)

* fix: enforce admin guard for sandbox file transfer tools

* refactor: deduplicate computer tools admin permission checks

* fix: add missing space in permission error message

* fix(core): 优化 File 组件处理逻辑并增强 OneBot 驱动层路径兼容性 (#5391)

* fix(core): 优化 File 组件处理逻辑并增强 OneBot 驱动层路径兼容性

原因 (Necessity):
1. 内核一致性:AstrBot 内核的 Record 和 Video 组件均具备识别 `file:///` 协议头的逻辑,但 File 组件此前缺失此功能,导致行为不统一。
2. OneBot 协议合规:OneBot 11 标准要求本地文件路径必须使用 `file:///` 协议头。此前驱动层未对裸路径进行自动转换,导致发送本地文件时常触发 retcode 1200 (识别URL失败) 错误。
3. 容器环境适配:在 Docker 等路径隔离环境下,裸路径更容易因驱动或协议端的解析歧义而失效。

更改 (Changes):
- [astrbot/core/message/components.py]:
  - 在 File.get_file() 中增加对 `file:///` 前缀的识别与剥离逻辑,使其与 Record/Video 组件行为对齐。
- [astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py]:
  - 在发送文件前增加自动修正逻辑:若路径为绝对路径且未包含协议头,驱动层将自动补全 `file:///` 前缀。
  - 对 http、base64 及已有协议头,确保不干扰原有的正常传输逻辑。

影响 (Impact):
- 以完全兼容的方式增强了文件发送的鲁棒性。
- 解决了插件在发送日志等本地生成的压缩包时,因路径格式不规范导致的发送失败问题。

* refactor(core): 根据 cr 建议,规范化文件 URI 生成与解析逻辑,优化跨平台兼容性

原因 (Necessity):
1. 修复原生路径与 URI 转换在 Windows 下的不对称问题。
2. 规范化 file: 协议头处理,确保符合 RFC 标准并能在 Linux/Windows 间稳健切换。
3. 增强协议判定准确度,防止对普通绝对路径的误处理。

更改 (Changes):
- [astrbot/core/platform/sources/aiocqhttp]:
  - 弃用手动拼接,改用 `pathlib.Path.as_uri()` 生成标准 URI。
  - 将协议检测逻辑从前缀匹配优化为包含性检测 ("://")。
- [astrbot/core/message/components]:
  - 重构 `File.get_file` 解析逻辑,支持对称处理 2/3 斜杠格式。
  - 针对 Windows 环境增加了对 `file:///C:/` 格式的自动修正,避免 `os.path` 识别失效。
- [data/plugins/astrbot_plugin_logplus]:
  - 在直接 API 调用中同步应用 URI 规范化处理。

影响 (Impact):
- 解决 Docker 环境中因路径不规范导致的 "识别URL失败" 报错。
- 提升了本体框架在 Windows 系统下的文件操作鲁棒性。

* i18n(SubAgentPage): complete internationalization for subagent orchestration page (#5400)

* i18n: complete internationalization for subagent orchestration page

- Replace hardcoded English strings in [SubAgentPage.vue] with i18n keys.
- Update `en-US` and `zh-CN` locales with missing hints, validation messages, and empty state translations.
- Fix translation typos and improve consistency across the SubAgent orchestration UI.

* fix(bug_risk): 避免在模板中的翻译调用上使用 || 'Close' 作为回退值。

* fix(aiocqhttp): enhance shutdown process for aiocqhttp adapter (#5412)

* fix: pass embedding dimensions to provider apis (#5411)

* fix(context): log warning when platform not found for session

* fix(context): improve logging for platform not found in session

* chore: bump version to 4.18.2

* chore: bump version to 4.18.2

* chore: bump version to 4.18.2

* fix: Telegram voice message format (OGG instead of WAV) causing issues with OpenAI STT API (#5389)

* chore: ruff format

* feat(dashboard): add generic desktop app updater bridge (#5424)

* feat(dashboard): add generic desktop app updater bridge

* fix(dashboard): address updater bridge review feedback

* fix(dashboard): unify updater bridge types and error logging

* fix(dashboard): consolidate updater bridge typings

* fix(conversation): retain existing persona_id when updating conversation

* fix(dashboard): 修复设置页新建 API Key 后复制失败问题 (#5439)

* Fix: GitHub proxy not displaying correctly in WebUI (#5438)

* fix(dashboard): preserve custom GitHub proxy setting on reload

* fix(dashboard): keep github proxy selection persisted in settings

* fix(persona): enhance persona resolution logic for conversations and sessions

* fix: ensure tool call/response pairing in context truncation (#5417)

* fix: ensure tool call/response pairing in context truncation

* refactor: simplify fix_messages to single-pass state machine

* perf(cron): enhance future task session isolation

fixes: #5392

* feat: add useExtensionPage composable for managing plugin extensions

- Implemented a new composable `useExtensionPage` to handle various functionalities related to plugin management, including fetching extensions, handling updates, and managing UI states.
- Added support for conflict checking, plugin installation, and custom source management.
- Integrated search and filtering capabilities for plugins in the market.
- Enhanced user experience with dialogs for confirmations and notifications.
- Included pagination and sorting features for better plugin visibility.

* fix: clear markdown field when sending media messages via QQ Official Platform (#5445)

* fix: clear markdown field when sending media messages via QQ Official API

* refactor: use pop() to remove markdown key instead of setting None

* fix: cannot automatically get embedding dim when create embedding provider (#5442)

* fix(dashboard): 强化 API Key 复制临时节点清理逻辑

* fix(embedding): 自动检测改为探测 OpenAI embedding 最大可用维度

* fix: normalize openai embedding base url and add hint key

* i18n: add embedding_api_base hint translations

* i18n: localize provider embedding/proxy metadata hints

* fix: show provider-specific embedding API Base URL hint as field subtitle

* fix(embedding): cap OpenAI detect_dim probes with early short-circuit

* fix(dashboard): return generic error on provider adapter import failure

* 回退检测逻辑

* fix: 修复Pyright静态类型检查报错 (#5437)

* refactor: 修正 Sqlite 查询、下载回调、接口重构与类型调整

* feat: 为 OneBotClient 增加 CallAction 协议与异步调用支持

* fix(telegram): avoid duplicate message_thread_id in streaming (#5430)

* perf: batch metadata query in KB retrieval to fix N+1 problem (#5463)

* perf: batch metadata query in KB retrieval to fix N+1 problem

Replace N sequential get_document_with_metadata() calls with a single
get_documents_with_metadata_batch() call using SQL IN clause.

Benchmark results (local SQLite):
- 10 docs: 10.67ms → 1.47ms (7.3x faster)
- 20 docs: 26.00ms → 2.68ms (9.7x faster)
- 50 docs: 63.87ms → 2.79ms (22.9x faster)

* refactor: use set[str] param type and chunk IN clause for SQLite safety

Address review feedback:
- Change doc_ids param from list[str] to set[str] to avoid unnecessary conversion
- Chunk IN clause into batches of 900 to stay under SQLite's 999 parameter limit
- Remove list() wrapping at call site, pass set directly

* fix:fix the issue where incomplete cleanup of residual plugins occurs… (#5462)

* fix:fix the issue where incomplete cleanup of residual plugins occurs in the failed loading of plugins

* fix:ruff format,apply bot suggestions

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* chore: 为类型检查添加 TYPE_CHECKING 的导入与阶段类型引用 (#5474)

* fix(line): line adapter does not appear in the add platform dialog

fixes: #5477

* [bug]查看介入教程line前往错误界面的问题 (#5479)

Fixes #5478

* chore: bump version to 4.18.3

* feat: implement follow-up message handling in ToolLoopAgentRunner (#5484)

* feat: implement follow-up message handling in ToolLoopAgentRunner

* fix: correct import path for follow-up module in InternalAgentSubStage

* feat: implement websockets transport mode selection for chat (#5410)

* feat: implement websockets transport mode selection for chat

- Added transport mode selection (SSE/WebSocket) in the chat component.
- Updated conversation sidebar to include transport mode options.
- Integrated transport mode handling in message sending logic.
- Refactored message sending functions to support both SSE and WebSocket.
- Enhanced WebSocket connection management and message handling.
- Updated localization files for transport mode labels.
- Configured Vite to support WebSocket proxying.

* feat(webchat): refactor message parsing logic and integrate new parsing function

* feat(chat): add websocket API key extraction and scope validation

* Revert "可选后端,实现前后端分离" (#5536)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: can <51474963+weijintaocode@users.noreply.github.com>
Co-authored-by: Soulter <905617992@qq.com>
Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com>
Co-authored-by: letr <123731298+letr007@users.noreply.github.com>
Co-authored-by: 搁浅 <id6543156918@gmail.com>
Co-authored-by: Helian Nuits <sxp20061207@163.com>
Co-authored-by: Gao Jinzhe <2968474907@qq.com>
Co-authored-by: DD斩首 <155905740+DDZS987@users.noreply.github.com>
Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: エイカク <62183434+zouyonghe@users.noreply.github.com>
Co-authored-by: 鸦羽 <Raven95676@gmail.com>
Co-authored-by: Dt8333 <25431943+Dt8333@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Li-shi-ling <114913764+Li-shi-ling@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Limitless <127183162+Limitless2023@users.noreply.github.com>
Co-authored-by: Limitless2023 <limitless@users.noreply.github.com>
Co-authored-by: evpeople <54983536+evpeople@users.noreply.github.com>
Co-authored-by: SnowNightt <127504703+SnowNightt@users.noreply.github.com>
Co-authored-by: xzj0898 <62733743+xzj0898@users.noreply.github.com>
Co-authored-by: stevessr <89645372+stevessr@users.noreply.github.com>
Co-authored-by: Waterwzy <2916963017@qq.com>
Co-authored-by: NayukiMeko <MekoNayuki@outlook.com>
Co-authored-by: 時壹 <137363396+KBVsent@users.noreply.github.com>
Co-authored-by: sanyekana <Clhikari@qq.com>
Co-authored-by: Chiu Chun-Hsien <95356121+911218sky@users.noreply.github.com>
Co-authored-by: Dream Tokenizer <60459821+Trance-0@users.noreply.github.com>
Co-authored-by: NanoRocky <76585834+NanoRocky@users.noreply.github.com>
Co-authored-by: Pizero <zhaory200707@outlook.com>
Co-authored-by: 雪語 <167516635+YukiRa1n@users.noreply.github.com>
Co-authored-by: whatevertogo <1879483647@qq.com>
Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
Co-authore…
@dosubot dosubot bot mentioned this pull request Mar 2, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants