Skip to content

Releases: sakurawwwxh/qq-agent-plus

v0.8.5 —— 一键安装脚本、镜像拉取内置兜底、联网搜索与人设编辑改进、多项问题修复

Choose a tag to compare

@github-actions github-actions released this 13 Oct 01:16

本次改动

这一版新增零问答的一键安装脚本 install.sh;修复部署链路上的两处问题(受限网络下拉取镜像
失败、网页终端里的预检误判);补强联网搜索的触发规则,修复人设编辑中两处「看似已保存、
实际未生效」的路径,并为表情收藏的判定标准补上控制台入口、统一日报与每日动态的称呼口径。
此外修复了一批稳定性与数据问题。

一键安装脚本 install.sh(零问答)

bash install.sh 全程零提问:自动安装 Docker、拉取协议端镜像、部署 QQ Agent 并生成全部
凭据。模型配置默认留到网页控制台完成(控制台提供各服务预设与连通性测试);需要在安装时
写入的,传 --model-base-url / --model-api-key / --model(或同名环境变量)即可。安装
完成后脚本从凭据文件读取实际地址,打印「接下来三步」:带登录令牌的控制台直达链接、扫码
登录入口与密码、白名单入口,另附云安全组需要放行的端口(按实际部署地址解析)与查看日志
的命令;失败时给出日志路径并透传退出码。--check-only 只执行预检、不安装任何东西。

交互式 deploy-all.sh 同步调整:模型 API Key 留空即跳过该段配置(此前留空会直接报错
退出),安装完成后在控制台补填。

修复:受限网络下拉取 SnowLuma 镜像失败(内置兜底镜像站)

现象:全新部署在拉取 SnowLuma 镜像时失败(dial tcp …: connect: connection refused)。

根因:Docker Hub(registry-1.docker.io)在受限网络下直连失败,而部署脚本此前不提供
默认镜像站,每次全新部署都需要先遭遇一次失败、再手动指定镜像站重跑。

改动:直连失败时自动按镜像站列表重试——--image-mirror / QQ_AGENT_IMAGE_MIRROR
指定的排在前面,内置的两家(docker.1panel.live、docker.m.daocloud.io)兜底、重名不
重复;拉取成功后把实际使用的镜像站写入 snowluma/.env。另外修复一处换站失效:.env 中
保存着旧镜像前缀时,重跑会先剥离当前列表中已识别的前缀再拼接新站(此前旧镜像站失效后,
按提示换站会静默无效)。镜像站为第三方服务,内置两家仅作兜底,可用 --image-mirror
换成自选镜像站。

实测(生产环境、同样直连不通):内置两家均可完成拉取。

修复:网页终端里部署脚本被预检误拒(XDG_RUNTIME_DIR)

现象:在宝塔等面板的网页终端里运行 deploy-all.sh,完成问答后被「The systemd user
manager is unavailable」拒绝。

根因:非登录会话(网页终端 / su / sudo -u)不设置 XDG_RUNTIME_DIR,systemctl --user 固定报「Failed to connect to bus」——与用户管理器是否可用无关。

改动:两个部署脚本在探测前自动补齐该变量(仅当变量为空且 /run/user/<uid> 存在;不
覆盖已有值);拒绝与 linger 提示改为可直接照做的两步;deploy.sh 的 linger 警告更正为
完整后果(未开启 linger 时,登录会话退出即连带停止服务);docs/BAOTA.md 三处因果说明
更正。

联网搜索:触发规则补强

现象:机器人遇到不认识的词不会主动搜索;搜索过之后,回答与未搜索时没有区别。

根因:搜索能力与后端本身正常,缺口在触发规则——提示词只在「需要实时信息、不确定的
事实」类场景要求搜索,聊天里遇到生词、被生词调侃这类语境没有覆盖。

改动:场景规则补充三条:①聊天里遇到看不懂或拿不准的词/说法(新梗、缩写、黑话、谐音、
圈内叫法、不认识的专有名词),先用 web_search 查询含义再回应;②被一个词或称呼调侃而
拿不准时(即使该词本身认识,也可能是新梗或新外号)同样先搜索,一轮没有结果就更换关键词
(补充「梗」「出处」「抖音」等)再试;③搜索之后,回答要能体现出查过。同时明确边界:仅
搜到字面意思时,不作出「没有这个梗」的结论——短视频平台刚流行的梗,搜索引擎常常尚未收录;
此时如实说明「只搜到字面意思、未找到出处」,并顺势询问或接续话题。web_search 工具描述与
人格卡规则同步更新。

实测(生产同款模型 + 真实聚合搜索):被生词调侃时会先搜索再回应;仅搜到字面意思时
如实说明并反问,不再给出「没有这个梗」的结论。

人设编辑:换卡与清交接的防误操作

现象:更换人设并清空交谈之后,机器人仍在使用之前的人设。

根因:重置路径本身没有缺陷——换卡或修改正文后点「保存设置」即生效;「换人设后清空
交接」只清除会话交接与线程状态,并不修改人设。问题在于页面上两处「看似已保存、实际未
生效」的路径。

改动:点击「换人设后清空交接」时先检查人设草稿(包括逐节编辑框中尚未保存的改动),
存在未保存内容即拦下,并提示先点「保存设置」;删除「正在使用」的自定义卡时,确认框与
结果提示都说明「删卡不等于换人设,正文仍在生效,保存设置后才会切换」;docs/PERSONAS.md
新增「换卡之后:清残留」一节。

控制台:表情收藏的判定标准补上入口

v0.8.4 引入的 sticker.collectCriteria(表情收藏的判定标准)此前只能编辑 config.json;
这一版在「设置 → 聊天设置」下补上多行文本域:留空即内置默认(与之前完全一致),填写后
即时生效;超过 2000 字按前 2000 字生效。该标准只影响哪些图会被收进表情库,不影响聊天与
发送。

群日报与每日动态:人名统一走备注/代号

v0.8.3 的称呼统一覆盖了聊天提示词与工具出口,但群日报与每日动态的生成材料仍在用群名片。
本版将这两处也改为备注/代号优先,与聊天侧保持同一口径。

其他修复

  • 提醒与调度:时间控制的静默段不再丢弃到点提醒与自安排唤醒(顺延到窗口恢复后派发);
    自安排唤醒在会话被移出允许列表后显式作废并记日志;手动唤醒遇到未回收的运行租约时明确
    拒绝(此前静默中止、控制台却显示「已触发」);进程退出时提醒循环与到点唤醒一并停止。
  • 群游戏:终局播报发送失败不再丢局——暂存并在下一轮重试(送达后才收尾),重试带指数
    退避;游戏关闭时同步摘除对应工具。
  • 群日报、每日动态与空间互动:群日报当天错过会补跑、被观察或暂停挡下时每 30 分钟重试
    (每天最多 8 次);每日动态遇到「忙碌」不再跳过当天;跨午夜槽位按自身日期判定;空间互动
    在账号不一致时走退避(此前会持续重试)。
  • 数据与会话:会话文件淘汰不再删除运行中或等待中的记录;会话留档保留期 90 天(此前
    默认永不清理);人物记忆、每日动态、空间互动的状态文件损坏时先保留 .broken-<时间戳>
    备份再重建;好友列表拉取失败时沿用已有数据,不再清空。
  • 工具输出:群资料、群文件、表情、看图、回复、翻译、语音转写、OCR 等工具出口的伪造
    段头标记全面弱化(缩小提示词注入面)。
  • 控制台:人物记忆页的 15 秒轮询不再叠加监听(一次「保存备注」不再发出多个请求);
    存档页的六个动作失败时给出明确提示(不再静默);用量页「全部」档给出全部区间(此前实际
    是最近 7 天)、首次加载失败后能自行恢复;外观各轴在启动时即按服务端配置套用;设置页
    若干「填 0 / 留空」的保存口径修复(报名秒数、每日预算、聚合搜索源)。
  • 运维与发布工具链:备份打包增加 30 分钟超时(此前在停服务之后可能无限等待);更新器
    的忙碌判定与「无需更新」原因说明修正;发布脚本拒绝输出到仓库内部路径(含符号链接与盘符
    大小写变体);巡检的「预算已用尽」豁免只认当天用量(昨天的用量文件不再豁免今天的停滞
    告警);巡检、通知、探测等 5 处网络响应的读取加上字节上限;协议端 .env 写入改为原子写
    (保留原文件权限);CI 与发布闸门补上「无 devDeps」环境的复跑(与更新器的真实依赖面
    一致);deploy-all.sh 兼容 bash 4.3 及更旧版本的空数组展开。
  • 提示词:grounded 档中 remind / schedule_wake 的用法教学与「没有安排定时承诺的
    工具」的自述相矛盾,已统一为一致的边界表述。
  • 测试:回归测试不再读写真实数据目录;移除已失效的 test/selftest.mjs。

