Skip to content

v0.2.0 — DSH 0.2.0-rc.2 适配(会话队列投影 / 工具守卫对账)与全量回归

Choose a tag to compare

@Derpyu520 Derpyu520 released this 02 Oct 13:39
· 6 commits to main since this release

release: v0.2.0 — DSH 0.2.0-rc.2 适配(会话队列投影 / 工具守卫对账)与全量回归

版本号与 DSH 对齐(0.2.0)。本次的核心是实测 DSH 0.2.0-rc.2 的线上契约、
找出真实断裂点并修掉,而不是照着 release notes 猜。

════════════════════════════════════════════════════════════════════
一、核心断裂:会话待处理队列换了存放位置(每一次退役会话都在失败)
════════════════════════════════════════════════════════════════════
session/control 的开场 baseline 在 0.1.7 里把待处理队列平铺在
value.queues[<sessionId>];0.2.0 统一并入投影表:

{ type:'baseline', value:{ projections:{ <sessionId>:{ asOfSeq, values } } } }
队列 = values.inbox = { 'next-turn': UserMessage[], 'next-step': UserMessage[] }

而 stopSessionWork(退役/重置 QQ 会话时先清队列、再取消当前 turn)认死 queues,
于是每一次退役都抛 invalid session/control baseline。后果不是「少清几条」:
队列清不掉 ⇒ 旧任务继续在 DSH 里跑、继续烧 token,而桥接日志里只有一行
「⚠️ 停止旧会话失败 …」。这条缺陷此前没有任何测试覆盖。

修复(src/dsh-client.js):

  • 主路径改为一元 RPC session/projections(0.2.0 起可用,非激活读取,不开 WebSocket、
    可取消、可超时、可单测),返回 { asOfSeq, values } | null。
  • 回退路径保留 session/control,但按 0.2.0 的形状解析 projections[<id>].values.inbox。
  • 解析逻辑抽成具名导出 inboxItemsOfProjections / inboxItemIds /
    controlBaselineInboxItems,由测试直接钉住;结构变了显式抛错,
    绝不静默当成空队列(静默正是这个缺陷的形态)。

新增回归:

  • scripts/test-dsh-inbox-projection.mjs(12 项,离线,已进 test:audit)——
    用从真实 0.2.0 采样下来的帧骨架钉住层级与键名,并断言旧的 queues 形状不再被接受。
  • scripts/test-dsh-session-retire.mjs(11 项,需活 DSH)——端到端:真实会话上
    stopSessionWork 必须返回且真的把排队消息清掉(实测 removed:1)。
  • test-audit-protocol.mjs 的 3 条会话退役用例按两条路径重写(含 0.2.0 形状断言)。

════════════════════════════════════════════════════════════════════
二、QQ 安全守卫按 0.2.0 真实工具清单重新对账(L7 边界)
════════════════════════════════════════════════════════════════════
tools.restrict() 对不存在的工具名会抛错,而守卫逐个 try/catch ⇒ 拼错/过期的名字
= 静默 no-op;反过来,新版本新增的能力漏进名单 = 该隐藏的工具留在仿真会话
schema 里。两类漂移都只能靠拿安装包重新对账发现。

新增 scripts/scan-dsh-tool-names.mjs + scripts/dsh-tool-names.mjs(npm run scan:tool-names):
从已安装的 @deepseek-ai/dsh-* 与用户插件里抽真实工具名,与守卫名单双向求差。
抽取实现只有一份,扫描器与 test-preset-local-tools.mjs 共用 —— 测试原先自带一份
模糊抽取器(全文件乱扫 + 只认少数包名前缀 + 只读包根 patch),0.2.0 对账时把
10 个真实存在的工具名误报成「假名」,方向危险:它会逼人把真名字删掉。

0.2.0 对账结论:旧名字全部还在(0 假名),新增 15 个必须隐藏的:

  • list_mcp_resources / list_mcp_resource_templates / read_mcp_resource
    —— dsh-base 的 mcp-resources 行默认启用,每个 QQ 会话都继承。
    read_mcp_resource 的 server 参数可指向任意已配置的 MCP server:用户一旦挂了
    filesystem 之类的能力型 MCP,QQ 群就通过它拿到了文件读取权 —— 正是 L7 要挡的越权路径。
  • subagent_codex / subagent_claude_code(外部编码 agent,等于完整执行能力)、
    spawn_teammate / team_task_{create,get,list,update}(实验性 Team 模式)
  • plugin_manager(装卸插件,内部 spawn pnpm ⇒ 任意代码执行)
  • schedule_{create,list,update,delete}(无人值守的自我调度)

守卫名单 52 → 67。verify:adaptation 与 test-preset-local-tools 都加了对应断言。

════════════════════════════════════════════════════════════════════
三、qq-mode-console:清理 0.2.0 已删除 API 留下的死路
════════════════════════════════════════════════════════════════════
DSH 0.2.0 删除了 ctx.settings.register()(公开面只剩 configure/describe/update/
replace/mutate),命名空间改为从 profile 的 loader entry 推导。插件原先的
apply() 会先撞 typeof settings.register !== 'function' 早退,看起来一切正常 ——
所以只有静态断言能防止有人照旧注释把它加回来。

