dsh-deepmoyu 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  ## 维护本机链接安装
6
6
 
7
- 本机运行中的安装可能通过 `file:` / `link:` 或 junction 指向本仓库,并包含未提交改动。**不要对当前本地分叉执行 `dsh plugin add` 或用 registry 更新覆盖源码。** 普通外部用户按 README 正常安装不受此约束。本项目不提供自动更新入口;不要恢复 `/upgrade`、`/update` 或自动重装逻辑。
7
+ 本机运行中的安装可能通过 `file:` / `link:` 或 junction 指向本仓库,并包含未提交改动。**不要对当前本地分叉执行 `dsh plugin add` 或用 registry 更新覆盖源码。** 普通外部用户按 README 正常安装不受此约束。
8
+
9
+ 更新通道(2026-10-30 使用者要求恢复):`GET /api/bili-duty/update` 查 registry 上的最新版,`POST /api/bili-duty/upgrade {confirm:true}` 由设置页按钮触发。它**不是**原来那个裸的重装按钮,`lib/update.mjs` 里三道闸一个都不能删:① 只有 profile 下真实目录(registry 安装)才 `canUpgrade=true`,目录联接/`.git` 源码检出/不在 profile 里的安装一律锁定并给手动办法;② 有刷站、后台、渲染在跑时拒绝;③ 单飞,同一时间只允许一个升级进程。改这一节前先读文件头那段 m17605 的事故注释。
8
10
 
9
11
  不要擅自重启用户正在使用的 DSH、修改真实 profile / 模型凭证 / 全局权限,或向平台发送真实评论、私信或动态。隔离测试使用独立临时目录、受控服务与合成数据。修改 JS 后需要完全重启 DSH 才能生效,页面刷新与 patch 热重载不会清除模块缓存。
10
12
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # 更新记录
2
2
 
