github zhu1090093659/dsh-web v0.4.17
dsh-web v0.4.17

one hour ago

本版把宿主下限抬到 @deepseek-ai/dsh >= 0.2.2-rc.2:请先升宿主,再升插件——用 npm i -g @deepseek-ai/dsh@next 装宿主,再以 dsh --version 校验,因为 npm 上的 latest 仍指向 0.2.2-rc.1,用 latest 装到的宿主不满足本版下限,在它上面点「更新」会被保护性拒绝。除此之外,本版把浏览器视图的闲置页面回收、梁神模式的按需工具族分页与插件页的批量更新各推进一步,并从任务看板移除了 /goal 运行选项与 goal 验收;对已经跑在 0.2.2-rc.2 上的用户,本版同时是一次修复:0.4.16 的 web-ui-agents 行在 rc.2 宿主上导入即失败。四个卫星仓在本版同版本对齐到 0.4.17。

宿主下限抬到 @deepseek-ai/dsh >= 0.2.2-rc.2,先升宿主再升插件:全部家族包的 dsh.engines.dsh 与 @deepseek-ai/dsh 对等依赖下限从 >=0.2.2-rc.1 移到 >=0.2.2-rc.2,随本版一起移到 rc.2 的还有工作区全部 @deepseek-ai/* 区间、CI 与发布挂载的 DSH_CLI_VERSION pin、两个根 README 与全部 32 个包 README 的徽章,以及 lockfile。升级顺序是宿主优先:npm i -g @deepseek-ai/dsh@next 之后用 dsh --version 确认已到 0.2.2-rc.2,再在插件页升级全家桶。npm 的 latest 仍是 0.2.2-rc.1(rc.2 只发在 next 标签上),用 latest 装出来的宿主不满足本版下限:dsh --version 若还报 0.2.2-rc.1,这趟升级就没到位,别急着升插件。
rc.1 宿主上点「更新」会看到保护性拒绝,这是预期行为而不是故障:更新请求在动手之前解析目标包声明的宿主下限,运行中的宿主不满足即返回 HTTP 412,界面显示 ... requires DSH >=0.2.2-rc.2 (current DSH 0.2.2-rc.1); upgrade DSH before updating(宿主侧完整形态是 plugin-manager: <包名> requires DSH >=0.2.2-rc.2 (current DSH 0.2.2-rc.1); upgrade DSH before updating)。这条拒绝发生在任何写入之前,写入为零:profile、磁盘上的包与看板账本都不会被改动,按提示把宿主升到位后重试即可。
若在 rc.1 上手工装了 0.4.17:dsh web 仍能启动,但整套 dsh-web 插件不加载:官方 boot gate 按 bundle 粒度跳过不满足下限的包,stderr 只有一行 dsh: skipping profile bundle "@linxin666/dsh-web-all": <原因>(格式为 <binName>: skipping profile bundle <JSON 包名>: <原因>),不抛错、不删任何数据、其余 bundle 照常启动。把宿主升到 0.2.2-rc.2 再重启即恢复,profile 不用改。应急手段有两条:一是为这个精确版本开豁免——dsh plugin --profile <profile> allow-version <包名>@<版本> --dsh-version 0.2.2-rc.1 --accept-risk,豁免只作用于该包与该 DSH 版本的组合;二是把包移除、回退到 0.4.16。
对已在 0.2.2-rc.2 上的宿主,本版是一次修复:rc.2 移除了 @deepseek-ai/dsh-subagent/internal 子路径导出,而 0.4.16 的 dsh-agents 在模块顶层静态导入它,于是聚合包的 web-ui-agents 行在导入阶段抛出 ERR_PACKAGE_PATH_NOT_EXPORTED: './internal',被故障隔离壳按 stage: import 记为降级行——这一行的界面与工具全部缺失,其余行照常工作。本版改为优先探测 Symbol.for('dsh.subagent.deliverPrompt')、缺省回退到 subagents.sendMessage(caller, target.id, content, { signal }),同一份产物在两代宿主上都能加载与工作。
浏览器视图:闲置满 3 天的页面自动关闭:TabRecord.lastActiveAt 在接入 target 时落一次,之后只由能解析到该页面的 browser_* 调用刷新;BrowserViewService.sweepIdle 每十分钟扫一次(定时器 unref、dispose 时清除),把超出窗口的页面走既有的 closeTab 路径关掉——指向它的会话绑定被丢弃、钉在它上面的面板回退到剩下的标签页、面板状态被广播;没有浏览器在跑时既不扫描也不拉起。窗口是新设置 idleCloseDays:默认 3 天、最小 0、设 0 关闭该能力,每次扫描现读,所以改设置在下一次扫描就生效,既不用重挂插件也不用重连浏览器。接管的真实浏览器页面永远不会被自动关闭:候选只限插件自己创建(origin: 'created')的标签页,在面板里看它、选中它的 chip 与投屏帧都不算活跃。闲置时钟只在内存里,宿主重启后每个页面的计时重新开始。
梁神模式:按需工具族出厂开启:出厂 pagedToolPatterns 从只分页 MCP 桥接扩到 ['mcp__*', 'browser_*', 'task_board_*', 'team_*', 'squad*', 'ui_*', 'ssh_*'],宿主内建工具(shell、文件系统、glob/grep、后台作业、skill、ask_user_question、todo_write、web_search、子代理委派、plan、goal、fact_register、tool_activate 与 MCP 资源读取)全部常驻。被扣留的族在目录里以一行命名空间摘要宣告,首次调用前需要 tool_activate({ namespace }) 把命名空间挂回:mcp__* 取工具名的第一个 __ 分段(mcp__codegraph__codegraph_explore → codegraph),其余按字面头部推导(task_board_* → task_board、squad* → squad),裸写一个 * 会被拒绝。最多同时保持 3 个已激活的命名空间,激活第四个时把最久未用的那个逐回摘要;ptc 呈现下这些摘要代表真正不可达的命名空间——既不在该请求的 tools:sdk 段,也不能在 run_code 程序内调用。设置卡片把列表暴露为「按需工具族」字段,写成空值即取消分组、恢复全量常驻(三处读取同一份模式表的行一并改写)。实测同一套会话的系统提示词从 104,949 字符降到 58,961 字符,tools:sdk 段从 73,675 字符降到 27,666。
任务看板移除 /goal 运行选项与 goal 验收:goalRunEnabled 与 TaskRecord.goalRun、/goal 的派发、session/projections 的目标阶段判定、五个 goalVerification* 配置键、TaskRecord.skipVerification、ExecutionRecord.verification 契约、tools/pre-execute 上的 update_goal(action: complete) 拦截门、裁判模型执行器与 task_board_manage(action: 'reset-verification') 一并移除;手动执行、定时触发与 rework 返工一律按普通单回合运行,结算只看该回合的 turn/end 结果,不再产生额外的裁判模型调用。旧卡片与历史执行记录完整保留:权威账本 $DSH_HOME/task-board/ledger-v2.json 仍是 schemaVersion: 7,旧行残留的 goalRun、skipVerification、verification 在加载时直接丢弃、绝不因此拒绝整行,也不再回写;非严格插件配置加载同样忽略旧 profile 补丁里残留的键。对 task_board_manage 传 reset-verification 现在按未知动作拒绝。
插件页批量更新:一次请求跑完整批,单行被拒不中断其余行:更新改走官方 updateBundle(name, version, { requestId }) 这一条原语(管理器自己校验目标、移除已装版本、按找到的勾选状态安装、失败时把旧版本装回),批量成为本 host 对同一原语的串行排序执行,浏览器只发一次请求并轮询同一个任务。每行在任务里有一条自己的结果(phase/targetVersion/landed/error/restartRequired),请求在动手前按单条路由自己的规则逐条解析,拒绝变成那一行的错误而不是整批失败;整批上限 50 行、重复 id 只解析一次,最后一行答复后整批一律结束。已落地但当前进程加载不了的包记成「需要重启」的行而不是错误,两处界面都写明包名——更新后可能仍需重启 DSH 才加载新版本。
全新安装若报 ERR_PNPM_IGNORED_BUILDS,按提示先批准构建再重跑:在还没有批准过构建脚本的 profile 上首次安装,pnpm 会以 [ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: cloudflared@..., cpu-features@..., ssh2@... 结束并给出 Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.,dsh plugin add 随之以退出码 1 结束,且不会把包挂进 dsh.profile.bundles——也就是「装了但没生效」。按提示先执行 pnpm approve-builds 批准这三个依赖的构建脚本,再重跑同一条 dsh plugin add。

升级须知

  • 宿主下限从 >=0.2.2-rc.1 抬到 >=0.2.2-rc.2:本版抬高了 dsh.engines.dsh 与 @deepseek-ai/dsh 对等依赖下限,停在 0.2.2-rc.1 的宿主不再满足条件。升级顺序是宿主优先,而 npm 的 latest 仍是 0.2.2-rc.1:装宿主必须用 next 标签(npm i -g @deepseek-ai/dsh@next),装完用 dsh --version 校验。
  • rc.1 宿主上的更新拒绝是保护性设计:HTTP 412 加 requires DSH >=0.2.2-rc.2 (current DSH 0.2.2-rc.1); upgrade DSH before updating,发生在任何写入之前,不留下半装状态。看到它说明宿主还没升到位,不需要排查插件。
  • rc.1 上手工装了 0.4.17 的恢复路径:dsh web 仍能启动,stderr 只有一行 dsh: skipping profile bundle ...,整套插件不加载;把宿主升到 0.2.2-rc.2 重启即恢复,profile 无需改动。应急可用 dsh plugin --profile <profile> allow-version <包名>@<版本> --dsh-version 0.2.2-rc.1 --accept-risk 为该精确组合开豁免,或移除包回退到 0.4.16。
  • rc.2 宿主上的 web-ui-agents 导入失败已在本版修复:0.4.16 在 rc.2 上会因 ./internal 子路径消失而以 ERR_PACKAGE_PATH_NOT_EXPORTED 丢掉这一行;本版的双队列探测让同一产物在 rc.1 与 rc.2 上都能加载。
  • 浏览器视图新增 idleCloseDays,默认 3 天:只回收插件自己创建的页面,接管的真实浏览器页面不受影响;设 0 关闭该能力;闲置时钟只在内存,宿主重启后计时重来。只在面板里被观看、没有任何工具调用的页面同样会在窗口到期时关闭。
  • 梁神模式的工具面默认分组:出厂分页 mcp__*、browser_*、task_board_*、team_*、squad*、ui_*、ssh_*,宿主内建工具常驻;被扣留的族在 tool_activate({ namespace }) 之前不可达,最多同时激活 3 个;设置卡片里把「按需工具族」写成空值即恢复全量常驻。
  • 任务看板的退役字段与账本兼容:本版删除 /goal 运行选项与 goal 验收,账本 schema 仍是 v7,旧卡片与历史执行记录完整保留,退役字段在加载时丢弃且不回写,不需要任何迁移动作。
  • 批量更新与重启事实:插件页的「全部更新」现在是一次请求、一个任务,单行被拒不中断其余行;已落地但当前进程加载不了的包会明确标注需要重启 DSH。本版不再自拼「移除再安装」的两阶段替换,层序(dsh.profile.bundles 中的位置)在连续更新后保持原样。
  • 全新安装的 ERR_PNPM_IGNORED_BUILDS 陷阱:dsh plugin add 会以退出码 1 结束且不挂 bundle,先 pnpm approve-builds 再重跑。
  • 平台验证声明:本版在 Linux(Ubuntu x86_64)的钉定 rc.2 宿主树上跑过仓库门禁(typecheck、build、test、test:scripts、libs:check、aggregate:check、docs:check、i18n:check、emoji:check 等)。Windows 与 macOS 未在物理实机上验证。发布时运行中的宿主未被重启,因此新增的 idleCloseDays 设置卡与插件页批量更新工具条的真实 GUI 观察,要在重启宿主并刷新页面之后才能补上。

新功能

  • [plugin-manager] 批量更新改走官方 updateBundle 并在本 host 串行排序执行:一次请求、一个任务、每行一条自己的结果,单行被拒不中断其余行,需重启的行记为已落地事实
  • [remote-bridge] 新增 session.attachment.path:按会话自己 transcript 里点名的绝对路径提供那张图的字节,白名单只由该会话的 offload 占位符与 durable 工具结果发布的图片引用构成,路径须逐字节相等、realpath() 解析到自身、确实是常规文件且首字节命中四种图片签名之一(不看扩展名)

修复

  • [agents,liangshen] 桥接 DSH 0.2.2-rc.2 的运行期契约:dsh-agents 去掉对 @deepseek-ai/dsh-subagent/internal 的静态导入并双队列探测投递、startActivation 同时传顶层字段与 request 嵌套字段、兼容 drainChildren/drainDescendants/started.dispose(),并在官方 agent-team 行在场时让位;dsh-liangshen 的 dispatch-guard 在 agents.roots() 缺省时回退探测 subagents.rootOf(caller)
  • [sdk] 根宿主面承载 @deepseek-ai/dsh-error@0.2.2-rc.2,dsh-agents 与 dsh-remote-web-ui 把 zod 钉到 4.6.6,并由 dsh-agents 自持 Team identity 的 prompt context 位置 125(rc.2 已从中央顺序表删掉该名字)
  • [agents] 成员与小队草稿被拒时点名具体字段:gateMemberRequestBody/gateTeamRequestBody 返回单条原因,取代固定的「not a usable definition」,客户端改名或新增字段时能看出是哪一个不被接受
  • [remote-bridge] 一并读取 durable 工具结果里的 JSON 图片引用({ path, image: { ... } },即 run_code 程序自身返回值的持久化形态),并以 mediaType 必须以 image/ 开头作为闸门,避免把工具仅仅提到过的文件放进白名单
  • [sdk] 0.2.2-rc.2 宿主上聚合包 web-ui-agents 行的 ERR_PACKAGE_PATH_NOT_EXPORTED: './internal' 导入失败,由上面两条双队列改动消除

行为变化

  • [browser-view] 插件自己创建的页面闲置满窗口即自动关闭:窗口是新设置 idleCloseDays(默认 3 天、最小 0、设 0 关闭),每次扫描现读;接管的真实浏览器页面不是候选,在面板里观看也不算活跃;闲置时钟只在内存,宿主重启后计时重来
  • [liangshen] 按需工具族出厂开启:pagedToolPatterns 默认 ['mcp__*', 'browser_*', 'task_board_*', 'team_*', 'squad*', 'ui_*', 'ssh_*'],宿主内建工具常驻;被扣留的族在 tool_activate({ namespace }) 之前既不在 wire 与生成的 SDK 上、也不能在 run_code 程序内调用,最多同时激活 3 个,设置里把列表写成空值即取消分组
  • [task-board] 移除 /goal 运行选项与 goal 验收:执行形态统一为普通单回合、结算只看 turn/end,不再有裁判模型调用与 reset-verification 解卡动作;账本 schema 仍是 v7,旧卡片与历史执行记录完整保留,退役字段加载时丢弃且不回写

其他改动

  • [market] 把 2026-11-08、11-09、11-10、11-11 四轮卫星登记与本轮的升序索引重建依次钉进子模块 gitlink 并重出 market/dist,当前商店目录为 66 套皮肤、12 只宠物、124 个插件、33 个预设
  • [task-board] 详情页运行折叠行的 key 从 detail.settings.advanced 改为 detail.settings.run,标题与新建对话框统一为「运行方式」/Run Mode/Режим запуска,折叠结构与唯一的会话复用开关不动
  • [team-hub] PROTOCOL.md 七处把「自动验收」改写为按宿主自己的执行判定结算(移除验收门后的对齐),字段名、结算端点签名、in_review/failed 状态与全部数值常量未变
  • [docs] 浏览器与 GUI 验收改为以会话挂载的 browser_* 工具为主,Playwright 退为回退手段与提交门禁 pnpm test:mount,并把这条默认的归属写进决策笔记
  • [lib] dsh-market 与 dsh-web-all 提交的 lib/ 产物随区间内改动重建
  • [satellites] dsh-skins、dsh-pet、dsh-community-plugins 与 dsh-presets 四个子模块 gitlink 前移到各自的 0.4.17 发布提交
  • [remote-bridge] 附件门禁用例按门禁要求的角色命名(仅测试重命名,无行为变化)
  • [notes] 记录 0.2.2-rc.2 队列及其原生优先重叠盘点等决策笔记

English

This release raises the host floor to @deepseek-ai/dsh >= 0.2.2-rc.2: upgrade the host first, then the plugins. Install it with npm i -g @deepseek-ai/dsh@next and confirm with dsh --version, because npm's latest still points at 0.2.2-rc.1 and a host installed from latest does not satisfy this release's floor - its update button refuses the upgrade protectively. Beyond that, this release moves browser-view idle page reclamation, LiangShen's on-demand tool-family paging and the Plugins page batch update one step each, and removes the task board's /goal run option and goal acceptance; for anyone already on 0.2.2-rc.2 this release is also a fix, because 0.4.16's web-ui-agents row fails to import on an rc.2 host. The four satellite repositories align to 0.4.17.

The host floor rises to @deepseek-ai/dsh >= 0.2.2-rc.2; upgrade the host first: dsh.engines.dsh and the @deepseek-ai/dsh peer dependency of every family package move from >=0.2.2-rc.1 to >=0.2.2-rc.2, and the same move carries every workspace @deepseek-ai/* range, the DSH_CLI_VERSION pin in CI and the release mount, the badges of both root READMEs and all 32 package READMEs, and the lockfile. Upgrade the host first: npm i -g @deepseek-ai/dsh@next, confirm with dsh --version that it reads 0.2.2-rc.2, then update the family on the Plugins page. npm's latest still reads 0.2.2-rc.1 (rc.2 ships on the next tag only), so a host installed from latest does not satisfy this floor: if dsh --version still reports 0.2.2-rc.1, the host upgrade has not landed yet and the plugins should wait.
On an rc.1 host the update button shows a protective refusal, which is expected rather than a fault: the update request resolves the target package's declared host floor before touching anything and answers HTTP 412 when the running host does not satisfy it, showing ... requires DSH >=0.2.2-rc.2 (current DSH 0.2.2-rc.1); upgrade DSH before updating (the host-side full form is plugin-manager: <package> requires DSH >=0.2.2-rc.2 (current DSH 0.2.2-rc.1); upgrade DSH before updating). The refusal happens before any write, so nothing is written: the profile, the packages on disk and the task board ledger are untouched, and retrying after the host upgrade is all that is needed.
If 0.4.17 was installed by hand on rc.1, dsh web still starts but the whole dsh-web plugin set does not load: the official boot gate skips bundles that do not satisfy the floor, one bundle at a time, and stderr carries a single line, dsh: skipping profile bundle "@linxin666/dsh-web-all": <reason> (the format is <binName>: skipping profile bundle <JSON package name>: <reason>). Nothing is thrown, no data is deleted, and the remaining bundles boot normally. Raise the host to 0.2.2-rc.2 and restart to recover; the profile needs no edit. Two stopgaps exist: grant the exact-version exemption with dsh plugin --profile <profile> allow-version <package>@<version> --dsh-version 0.2.2-rc.1 --accept-risk, which applies only to that package and that DSH version, or remove the package and roll back to 0.4.16.
For hosts already on 0.2.2-rc.2, this release is a fix: rc.2 removed the @deepseek-ai/dsh-subagent/internal subpath export, and 0.4.16's dsh-agents imported it statically at module top level, so the aggregate's web-ui-agents row threw ERR_PACKAGE_PATH_NOT_EXPORTED: './internal' at import time and was recorded as a degraded row at stage: import - that row's UI and tools were missing while the other rows kept working. This release probes Symbol.for('dsh.subagent.deliverPrompt') first and falls back to subagents.sendMessage(caller, target.id, content, { signal }), so one artifact loads and works on both host generations.
Browser view: pages idle for three days close themselves: TabRecord.lastActiveAt is stamped when a target is adopted and refreshed only by a browser_* call that resolves to that page; BrowserViewService.sweepIdle runs every ten minutes (an unref'd timer, cleared on dispose) and closes pages past the window through the existing closeTab path - the session binding pointing at it is dropped, a panel pinned to it falls back to the remaining tabs, and panel state is broadcast; nothing is scanned and no browser is launched when none is running. The window is the new idleCloseDays setting: default 3 days, minimum 0, 0 disables the capability, read on every sweep, so an edit takes effect on the next sweep without remounting the plugin or reconnecting the browser. Adopted real-browser pages are never candidates: only tabs the plugin created (origin: 'created') qualify, and watching a page in the panel, selecting its chip or projecting its frames does not count as activity. The idle clock lives in memory only, so a host restart restarts every page's window.
LiangShen pages plugin tool families by default: the shipped pagedToolPatterns grows from the MCP bridge alone to ['mcp__*', 'browser_*', 'task_board_*', 'team_*', 'squad*', 'ui_*', 'ssh_*'], while host builtins (shell, the file system, glob/grep, background jobs, skill, ask_user_question, todo_write, web_search, subagent delegation, plan, goal, fact_register, tool_activate and the MCP resource readers) stay resident. A withheld family is announced as one namespace summary in the catalog and needs tool_activate({ namespace }) before its first call: mcp__* takes the first __ segment of the tool name (mcp__codegraph__codegraph_explore becomes codegraph) while the others derive a literal-prefix family (task_board_* to task_board, squad* to squad), and a bare * is refused. At most three activated namespaces are held at once, and activating a fourth returns the least recently used one to its summary; under ptc those summaries are genuinely unreachable - absent from the request's tools:sdk section and not callable inside a run_code program. The settings card exposes the list as the on-demand tool family field, and writing it empty removes the grouping and returns every tool to the resident face (all three rows that page from that one pattern list are rewritten together). Measured on one session, the system prompt dropped from 104,949 to 58,961 characters and the tools:sdk section from 73,675 to 27,666.
The task board loses the /goal run option and goal acceptance: goalRunEnabled and TaskRecord.goalRun, the /goal dispatch, the session/projections goal-phase settlement, the five goalVerification* keys, TaskRecord.skipVerification, the ExecutionRecord.verification contract, the update_goal(action: complete) gate on tools/pre-execute, the judge-model runner and task_board_manage(action: 'reset-verification') are all removed; manual runs, scheduled triggers and rework now run as an ordinary single turn and settle from that turn's turn/end result, with no extra judge-model calls. Existing cards and execution history stay intact: the authoritative ledger $DSH_HOME/task-board/ledger-v2.json keeps schemaVersion: 7, leftover goalRun, skipVerification and verification fields on old rows are dropped at load rather than rejecting the row, and they are never written back; non-strict plugin config loading likewise ignores leftover keys in old profile patches. Passing reset-verification to task_board_manage is now refused as an unknown action.
Plugins page batch update: one request runs the whole batch, and a refused row does not stop the others: updates now go through the official updateBundle(name, version, { requestId }) primitive (the manager verifies the target, removes the installed version, installs with the selection it found and restores on failure), so a batch is this host running that one primitive over its rows in order and the browser sends one request and polls one job. Each row carries its own result (phase, targetVersion, landed, error, restartRequired), the request resolves every id by the single route's own rules before touching anything, and a refusal becomes that row's error instead of failing the batch; a batch is capped at 50 rows, duplicate ids resolve once, and the batch always settles after the last row answers. A package that landed but cannot be loaded in this process is a restart row rather than an error, named on both surfaces - a restart of DSH may still be needed before the new version loads.
A fresh install that reports ERR_PNPM_IGNORED_BUILDS needs the builds approved and the command re-run: on a profile that has never approved build scripts, pnpm ends with [ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: cloudflared@..., cpu-features@..., ssh2@... plus Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts., dsh plugin add exits 1, and the package is not added to dsh.profile.bundles - installed but not in effect. Run pnpm approve-builds for those three dependencies and re-run the same dsh plugin add.

Upgrade Notes

  • The host floor moves from >=0.2.2-rc.1 to >=0.2.2-rc.2: this release raises dsh.engines.dsh and the @deepseek-ai/dsh peer floor, so a host on 0.2.2-rc.1 no longer qualifies. The host goes first, and npm's latest is still 0.2.2-rc.1: install it from the next tag (npm i -g @deepseek-ai/dsh@next) and verify with dsh --version.
  • The refusal on an rc.1 host is protective by design: HTTP 412 plus requires DSH >=0.2.2-rc.2 (current DSH 0.2.2-rc.1); upgrade DSH before updating, raised before any write, leaving no half-installed state. Seeing it means the host has not been upgraded yet, and nothing about the plugin needs investigating.
  • Recovery after a hand install of 0.4.17 on rc.1: dsh web still starts, stderr carries a single dsh: skipping profile bundle ... line, and the plugin set does not load; raising the host to 0.2.2-rc.2 and restarting recovers it without touching the profile. The stopgaps are dsh plugin --profile <profile> allow-version <package>@<version> --dsh-version 0.2.2-rc.1 --accept-risk for that exact combination, or removing the package and rolling back to 0.4.16.
  • The web-ui-agents import failure on rc.2 hosts is fixed here: 0.4.16 lost that row on rc.2 to ERR_PACKAGE_PATH_NOT_EXPORTED once the ./internal subpath disappeared; the dual-cohort probing in this release makes one artifact load on both rc.1 and rc.2.
  • Browser view gains idleCloseDays, default 3 days: only pages the plugin created are reclaimed, adopted real-browser pages are untouched, 0 disables the capability, and the idle clock lives in memory so a host restart restarts the window. A page watched in the panel with no tool call still closes when its window expires.
  • LiangShen's tool face is grouped by default: the shipped page list is mcp__*, browser_*, task_board_*, team_*, squad*, ui_* and ssh_* while host builtins stay resident; a withheld family is unreachable before tool_activate({ namespace }), at most three stay activated, and writing the on-demand tool family field empty restores the full resident face.
  • Retired task board fields and ledger compatibility: this release removes the /goal run option and goal acceptance while the ledger schema stays v7, existing cards and execution history stay intact, retired fields are dropped at load and never written back, and no migration step is needed.
  • Batch update and the restart fact: the Plugins page's update all is now one request and one job, a refused row does not stop the others, and a package that landed but cannot load in this process is explicitly marked as needing a DSH restart. The hand-written remove-then-install replacement is gone, and the layer order (the position in dsh.profile.bundles) survives consecutive updates.
  • The fresh-install ERR_PNPM_IGNORED_BUILDS trap: dsh plugin add exits 1 without adding the bundle, so run pnpm approve-builds first and retry.
  • Platform verification: this release ran the repository gates on Linux (Ubuntu x86_64) against a pinned rc.2 host tree (typecheck, build, test, test:scripts, libs:check, aggregate:check, docs:check, i18n:check, emoji:check among them). Windows and macOS were not verified on physical hardware. The host running at release time was not restarted, so the real GUI observation of the new idleCloseDays settings card and the batch update toolbar lands only after the host is restarted and the page refreshed.

New Features

  • [plugin-manager] Batch updates go through the official updateBundle as one ordered run in this host: one request, one job, one result per row, a refused row leaves the rest running, and a restart-required row is recorded as a landed fact
  • [remote-bridge] Add session.attachment.path, serving the bytes of one image by the absolute path that session's own transcript named, with a whitelist built only from that session's offload placeholders and the image references its durable tool results published; a path is served only when it matches byte for byte, realpath() resolves it to itself, a regular file is there and its first bytes match one of four image signatures (the extension is never consulted)

Bug Fixes

  • [agents,liangshen] Bridge the runtime contracts of a DSH 0.2.2-rc.2 host: dsh-agents drops its static @deepseek-ai/dsh-subagent/internal import and probes both delivery paths, passes both top-level fields and the nested request to startActivation, accepts drainChildren, drainDescendants and started.dispose(), and steps aside while the official agent-team row is present; dsh-liangshen's dispatch guard falls back to subagents.rootOf(caller) when agents.roots() is absent
  • [sdk] Carry @deepseek-ai/dsh-error@0.2.2-rc.2 as a root host face, pin zod to 4.6.6 in dsh-agents and dsh-remote-web-ui, and have dsh-agents supply the Team identity prompt-context position 125 that rc.2 dropped from the central order table
  • [agents] Name the refused field when a member or squad draft is not usable: gateMemberRequestBody and gateTeamRequestBody return one reason instead of the fixed "not a usable definition", so a client that renames or adds a draft field can tell which one was not accepted
  • [remote-bridge] Read the JSON image reference of a durable tool result too ({ path, image: { ... } }, the persisted shape of a run_code program's own return value), gated on an image object whose mediaType starts with image/ so a file a tool merely named never enters the whitelist
  • [sdk] The ERR_PACKAGE_PATH_NOT_EXPORTED: './internal' import failure of the aggregate's web-ui-agents row on a 0.2.2-rc.2 host, removed by the two dual-cohort changes above

Behavior Changes

  • [browser-view] Pages the plugin created close themselves once idle past the window: the window is the new idleCloseDays setting (default 3 days, minimum 0, 0 disables), read per sweep; adopted real-browser pages are never candidates and watching a page in the panel is not activity; the clock lives in memory, so a host restart restarts it
  • [liangshen] On-demand tool families ship enabled: pagedToolPatterns defaults to ['mcp__*', 'browser_*', 'task_board_*', 'team_*', 'squad*', 'ui_*', 'ssh_*'] while host builtins stay resident, a withheld family is absent from the wire and the generated SDK and cannot be called inside a run_code program before tool_activate({ namespace }), at most three stay activated, and writing the list empty in the settings removes the grouping
  • [task-board] The /goal run option and goal acceptance are removed: execution is an ordinary single turn and settlement reads turn/end, with no judge-model calls and no reset-verification unstick action; the ledger schema stays v7, existing cards and execution history stay intact, and retired fields are dropped at load and never written back

Other Changes

  • [market] Pin the 2026-11-08, 11-09, 11-10 and 11-11 satellite registration rounds and this round's ascending-id index rebuild into the submodule gitlinks and regenerate market/dist, leaving the store catalog at 66 skins, 12 pets, 124 plugins and 33 presets
  • [task-board] Rename the detail run fold key from detail.settings.advanced to detail.settings.run and give it the create dialog's title (Run Mode / 运行方式 / Режим запуска), keeping the fold and its single session-reuse switch
  • [team-hub] Rewrite seven places in PROTOCOL.md so settlement follows the host's own execution verdict rather than automated acceptance, with field names, the settle endpoint signature, the in_review/failed states and every numeric constant unchanged
  • [docs] Drive browser and GUI verification with the session's browser_* tools, keeping Playwright as the fallback and as the committed mount gate pnpm test:mount, and record the owner of that default in a decision note
  • [lib] Rebuild the committed lib/ artifacts of dsh-market and dsh-web-all with the changes in this range
  • [satellites] Advance the four submodule gitlinks (dsh-skins, dsh-pet, dsh-community-plugins, dsh-presets) to their 0.4.17 release commits
  • [remote-bridge] Name the attachment gate cases by the role the gate requires (a test rename only, no behavior change)
  • [notes] Record the 0.2.2-rc.2 cohort and its native-first overlap inventory, among other decision notes

Don't miss a new dsh-web release

NewReleases is sending notifications on new releases.