实测确认(0.2.0-rc.2):qq-mode 仍在 settings/describe 里、仍可写。三点契约
(entry id == 命名空间名 / 导出 Config / 至少一个 volatile 字段)依然成立且是唯一路径;
dsh/agent-presets/*/qq-tool-restrict.mjs 的 re-export 壳与 bundle patch 机制均兼容。

  • apply() 改为显式空实现 + 诊断日志(不再调用已删除的 API,也不调 configure())。
  • test-qq-mode-plugin.mjs 重写为 6 项:断言三点契约齐备(含 schemastery 引用表形状的
    volatile 检测)、schema 接受全部 mode 拼写、以及不再出现 settings.register。
  • 顺带修正 schemastery toJSON() 的解析:顶层是 {uid, refs},dict 在 refs 里而非顶层。

════════════════════════════════════════════════════════════════════
四、探针与测试夹具:把 DSH 0.1.7 起就已失效的鉴权路径修好
════════════════════════════════════════════════════════════════════
六个探针 + 四个运维脚本各自复制了一份「从 ~/.dsh/guard/logs/server-*.out.log 抓
?token= 再换 Cookie」的引导。那条路在 0.1.7 起就死了(启动 token 只存在内存;
日志里是上一个进程的陈旧值),于是一律 401 —— 诊断工具本身坏了,而没人会注意
(探针不在回归套件里)。桥接生产代码当时修好了,工具没跟上。

  • 新增 scripts/probe-auth.mjs:统一走本机签名密钥离线铸造 Cookie,并提供
    probeRpc / probeMuxSocket / probeWorkspaceDir(工作区落在系统临时目录)。
  • 五个 DSH 探针 + cleanup-*/diag-archive/fix-probe-workspace-title 全部改用它。
  • 删除 scripts/probe-015.mjs(0.1.5 专用,已被 verifier 取代,走的是旧点号协议路径)。
  • 测试夹具从仓库 state/ 迁到系统临时目录(探针、self-test、
    test-setup-dsh-idempotent、test-dsh-v2-wait):harden-state-acl.mjs 会把 state/ 的
    继承 ACE 去掉,DSH 沙箱的能力 SID 不在 ACL 里 ⇒ 在沙箱会话里跑测试会直接 EPERM。
    迁完后 npm run self-test 可在沙箱内完整跑通。

════════════════════════════════════════════════════════════════════
五、复核通过、无需改动的部分(都做了实测或源码级核对)
════════════════════════════════════════════════════════════════════

  • RPC 面:0.2.0 的全部远端 endpoint 清单里,桥接用到的每一个都还在
    (session/{list,create,prompt,cancel,selectModel,rename,fork,updateQueue,page,search,
    follow,modelCatalog}、workspace/*、agentPresets/list、settings/describe)。
  • $events / $events/result 与 waterfall 帧形状不变
    ({type:'waterfall', event, eventId, agentId, request})⇒ 提问/审批链路不变。
  • 三个 MCP server 与 0.2.0 的 MCP 客户端(官方 SDK v2)完全兼容:五个协议修订版本
    两侧一致,server/discover 探测失败会干净回落到 2025 握手;toolCallTimeoutMs /
    serverName / 工具名前缀 / serverInfo 形状均未变;MCP 输入 schema 原样透传
    (anyOf 不受 harness 的 JSON Schema 子集约束,那套只作用于 output schema)。
    实测 mcp__snowluma__qq_status 全链路可用。
  • 模型目录:deepseek-flash 仍在(思考档位 off/low/high/max);控制台的
    off 过滤是有意的产品决策,与内置白名单自洽,未改。
  • 0.2.0 的 settings/update 是多参数 RPC({ns,patch,expectedRevision} 平铺,
    不是 {request:{...}}),桥接原本就是平铺调用,正确。

════════════════════════════════════════════════════════════════════
六、验证脚本与文档
════════════════════════════════════════════════════════════════════

  • verify-dsh-015-adaptation.mjs → verify-dsh-020-adaptation.mjs(一个自称 015
    却在验 0.2.0 的脚本只会误导人;npm run verify:adaptation 入口名不变)。
    活 DSH 段重写:qq-mode 可列可写、session/projections、control baseline 形状、
    stopSessionWork、$events ready、modelCatalog、点号 endpoint 仍 404。65 项断言全过。
    语法检查在禁止 spawn 的环境里显式记为「跳过」而不是批量假失败。
  • 版本身份 0.1.7 → 0.2.0(package.json / package-lock.json / 两个插件包 /
    三个 MCP serverInfo),由 verify:adaptation 的既有断言钉住。
  • 文档纠偏:README 与 README.en 的版本行与「DSH 侧」描述;README.en 原先停在
    v0.1.5 且仍在介绍已删除的 start_snowluma/stop_snowluma;DSH_SETUP 的 preset 安装
    位置、qq-mode 命名空间机制、401 排查步骤;FOLDER_MAP 的脚本清单与新增脚本;
    SECURITY_BASELINE 的守卫两道闸门与对账工具;RULES 的 preset 修改说明;
    PROJECT_GUIDE 的过期清单补两条(preset 机制、鉴权链路)。

════════════════════════════════════════════════════════════════════
七、回归
════════════════════════════════════════════════════════════════════

  • npm run test:audit:30/30 脚本通过(新增 2 个脚本进清单)。
  • npm run verify:adaptation:65 通过 / 0 失败。
  • npm run test:dsh-auth 17 项、self-test 端到端链路、test:mcp-servers(三个
    MCP server 共 38 个工具)、test:setup-idempotent、verify:persona 全绿。
  • npm run privacy-scan:git ls-files 的 138 个文件,未发现敏感信息。