3
+ ## 2.6.0
4
+
5
+ 本节对应当前源码,不能据此推断 npm 或插件市场已更新。
6
+
7
+ - 表情收藏上限从 60 提到 **100**(`STICKER_MAX`、`stickerCapacity` 默认值与取值上限、工作台输入框同步)。
8
+ - 评论区自动收集新增**大模型判趣味**(`lib/sticker-judge.mjs`):收图前先让宿主自己的模型看这条评论的正文、发图人、点赞数和图片尺寸,给一个 0–100 的趣味分+一句使用场景+几个标签,分数低于 50 的不收。用的是后台模型那条线路,不额外要密钥、不自动换模型。
9
+ - 模型**看不到图片字节**,只看文字线索——提示词里把这一点写死了,免得模型编造画面细节。所以不会因为线路不支持多模态就失效。
10
+ - 本地词表先兜一层:二维码/扫码/加微信/进群/刷单,色情裸露,血腥自伤,身份证银行卡手机号住址——命中直接丢,不给模型再判一次的机会。**这一层与开关无关,永远生效。**
11
+ - 判图**一律 fail open**:没有线路、线路报错、超时、模型输出不可解析,都照老规矩收,只是这张没有分数。不能因为模型抽风把自动收集整个停掉。
12
+ - 关闭人工审核时,自动收集新增**淘汰机制**:库满之后不再直接放弃,而是比分数——新图分数**严格高于**库里最低分那张才顶掉它(同分不算更好,免得来回换);被顶掉的图片文件真删,`state.json` 其它字段不受影响。待审图片不参与淘汰(删了等于替人拒了);手工上传和人审开着的自动收集也不淘汰(机器不替人做决定)。
13
+ - 挑图不再纯随机:高赞、接近方形的更可能是梗图而不是随手截屏,按权重抽;真正的"够不够有趣"仍由判图决定。
14
+ - 收集窗口与图库列表带上分数与判定来源,工作台看到「收了一张表情(82 分),顶掉了「xxx」」这种实话。
15
+ - 新增开关「大模型判断值不值得收」(`stickerJudge`,默认开),关掉只跳过大模型,本地词表照旧。
16
+ - 新增 `tests/sticker-judge.test.mjs`:锁住词表拦截、模型回复宽容解析、fail open 三态、低分拒绝、淘汰只在"自动收集 + 关审核"时发生、同分不换、被判不合适的图根本不下载、判图炸了照旧收、关掉判图不叫模型但词表仍生效。
17
+
18
+ ## 2.5.0
19
+
20
+ 本节对应当前源码,不能据此推断 npm 或插件市场已更新。
21
+
22
+ - 修 `2026-11-02` 真机事故:自动发说说一直发不出去,日志里连拦截记录都没有。根因是「当日计数」的跨天归零在三处各写了一份(`bumpState`、私信记账、`bumpMomentBy`),前两处都漏了 `moments`,跨天之后 `state.json` 里 `day` 已经是今天而 `moments` 还留着昨天的 20,`bili_moment` 于是判定 `used >= cap` 并**静默**返回 `blocked`。现在归零名单只有一份(`lib/state.mjs` 的 `DAY_COUNTER_FIELDS`/`dayResetPatch`),并给 `moments` 加了 `momentsDay` 自证日期、无该字段时按 `lastMomentAt` 判断,历史脏状态也能自己恢复。
23
+ - 说说的每一种拦截(自动发说说未开、每日上限为 0、额度已满)现在都会写进 `duty.log`,不再有"什么都不发生"的黑洞。
24
+ - 新增 `bili_comment`:**直接发在视频下面的新评论**,参数里没有 `rpid`/`root`,模型想留言时不必再拐进"回复某人"的语义。同一套质量、判重、节奏、审核、风控闸;同一条视频仍只发一条新评论。
25
+ - `bili_reply` 收敛为纯楼层回复,描述里明确"想给这条视频留言、想让更多人看见 ⇒ 用 `bili_comment`"。工具清单 39 项,`docs/tool-evidence.json`、`TEST_MATRIX.md` 同步。
26
+ - 提示词改为默认在视频下发新评论:`lib/prompts.mjs` 的【在视频下面怎么说话】、`lib/persona.mjs` 的回复守则第 1 条、`prompts/duty.md` 的【想说才说】都点明"楼层回复只有对方看得见,那不是默认开口方式"。
27
+ - 降噪:表情收集区满 60 张的 `collect-skipped` 从"每几分钟一条"改为 6 小时只提示一次(其它跳过原因照旧逐条记录)。- 后台模型桥接:`RELAY_BUSY` 不再由元数据查询(`resolve`)抢占,只有流式请求才占连接,撞车时先等最多 30 秒再判;日志里 10-30 两次、11-01、11-02 的 learn-review 全部失败就是这里。
28
+ - 后台模型线路没注册时,报错会列出宿主**当前真正可用的线路**,而不是只说"没有注册"。
29
+ - 新增 `tests/moment-quota.test.mjs`:锁住跨天重置名单、陈数不拦今天的说说、同一天额度仍然生效、无 `momentsDay` 的历史状态靠 `lastMomentAt` 判定、以及 `bili_comment` 绝不写成楼层回复。
30
+
31
+ ## 2.4.2
32
+
33
+ 本节对应当前源码,不能据此推断 npm 或插件市场已更新。
34
+
35
+ - 恢复更新通道:账号与环境页可「检查更新」「升级到最新版」,查询 npm registry 的 dist-tags(默认走 npmmirror,可改成官方源)。
36
+ - 升级带三道闸:只有 profile 下真实目录(registry 安装)才允许一键升级;目录联接、源码检出、不在 profile 里的安装一律锁定并说明手动办法;刷站/后台/渲染运行中拒绝;同一时间只允许一个升级进程。
37
+ - 修复升级失败回执被检查结果覆盖成成功的问题(返回体合并顺序)。
38
+ - 新增 `lib/update.mjs` 与其单元/路由回归:版本比较、packument 解析、安装方式识别、失败原因翻译、日志脱敏。
39
+
3
40
  ## 2.4.1(待发布)
4
41
 
5
42
  本节对应当前源码,不能据此推断 npm 或插件市场已更新。