已知问题

以下问题已登记在 docs/KNOWN-ISSUES.md:

  • 控制台「一键更新协议端」与「随智能体版本对齐」暂不经过内置镜像兜底:受限网络下这条更新
    路径仍会报拉取失败(首次全栈安装不受影响)。
  • 主动运行期间在控制台点「核对」存在一条窄竞态:会清掉该次在途发送的「结果未知」记录
    (触发需要人工操作与运行同刻)。

升级影响

  • 没有破坏性变更,不需要修改任何配置;在控制台执行「更新」即可升级,或在服务器上重跑
    部署脚本。
  • 全新部署:bash install.sh 一条命令完成(模型配置可以留到网页控制台);受限网络下会
    使用内置兜底镜像站,不再需要手动指定。
  • 在网页终端(宝塔等)里运行部署脚本的部署:不再被预检误拒。
  • 排查提示:聚合搜索中保留了 tavily / doubao 两个源而没有配置对应 Key 时,这两个源会
    在每次搜索中静默失败(实际只有 Bing 生效);补上 Key 或将其从源列表移除即可。

安装与部署见 docs/OPS.md。

v0.8.4 —— 表情收藏判定标准可配置(PR #31)、更新门禁不再依赖机器的 docker 访问

Choose a tag to compare

@github-actions github-actions released this 10 Oct 14:18

本次改动

这一版包含两项内容。表情收藏的判定标准改为可配置(社区贡献 PR #31):该标准此前写死在
代码里,调整只能改源码,而升级会整树替换 src/、本地改动一次即失;现在留空即内置默认
(与之前逐字相同),填入自己的标准即生效。另一项是更新门禁的第二处环境依赖修复:在
"管理员已在 docker 组、但 systemd 用户管理器未继承该组"的机器上,门禁中的 3 条巡检用例
必然失败、更新本体被直接拦死(Issue #30 评论实测复现)—— 修复后这类机器无需先处理
docker 组即可升级本体。

表情收藏的判定标准可配置(PR #31,贡献者 muhan5102)

背景:表情自动收藏与聊天里的 collect_sticker 共用一段判定提示词("以后聊天时用得上
吗"),标准写死;不同部署的取向不同(有的希望以 ACG/干净卡通为主,技术群可能相反),想调
只能改源码 —— 升级时 src/ 被整树替换(rsync --delete),本地改动一次即失。

改动:新增可选配置 sticker.collectCriteria(config.json 的 sticker 段,字符串,
默认空):

  • 留空 = 内置默认,默认文案与改动前逐字相同(老配置一字不改照旧运行);
  • 只写"收什么 / 不收什么",身份那句(你是「谁」、正在看谁的图)仍由代码补充在前;
  • 运行期读取,改完即时生效、不用重启;自动收藏与主动收藏共用同一份标准;
  • 超长截断到 2000 字(判定是一次 maxTokens: 600 的小调用,防止请求被撑爆);
  • collect_sticker 被拒时给模型看的理由不再复述默认标准(避免与自定义标准相互矛盾)。

修复:更新门禁不再依赖机器的 docker 访问

现象:在"管理员已加入 docker 组、但 systemd 用户管理器未重建(因而未继承 docker 组)"
的机器上,更新协议端会报权限错误(Issue #30 原报告);此外这类机器更新本体也会被门禁
拒绝
—— 实测:升级到 v0.8.3 仍失败,重启用户管理器之后才通过(Issue #30 评论)。

根因:test/ops-health.test.mjs 中有 3 条断言"全绿 / healthy"的巡检用例直连真实
/var/run/docker.sock。更新器的部署前门禁运行在加固 unit 里:该单元未继承 docker 组时
套接字必然不可访问,而 sudo -n docker 回退又必被 NoNewPrivileges 挡死 —— 3 条用例
必然失败、门禁拒绝升级。CI 与开发机的测试进程都在 docker 组中,这条机器环境依赖一直不可见
(与 v0.8.2 的 NoNewPrivileges 事故同族:门禁用例不应依赖运行机器的环境)。

改动:

  • 该测试文件统一把 DOCKER_HOST 指向自建的可读假套接字,与机器环境解耦;真实失败路径
    (不可访问 → 报红)仍由专用用例(000 权限套接字 + 注入 NNP 状态)确定性覆盖;"sudo 回退
    可用 → 报绿"那一支新增可注入的 sudoDockerProbe 并补确定性用例。
  • CI 与发布闸门的"按 unit 的加固环境复跑"新增**"无 docker 访问"维度**(000 权限假套接字
    • DOCKER_HOST),此类环境依赖从此无法通过发布闸门。

升级影响

  • 没有破坏性变更,不需要修改任何配置。
  • 表情收藏:不配置 sticker.collectCriteria 时行为与本版之前完全一致;自定义标准的写法见
    docs/CONFIG-EXAMPLES.md。
  • 曾受"更新装不上"影响的部署(用户管理器缺 docker 组的机器):自本版起,更新本体不再
    需要 docker 组
    ,可直接升级;更新协议端仍需要 docker 权限(处理步骤见 Issue #30)。

安装与部署见 docs/OPS.md。

v0.8.3 —— 修复「更新装不上」(v0.8.2 门禁在加固单元里必败)、报错可读性、提醒归属与称呼统一

Choose a tag to compare

@github-actions github-actions released this 10 Oct 03:34

本次改动

这一版首先修复一个发布级缺陷:v0.8.2 的更新门禁在标准部署里必然失败,导致所有标准部署
(含我们自己的服务器)都装不上新版本
—— 控制台点「更新」与定时检查的失败根因同此;同时把
更新失败的报错改为直接列出失败用例(此前那段"全是群游戏测试名"的报错就是这里来的),并落地
两条群友反馈的改进:提醒归属(不再把别人托的提醒说成被提醒人自己设的)与称呼统一(按 QQ 的
备注/代号在聊天记录、成员列表、提示词里保持一个口径)。

修复:v0.8.2 的更新门禁在加固单元里必败(发布级)

现象:控制台点「更新」或定时检查 → 门禁拒绝部署 → 装在 v0.8.1/v0.8.2 的机器全部装不上
新版本(Issue #30 报告者与群友反馈的"升级失败"同一根因)。失败还会触发"自动更新自停 +
私聊通知 + 巡检报红",而通知里嵌的错误信息整段是群游戏测试名 —— 那是报错信息的组装
方式
造成的:更新器此前截取测试输出结尾 2000 字符,而测试文件按名字排序收尾时最后正好
是狼人杀/卧底这类文件,真正的失败(两条 snowluma 用例)在三百条之前,一条也看不到。

根因:v0.8.2 里两条 sudo -n docker 回退用例在构造更新器时没有注入 NoNewPrivileges
探测,落到了真实的 /proc/self/status;而门禁本身就跑在带 NoNewPrivileges=true 的
systemd 单元里(标准部署默认如此加固)。加固环境下程序会正确地"一次 sudo 都不试"并给出
加固提示,与用例的断言相矛盾 —— 于是门禁必然失败。CI 与普通命令行没有 NNP,因此发布前
一路绿灯。

改动:

  • 两条用例改为显式注入环境(默认未加固;加固路径由专门用例覆盖);CI 与发布闸门各新增
    一步"更新器门禁全清单(umask 0077 + setpriv --no-new-privs 复跑 unit + local +
    四个回归 harness + scan)",把"只在门禁环境里才失败"的问题挡在发布之前。
  • 失败信息组装改为 failureDetail():有失败项就列出用例名 + 断言原因 + 汇总(含
    describe() 内的缩进形态与块形式诊断),找不到才退回旧口径。
  • 顺带纠正一处部署记录口径:手工 tar 部署须把版本 SHA 写进数据目录的
    deployed-revision(写进应用树里的那份无效,会导致更新器一直误判"落后"、每次检查都
    真跑一遍完整更新管线然后失败)。

升级后的建议操作:更新到 v0.8.3 后「更新」按钮与定时检查即恢复正常;若按钮仍报旧错,
按控制台提示重跑一次部署脚本即可。

提醒归属:谁提的、提醒谁

现象:a 说"12 点提醒 b 去吃饭",到点机器人却说成"b 自己设的提醒"。

根因:提醒不记归属(旧字段存的是机器人自己的名字、从未使用);派发时整条内容被套进
"你之前给自己留过话"(那是"自安排唤醒"的框架),且只有"有人让你"没有名字 —— 模型只能
自己圆,圆出了错误归属。

改动:remind 支持 to(提醒谁)/ from(谁请你提醒的;必须能在本轮消息里对上人,
对不上就不记 —— 防"就说是管理员让提醒的"这类借名与猜错);到点话术带上归属("彭于晏 之前
让你在 12:00 提醒吴彦祖:去吃饭"),没有归属时明确写"别猜、也不要说是被提醒的人自己设的";
定时提醒的唤醒改走逐字分支、不再自称"管理员从控制台唤醒"。控制台 → 设置 → 定时提醒,
列表每行显示"谁请你提醒谁"。

称呼统一:按 QQ 的备注/代号

现象:"我让 bot 以后叫他 pyy",机器人有时喊 pyy、有时喊群名片。

改动:备注(称呼/代号)本就存在(控制台「人物记忆页 → 备注」),这一版把它做成全局
一个口径
:聊天记录、成员列表、消息详情、成员提示与提示词都走同一个"备注优先";并在群里
直接可记 —— 群友让机器人"以后叫他 X"时,用新工具 set_member_note 落一条(只给当前会话
出现过的人设,号码编错会被拒);set_remark(QQ 客户端备注)的说明改为指向它。控制台
人物记忆页的标题改为「备注(称呼/代号)」,并注明与聊天侧是同一份数据。

升级影响

  • 没有破坏性变更,不需要修改任何配置。
  • 曾受更新门禁影响的部署:更新到 v0.8.3 后恢复(门禁校验的是新版本的测试,v0.8.3 的
    测试已确定性通过)。
  • 若曾手工 tar 部署过:deployed-revision 的权威位置是数据目录(QQ_AGENT_DATA_DIR);
    写错位置不影响运行,只会让更新器误判版本,按 docs/OPS.md 的口径写对即可。

安装与部署见 docs/OPS.md。

v0.8.2 —— 控制台更新协议端修复(Issue #30)、协议端随版本对齐、值班台视觉身份

Choose a tag to compare

@github-actions github-actions released this 09 Oct 13:28

本次改动

这一版修复 Issue #30「控制台更新协议端报 docker 权限不足」,覆盖从根因定位、部署脚本自愈到
提示文案的完整链路;新增协议端随智能体版本对齐 —— 发版触及协议端基线时,更新本体即顺带
升级,默认开启;控制台立起「值班台」视觉身份(一页两级表面与细线、设计标度收口、材质轴五档
与边缘折射);并收口一批安全与稳定性问题(工具 URL 直通协议端形成的 SSRF、上游响应缺少字节
上限、send_group_file 可读取本机文件外发、docker compose pull 期间主进程事件循环被阻塞、
台账保留期清理长期未执行)。

Issue #30:控制台「更新协议端」报 docker 权限不足

现象:控制台点击「更新协议端」报
permission denied while trying to connect to the Docker daemon socket,而同一账号在交互
shell 中可正常使用 docker。

根因:问题不在 docker.sock 的权限配置,而是运行控制台的进程没有 docker 组。控制台以
systemd 用户服务运行,其补充组在 user manager 启动时即被固定(启用 linger 的机器上即开机
时);后续执行 usermod -aG docker 仅影响新建的 shell,长期运行的 manager 不会随之更新。
实测非 root 进程无法为自身补充缺失的组,因此不存在"进程内自愈"的方案。

改动:

  • 部署脚本自愈:deploy.sh / deploy-all.sh 在收尾阶段检查部署用户的 docker 组身份与
    服务进程的组快照,缺项即补齐 —— 加入 docker 组,并以 stop → sleep 3 → start 重建 user
    manager(服务中断约 10 秒后自动恢复)。同时移除两条不可靠的修复方式:systemctl restart user@(实测触发 status=219/CGROUP,且失败后不会自动恢复)与 loginctl terminate-user
    (不会自动带回管理器);文档、错误提示与测试均不再推荐。
  • 提示与预检:更新前的预检先探测 docker info(含回退路径),两条路径都不可用时才给出
    人工修复步骤;错误信息按当前机器的实际状态生成可直接执行的命令。
  • 运行时回退(仅限未启用加固的部署):直连遇到套接字权限错误时,回退以 sudo -n docker
    执行(非交互、仅一次;无免密 sudo 时立即失败,不会挂起)。标准 unit 默认带
    NoNewPrivileges=true,加固部署下该回退不生效,程序会如实报告原因;可用
    QQ_AGENT_NO_SUDO_DOCKER=1 关闭。
  • 修正一处永不失效的误报:健康检查此前以巡检进程自身的 sudo 结果为判据,在加固部署下会
    长期显示正常;现改为读取主进程的 NNP 状态,并新增 docker-socket 巡检项。
  • 代价说明(docs/LINUX.md):将账号加入 docker 组等价于授予其 root 权限(可挂载宿主文件
    系统);文档列出三条处置路径(接受 / 关闭控制台更新并移除组 / 将 server.host 收回
    127.0.0.1)。

升级后的建议操作:更新到 v0.8.2 后重跑一次部署脚本(deploy.sh / deploy-all.sh),
或按控制台给出的命令执行;完成后「更新协议端」即可正常使用。

协议端随智能体版本对齐(新功能,默认开启)

协议端基线(镜像版本)是代码常量,只随本体新版本到达用户机器;"部署新版本后主服务携带新代码
重启"是机器上出现新基线的唯一时机,此前该时机不做任何事 —— 导致"能力已具备、协议端版本过旧"
(例如贴纸显示成图片)只能由用户自行升级协议端。现在:

  • 发版触及协议端基线时,更新本体即顺带完成对齐;基线未变时不产生任何动作(基线未变时
    outdated 为假,自然成为 no-op)。对齐复用更新器的完整流程:修改 .env → pull →
    up → 等待 WebUI 就绪 → 失败自动回滚;数据卷不动、登录态保留。
  • 自定义镜像(autoUpdate.snowluma.image)始终优先,锁定版本的机器不会被自动变更;设置页
    OneBot 分区新增「随智能体版本对齐协议端」开关(默认勾选)。
  • 对齐失败仅记录日志,不影响本体更新自身的结果。

配套:更新器由同步执行改为异步执行 —— 此前 docker compose pull(超时 300 秒)期间
整个主进程事件循环被阻塞(消息不处理、控制台无响应、健康探测失败),而手动 / 周期 / 随版本
对齐三条路径共用该执行器,本功能默认开启后会常态触发。

控制台:值班台视觉身份

  • 一页两级表面:KPI 行作为一整块表面呈现(格与格之间为 1px 发丝线),展开面板改为
    细线分节(不再逐块成卡);玻璃材质施加于整行,而非逐格。
  • 设计标度收口:323 处字号与 139 处圆角(含 JS 内联样式)全部改用 token;同时清理
    [data-theme='dark'] 中已被 :root 覆盖的圆角死声明(此类声明表现为"修改不生效")。
  • --voice(黄铜):全站唯一暖色,只用于标识机器人自己发出的内容 —— 「已发言」徽章、
    上下文中 assistant 回合、归档表中机器人发送的消息、总览页「机器人」卡片。
  • 材质轴(第 8 轴):实心 / 磨砂 / 液态玻璃 / 亚克力 / 描边五档;另有实验性的边缘折射
    (默认关闭,按每个表面的实际尺寸计算位移场、仅边缘弯折;不支持的环境自动不生效)。
  • 动效收口:折叠面板展开与收起均走高度过渡;列表行退场先量后写(删除 N 行不再触发 N 次
    强制重排)、行重新出现时取消退场;KPI 数字计数在数值未变时不滚动;悬浮位移统一限定在
    (hover:hover) 与 (pointer:fine) 环境。另修复用量页「展开全部」按钮此前未绑定事件的
    问题(模型超过 20 个时无法看全)。

安全与稳定性收口

  • 工具 URL 直通协议端形成 SSRF(send_group_file / set_my_avatar / read_image_text /
    upload_to_group_album):模型给出的地址被原样交给协议端请求,该请求发生在程序网络层之外、
    safe-fetch 无法覆盖;提示词注入配合内网地址即构成 SSRF 与外带通道。现已新增公网地址
    校验,并在上述 4 处接入。
  • send_group_file 拒绝非 http 地址:非 http 前缀(file://…、/app/data/...)会被
    协议端按本机路径读取并以群文件发送;本机内容改由 base64 通道传递。
  • 上游响应全面收口为有界读取:18 处"先整体读入再判断长度"(长度上限实际无效)改为边读
    边截断、按用途分档上限;safe-fetch 的截断标记改由读取路径如实给出。
  • 租约回收与在跑执行冲突导致重复回复:现在会排除本进程仍在运行的租约,同一批消息不会被
    回答两遍。
  • 台账保留期清理此前"在线等于未执行":三本台账的清理挂在进程启动时(连续运行数周的实例
    上从不触发),改为进程内每日闸门;runs 台账也纳入控制台每日清理。
  • SnowLuma 备份保留 3 份:此前每次升级都会新建备份且从不清理,反复升级会占满数据盘。
  • deploy-all.sh 的 SnowLuma 密码轮换此前恒不执行(改写后以新值比较新值),会出现容器内
    仍为旧密码、无法登录且无任何报错的情况;已修正。
  • 测试卫生:全量测试不再向系统临时目录堆积 220 个测试目录(运行结束整体清理)。

升级影响

  • 没有破坏性变更,不需要修改任何配置:老配置照常工作,升级流程与上一版相同。
  • 协议端基线本版未变更(仍为 v1.14.22):已运行该版本的部署在更新后不会触发任何协议端
    动作;低于基线的部署(例如 v1.14.15)会在更新本体后被顺带对齐至 v1.14.22。该动作要求
    服务进程可访问 docker —— 先按上文重跑一次部署脚本完成 docker 组自愈即可。
  • 重跑部署脚本会执行一次 docker 组自愈:将部署账号加入 docker 组,并以
    stop → sleep 3 → start 重建 user manager(服务短暂中断约 10 秒)。加入 docker 组等价于
    给该账号 root 权限
    ,取舍与替代方案见 docs/LINUX.md 的「代价须知」一节。
  • 控制台外观有可见变化:KPI 行为一整块表面加发丝线、面板改为细线分节;侧栏外观沿用上一版。
    「材质」与「边缘折射」可在「设置 → 外观」中调整。

安装与部署见 docs/OPS.md。

v0.8.1 —— 控制台外观 v3、平台能力与协议端 1.14.22 接入

Choose a tag to compare

@github-actions github-actions released this 08 Oct 13:57

本次改动

这一版把控制台的外观能力补到"能按自己喜好调"(七轴 + 主题切换波纹 + 侧栏图标条 + 设置页分区
菜单),把平台能力做成可细调(读写分开关、每项列出所属工具、闸门配额可调可看、按群覆盖),
并接入协议端 SnowLuma v1.14.22 的新能力(表情回应收发、私聊正在输入、资料与群资料、QQ 语音、
OCR、转写快路径、在线表情目录),同时让协议端版本在控制台可见、可一键或自动更新。
此外是第十七轮全项目复审(8 P2 + 18 P3)与两轮控制台 / 运行时复审的收口。

控制台外观 v3:七轴、主题切换波纹、侧栏

现象:外观只有零散五项,"改完没生效"的窗口多;换主题是硬切;改完不保存、切走再切回,面板会
"忘了"刚做的选择
;分段控件没有滑块、方向键按了没反应;参数行里的输入框一路撑满整行,一页看起来
像堆满表单;窄窗口下侧栏还跟内容抢高度。

改动:外观扩成七轴(明暗 / 深色强度 / 色板 / 强调色含作用范围 / 侧栏样式 / 背景 / 字体 /
圆角 / 缩放 / 密度 / 对比度),点了就生效、点「保存设置」才落服务端,并在设置页里独立成
「外观」分区(老配置自动迁移,选过的主题、配色、强调色都保留)。换主题 / 强调色 / 色板走
"从点击处扩散"的圆形波纹,关掉动效时直切。

  • 侧栏:改成悬停 / 焦点自动展开的图标条(248 ↔ 64,260ms),标签淡入淡出而不是被切断;
    底部新增「固定」图钉,与「外观 → 固定侧栏」互为镜像(想常驻展开点一下就行)。
  • 设置页分区菜单:选中块会滑过去,切分区时自动把当前项带进视野(只滚这根侧栏自己,
    不动右侧表单),当前项带 aria-current。
  • 其余:分段控件补上滑块与方向键;开关统一自绘;参数行改成「标签列 + 控件」两栏;
    图标换成统一 SVG;新增「总览」首页并作为默认落点。

平台能力:写入闸门与控制台细化

改动:写入闸门按"真人的标准"收紧(换头像按周,其余 10/小时 · 1/天 · 1/天);控制台
「平台能力」页重塑为能力卡片 + 工具名 + 配额表 + 按群覆盖面板,读写分开 15 个开关、每项列出
所属工具、配额可调可看;新增换头像 / 改 QQ 资料(默认关)、QQ 语音音色(控制台可选并固定
音色)、合并转发卡片、群文件、群相册、已读标记。

并修掉一批复审发现的问题:控制台平台页 11 个开关此前一个也存不下去(装配挂错了分区,界面还
提示"已保存");三条可见写入(QQ 语音 / 群文件 / 传相册)此前绕过发送队列(无限频、无 outbox、
禁言不走预检);按群覆盖行间的游离逗号;配额用量对不上规范键(真机才暴露)。

协议端 SnowLuma v1.14.22 的能力接入

现象:协议端镜像一直钉在 v1.14.15,升级只能 ssh 进去手改 .env 再 docker compose up -d
—— 于是"贴纸显示成图片"这类**"能力已经有了、协议端太老"**的问题,只有手动升级过的人能解决。

改动:基线镜像升到 v1.14.22,并接入表情回应(收发)、私聊「正在输入」、资料 / 群资料、
QQ 内置 AI 语音、OCR、语音转写快路径、表情目录在线化。贴纸改按「表情样式」发送
(image 段带 sub_type + summary)——这正是"发表情包变成图片"的修法。控制台新增协议端版本显示与
一键 / 自动更新:目标镜像沿用当前镜像站前缀、只改 .env 里那一行(其余键含令牌原样保留)、
备份后 pull + up、pull 失败只还原配置、起不来自动回滚旧镜像;巡检新增 protocol-version 项,
低于推荐版本时提示"贴纸会显示成图片、可一键更新"(不算不健康)。

复审收口(第十七轮 8 P2 + 18 P3,以及两轮控制台 / 运行时复审)

  • #wake 的前置判定与排程预判段整体兜底:store 一报错,等待中会话会永远停在"等待中"、
    pendingWake 永久占位会让那个群从此不再被自动唤醒(此前要重启才能恢复)。
  • allow / deny 名单形状坏掉不再抛异常 —— 那条判断在每条消息的进路上,抛一次就等于机器人
    静默不吭声。
  • 超预算"降级"时不再回没被 @ 的群(未读扫描窗口此前比实际领取的批次更大,判定会看见"批外"的 @)。
  • 控制台的 moduleLog 不再把纯文本日志当成 error(群日报 / 群游戏 / 空间互动一开就是假告警风暴)。
  • 数字字段填 0 不再被当成"没填":按字数附加间隔、批次间隔、温度、触发概率都按 min="0" 的
    语义生效。
  • 一批控制台缺陷:总览页两条跳转链接此前点了只抛错、异常等级 / 状态显示成英文、总览日期改用
    上海时区、存档 / 会话记忆 / 用量表补键盘可达、浮层支持 Esc、侧栏底部图标不再越界、
    自定义强调色不再被打回预设、思考档位的滑块与方向键不再失效。
  • 协议端更新的镜像引用加白名单校验(此前可往 .env 注入变量)。

升级影响

  • 没有破坏性变更,不需要修改任何配置:老配置会自动从外观 v2 迁到 v3,选过的主题 / 配色 /
    强调色都保留。
  • 能看到的变化:左侧栏默认收成图标条、鼠标移上去展开 —— 想常驻展开,点侧栏左下角的
    「图钉」或到「设置 → 外观 → 固定侧栏」;设置页左侧分区菜单多了「外观」一项(共 18 项)。
  • 协议端镜像基线升到 v1.14.22:仍在旧版本的部署,控制台会提示可一键更新。
  • 部署侧无新增运行时依赖,沿用上一版的升级流程即可。

安装与部署见 docs/OPS.md。

v0.8.0 —— 群名片工具、16 轮全项目复审与生图发送修复

Choose a tag to compare

@github-actions github-actions released this 07 Oct 03:36

本次改动

这一版的主体有两件事。其一,机器人现在可以修改自己在群里的群名片——社区贡献(@foxlesbiao,PR #19)。其二,修复了生图发送「时好时坏」的根因:协议端的 HTTP 通道对请求体有 ≈2MiB 上限,大图的 base64 传输会被掐断(真实用户报告,Issue #21)。此外是第十六轮全项目复审的集中收口(11 P2 + 8 P3),以及此前数轮复审的零散修复。

生图发送「时好时坏」的根因:协议端 HTTP 请求体上限(Issue #21)

现象:生成贴纸发送时好时坏——小图偶尔能发出去,大图必失败;异常面板里只有一条 fetch failed,没有任何可定位的信息。
根因:SnowLuma 的 OneBot HTTP 端点对请求体有 ≈2MiB 的硬上限,超过时在 100-Continue 之后直接掐断连接(实测:2MB 通过、2.5MB 掐断)。而生图发送的链路是把整张图 base64 后塞进消息段,1024×1024 的图 base64 后通常 2~3MB+,正好压线:图小碰巧成功、图大必挂。文字消息只有几十字节、旧表情发送的是 QQ CDN 链接,都不经过这条上限,所以此前只有生图受影响。看不见真因是第二个问题:undici 的外层报错恒为 fetch failed,真正的 ECONNRESET 嵌在 error.cause 里,落库时被丢掉了。
改动:请求体超过 1.5MiB 的 OneBot 调用自动改走 WebSocket 通道(事件常驻连接,实测 3MB 无恙);结算口径与 HTTP 通道一致——协议端明确拒绝报失败可重试,断线与超时按「结果未知」处理,不会把没把握的失败降级成静默重发。WS 未连接时回落 HTTP,行为与此前一致。同时异常落库会解出 error.cause 链(最多 3 层、逐层脱敏),以后这类网络异常在面板上直接显示「真因: ECONNRESET …」。

群名片工具 set_group_card(社区贡献)

现象:此前机器人无法修改自己在群里的群名片。
改动:新增 set_group_card 工具,模型可在被要求时修改机器人在群里的群名片(@foxlesbiao,PR #19)。合并后补了两处:送达后记账走统一兜底路径、取消信号全链透传。

控制台 server 整节替换的令牌清空窗口

现象:持有控制台令牌的人提交整节替换配置的请求可清空令牌——运行中的实例不随配置重绑套接字,原本「令牌 + 仅本机可达」的保护会静默降级为免鉴权。
根因:此前的加固只覆盖了「重启后拒绝以无令牌启动」的路径,漏掉了运行期窗口;且「本机判定」原先看可伪造的 Host 头。
改动:本机判定改看 TCP 对端地址;实例仍绑在非回环地址时,拒绝一切会清空令牌的 server 整节替换(409),且属于预期内拒绝、不会触发异常告警。

会话静默卡死的一处窗口

现象:极小概率下,某个会话此后不再响应任何消息(被 @、被唤醒、恢复循环全部绕开),直到重启。
根因:唤醒流程里「登记为运行中」与主逻辑之间的同步准备段(读线程、会话落盘、事件监听)一旦抛错,收尾代码不执行,运行中标记永远不被清掉。
改动:准备段单独兜底,出错时清理控制器、定时器与租约,会话落 error 终态后原样上抛,不再留卡死窗口。

认不出自己:登录信息获取失败无重试

现象:协议端重启等竞态下,机器人可能认不出自己的消息与被 @ 的判定;严重时对指令整体无响应。
根因:获取自身账号信息只在连接建立时调用一次且没有重试,失败一次留下的空值会一直生效。
改动:退避重试 + 60 秒兜底循环,并在相关判定处加双守卫。

网关返回畸形工具调用时的整批失败

现象:部分网关或自部署推理服务返回缺少 id 的工具调用时,下一轮请求被端点整体拒绝且不可重试,当前对话批次转人工、被 @ 的消息无人回。
根因:原生工具调用链路假定 id 一定存在,缺 id 的消息进不了标准格式。
改动:进消息列表前补全 id 与类型、参数字符串化,与文本兜底链路口径一致。

其余修复

  • 未读扫描窗口与批处理量统一,被 @ 的判定不再漏看较长的未读队列。
  • 节奏控制不再顶掉模型给自己安排的唤醒与留言。
  • 会话空跑不再虚增当日运行计数。
  • 兜底模型跨渠道时,费用按实际渠道计价。
  • 身份信息启动冲刷逐条隔离,单条坏记录不再让整个功能瘫痪。
  • 手动标记的好友关系在重新同步后保留。
  • 空间互动状态绑定账号,换号后不再执行旧积压。
  • 发送失败路径补记账兜底,不再被数据库错误顶掉真实错误。
  • 看图任务的落盘失败兜底(磁盘写满不再反复崩溃)。
  • 控制台查询串令牌收窄到事件流端点。
  • 登录失败限流改为逐条逐出并加全局告警。
  • 部署脚本:复用已安装运行时、失败自动回滚环境文件、镜像双源校验。

升级影响

  • 无破坏性变更,不需要迁移步骤,不需要修改任何配置。
  • 升级后:生图与大图发送不再受协议端请求体上限影响;异常面板的网络类错误直接显示真因;机器人可修改自己的群名片。
  • 部署侧无新增依赖,沿用上一版的升级流程即可。

安装与部署见 docs/OPS.md。

v0.7.8

Choose a tag to compare

@github-actions github-actions released this 04 Oct 03:49

本次改动

这一版的主体是控制台的一项日常操作:切换语音回复、语音转写、图片生成的服务预设时,已填过的 API Key 随服务一起切换,不再需要逐家重新获取并填写。围绕该功能建立的凭据记忆机制在四轮发布前审查中修正了 20 处缺陷,其中 4 处为加固过程引入的回归。

切换服务预设时 API Key 跟随切换

现象:切换服务预设后 Key 输入框变为空白;重新填写后,先前那家服务的 Key 被覆盖,切换回去时无法取回,只能重新获取。
根因:三类凭据此前只存单槽,任何一次填写都会覆盖上一次的值,服务之间没有各自的存储位置。
改动:三类凭据改为按槽位记忆——语音回复按服务标识、图片生成按主机名、语音转写按「服务 + 主机名」(转写含 apiKey / secretId / secretKey 三种)。映射由服务端独占写入,客户端提交的同名字段在保存前丢弃。每把凭据同时记录归属标记(tts.apiKeyService、imageGen.apiKeyHost、asr.apiKeyProvider + asr.apiKeyHost),运行期仅发送归属匹配的那一把——将 A 家的 Key 发往 B 家不会报错,只会在对方后台留下一条 401 或一笔异常计费,因此该约束为强制校验。切换离开时,当前凭据先归档至其所属槽位再置空(存量实例的 Key 仅存在于单一字段,直接置空即永久丢失);控制台「显示」按钮按当前选中项回读,不再回显其他服务的明文。

语音回复单槽 Key 在「切换服务并填写新 Key」时丢失

现象:存量实例中,切换服务并填写一把新 Key 后,再切回原服务且不填 Key 时,原 Key 已不可用,且无法从任何位置取回。
根因:归档逻辑原先只位于「未填写新 Key」的分支内,「切换服务并填写新 Key」这一常用路径完全绕过归档——旧 Key 留在单槽,归属标记却已指向新服务;此后切回原服务时,归属比较判定为「他家的」,而归档位置已被新 Key 占用,于是跳过归档并直接置空。
改动:归档提至两个分支之前无条件执行,与图片生成、语音转写保持同一口径;填写新 Key 的分支同时将单槽替换为新值,保证「单槽所载」与「归属标记所指」始终为同一把。

语音转写归档槽缺少类型守卫

现象:asr.keys 中被手工改为标量的槽位({"tencent":"SCALAR"})会在保存时被展开为 {0:'S',1:'C',…} 形式的字符索引键并写入 config.json。
根因:同一段代码中目标槽位已设「条目必须为对象」的守卫,归档槽位未设,属遗漏。
改动:两处共用同一个 entryOf();migrateConfig 补充条目级归一,覆盖读盘路径。

异常日志筛选与人物印象搜索无响应

现象:异常处理页的状态与等级筛选选定后表格不刷新;人物印象页搜索框按回车无响应。用户表现为筛选时灵时不灵,而异常日志页在此期间将未筛选的行作为筛选结果呈现。
根因:整块重画前设有「焦点位于输入控件时暂不写入」的守卫,用以避免后台轮询覆盖正在输入的内容,但该守卫无法区分「后台轮询触发的重画」与「该控件自身触发的重画」。
改动:新增 options.force 通道,由用户主动触发的刷新显式传入,后台轮询仍受原守卫保护;重画后将焦点归还至同一控件,光标置于末尾。

服务端纠正后的值被未保存的输入覆盖

现象:好友管理页将 99999 填入上限为 365 的输入框,服务端存储 365,界面重画后仍显示 99999,即存储值与显示值不一致。
根因:「保留未保存输入」仅判断「用户是否修改过该控件」,未判断「服务端本轮是否也修改了该值」。
改动:两个条件同时成立时才回填,服务端修改过的字段一律以服务端为准。同处一并修正同名控件更换类型、下拉选项消失时不强塞非法值的情形。

凭据与配置的边界

  • migrateConfig 不再为归属未知的图片生成 Key 补记归属。合并路径补记等同于将其绑定到本次保存刚修改的地址,用户切回原地址时校验不通过,凭据被锁定;读盘阶段仍照常补记(移入 loadConfig),升级中的实例不受影响。
  • asr.providerDefaulted 改为在整节替换的有效体上删除。节点层的 delete 在 __replace__ 下不作用于最终配置,替换体中的同名字段会落盘并永久屏蔽 ASR_API_KEY 环境变量。
  • 配置段被提交为标量或数组时按「该段未修改」处理,不再导致整个 /api/config 返回 500。

其余修复

  • 滑条回填服务端实际存储值后补发 input 事件:程序化写入 .value 不触发该事件,导致滑块读数与填充色停留于旧值。
  • 语音转写配额在下载、转码、超时失败时退还。
  • 模型重试耗尽后不再重复投递;响应为空时明确报错,不再静默返回。
  • 当日预算耗尽的提示改为每个会话每天仅提示一次,主动发言路径不再重复播报。
  • 记忆查询补充返回条数上限,并按最近观测时间排序。
  • 贴纸收藏的 collect 在失败时退还计数。
  • deploy.sh 的失败路径补上真实调用点锚点:原锚点位于函数定义处,任何改动均可通过校验。
  • 发布流水线新增「tag 与 package.json 版本号一致」校验,版本号不符时中止发布。

升级影响

  • 无破坏性变更,不需要迁移步骤,不需要修改任何配置。 存量凭据在读盘阶段自动补记归属,升级后照常生效。
  • 控制台行为变化:切换服务预设时已填写的 Key 随服务切换;异常日志页筛选与人物印象页搜索在触发后立即生效(此前需待焦点移开);服务端纠正过的值在整块重画的表单中立即显示为纠正后的值(此前会短暂显示用户输入的原值)。
  • 部署侧无新增依赖,沿用上一版的 npm ci 流程即可。

安装与部署见 docs/OPS.md。

v0.7.7 —— 控制台性能、删除/保存体验与巡检判据修正

Choose a tag to compare

@github-actions github-actions released this 02 Oct 04:18

本次改动

这一版集中在控制台的响应速度与「整页重拉」体验,外加健康巡检判据的一次修正、两处依赖升级,以及发布前审查补上的一批小项。

控制台响应性能

现象:每个页面打开都要等几秒,用量页更久;贴纸列表里缩略图多时尤其明显。
根因:① JSON 响应完全不压缩;② 贴纸图片每次打开都全量重下;③ 用量统计每次都要遍历并解析全部会话文件,而 20 秒的单槽缓存在「状态栏按今天、页面按 7 天」两个区间之间来回被顶掉,几乎从不命中。
改动:超过 1KB 的 JSON 响应启用 gzip(按 Accept-Encoding 协商,压缩与不压缩两侧都发 Vary);贴纸图片按本地库 / 远程地址两个分支发 ETag 与长缓存(本地 7 天 immutable、远程 24 小时),If-None-Match 命中回 304;用量统计加 mtime 预筛(早于窗口起点 6 小时的文件直接跳过)与按区间分槽的 60 秒缓存。

删除与保存不再整页重拉

现象:观测页删一张图后整页刷新、滚动位置被顶回顶部;资产编辑/新增保存后整格重渲染两次。
根因:服务端先广播 SSE、后回 HTTP 响应,删除/保存请求自己的回声比响应先到,触发了「清空状态 + 重拉」。
改动:删除改为就地摘除(本地列表摘一条 + 概览计数减一 + 直接移除卡片节点,拿不到节点才退回重渲染),SSE 的 delete 事件同样就地过滤且幂等;保存侧引入写入在途守卫(计数语义,连续保存不会互相顶掉窗口)与就地重拉并还原滚动位置;身份、记忆的保存与删除所触发的 identity-pilot-update 回声也纳入同一道守卫。

健康巡检判据修正

现象:安静时段与「按响应概率决定不回」被误判成收发停止,连续三次给 owner 弹私聊告警。
根因:旧判据是「入站新 → 出站必须新」,把合法沉默当成故障。
改动:判据改为「到期的入站消息有没有被处理」——停在 pending 且超过 60 分钟宽限的,加上重试耗尽(failed)且发生在最后一次成功出站之后的(模型网关整体打挂时消息会在十几秒内全部转 failed,只看 pending 会静默漏报);observe 模式、Agent 暂停、当日预算降级三种「故意不处理」自动跳过。已知边界:发送结果待确认(held / outbox sending|unknown)的批次不在判据内,仍由控制台「待核对」流程人工兜底。

依赖与 CI

  • ws 8.21.3 → 8.22.0、undici 6.28 → 8.11.2;undici 8 要求 Node ≥22.19,项目低限随之从 22.13 抬到 22.19,并新增用例钉住「运行期依赖的 engines 不得越过项目地板」。
  • GitHub Actions:actions/checkout v7.0.1、actions/setup-node v7.0.0(仍按 commit SHA 钉住)。

升级影响

  • 运行期依赖有变化:手工 git pull 部署需要重新执行 npm ci;Node 需 ≥22.19(使用 deploy.sh 时其 .runtime 会自动安装 22.23.2,无需手工处理)。
  • 健康巡检判据变化:安静时段与「按概率不回」不再误报;「消息重试耗尽且此后没有成功发出」会新增告警(这是修漏报),包括低峰时段单条消息重试耗尽的场景,约 1 小时后也会告警。
  • 控制台行为变化:删图与保存不再整页刷新,滚动位置保留。

安装与部署见 docs/OPS.md。

v0.7.6 —— 架构拆分、图片生成与三轮审查修复

Choose a tag to compare

@github-actions github-actions released this 01 Oct 15:18

本次改动

这一版把控制台做了一次结构性拆分(ui/app.js 从 13130 行降到 1019 行,ui/ 全量转为 ES module),加入图片生成与完整的密钥控制,并收口三轮对抗性审查发现的问题;另外让 WebSocket 客户端兼容不回应 PING 的 NapCat 协议端。

图片生成(Issue #21)

  • 背景:此前只会"收图、发图",群友让画一张时只能拿库里的旧图应付。
  • 改动:新增 generate_image 工具,支持 OpenAI 兼容接口与 Pollinations 两种请求形状;生成的图落进表情库后用现成的发图通道发出。控制台「图片生成」内置五个服务预设(Pollinations 免 Key、智谱 CogView-3-Flash、硅基流动、魔搭、自定义),选中即回填地址与模型,免 Key 的那家把 Key 栏置灰。
  • 三条硬约束:Key 归属守卫(地址与聊天模型不同域时拒绝复用模型 Key);唯一成本闸门 maxPerHour(默认 6 张/小时);不发 response_format(新版 OpenAI 因未知参数报错)。画图额度改为事前原子扣减、失败退回。实测 8~9 秒出一张 1024×1024。
  • 默认关闭:不开启时不注入工具,提示词也不提画图。

控制台:密钥控制、版本号与对齐

  • 密钥控制补齐到 15 个密钥/令牌的「显示/隐藏」(此前生图 Key 只有单向「显示」,语音回复、语音转文字、OneBot 的 WS/HTTP 令牌完全没有控件)。明文只走各自的受守卫端点;「显示」取回明文之后才切换按钮状态,失败保持原样并给出原因;OneBot 令牌的"保持不变"是留空,不是掩码。
  • 设置页对齐修掉两个根因:同一行的两个字段内容高度不同时,矮的那格被整块压到底部(实测差 18px);复选框行的标签被字段标题样式覆盖(方框与首行文字偏移)。修完后 46 个勾选/单选行的偏差实测为 0。
  • 侧栏与部署卡片显示当前版本号。

架构与运维加固

  • UI 拆分与 ESM 化:ui/app.js 13130 → 1019 行,ui/ 全量转 ES module(249 条共享全局降到 0),模块清单由用例双向校验。
  • 静态资源真 304 + 内容哈希长缓存:线上实测证伪了"缓存问题已经解决"的旧结论——此前只写 ETag、从不判 If-None-Match,每开一次控制台就把整套脚本重下一遍。
  • 可观测性与限额:审计日志(落盘 0600)、结构化日志与请求 trace、每日预算、配额双闸、记忆可见性页。
  • 部署与更新链路:路径嵌套双向拒绝且判定前归一化、临时目录进入退出清理、抢锁走互斥且陈旧锁接管有宽限与二次判定、自动更新锁的同类加固、巡检探测加超时、备份单元的路径与服务名正确转义、控制台事件流加背压与心跳。

三轮审查修复(择要)

  • deploy.sh 陈旧锁的接管判据方向写反:进程被强杀之后,无人值守的自动更新会永久卡死,而刚建立的并发锁反被抢走——已修,并补上"重判 → 删锁 → 重建"的顺序断言。
  • 每日动态的群聊材料、抓回正文与模型整理的交接文本统一过清洗:伪造段头经模型写进交接后会绕过前一处修复,径直进入提示词。
  • release.yml 里一行只有 name: 的孤儿步骤会让 GitHub 判定整份工作流非法——打 tag 时 Release 出不来;已删,并新增用例逐步骤校验所有工作流文件。
  • 用量页的搜索副行在"只有总数、没有工具明细"时不再断言成"只抓了网页";两张 token 卡的主数字与副行补上断言。
  • ops audit 的"关键补丁标记"里有一条 CJS 时代的锚点,对上 ES module 的写法永远不命中,每次体检都报一条假故障;已修,并给整张标记表加了用例。

NapCat 兼容(Issue #22)

  • 现象:运行日志里反复出现"补进 N 条(重启/断线期间漏掉的)"且计数稳定增长;协议端没有任何报错;断开事件在日志里完全不可见。
  • 根因:NapCat 的 OneBot WebSocket 服务端不回应 PING,而是直接销毁连接(客户端看到 close code=1006、永远收不到 pong)。客户端每 30 秒发一次 PING,连接生命周期因此被压到约 30 秒,每轮留下 3~4 秒接收盲窗。
  • 改动:心跳策略可配 onebot.wsHeartbeat(默认 auto:首次遇到"发 PING 就被断开"的对端后不再发送,改由断开事件发现异常;会回 pong 的协议端行为不变);断开与重连都记录日志;补课日志把"超过回复窗口、只补记录"的条数写出来,该窗口可配(onebot.catchupReplyWindowMs,默认 30 分钟)。控制台「设置 → OneBot」有这两个控件。
  • 升级影响:会回 pong 的协议端(如 SnowLuma)行为与旧版一致;NapCat 用户在默认配置下自愈(启动后最多断一次,之后不再发送 PING)。

升级影响

  • 所有默认值保持不变,新增配置键随默认值自动补齐,老配置无需手改。
  • 控制台新增:图片生成页(默认关闭)、「设置 → OneBot」的心跳与补课控件,以及若干密钥开关。

v0.7.5

Choose a tag to compare

@github-actions github-actions released this 29 Sep 08:39

本次改动

这一版加入三款群游戏(数字炸弹 / 谁是卧底 / 狼人杀),语音回复扩到四家服务商(含豆包语音合成 2.0),并补上定时提醒的控制台页与群日报;另外修掉一批由三轮对抗性审查发现的边界问题。

群游戏:数字炸弹 / 谁是卧底 / 狼人杀

  • 背景:群游戏需要同时满足"隐藏信息、轮次推进、超时兜底",而主持模型不能看到牌面(身份、词与查验结果)。真人群里还有一批模拟环境覆盖不到的情况:未参加的人在群里插话、只潜水不发言、中途退出、夜间不交行动、白天无人先开口。
  • 开局方式:需要私聊的游戏先挂一段报名(默认 45 秒,想玩的在群里回一句「我玩」),到点人数不足即取消 —— 不再把仅在群里插话的围观者拉进局,也不给他们发身份私聊。
  • 真相与话术分离:身份、词与查验结果只走私聊;模型只拿到公开摘要(存活名单、天数、公开死讯),结构上无法说出身份。引擎发给玩家的私聊在落库时即标记为 game-secret,提示词历史、翻页工具与消息详情等所有出口统一过滤。
  • 真人群的边界:白天以三个出口中先到者为准(固定讨论时长 / 超过半数存活玩家说「投吧」/ 全员发言完毕);夜间行动窗口 90 秒,到点按已收到的结算;出局者在群里的发言与投票一律不计入判定(并在出局时私聊本人说明);同一玩家反复私聊刷行动时,按"每人每夜最多 4 条回执"限制。
  • 游戏期间私聊豁免:引擎发给本局在册玩家的私聊不受 allow.private 白名单限制(deny 仍优先、仅在本局进行中生效、失败不重试、内容全部为引擎文本)。狼人杀整局依赖私聊,没有这项豁免就只有"全员加入白名单"才可进行;模型自身发送的消息仍受白名单约束。
  • 狼人杀规则:6~9 人(6 人局为 2 狼 / 1 预言家 / 1 女巫 / 2 民,7 人起加入守卫,9 人为 3 狼)。女巫持有解药与毒药各一次,一晚最多使用一瓶,不能自救;守卫与女巫同时保护同一人时该人仍然出局(同守同救必死);狼刀确定之后才私聊询问女巫(提示中写明被刀者),此后狼方改刀会被拒绝。
  • 开启方式:控制台 → 设置 → 实验功能 → 群游戏:勾选允许的游戏、报名时长、白天讨论时长、每局人数上限、结算是否公开身份。默认关闭。

语音回复扩到四家服务商(含豆包语音合成 2.0)

  • 背景:此前只支持 OpenAI 兼容一家。火山 v1 的鉴权(Bearer;<token>)与豆包 2.0 的 v3 流式(X-Api-Key / X-Api-Resource-Id、NDJSON 分片内 base64)形态完全不同,写死一种在其余服务上必然失败。
  • 改动:新增 OpenAI 兼容、火山 v1、豆包 2.0、MiniMax 四家适配;控制台按服务商预设填写地址、Key、模型与音色(豆包内置官方 2.0 音色清单 102 条,按控制台分类分组),并支持试听。
  • 附带修复:语音与图片类发送的超时由 15 秒放宽到 60 秒 —— 实测一条 7 秒语音协议端需要 16.4 秒,原超时会把已发出的语音记为"结果未知"。

定时提醒与群日报

  • 现象:提醒此前只存在于内存,重启即丢;多条同时到点会叠加派发;派发前未做预检,会把尚未提醒的记录标记为"已提醒"。
  • 改动:改为落盘持久化、同一会话多条合并、派发前预检;控制台新增"定时提醒"页(启用开关、待触发与最近完成列表、单条取消),该开关同时门控 remind 工具与到期派发。群日报把最近 24 小时的群聊汇总发送到指定群,发送时间格式做了校验与兜底(写错不再静默不跑)。

审查修复

  • 引擎私聊的隔离此前只做了一半:game-secret 标记只打在 ingest 回显侧,而存储按 (chat_key, mid) 幂等、命中重复不回填,标记因此恒不生效 —— 模型在私聊里能看到自己的身份与查验结果。现改为发送侧落库时即标记,按 id 单查、列出可见 id 等出口一并过滤。
  • 可无限刷新的三条路径:出局者反复发「不玩了」可绕过"每人每夜 4 条"限制(实测 20 条得到 20 条回执);投票阶段反复「投自己」每次都回一条群消息;数字炸弹的越界提示可无限刷新。三处分别按配额或"每人每阶段一次"限制。
  • 重启会重复播报:tick 推进的阶段不落盘(此前只在 phase === 'ended' 时保存),任何一次重启都会回滚一个阶段,重发"天亮了"与夜行动提示。现改为发送成功后落盘,并把存盘改为 tmp/rename 原子写。
  • 退出的守卫仍然挡刀:pending.guard 保存的是目标,作废时却与提交者的 uid 比较,只有"守自己"才会命中 —— 已出局玩家的行动会改变存活结果。现按角色清理。
  • 其余:同一个人在两个群各一局时,一条私聊被两局各执行一次;狼在刀口锁定前减员导致刀口永不锁定、女巫整夜收不到询问;名单未去重(模型把同一人写两遍,该玩家会拿到多张身份);roundSeconds 对狼人杀是死配置、recruitSeconds 写 null/空串的语义与文档不一致;名单报错现在区分"对不上"与"被人数上限截断"。
  • 升级影响:群游戏默认关闭,不开启则不影响任何既有行为;语音与提醒的默认行为不变。

安装与部署见 docs/OPS.md,逐条改动见 docs/CHANGES.md。