From ab2246da47711e9cb9a7b1afdb6dea78b682d5e5 Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Wed, 23 Sep 2026 23:58:59 +0800 Subject: [PATCH] chore(release): cut v0.21.1 as the stable adapter for DSH 0.1.7-rc.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rc.1 adapter first went out as 0.21.0-rc.1, but DSH 0.1.7-rc.1 is upstream's release batch, so the plugin that targets it must not sit on a prerelease channel. Re-cut it as v0.21.1: the version is now free of `-`, so release.yml publishes it under npm's `latest` instead of `alpha`. Version literals only — package.json, dsh.plugin.json and SIDEBAR_SERVICE_VERSION (tests/service.spec.ts still pins the last two together). No code change: the peer floor stays ^0.1.7-rc.1 and the support line stays "DSH 0.1.7-rc.1 and later". Docs follow the channel move: README/README_EN/guide drop the "alpha channel" framing, the install command becomes @latest, and every older DSH line now gets an explicitly pinned command in the version table (@0.19.1 and so on) because `latest` no longer covers them. The rc.1 design record keeps its history and gains a §8 for the re-cut. --- AGENTS.md | 4 +- README.md | 42 +++++++++--------- README_EN.md | 44 +++++++++---------- docs/external-plugin-guide.md | 12 ++--- ...2026-09-22-dsh-0.1.7-alpha.1-adaptation.md | 2 +- .../2026-09-23-dsh-0.1.7-rc.1-adaptation.md | 11 ++++- dsh.plugin.json | 2 +- package.json | 2 +- src/client/service.ts | 2 +- 9 files changed, 65 insertions(+), 56 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6fddcf1..95e63ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,7 @@ ## 3. DSH 0.1.7 适配要点(0.1.7-rc.1+ 基线) -**v0.21.0-rc.1 起仅支持 DSH 0.1.7-rc.1+**(peer 下限 `^0.1.7-rc.1`,CI 钉 `@deepseek-ai/dsh@0.1.7-rc.1`)。**下限必须动**:semver 的预发布规则要求比较子与候选版本同 `[major,minor,patch]` 元组,`^0.1.6-alpha.2` 在数学上**永远容纳不下任何 `0.1.7-*` 预发布版**(实测 `0.1.7-alpha.1` 对 `^0.1.6-alpha.2` / `>=0.1.6-alpha.2` 全是 false),而 `^0.1.7-rc.1` 天然容纳 0.1.7 的 rc/正式全线。**下限从 alpha.1 收到 rc.1 是刻意的**:`0.21.0-alpha.1` 从未发布到 npm(`latest` 仍是 v0.19.1,npm 上没有 alpha.1 的下载量),而修掉 rc.1 的 `DiffBlockLabels` 之后,同一份代码在 alpha.1 上会**崩**——alpha.1 的 `DiffBlock` 仍读上游已删除的 `labels.files`。声明与「真机验证过的宿主」一致,比声明一条跑不通的支持线更安全;rc.1 起 peer 还是**启动期硬约束**(见 §3.4 第 12 条),声明过宽的代价从「可能报错」变成「静默不加载」。**支持线取舍**:0.1.6-alpha.2 及更早的用户停留在 **v0.19.1**(0.20.0 这一版从未发布到 npm)——本版破坏面足够大(`dsh-settings` 整体重写、25 个图标具名导出消失、会话格式 v3→v4),按本仓库先例不写运行时兼容层。发版:release.yml 按版本号是否含 `-` 自动选 `alpha`/`latest` dist-tag(`0.21.0-rc.1` 含 `-` → `alpha`;npm `latest` 仍是 0.19.1)。 +**v0.21.1 起仅支持 DSH 0.1.7-rc.1+**(peer 下限 `^0.1.7-rc.1`,CI 钉 `@deepseek-ai/dsh@0.1.7-rc.1`)。**下限必须动**:semver 的预发布规则要求比较子与候选版本同 `[major,minor,patch]` 元组,`^0.1.6-alpha.2` 在数学上**永远容纳不下任何 `0.1.7-*` 预发布版**(实测 `0.1.7-alpha.1` 对 `^0.1.6-alpha.2` / `>=0.1.6-alpha.2` 全是 false),而 `^0.1.7-rc.1` 天然容纳 0.1.7 的 rc/正式全线。**下限从 alpha.1 收到 rc.1 是刻意的**:`0.21.0-alpha.1` 从未发布到 npm(`latest` 仍是 v0.19.1,npm 上没有 alpha.1 的下载量),而修掉 rc.1 的 `DiffBlockLabels` 之后,同一份代码在 alpha.1 上会**崩**——alpha.1 的 `DiffBlock` 仍读上游已删除的 `labels.files`。声明与「真机验证过的宿主」一致,比声明一条跑不通的支持线更安全;rc.1 起 peer 还是**启动期硬约束**(见 §3.4 第 12 条),声明过宽的代价从「可能报错」变成「静默不加载」。**支持线取舍**:0.1.6-alpha.2 及更早的用户停留在 **v0.19.1**(0.20.0 这一版从未发布到 npm)——本版破坏面足够大(`dsh-settings` 整体重写、25 个图标具名导出消失、会话格式 v3→v4),按本仓库先例不写运行时兼容层。发版:release.yml 按版本号是否含 `-` 自动选 `alpha`/`latest` dist-tag(`0.21.1` 不含 `-` → `latest`,npm `latest` 由此从 0.19.1 前移到 0.21.1;中间的 `0.21.0-rc.1` 含 `-`,只进过 `alpha` 通道)。 ### 3.1 本版四类硬破坏(都是「不修就崩或静默失效」) @@ -71,7 +71,7 @@ 8. **插件开发树的 dsh-* 传递 peer 需提升为 devDependencies**(alpha.3 首见):`dsh-subagent` 等 npm 包把 `dsh-attachment` 等 dsh-* 姊妹包声明为 peerDependencies(由宿主 bundle 树统一提供,宿主侧无此问题),插件仓库若只直接依赖其中一部分,其余 peer 在 pnpm 下会解析到树上残留的旧版——如 `dsh-attachment@0.1.1-rc.1` 缺 `admitPromptContent` 导出、`dsh-subagent` 产物 import 它时测试加载即崩。因此 devDependencies 需涵盖 dev 树实际触达的全部 peer(attachment / scope / session-projection / system-prompt / user-approval / util-time 等,与直接依赖同款精确钉版);**`@deepseek-ai/cordis` peer 自 alpha.3 起要求 `^4.0.2`,0.1.7-alpha.1 要求 `^4.0.3`,rc.1 起要求 `~4.0.4`**(rc.1 顺手把 vendor 一起抬了:cordis 4.0.4 / schemastery 3.18.4 / cordis-plugin-group 1.0.4 / cordis-plugin-include 1.0.9;loader 保持 1.0.5)。**rc.1 实测的教训**:只改 `dependencies` 里的 schemastery 和 peer 里的 cordis **不够**——`cordis-plugin-group` / `-include` 是 `dsh-app-boot` / `dsh-config-editor` 的**传递 peer**,pnpm 会继续沿用 lockfile 里的旧副本,`pnpm peers check` 一次报出四类 unmet peer;按本条把四个 vendor 包**显式钉进 devDependencies** 才收敛(这也是 rc.1 那条「DSH 内部引用一律精确版本」的 workspace-release-ranges 政策的直接后果)。适配新版本时先跑 `pnpm peers check`,把新失配的传递 peer 一并提升进 devDependencies。**0.1.7 新增的同类问题**见 §3.3 第 12 条(`ui-primitives` 的第二层裸 import)。例外:**`dsh-client-locale` 的 peer/devDep 允许落后于基线**的历史已消除,rc.1 起上游恢复发版,0.1.7 起与 DSH 同 tuple。 9. **右侧栏是 DSH 原生栏,插件只提供 tab 类型**:聊天里一切文件打开(工具行 / 产物行 / 正文提及 / 行内代码路径)统一走 `ctx.sidebarRight.openResource(fileAddressFor(sessionId, cwd, path))`。插件的每个 `TabDescriptor` 注册成原生 tab 类型(`kind = descriptor.id`,`extension` 带)+ 原生 tab 体(`sidebar.right.pane.tab` keyed 槽,key = `dsh-better-sidebar:`);`editor` 类型同时认领 `dsh-resource://file/**`(**但按 §3.2 第 5 条的清单让出一部分**),并接管内置 `files` 页面 kind(`openTab('files')` 打开插件文件树,注销即复位)。`ctx.sidebarRight.openTab/openResource/close` 只对**在屏会话**写入;跨会话用具体类上的 `openTabIn/openResourceIn/closeIn`。原生 tab 的插件侧状态在 `src/client/native/tab-adapter.tsx`。**注册必须等服务、不能等槽声明**:原生栏先声明 `sidebar.right.pane.tab` 再 `provide('sidebarRightTabs')`,真机 profile 上槽声明回调里 `ctx.get('sidebarRightTabs')` 仍是 undefined(实测 3 秒后才出现),按槽触发注册会静默什么都不注册且永不重试——用 `ctx.inject(['sidebarRightTabs'], cb)` 驱动类型注册(`tests/native-surface.spec.ts` 以「槽先触发、服务后到达」的顺序守护)。**0.1.6-alpha.2 起**:`SidebarRightGuideEntry.id` 必填且同 provider 内唯一(缺它会静默清空整个承载面);同 provider 的 guide 条目是**追加**关系,`chain` 那种「替换宿主条目」的语义只存在于 `conversation.chat.turnTail` 且已被上游改成 `list`。**0.1.7 起**:`sidebar-right` 本体的契约**零破坏**——槽名无改名、`SidebarRightTabDefinition` 没有新的必填字段、只有新增的可选 `keepMounted` 与 `ISidebarRight.mounted`,`openTabIn/openResourceIn/closeIn` 在具体 class 上字节不变;`tab.visible` 语义收紧为 `active && (...)`(全局面板/后台会话时 body 不挂载);持久化校验收紧(非根 / 列轴 / 嵌套 / 3+ docked pane 的 split 一律判非法**整份丢弃**,0.1.6 的 sidebar 自己产不出这种布局);`RightbarRoot` 现在为每个保留会话渲染一个隐藏 seat(`SidebarSessionViews`,未导出、插件拿不到)。原生 tab 体宿主 `.paneBody` 是**有确定高度的块级滚动容器**(非 flex 容器),native 适配层因此给每个 tab 体包一层 `height:100%` 的列 flex 宿主(`sidebar.module.css` 的 `.nativeTabHost`),tab 组件根继续用 `flex:1`/`height:100%`。全局面板(根级 `main` keyed 槽 + `sidebar.panellist` + `ctx.layout.selectPanel/beginNavigation`、`rightbar` 改根级并新增 `rightbar.session`)插件**不接入**,只做兼容。底部工作台的中心列锚点认 `[data-slot="main.conversation"]`(并跳过 `display: contents` 祖先,同时保留 alpha.1 的 `[data-slot="conversation"]`);文件地址语法跟随 alpha.2(`fileAddressFor` 一律 session 作用域、绝对路径保留前导 `/`、`parseFileAddress` 前缀解析并去 `?`/`#`)。**原生指南说明的 4 条上限(归属更正:这是 0.1.7-alpha.1 起就存在的既成事实,不是 rc.1 的变更)**:`ui-sidebar-right/src/client/tabs/guide/GuideBody.tsx` 的 `MAX_DESCRIBED_ENTRIES = 4` 及其判定行在 alpha.1 / alpha.2 / rc.1 三个 tag 上**逐字相同**,宿主终端的 guide 条目(`ui-sidebar-terminal/src/client/TerminalGuide.tsx`)也是 alpha.1 就有了,`ui-sidebar-terminal/src` 在 rc.1 **零改动**。含义不变:条目 ≤ 4 条才渲染说明,更长的列表是整列丢弃而不是截断——插件默认贡献 4 个 guide 条目(文件 / 文件变动 / 任务管理 / 侧边对话),与宿主的终端条目合计后可能超过 4 条,此时说明不渲染;插件**不恢复**旧的 `nativeGuideDesc` 通用兜底句。 10. **自定义种子必须带 fork 标记对(`meta.isSeeded: true` + `inheritedEventCount`)**:dsh-session 契约「只给 seed 不标 isSeeded,种子算重放历史而非继承前缀」——缺标记时 `Session.ownEvents()` 含整个种子,子会话的 inbox 折重放种子里的 `agent/inbox/spliced`,**继承父会话切割时刻未领取的 inbox 输入**。宿主 `session.fork`(api-session-controller)的调用即规范形态;回归由 `tests/sidechat-seed-validation.spec.ts`(真实 `Session.create` + 对 `ownEvents()` 跑 inbox 折)守护。**0.1.7 复查:`Session.create` 的五个参数与函数体逐字相同**;上游新增了 `buildForkSeed(events, boundary)` 导出,与本插件手工的 `buildSidechatInheritance` 职责重叠但**收益不明确**(插件还有 dangling-tool-call 退回结构化快照的自有需求),暂不替换。 -11. **`dsh.plugin.json` 从来不是 DSH 契约**:0.1.7 全树 grep 仍 **0 命中**。DSH 真正读的是 `package.json` 的 `dsh.bundle.patch` + `dsh.client`(`platform`/`inject`/`immediately`/`external`)+ `exports["./client"]`。`dsh.plugin.json` 只服务第三方 registry(社区市场)通道,`lib/client-registry.js` 同理;`tests/manifest-consistency.spec.ts` 守护的是这条通道的形状,不是 DSH 契约。**`dsh.manifestVersion` 也仍是「只声明、零读」**(0.1.7 的 `package-manifest` README 自己写明「Current installers and loaders do not enforce `dsh.manifestVersion` or `engines.dsh`」)。0.1.7 的官方管控面新增:`plugin-manager` 的**多 registry 回退**(`registryPlan()`:配置源 → `fallbackRegistries`(默认含 `registry.npmmirror.com`),`network`/`timeout`/`not-found`/`no-matching-version` 会自动问下一个源;`registry` 入参只存在于 Web 对话框与 `plugin_manager` 工具,**CLI 没有 registry flag**)、`waitForInstall`、以及**插件展示元数据**:`package.json#icon`(36×36、纯填充无描边、**双色渐变**——与 DSH 自带的 `PluginArtwork*` 同风格:它们「carry their own brand colors instead of riding currentColor」,且每个都是多色,单色版在 30px 行内尺寸下会读成一块纯色板)+ `locale/.json` 的 `meta.title`/`description`。本插件**已采纳**(v0.21.0-rc.1):`icon.svg` + `locale/en.json` + `locale/zh.json`,并由 `tests/plugin-meta.spec.ts` 守护「声明了就必须发布出去」。**rc.1 复查:`packages/boot/app-boot/src/package-meta.ts` 与 `scripts/verify-package-meta.ts` 在两个 tag 上逐字相同**(`ui-plugin-manager/src/client/presentation.ts` 只多了一段 `incompatible-version` 文案),所以上面三条硬规则与「**没有任何尺寸/渐变校验**」都仍然成立。三条硬规则:icon 必须是**包内相对路径**且写进 `files`;`locale/en.json` 是发现基线(缺它整个目录都不读);`exports` 必须暴露 `./locale/*.json` 与 `./package.json`。**所有失败都是静默的**——元数据有问题只会退化成一行纯文字,没有任何报错。`dsh.bundle.patch` 现在多了形状校验(非法值会 `throw`,引导期跳过该 bundle),本插件是合法的单字符串。 +11. **`dsh.plugin.json` 从来不是 DSH 契约**:0.1.7 全树 grep 仍 **0 命中**。DSH 真正读的是 `package.json` 的 `dsh.bundle.patch` + `dsh.client`(`platform`/`inject`/`immediately`/`external`)+ `exports["./client"]`。`dsh.plugin.json` 只服务第三方 registry(社区市场)通道,`lib/client-registry.js` 同理;`tests/manifest-consistency.spec.ts` 守护的是这条通道的形状,不是 DSH 契约。**`dsh.manifestVersion` 也仍是「只声明、零读」**(0.1.7 的 `package-manifest` README 自己写明「Current installers and loaders do not enforce `dsh.manifestVersion` or `engines.dsh`」)。0.1.7 的官方管控面新增:`plugin-manager` 的**多 registry 回退**(`registryPlan()`:配置源 → `fallbackRegistries`(默认含 `registry.npmmirror.com`),`network`/`timeout`/`not-found`/`no-matching-version` 会自动问下一个源;`registry` 入参只存在于 Web 对话框与 `plugin_manager` 工具,**CLI 没有 registry flag**)、`waitForInstall`、以及**插件展示元数据**:`package.json#icon`(36×36、纯填充无描边、**双色渐变**——与 DSH 自带的 `PluginArtwork*` 同风格:它们「carry their own brand colors instead of riding currentColor」,且每个都是多色,单色版在 30px 行内尺寸下会读成一块纯色板)+ `locale/.json` 的 `meta.title`/`description`。本插件**已采纳**(v0.21.1):`icon.svg` + `locale/en.json` + `locale/zh.json`,并由 `tests/plugin-meta.spec.ts` 守护「声明了就必须发布出去」。**rc.1 复查:`packages/boot/app-boot/src/package-meta.ts` 与 `scripts/verify-package-meta.ts` 在两个 tag 上逐字相同**(`ui-plugin-manager/src/client/presentation.ts` 只多了一段 `incompatible-version` 文案),所以上面三条硬规则与「**没有任何尺寸/渐变校验**」都仍然成立。三条硬规则:icon 必须是**包内相对路径**且写进 `files`;`locale/en.json` 是发现基线(缺它整个目录都不读);`exports` 必须暴露 `./locale/*.json` 与 `./package.json`。**所有失败都是静默的**——元数据有问题只会退化成一行纯文字,没有任何报错。`dsh.bundle.patch` 现在多了形状校验(非法值会 `throw`,引导期跳过该 bundle),本插件是合法的单字符串。 12. **peer 声明是启动期硬约束(rc.1 起;本版最容易静默失效的一处)**:`packages/boot/app-boot/src/plugin-compatibility.ts`(rc.1 **新文件**)只做一件事——遍历 profile 行的 `peerDependencies`,对**名字为 `@deepseek-ai/dsh` 或以 `@deepseek-ai/dsh-` 开头**的条目跑 `semver.satisfies(宿主版本, range, { includePrerelease: true })`,不满足就把整行 `row.disabled = true`(group 行连 `group` 一起关掉),只在 stderr 留一行 `disabling profile plugin