package/README.md CHANGED
@@ -4,7 +4,9 @@ DeepMoYu 是一个 DSH(DeepSeek Harness)插件:**让一只鲸鱼娘自己
4
4
 
5
5
  一句话概括:**把 DeepSeek 这类模型接进 B站当一个真正的up主级用户**——自主浏览、自主互动、有自己的口味和记性,而不是一个只会念摘要的机器人。
6
6
 
7
- 本项目基于 `dsh-bili-scroll` 0.1.19(MIT)开发,保留原作者和 vendored 代码署名,不包含自动更新入口。当前源码版本为 **2.4.1**;源码版本、npm 发布和市场收录分别管理,详见 [更新记录](CHANGELOG.md) 与 [发布说明](docs/RELEASING.md)。
7
+ 本项目基于 `dsh-bili-scroll` 0.1.19(MIT)开发,保留原作者和 vendored 代码署名。当前源码版本为 **2.4.2**;源码版本、npm 发布和市场收录分别管理,详见 [更新记录](CHANGELOG.md) 与 [发布说明](docs/RELEASING.md)。
8
+
9
+ **更新**:点鲸鱼 → 设置 → 账号与环境 → 「检查更新」。registry 安装(从 npm 或插件市场装的)可以直接点「升级到最新版」,升级后完全重启 DSH 生效。目录联接/源码检出这类本地安装不会被自动覆盖——按钮会锁定并说明手动升级办法,因为运行数据在插件家目录,源码改动应当由你自己用 git 管理。
8
10
 
9
11
  ## 她会自己做什么
10
12
 
@@ -155,7 +157,7 @@ npm run release:build
155
157
  npm run release:smoke
156
158
  ```
157
159
 
158
- 构建生成 `release/dsh-deepmoyu-2.4.1.tgz`、源码 ZIP、归档验证报告与 `SHA256SUMS`。构建会验证实际文件内容,不包含运行凭证、日志、缓存或 node_modules;源码包包含测试与发布文档。发布前先完成全部验收,见 [发布指南](docs/RELEASING.md)。
160
+ 构建生成 `release/dsh-deepmoyu-2.4.2.tgz`、源码 ZIP、归档验证报告与 `SHA256SUMS`。构建会验证实际文件内容,不包含运行凭证、日志、缓存或 node_modules;源码包包含测试与发布文档。发布前先完成全部验收,见 [发布指南](docs/RELEASING.md)。
159
161
 
160
162
  插件市场需要单独提交目录条目,npm 发布不会自动收录。可提交文件为 [marketplace/pyyxzi__dsh-deepmoyu.yml](marketplace/pyyxzi__dsh-deepmoyu.yml),截图由仓库根的 `screenshots.json` 声明。外部仓库主题、条目审核、账号权限和 npm 认证需在发布时核对。
161
163
 
package/client.js CHANGED
@@ -2150,6 +2150,62 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
2150
2150
  !reviewStatus.running && Number(reviewStatus.nextRetryAt) > 0 ? h('div', { 'data-bd-note': '' }, '自动重试最早:' + formatTime(reviewStatus.nextRetryAt) + (reviewStatus.enabled ? '' : '(自动审核未开启)')) : null,
2151
2151
  h('div', { 'data-bd-note': '' }, reviewStatus.lastFinishedAt ? '上次结束:' + formatTime(reviewStatus.lastFinishedAt) : '暂无审核记录。启用自动审核后,将在下次检查时处理待审队列。'))
2152
2152
 
2153
+ /* 🔄 更新通道(2026-10-30 使用者「加一个更新通道,方便及时更新」)
2154
+ * 只在这里查/升级;普通鱼工具拿不到升级能力(换版本要重启宿主,不能让会话模型自己决定)。
2155
+ * 状态只在展开这一卡时按需拉取,不并进 /status 轮询。 */
2156
+ const [updateState, setUpdateState] = React.useState(null)
2157
+ const [updateError, setUpdateError] = React.useState('')
2158
+ const [updateBusy, setUpdateBusy] = React.useState('')
2159
+ const updateAlive = React.useRef(true)
2160
+ React.useEffect(() => () => { updateAlive.current = false }, [])
2161
+ const pullUpdate = async (force = false) => {
2162
+ try {
2163
+ const r = await jget('/update' + (force ? '?force=1' : ''))
2164
+ if (!updateAlive.current) return
2165
+ if (!r || r.ok === false && !r.current) throw new Error((r && r.error) || '检查更新失败')
2166
+ setUpdateState(r)
2167
+ setUpdateError(r.error || '')
2168
+ } catch (e) { if (updateAlive.current) setUpdateError(String(e && e.message || e)) }
2169
+ }
2170
+ React.useEffect(() => { pullUpdate() }, [])
2171
+ const updateCard = (() => {
2172
+ const u = updateState || {}
2173
+ const install = u.install || {}
2174
+ const checking = updateBusy === 'check'
2175
+ const upgrading = u.upgrading || updateBusy === 'upgrade'
2176
+ const canUpgrade = Boolean(u.canUpgrade) && !u.blocker && !upgrading
2177
+ const badges = []
2178
+ if (u.current) badges.push(h('span', { key: 'cur', 'data-bd-badge': '' }, 'v' + u.current))
2179
+ if (u.latest && u.latest !== u.current) badges.push(h('span', { key: 'latest', 'data-bd-badge': '', 'data-live': '' }, '最新 v' + u.latest))
2180
+ if (install.mode) badges.push(h('span', { key: 'mode', 'data-bd-badge': '' }, { registry: 'registry 安装', link: '目录联接', source: '源码检出', unknown: '未知安装' }[install.mode] || install.mode))
2181
+ return h('section', { 'data-bd-sec': '', 'data-bd-span': '2', 'data-bd-update': '' },
2182
+ sectionHead('pulse', '检查更新'),
2183
+ h('div', { 'data-bd-row': '' }, ...badges),
2184
+ h('div', { 'data-bd-note': '' }, u.ok === false
2185
+ ? '暂时查不到最新版本:' + (u.error || updateError || '稍后再试')
2186
+ : (u.hint || '点「检查更新」问一下 registry 上有没有新版本。')),
2187
+ u.upgrading ? h('div', { 'data-bd-msg': '', role: 'status' }, '正在安装新版本…完成后需要完全重启 DSH。') : null,
2188
+ u.blocker ? h('div', { 'data-bd-note': '' }, '升级暂不可用:' + u.blocker) : null,
2189
+ h('div', { 'data-bd-row': '' },
2190
+ h('button', { 'data-bd-btn': '', disabled: Boolean(busy) || checking || upgrading, onClick: async () => {
2191
+ setUpdateBusy('check')
2192
+ try { await pullUpdate(true); safeSay('已检查更新。', 'good') }
2193
+ catch (e) { safeSay(String(e && e.message || e), 'warn') }
2194
+ finally { if (updateAlive.current) setUpdateBusy('') }
2195
+ } }, checking ? '正在检查…' : '检查更新'),
2196
+ h('button', { 'data-bd-btn': '', 'data-primary': '', disabled: Boolean(busy) || !canUpgrade, title: !canUpgrade && install.reason ? install.reason : '从 registry 安装新版本(会重启宿主后生效)', onClick: async () => {
2197
+ if (typeof window.confirm === 'function' && !window.confirm('从 registry 安装 ' + (u.latest || '新版本') + '?\n\n升级完成后需要完全重启 DSH 才生效。运行数据(凭证、日报、图库、账本)都在家目录,不受影响。')) return
2198
+ setUpdateBusy('upgrade')
2199
+ try {
2200
+ const r = check(await jpost('/upgrade', { confirm: true }), '升级失败')
2201
+ if (r.ok === false) { safeSay(r.error || '升级没有开始', 'warn'); if (r.install) setUpdateState(r) }
2202
+ else { safeSay('已开始升级到 ' + (r.to || '新版本') + ',完成后完全重启 DSH。', 'good'); setUpdateState((old) => ({ ...(old || {}), upgrading: true })) }
2203
+ } catch (e) { safeSay(String(e && e.message || e), 'warn') }
2204
+ finally { if (updateAlive.current) setUpdateBusy('') }
2205
+ } }, upgrading ? '正在升级…' : '升级到最新版')),
2206
+ h('div', { 'data-bd-note': '' }, '只对「registry 安装」开放一键升级。本机分叉(目录联接)、源码检出或不在 profile 里的安装会锁定按钮并说明手动升级办法——2026-10-21 的自动升级曾把本机分叉整包换掉。'))
2207
+ })()
2208
+
2153
2209
  const mirror = (st && st.mirror) || {}
2154
2210
  const mirrorCard = h('section', { 'data-bd-sec': '' },
2155
2211
  sectionHead('dots', '记录镜像'),
@@ -2228,7 +2284,7 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
2228
2284
  h('div', { 'data-bd-pghead': '' }, h('div', {}, h('b', {}, view === 'learning' ? '学习与记忆' : '账号与环境'),
2229
2285
  h('p', {}, view === 'learning' ? '管理学习审核、角色记忆、口味统计和历史记录。' : '管理账号登录、运行依赖和本地数据。'))),
2230
2286
  h('div', { 'data-bd-pet-grid': '', 'data-bd-other': '' },
2231
- ...(view === 'learning' ? [memoryOverviewCard, memoryExplorerCard, reviewCard, learnCard, soulCard] : [accountCard, asrCard, mirrorCard, groundCard])))
2287
+ ...(view === 'learning' ? [memoryOverviewCard, memoryExplorerCard, reviewCard, learnCard, soulCard] : [accountCard, updateCard, asrCard, mirrorCard, groundCard])))
2232
2288
  }
2233
2289
 
2234
2290
 
@@ -2356,7 +2412,7 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
2356
2412
  const reading = React.useRef(false)
2357
2413
  const writing = React.useRef(false)
2358
2414
  const [loading, setLoading] = React.useState(false)
2359
- const defaults = { stickerCollectProbability: 35, stickerCollectCooldown: 60, stickerSendProbability: 30, stickerSendCooldown: 180, stickerCapacity: 60 }
2415
+ const defaults = { stickerCollectProbability: 35, stickerCollectCooldown: 60, stickerSendProbability: 30, stickerSendCooldown: 180, stickerCapacity: 100 }
2360
2416
  const field = (key) => draft[key] ?? cfg[key] ?? data?.settings?.[key] ?? defaults[key]
2361
2417
  const pull = async () => {
2362
2418
  if (reading.current || writing.current) return
@@ -2389,7 +2445,7 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
2389
2445
  const save = async (kind) => {
2390
2446
  try {
2391
2447
  const p = kind === 'collect' ? {
2392
- stickerCollectProbability: integer('stickerCollectProbability', 0, 100), stickerCollectCooldown: integer('stickerCollectCooldown', 0, 86400), stickerCapacity: integer('stickerCapacity', 1, 60),
2448
+ stickerCollectProbability: integer('stickerCollectProbability', 0, 100), stickerCollectCooldown: integer('stickerCollectCooldown', 0, 86400), stickerCapacity: integer('stickerCapacity', 1, 100),
2393
2449
  } : { stickerSendProbability: integer('stickerSendProbability', 0, 100), stickerSendCooldown: integer('stickerSendCooldown', 0, 86400) }
2394
2450
  if (await patch(p, kind === 'collect' ? '自动收集参数' : '自动配图参数')) setDraft((old) => Object.fromEntries(Object.entries(old).filter(([key]) => !(key in p))))
2395
2451
  } catch (e) { say(String(e?.message ?? e), 'warn') }
@@ -2488,9 +2544,10 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
2488
2544
  h('div', { 'data-bd-pghead': '' }, h('div', {}, h('b', {}, '表情收藏'), h('p', {}, '管理表情收集、上传、审核和评论配图。'))),
2489
2545
  h('div', { 'data-bd-pet-grid': '', 'data-bd-sticker-controls': '' },
2490
2546
  h('section', { 'data-bd-sec': '' }, h('div', { 'data-bd-h': '' }, h('b', {}, '表情收集')),
2491
- ToggleRow({ label: '从评论区自动收集', on: Boolean(cfg.stickerAutoCollect), disabled, note: '读取评论时,按设定概率收集附图。相同图片不重复收集,达到容量后停止。', onChange: (v) => patch({ stickerAutoCollect: v }, '自动收集表情') }),
2547
+ ToggleRow({ label: '从评论区自动收集', on: Boolean(cfg.stickerAutoCollect), disabled, note: '读取评论时,按设定概率收集附图。相同图片不重复收集;收满后,关掉人工审核时新图分数更高会顶掉最低分的那张。', onChange: (v) => patch({ stickerAutoCollect: v }, '自动收集表情') }),
2548
+ ToggleRow({ label: '大模型判断值不值得收', on: cfg.stickerJudge !== false, disabled, note: '收集前先让模型看这条评论的文字、发图人、点赞和尺寸,判「当表情值不值得收」并打分。广告、二维码、隐私这类本地词表直接拦下。模型没给结论时照旧收,只是没有分数。', onChange: (v) => patch({ stickerJudge: v }, '表情趣味判断') }),
2492
2549
  ToggleRow({ label: '新表情人工审核', on: Boolean(cfg.stickerReview), disabled, note: '新收集和上传的图片经批准后方可配图。关闭仅影响后续新增图片,已有待审图片仍需审核。', onChange: (v) => patch({ stickerReview: v }, '表情人工审核') }),
2493
- input('收集概率', 'stickerCollectProbability', 100, '%'), input('收集冷却(秒)', 'stickerCollectCooldown', 86400, '秒'), input('收集容量', 'stickerCapacity', 60, '张', 1),
2550
+ input('收集概率', 'stickerCollectProbability', 100, '%'), input('收集冷却(秒)', 'stickerCollectCooldown', 86400, '秒'), input('收集容量', 'stickerCapacity', 100, '张', 1),
2494
2551
  h('button', { 'data-bd-btn': '', disabled, onClick: () => save('collect') }, '保存收集参数')),
2495
2552
  h('section', { 'data-bd-sec': '' }, h('div', { 'data-bd-h': '' }, h('b', {}, '评论配图')),
2496
2553
  ToggleRow({ label: '新评论自动配图', on: Boolean(cfg.stickerAutoSend), disabled, note: '评论权限已开启时,从可用图库选择配图。仅适用于支持图片的新评论。', onChange: (v) => patch({ stickerAutoSend: v }, '自动配图') }),
@@ -24,15 +24,15 @@ dsh plugin --profile web add dsh-deepmoyu
24
24
  下载维护者发布的 `.tgz` 和 `SHA256SUMS`,核对 SHA-256。PowerShell 可使用:
25
25
 
26
26
  ```powershell
27
- Get-FileHash -Algorithm SHA256 .\dsh-deepmoyu-2.4.1.tgz
28
- dsh plugin --profile web add "C:\Downloads\dsh-deepmoyu-2.4.1.tgz"
27
+ Get-FileHash -Algorithm SHA256 .\dsh-deepmoyu-2.4.2.tgz
28
+ dsh plugin --profile web add "C:\Downloads\dsh-deepmoyu-2.4.2.tgz"
29
29
  ```
30
30
 
31
31
  Linux/macOS 可使用:
32
32
 
33
33
  ```sh
34
- shasum -a 256 dsh-deepmoyu-2.4.1.tgz
35
- dsh plugin --profile web add "/absolute/path/dsh-deepmoyu-2.4.1.tgz"
34
+ shasum -a 256 dsh-deepmoyu-2.4.2.tgz
35
+ dsh plugin --profile web add "/absolute/path/dsh-deepmoyu-2.4.2.tgz"
36
36
  ```
37
37
 
38
38
  离线只有插件 tarball 仍不一定足够:首次安装需要解析其 npm 依赖,主 Python 工具链还需下载依赖。完全离线部署需预先准备对应平台的依赖缓存;项目不宣称一个 tarball 即覆盖全部离线依赖。
@@ -1,6 +1,6 @@
1
1
  # 发布审查记录
2
2
 
3
- 审查日期:2026-10-29。版本:2.4.1。结论:本轮发现的插件逻辑缺陷已修复,已完成下列功能、分发与安装验证。自动化测试和依赖审计不能证明不存在未知缺陷;外部服务、系统及模型边界单独列出。
3
+ 审查日期:2026-10-29。版本:2.4.2。结论:本轮发现的插件逻辑缺陷已修复,已完成下列功能、分发与安装验证。自动化测试和依赖审计不能证明不存在未知缺陷;外部服务、系统及模型边界单独列出。
4
4
 
5
5
  ## 环境与证据类型
6
6
 
@@ -25,9 +25,9 @@
25
25
  | 实际视频制作 | Pillow 13 环境完成封面→配音→1920×1080/30fps渲染→媒体校验→持久成片,见下节 |
26
26
  | uv 下载 | 当前实现实际从官方来源获取校验值,流式下载/校验/解压/执行版本检查,得到 uv 0.13.6;另有15项隔离回归 |
27
27
  | 主依赖审计 | 官方 npm audit:0 已知漏洞;pip-audit 2.10.1 对实际主 Python 环境:0 已知漏洞 |
28
- | `release:check/build/smoke` | bundle、文档链接、38工具证据、素材授权摘要、截图、凭证模式、归档内容与清洁 tarball 安装通过 |
28
+ | `release:check/build/smoke` | bundle、文档链接、39工具证据、素材授权摘要、截图、凭证模式、归档内容与清洁 tarball 安装通过 |
29
29
 
30
- 全工具逐项证据见 [功能矩阵](TEST_MATRIX.md) 与 `tool-evidence.json`。387 项 Node 测试包含正向的全部 38 个工具、模式切出后的 76 次旧句柄拒绝、长 ID 原始 MCP 传输和各深层专项,不用无效参数拒绝来代替成功路径覆盖。
30
+ 全工具逐项证据见 [功能矩阵](TEST_MATRIX.md) 与 `tool-evidence.json`。421 项 Node 测试包含正向的全部 39 个工具、模式切出后的 78 次旧句柄拒绝、长 ID 原始 MCP 传输和各深层专项,不用无效参数拒绝来代替成功路径覆盖。
31
31
 
32
32
  ## 发现与修复
33
33
 
@@ -63,8 +63,8 @@
63
63
 
64
64
  ## 发布状态与复跑
65
65
 
66
- 当前版本是本地待发布2.4.1。最终安装包与完整源码ZIP位于 `release/`,有逐文件验证报告和SHA-256;运行manifest删除开发命令是明确的发布投影,其他运行文件与源码逐字节核对。源码ZIP包含当前未提交的新文件,不混入历史工作区的旧包。
66
+ 当前版本是本地待发布2.4.2。最终安装包与完整源码ZIP位于 `release/`,有逐文件验证报告和SHA-256;运行manifest删除开发命令是明确的发布投影,其他运行文件与源码逐字节核对。源码ZIP包含当前未提交的新文件,不混入历史工作区的旧包。
67
67
 
68
68
  安装包清洁安装使用临时家目录与生产依赖,执行包内 `check` 自检。依赖代码、媒体和界面文件调整后需重新构建归档。维护者可以在源码根运行README/CONTRIBUTING列出的全部命令;输出可由 `QA_OUTPUT_DIR`、`RELEASE_TMPDIR` 和 `DSH_QA_TMPDIR` 指定位置。
69
69
 
70
- 市场所需条目、截图及客观描述已准备。GitHub仓库创建满一天的最早时间为 **2026-10-29 13:18:16(北京时间)**;此前外部年龄门禁不会放行。npm发布、GitHub上传与市场维护者审核尚未执行,不能由本地打包通过推断已发布或已收录,详见 [发布指南](RELEASING.md)。
70
+ 市场所需条目、截图及客观描述已准备。GitHub仓库创建满一天的最早时间为 **2026-10-29 13:18:16(北京时间)**;此前外部年龄门禁不会放行。npm发布、GitHub上传与市场维护者审核尚未执行,不能由本地打包通过推断已发布或已收录,详见 [发布指南](RELEASING.md)。
@@ -1,6 +1,6 @@
1
1
  # 功能与验收矩阵
2
2
 
3
- 本表对应 2.4.1。完整结果和环境边界见 [发布审查记录](RELEASE_AUDIT.md)。测试中的“发送成功”是受控 MCP 的包装器契约,不代表向真实账号发送过内容。
3
+ 本表对应 2.4.2。完整结果和环境边界见 [发布审查记录](RELEASE_AUDIT.md)。测试中的“发送成功”是受控 MCP 的包装器契约,不代表向真实账号发送过内容。
4
4
 
5
5
  ## 全部角色工具
6
6
 
@@ -35,6 +35,7 @@
35
35
  | `bili_comments` | 评论数量、正文、长评论/作者标识 |
36
36
  | `bili_sticker` | 图库列表;上传/审核另有专项 |
37
37
  | `bili_reply` | 评论包装器、纯文字与 BV 发送 |
38
+ | `bili_comment` | 直接发在视频下的新评论:无 rpid/root、同视频判重、不写楼层账本 |
38
39
  | `bili_status` | 登录、权限、长 UID 与运行状态 |
39
40
  | `bili_pace` | 指定通道的只读节奏状态 |
40
41
  | `bili_say` | 有效旁白记录及来源关联 |
@@ -62,7 +63,8 @@
62
63
  | 节奏、永久判重及发送复核 | `pace*.test.mjs`、`dup-cross-channel.test.mjs`、`reply.test.mjs`、`verify*.test.mjs`、`test_guard_cross_channel.py` |
63
64
  | 端到端长 ID | `mcp-ids.test.mjs`、`tool-surface.test.mjs`、`check-dm-uid-browser.cjs` |
64
65
  | 学习、记忆与隐私 | `memory-chain.test.mjs`、`memory-center*.test.mjs`、`background-output.test.mjs` |
65
- | 表情与图片 | `sticker*.test.mjs`、`test_reply_pictures.py`;包括格式、字节审核、公开地址、容量、取消和并发 |
66
+ | 表情与图片 | `sticker*.test.mjs`、`test_reply_pictures.py`;包括格式、字节审核、公开地址、容量、取消、并发、大模型判趣味与淘汰 |
67
+ | 表情判趣味与淘汰 | `sticker-judge.test.mjs`:本地词表拦截、模型回复宽容解析、fail open 三态、低分拒绝、只在自动收集且关审核时淘汰、同分不换、被判不合适的图不下载、判图故障不影响收集 |
66
68
  | 账本、回顾与陪伴 | `token-ledger.test.mjs`、`product-status*.test.mjs`、`companion*.test.mjs` |
67
69
  | 视频编排、终态与媒体 | `pv-*.test.mjs`、`test_pv_*.py`、实际配音/封面/渲染/校验全链 |
68
70
  | 可选 ASR 与收藏边车 | `test_optional_sidecars.py`:本地输入保留、独占清理、同视频并发、参数与失败;外部服务为 fake |
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.4.1",
2
+ "version": "2.6.0",
3
3
  "scope": "Isolated positive contracts, not real platform sends",
4
4
  "tools": [
5
5
  {
@@ -137,6 +137,11 @@
137
137
  "category": "send",
138
138
  "specialist": "send-review-routes.test.mjs"
139
139
  },
140
+ {
141
+ "tool": "bili_comment",
142
+ "category": "send",
143
+ "specialist": "send-review-routes.test.mjs"
144
+ },
140
145
  {
141
146
  "tool": "bili_status",
142
147
  "category": "local",