docs: record the 0.1.6-alpha.2 adaptation and resync the user docs
Adds the per-version adaptation record (docs/plans/2026-09-21-…-adaptation.md), rewrites AGENTS.md §3 for the new baseline (the two silent contract breaks, the terminal/browser handoff, the new official plugin-manager surface, the three ui-primitives breaks, the widened primitives bare-import set), and updates both READMEs and the integration guide for the v0.20.0 feature set. Also retires the support text the terminal deletion made false: the install flow is one step again (no build-script approval), the FAQ's two node-pty rows collapse into one pointing at DSH's own terminal, the platform note no longer promises prebuilt native binaries (there are no native deps left), and the installers drop the -Repair mode and their allowBuilds writes along with pnpm-workspace.yaml's now-empty allowBuilds block. Removes the dead browserNoSandboxWarning locale key (its setting went with the plugin's browser) and tightens theme.spec's capsule guard so a literal colour fails it. The e2e resolver now refuses a PATH that is not on the baseline line: this machine's PATH is 0.1.6-alpha.1, and running the mount lane against it fails at 'the bundle was not registered' with a zero exit from the CLI — a misleading dead end worth one explicit error instead.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> 面向 **消费插件开发者**:如何让你的插件向 better-sidebar 注册新的侧边栏页面(tab)和文件类型预览器。
|
||||
>
|
||||
> 适用版本:**v0.4.0+**(`ctx.betterSidebar` 服务);声明式设置 **v0.4.1+**;text/number 设置行 **v0.11.0+**;badge/生命周期/定向打开/插件设置/版本探测 **v0.12.0+**;select 设置行(`settingSelect`)与外链认领(`urlTarget`)**v0.13.0+**;统一 `@deepseek-ai/cordis` 类型基底 **v0.15.2+**;终端固定(pin)**v0.17.0+**。当前版本 **v0.19.1**(正式版,npm `latest`,仅支持 DSH **0.1.5-rc.1+**,已在 **0.1.5-rc.2** 上完成真机挂载验证;**0.1.5-alpha.2 及更早不再支持**——alpha.2 用户请用 v0.19.0-alpha.1,那是它的最后一版;0.1.2-rc.1 稳定线请用 v0.18.x)。**v0.19.0 移除了自绘右侧面板与自由窗口**(见 §0、§11)。
|
||||
> 适用版本:**v0.4.0+**(`ctx.betterSidebar` 服务);声明式设置 **v0.4.1+**;text/number 设置行 **v0.11.0+**;badge/生命周期/定向打开/插件设置/版本探测 **v0.12.0+**;select 设置行(`settingSelect`)与外链认领(`urlTarget`)**v0.13.0+**;统一 `@deepseek-ai/cordis` 类型基底 **v0.15.2+**。当前版本 **v0.20.0**(正式版,npm `latest`,仅支持 DSH **0.1.6-alpha.2+**)。**v0.19.0 移除了自绘右侧面板与自由窗口**(见 §0、§11);**v0.20.0 移除了插件自带的终端与浏览器视图**(宿主 0.1.6 自带两者,见 §4.4)、**移除了终端固定(pin)**(终端本身不再由本插件提供),并**删除了轮尾产物行接管**(DSH 0.1.6 把 `conversation.chat.turnTail` 从 chain 改成只能追加的 list,替换语义不复存在)。
|
||||
> 权威代码:`src/client/service.ts`(服务实现)、`src/client/builtins/`(内置 8 tab + 6 viewer 参考实现)、`lib/types/client/service.d.ts`(类型声明)。
|
||||
> 仓库开发规则(硬约束 / CI / 发版)见 [AGENTS.md](../AGENTS.md)。
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
## 0. 承载面:DSH 原生右侧栏 + 插件底部工作台(v0.19.0-alpha.0 起)
|
||||
|
||||
从 v0.19.0-alpha.0 起,**右列完全属于 DSH**:你的 tab 渲染在 **DSH 自己的右侧栏**里(`ctx.sidebarRight` / `ctx.sidebarRightTabs`),插件把每个 `TabDescriptor` 注册成原生 tab 类型(`kind = descriptor.id`)+ 一个原生 tab 体。插件自己只保留**底部工作台**(分栏树、终端、会话内持久化)。对你的接入代码**没有影响**——仍然只调用 `ctx.betterSidebar`:
|
||||
从 v0.19.0-alpha.0 起,**右列完全属于 DSH**:你的 tab 渲染在 **DSH 自己的右侧栏**里(`ctx.sidebarRight` / `ctx.sidebarRightTabs`),插件把每个 `TabDescriptor` 注册成原生 tab 类型(`kind = descriptor.id`)+ 一个原生 tab 体。插件自己只保留**底部工作台**(分栏树、会话内持久化;**v0.20.0 起其中不再有终端**——宿主 0.1.6 的右侧栏终端取代了它)。对你的接入代码**没有影响**——仍然只调用 `ctx.betterSidebar`:
|
||||
|
||||
- `registerTab` / `registerFileViewer` 签名不变;
|
||||
- `openTab` / `openFile` 默认落到原生右侧栏;新增可选 `OpenTabSeed.target`(`'right'` 默认 / `'bottom'` 落插件的底部工作台);
|
||||
@@ -21,7 +21,7 @@
|
||||
| 事项 | 说明 |
|
||||
|---|---|
|
||||
| 生命周期回调 | 原生面只有「一次打开」,不区分新建/聚焦,因此只触发 `onOpen`(`onActivate` 仅在插件自己的底部工作台里触发) |
|
||||
| 去重 | 原生按 `(kind, 地址)` 去重:有 `createTab` 的类型每次新开一个 tab(terminal / browser / sidechat / diff),其余聚焦已有 tab;`dedupeKey` 的自定义语义不参与原生面 |
|
||||
| 去重 | 原生按 `(kind, 地址)` 去重:有 `createTab` 的类型每次新开一个 tab(sidechat / diff),其余聚焦已有 tab;`dedupeKey` 的自定义语义不参与原生面 |
|
||||
| 布局持久化 | 原生栏的布局**只在内存**(刷新后回到折叠默认),插件自己的底部工作台仍然持久化 |
|
||||
| 跨会话打开 | 目标会话的右侧栏 store 未挂载时,打开会排队到该会话上屏后重放 |
|
||||
| 内置类型接管 | 插件的 `editor` 类型以 `extension` 优先级认领 `dsh-resource://file/**`(压过内置 `ui-sidebar-documentpreview` 的 `text` 预览——即 `fallback` 带),并接管内置 `files` 页面 kind(`openTab('files')` 打开插件的文件树);插件卸载/禁用时内置实现自动复位 |
|
||||
@@ -42,7 +42,7 @@ better-sidebar 从 v0.4.0 起把自己改造成一个**注册表服务**:
|
||||
- **新页面(tab)**:注册一种新的侧边栏 tab 类型,出现在侧边栏 `+` 菜单里,用户点击后在自己的分栏里打开你的 React 页面;
|
||||
- **文件预览器(file viewer)**:注册一种文件类型预览器,让用户在侧边栏打开文件时走你的渲染组件(覆盖或补充内置的 image/pdf/code 等)。
|
||||
|
||||
内置的 7 个 tab(editor / git——「文件变动」统一 tab(Git 视角 + 本轮文件视角)/ subagent / sidechat / terminal / browser / diff)和 6 个 viewer(image / pdf / markdown / html / code / binary-download)**自己也是通过同一套 API 注册的**(吃自己的狗粮),所以外部插件的能力与内置功能完全对等。
|
||||
内置的 5 个 tab(editor / git——「文件变动」统一 tab(Git 视角 + 本轮文件视角)/ subagent / sidechat / diff)和 6 个 viewer(image / pdf / markdown / html / code / binary-download)**自己也是通过同一套 API 注册的**(吃自己的狗粮),所以外部插件的能力与内置功能完全对等。**v0.20.0 起不再内置 terminal / browser 两个 tab 类型**:DSH 0.1.6 自带右列终端(`ui-sidebar-terminal`、kind `terminal`)与浏览器(`ui-sidebar-browser`、kind `browser`),插件的同名类型会让读者看到两份实现。
|
||||
|
||||
关键机制一句话:better-sidebar 的 client half 在 `apply()` 开头执行 `ctx.provide('betterSidebar', service)`(`src/client/index.tsx`),消费插件在 `inject` 里声明 `'betterSidebar'`,Cordis 保证服务就绪后才激活你的插件,然后你调用 `ctx.betterSidebar.registerTab(...)` / `registerFileViewer(...)` 完成注册,返回的 disposer 由 Cordis fiber 在卸载(HMR / 禁用)时自动调用。
|
||||
|
||||
@@ -192,11 +192,11 @@ interface TabDescriptor {
|
||||
description?: string | (() => string)
|
||||
/** 图标:ReactNode 或 (size: number) => ReactNode(不声明时宿主补一个方块占位) */
|
||||
icon?: ReactNode | ((size: number) => ReactNode)
|
||||
/** + 菜单排序(升序);默认 100。内置:editor=10, git=20, subagent=30, sidechat=35, terminal=40, browser=50 */
|
||||
/** + 菜单排序(升序);默认 100。内置:editor=10, git=20, subagent=30, sidechat=35 */
|
||||
order?: number
|
||||
/** 从 + 菜单隐藏(editor/diff 用:由其他流程触发打开,不在菜单里) */
|
||||
hidden?: boolean
|
||||
/** + 菜单禁用判定(如 terminal 配额满)。返回 false 只影响菜单 disabled,不拦截 openTab(只有设置页禁用开关会)。 */
|
||||
/** + 菜单禁用判定(用量配额一类)。返回 false 只影响菜单 disabled,不拦截 openTab(只有设置页禁用开关会)。 */
|
||||
available?: (ctx: Context, scope: SessionScope, state: SidebarState) => boolean
|
||||
/**
|
||||
* 单实例语法糖:`single: true` ≡ `dedupeKey: () => id`(打开时聚焦既有
|
||||
@@ -212,7 +212,7 @@ interface TabDescriptor {
|
||||
dedupeKey?: (tab: SidebarTab) => string | undefined
|
||||
/**
|
||||
* 自定义 tab 创建(minting SidebarTab + 状态 patch)。
|
||||
* 返回 null 拒绝创建。terminal 用它生成 terminal:<n> id 并递增 nextTerminal。
|
||||
* 返回 null 拒绝创建。sidechat 用它实现「一个线程一个 tab」。
|
||||
* 省略时用默认 { id, type, title } + seed 里的 path/diff。
|
||||
*/
|
||||
createTab?: (state: SidebarState) => { tab: SidebarTab; patch?: Partial<SidebarState> } | null
|
||||
@@ -220,7 +220,8 @@ interface TabDescriptor {
|
||||
* 外链认领(v0.13.0+,`features.includes('urlTarget')` gate):外链被接管
|
||||
* (`browserInterceptLinks` 总闸 + 协议开关均开)时,第一个 urlTarget 命中且未禁用的
|
||||
* 类型以 openTab({ type, url, title: hostname }) 打开,URL 预填 tab.path。先到先得;
|
||||
* 谓词抛错被吞。内置 browser 不声明、永远隐式兜底。
|
||||
* 谓词抛错被吞。v0.19.1 及更早内置 browser 隐式兜底;**v0.20.0 起本插件不再有
|
||||
* browser 类型**,无可认领的默认目标时接管直接放行给系统浏览器。
|
||||
* 多 URL 并存需 createTab 铸造 per-URL id,否则二次点击被 id 安全网聚焦、不覆写 path。
|
||||
*/
|
||||
urlTarget?: (url: URL) => boolean
|
||||
@@ -240,8 +241,8 @@ interface TabDescriptor {
|
||||
* - onOpen:openTab 真正**新建** tab 后(dedupe/id 安全网聚焦不算打开);
|
||||
* - onActivate:tab 被聚焦时(dedupe 聚焦、id 安全网聚焦、tab 栏点击激活);
|
||||
* - onClose:closeTab 关闭 tab 后。
|
||||
* 内置专属流程(diff 拆分放置、agent 终端 reconcile)直接改 state,不触发
|
||||
* 回调——但它们只作用于内置类型(diff/terminal),外部插件的 tab 永远走
|
||||
* 内置专属流程(diff 拆分放置、sidechat 线程重开)直接改 state,不触发
|
||||
* 回调——但它们只作用于内置类型(diff / sidechat),外部插件的 tab 永远走
|
||||
* service 路径。回调抛错只 console.error,绝不打断打开/关闭流程。
|
||||
* openTab 回调 scope 携带调用者传入的 { sessionId, cwd? };
|
||||
* closeTab/activateTab 仅显式传 scope 时带 cwd。
|
||||
@@ -356,7 +357,7 @@ ctx.effect(() =>
|
||||
)
|
||||
```
|
||||
|
||||
**自定义创建**(mint 自增 id,terminal 内置页同款):
|
||||
**自定义创建**(mint 自增 id):
|
||||
|
||||
```ts
|
||||
ctx.effect(() =>
|
||||
@@ -365,8 +366,8 @@ ctx.effect(() =>
|
||||
title: 'Console',
|
||||
order: 80,
|
||||
createTab: (state) => ({
|
||||
tab: { id: `console:${state.nextTerminal}`, type: 'my-plugin:console', title: `Console ${state.nextTerminal}` },
|
||||
patch: { nextTerminal: state.nextTerminal + 1 }, // 借内置计数器;也可自建 state 字段
|
||||
tab: { id: `console:${state.nextBrowser}`, type: 'my-plugin:console', title: `Console ${state.nextBrowser}` },
|
||||
patch: { nextBrowser: state.nextBrowser + 1 }, // 借内置计数器;也可自建 state 字段
|
||||
}),
|
||||
component: ({ tab, scope }) => <ConsoleView tabId={tab.id} sessionId={scope.sessionId} />,
|
||||
})
|
||||
@@ -400,12 +401,19 @@ ctx.effect(() => {
|
||||
| `git` | 20 | 是 | 是(本轮文件操作数) | 「文件变动」统一 tab(id 保留 `git` 以兼容持久化布局):**Git 视角**(原 Git 面板:staged/unstaged / 提交 / 历史 / worktree·子仓库选择)+ **本轮文件视角**(原 file-trace:模型读/写/编辑实时折叠,按文件分组、类型筛选);会话事件经插件自有宿主路由 `changes.ops` 供给(live 日志优先、冷会话回放持久化记录,`afterSeq` 增量),badge 读 tab 轮询写入的同步缓存。两视角共用底部可拖拽预览面板(`tab.meta.lens/previewH` 持久化),diff 渲染统一走 `src/client/diff/`(`DiffRows`/`DiffFiles`:mod 配对 + 行内高亮 + 语法着色 + 上下文折叠);Git 目标可展开为独立 diff tab(落进工作台的 diff 分栏) |
|
||||
| `subagent` | 30 | 是 | 否 | 子代理拓扑 |
|
||||
| `sidechat` | 35 | 否(`sidechat:<uuid>`,按 `meta.threadId` 去重) | 否 | 侧边对话(每对话一 Tab):打开即建空线程(首条消息赢得标签并同步标题);线程 = 插件自建子会话(种子继承父会话上下文,进行中回合以 `interrupted` 闭合;种子带合法 `subagent/descriptor`,SubagentView 按 `Side: ` 前缀过滤),`origin:'subagent'` 隐藏于主列表;走 `/sidebar/api/sidechat.*` 路由;头部菜单切换/重开(`parkSidechatReopen` + 确定性 id),关 Tab 释放 live agent;重开经 `collectOwnEvents` 回源到种子边界;「保存为新会话」= `session.fork`(`this` 敏感)。[设计文档](plans/2026-08-20-sidechat-tab-design.md) |
|
||||
| `terminal` | 40 | 否(`terminal:<n>`) | 否 | 终端。v0.17.0+ 右键「固定到工作区/全局」:跨会话不消失,TabBar 内联虚拟 Tab(`pinned:<homeSessionId>:<tabId>`),就地按 home scope 连 PTY;global 全会话可见、workspace 仅同 cwd;`tab.pin = { scope, homeCwd? }` 随会话持久化,渲染期解析(`collectPinnedTabs` → `createPinnedVirtualTab` → `injectPinnedIntoTree`) |
|
||||
| `browser` | 50 | 否(`browser:<n>`) | 否 | 内嵌浏览器(沙箱 iframe,可设置关沙箱) |
|
||||
| `diff` | -1 | 否(按 id 去重) | 是 | 差异查看(changes tab 的预览面板「展开为独立页签」触发,同一渲染栈) |
|
||||
|
||||
你的 `id` 不可与上述重复,否则 `registerTab` 抛 `"tab type \"X\" already registered"`。
|
||||
|
||||
**v0.20.0 删除的类型(迁移提示)**:
|
||||
|
||||
| 原 id | 现状 | 你该怎么做 |
|
||||
|---|---|---|
|
||||
| `terminal` | 宿主自带 `kind: 'terminal'`(`@deepseek-ai/dsh-client-ui-sidebar-terminal`) | 不要再注册同名 kind。插件侧 API(`api.ptyClose` / `agentPtyClose` / `agentSkipWait` / `terminalDeps` / `shellGet`)与 `SidebarTab.pin` / `SidebarState.nextTerminal` / `agentWaits` / `isAgentTabId` / `agentUuidOf` / `reconcileAgentTerminals` / `mirrorAgentWaits` / `setTabPin` 全部**已删除** |
|
||||
| `browser` | 宿主自带 `kind: 'browser'`(`@deepseek-ai/dsh-client-ui-sidebar-browser`) | 要打开网页请走宿主:`ctx.sidebarRight.openTab('browser', { params: { url } })`(宿主未装该包时 `openTab` 会抛,务必 try/catch 兜底)。插件侧的 `api.browserProbe` / `SidebarPrefs.browserNoSandbox` / `browserAllowedLoopback` 已删除;`browserInterceptLinks` / `browserInterceptHttp` / `browserInterceptHttps` **保留**(链接接管仍在,`urlTarget` 机制不变) |
|
||||
|
||||
**同时删除的还有**:轮尾产物行接管。DSH 0.1.6 把 `conversation.chat.turnTail` 从 `chain` 改成 `list`(list 槽要求 `options.id`,且只能**追加**,不能替换宿主的产物行),插件因此整体移除了 `registerTurnTailInterception` 与 `selectProducedFiles`。要往那个位置加东西,按 list 契约注册 `{ name: 'conversation.chat.turnTail', id: '<你的 id>', order }`;`openSidebarFile` 作为通用打开工具留在 `dsh-better-sidebar/src/client/sidebar-file.ts`(`resolveSidebarPath` 移到 `src/client/paths.ts`)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 文件预览器(FileViewer)注册 API
|
||||
@@ -811,7 +819,7 @@ ctx.effect(() =>
|
||||
- 展示:小卡片网格(图标 + 标题 + 类型 id),**高亮 = 启用**,勾选徽标钉在卡片最右端;viewer 卡片额外显示扩展名。
|
||||
- 持久化:开关写入 `SidebarPrefs.tabsEnabled / viewersEnabled`(开放 map,**缺省 = 启用**,显式 `false` 才禁用)。
|
||||
- 关闭语义:tab 从 `+` 菜单消失、`openTab` 拒绝新开(`console.warn`)、派生流程(子代理自动展开、agent 终端自动补 tab)停止,**已打开的 tab 保留**;viewer 被 `matchFileViewer` 跳过,文件落到下一个匹配。
|
||||
- `settings.toggles`(可选):在卡片行下追加**嵌套设置行**(仅父级启用时显示),绑定 `SidebarPrefs` 字段;通过卡片底部「功能设置」条在原生弹窗中编辑。行控件形状见 §4.1 的 `SettingRow`:`type: 'switch' | 'text' | 'number'`(v0.11.0+;text/number 行 blur/Enter 提交,number 行按 min/max 钳制,unit 渲染单位后缀)与 `type: 'select'`(v0.13.0+;`options` 支持 value/title/desc/icon,`multi` 多选存数组并按 options 顺序提交;任一项带 icon 时渲染大图标选项卡)。内置示例:subagent tab 的 `autoOpenSubagent`、terminal tab 的 `agentTerminalTools` + 自定义字体行、editor tab 的 `editorExplorer` 图标化下拉与 `workspaceFence` 开关(工作区路径围栏,见 §8.1)。
|
||||
- `settings.toggles`(可选):在卡片行下追加**嵌套设置行**(仅父级启用时显示),绑定 `SidebarPrefs` 字段;通过卡片底部「功能设置」条在原生弹窗中编辑。行控件形状见 §4.1 的 `SettingRow`:`type: 'switch' | 'text' | 'number'`(v0.11.0+;text/number 行 blur/Enter 提交,number 行按 min/max 钳制,unit 渲染单位后缀)与 `type: 'select'`(v0.13.0+;`options` 支持 value/title/desc/icon,`multi` 多选存数组并按 options 顺序提交;任一项带 icon 时渲染大图标选项卡)。内置示例:subagent tab 的 `autoOpenSubagent`、editor tab 的 `editorExplorer` 图标化下拉与 `workspaceFence` 开关(工作区路径围栏,见 §8.1)。
|
||||
- `settings.pluginToggles`(可选,v0.12.0+):**插件自有设置行**,行控件与 toggles 相同,但 key 是插件局部的——持久化在 prefs 文档的 `pluginSettings[<descriptor id>]`(开放 map,无需宿主 schema 字段)。tab 与 viewer 都可用(v0.12.0 起 viewer 卡片也有设置条)。
|
||||
- `settings.render`(可选,v0.12.0+):**自定义设置面板**——追加渲染在行列表之后,可单独存在。props 含 store/service/prefs、本 descriptor 的 `pluginSettings` blob、`updatePluginSetting(key, value)` 与 `close()`;抛错会被吞掉并显示内联错误。
|
||||
|
||||
@@ -851,7 +859,7 @@ ctx.effect(() =>
|
||||
)
|
||||
```
|
||||
|
||||
> ⚠️ **`toggles` 的 key 必须是宿主 PrefsSchema 的字段**(内置键:`autoOpenSubagent` / `agentTerminalTools` / `agentOpenTools` / `terminalFontFamily` / `terminalFontSize` / `editorExplorer` / `workspaceFence` / `htmlViewerNoSandbox` / `htmlViewerDefaultUnsafe` / `browserNoSandbox` / `browserInterceptLinks` / `browserInterceptHttp` / `browserInterceptHttps` / `changesDiffFloat`)。**v0.12.0 起设置 seam 已开放**:你自己的设置走 `pluginToggles`(声明式行)或 `render`(自定义面板),值持久化在 `pluginSettings[id]`——不再需要宿主 schema 字段,也不再被 seam 丢弃。值须 JSON 可序列化(行控件只产出 string/number/boolean;自定义面板自行负责)。
|
||||
> ⚠️ **`toggles` 的 key 必须是宿主 PrefsSchema 的字段**(v0.20.0 起的内置键:`autoOpenSubagent` / `autoOpenJobs` / `agentOpenTools` / `editorExplorer` / `workspaceFence` / `titleBarScheme` / `titleBarPresetId` / `customCss` / `titleBarCompat` / `titleBarStripPx` / `htmlViewerNoSandbox` / `htmlViewerDefaultUnsafe` / `browserInterceptLinks` / `browserInterceptHttp` / `browserInterceptHttps` / `tabsEnabled` / `viewersEnabled`;**已删除**:`agentTerminalTools` / `terminalShell` / `terminalShellArgs` / `terminalFontFamily` / `terminalFontSize` / `bottomPanelAutoTerminal` / `browserNoSandbox` / `browserAllowedLoopback`)。**v0.12.0 起设置 seam 已开放**:你自己的设置走 `pluginToggles`(声明式行)或 `render`(自定义面板),值持久化在 `pluginSettings[id]`——不再需要宿主 schema 字段,也不再被 seam 丢弃。值须 JSON 可序列化(行控件只产出 string/number/boolean;自定义面板自行负责)。
|
||||
|
||||
### 8.1 内置键 `workspaceFence`(工作区路径围栏)
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# DSH 0.1.6-alpha.2 适配(v0.20.0 基线)
|
||||
|
||||
日期:2026-09-21 分支:`feat/dsh-0.1.6-alpha.2`
|
||||
|
||||
## 背景
|
||||
|
||||
DSH `dsh-v0.1.5-rc.2..dsh-v0.1.6-alpha.2`(上游 **1687 个 commit**,中间还有 `dsh-v0.1.6-alpha.1`;`git diff --shortstat` = 5420 个变更文件 / +878 484 / −63 637)是 0.1.6 线的第二个预发布版,也是本插件自 rc.1 以来第一次遇到**真实破坏**的 delta。两件关键的事实构成这版全部工作:
|
||||
|
||||
1. **两条插件面向的硬契约收紧,且都是静默失败**(§上游 delta 1/2);三条 `ui-primitives` 破坏是编译期可见的。
|
||||
2. **宿主内置了插件原本自研的两个面**(右列终端、右列浏览器)。继续保留就是同一面存在两套实现,并且遮蔽产品正在开发中的宿主类型。
|
||||
|
||||
本计划记录该 delta、逐项结论与子系统级落点;上一版记录见 [2026-09-10-dsh-0.1.5-rc.2-adaptation.md](./2026-09-10-dsh-0.1.5-rc.2-adaptation.md),仓库级适配要点同步到 [AGENTS.md](../../AGENTS.md) §3(本版整体重写,是该节的权威摘要)。
|
||||
|
||||
本版由三条 commit 组成(`git log --oneline -4`,`git show --stat <sha>`):
|
||||
|
||||
| # | commit | 主题 | 规模 |
|
||||
|---|---|---|---|
|
||||
| 1 | `58ba17d` | `fix(native): meet DSH 0.1.6-alpha.2 guide-id and turn-tail contracts` | 17 files,+135 / −611 |
|
||||
| 2 | `d608998` | `chore(baseline): target DSH 0.1.6-alpha.2 and drop 0.1.5-rc support` | 27 files,+448 / −350 |
|
||||
| 3 | `5912274` | `feat: yield the terminal and browser to DSH 0.1.6's own sidebar types` | 79 files,+381 / −9 464 |
|
||||
|
||||
## 上游 delta(rc.2 → alpha.2,影响本插件的部分)
|
||||
|
||||
所有 alpha.2 结论都用 tag 取证(`git -C /tmp/dsh-0162 diff dsh-v0.1.5-rc.2 dsh-v0.1.6-alpha.2 -- <path>` / `git show dsh-v0.1.6-alpha.2:<path>`),不引用工作树、不引用记忆。
|
||||
|
||||
| # | 上游变更 | 上游文件(alpha.2) | 对插件的影响 |
|
||||
|---|---|---|---|
|
||||
| 1 | `SidebarRightGuideEntry.id` 变**必填**且同一 provider 内唯一;注册时 `new Set(entries.map(e => e.id)).size !== entries.length` 抛 `duplicate guide entry id` | `packages/client/ui-sidebar-right/src/client/tab-registry.ts`(抛点 `:251`,接口定义 `:61` 起;单测 `tests/tab-registry.client.spec.ts:302`) | **破坏(静默)**:插件两个 guide 条目都在 `undefined` 上相撞 → `register` 抛错 → 抛点在本插件 `ctx.inject(['sidebarRightTabs'], cb)` 的**回调体内**被 cordis 吞掉 → 整个原生承载面空掉(指南无条目、`openTab` 全拒)。→ §决策 6 |
|
||||
| 2 | `conversation.chat.turnTail` 由 `chain` 改 `list`;list 槽要求 `options.id`,缺则抛 `list slot "…" requires options.id` | `packages/client/ui-chat/src/client/contract/slots.ts:213`;抛点在 `packages/client/ui-slots/src/index.ts:1230` | **破坏(静默)**:插件传 `select` 直接抛(同样被吞)。语义也变了——chain 的 `select` 让一个条目**替换**宿主产物行,list 只能**追加**。→ §决策 5 |
|
||||
| 3 | `ui-primitives` 三处导出面变化:`IconSendOutline16` 不再导出(只剩 `IconSendOutline14`);`TerminalBlockLabels` 新增**必填** `noExitCode`;`ConnectionIndicator` 删掉 `reconnectLabel` | `packages/client/ui-primitives/src/icons/index.tsx`(`IconSendOutline16` 在 rc.2 有、alpha.2 无)、`src/**`(`git diff … -- packages/client/ui-primitives/src` 里 `−reconnectLabel` / `+noExitCode`) | **破坏(编译期)**:适配见 §改动清单「基线」 |
|
||||
| 4 | 宿主自带右列终端:`ui-sidebar-terminal` + `api-terminal-controller`;模型侧等价物 `packages/terminal/tool-terminal`(包名 `@deepseek-ai/dsh-tool-terminal`)**未被任何 shipped bundle 挂载** | `packages/client/ui-sidebar-terminal/**`、`packages/api/terminal-controller/**`、`packages/terminal/tool-terminal/**` | **让位** → 插件整套 PTY 栈删除。证据:`git grep -rn tool-terminal -- packages/bundle/` 在 alpha.2 只有 **1 处命中且是注释**(`packages/bundle/web-app/cordis.patch.yml:404`,讨论后台 job 注册表的生产者),没有任何 bundle 行挂载该包 |
|
||||
| 5 | 宿主自带 `browser` kind,且 `ui-chat` 已内置把正文 http(s) 链接委派给它 | `packages/client/ui-sidebar-browser/**`;`packages/client/ui-chat/src/client/apply.ts:147`(`ctx.get('sidebarRightTabs')?.get('browser')` 守卫后 `ctx.sidebarRight.openTab('browser', { params: { url } })`) | **让位** → 插件 `browser` tab 类型与宿主 `browser.probe` 路由删除;DOM 层链接接管保留(宿主不做按协议分流) |
|
||||
| 6 | 原生右列布局**现在持久化**:`localStorage` 的 `dsh.sidebar-right.v1.<sessionId>`(zod 校验,tab 体渲染前恢复) | `packages/client/ui-sidebar-right/src/client/persistence.ts`(`sidebarPersistence = 'dsh.sidebar-right.v1'`)、`README.zh.md:72` | §3.10 里「原生布局只在内存,不做持久化」的旧说法对 0.1.6 起**已失效**;插件不接入、只用现成面 |
|
||||
| 7 | `sidebar-right` 新增面:`SidebarRightTabDefinition.multiple?`、`SidebarRightPlacement.preferNewPane?`、class 上的 `openTabs` / `tabsIn(sessionId)` / `registerCloseHandler(kind, handler)`(**不在** `ISidebarRight` 接口里)、keyed 槽 `sidebar.right.tab.guide.entry` | `tab-registry.ts:103`、`contract/slots.ts:117`、`service.ts:218/228/261/387` | 插件只用到 `multiple`;其余仅保证不破坏 |
|
||||
| 8 | 官方管控面:新增 `packages/boot/plugin-manager`(`ctx.pluginManager` + `plugin_manager` 工具)与 `packages/client/ui-plugin-manager`(Web Plugins 页);`dsh.profile.patchReload` 在 alpha.2 已从**代码与文档整体消失**(rc.2 由 `apps/cli/src/plugin.ts` / `profile-boot.ts` 读取 + 4 份 `apps/cli/**` 文档 10 处说明;alpha.2 在 `packages/` + `apps/` 上 **0 命中**,只剩 `.agents/notes` 的陈旧文本);HMR 归 `packages/boot/hmr` | 同上 | **零额外声明**:两者都会接管任何已声明 `dsh.bundle.patch` 的包(本插件 `package.json` 的 `dsh.bundle.patch` = `./cordis.patch.yml`),本插件无需适配。`patchReload` 本插件**从未消费**(`grep -rn patchReload src tests scripts` 零命中),因此删除对本插件零影响 |
|
||||
| 9 | 依赖形状:`ui-primitives` 仍**不声明** `dependencies`(`peerDependencies` 只有 `@deepseek-ai/cordis`),bundle 新增裸 import `diff`(`src/DiffBlock.tsx:3`)与 `simple-icons`(`src/SiteGlyph.tsx:44`,全新 3 万余品牌图标集);`packages/code-runtime` **整包消失**(rc.2 还在,npm 上停在 0.1.5-alpha.2) | `packages/client/ui-primitives/package.json`、`packages/client/ui-primitives/src/*` | devDependencies 提升 `diff` / `simple-icons`;移除 `@deepseek-ai/dsh-code-runtime` |
|
||||
|
||||
**明确未变(不必再核)**:
|
||||
|
||||
- 指南「列出的条目 ≤ 4 条才渲染说明」的规则仍在:`MAX_DESCRIBED_ENTRIES = 4`(`packages/client/ui-sidebar-right/src/client/tabs/guide/GuideBody.tsx:40`)。
|
||||
- 一次性 token 鉴权、斜杠 RPC 方言、`MarkdownText` labels 嵌套契约、会话事件模型(`assistant/assistant-stream` 瞬时帧):全部沿用 0.1.5 线结论,本版零改动。
|
||||
- 插件的承载面用法不变:仍由 `ctx.inject(['sidebarRightTabs'], cb)` 驱动类型注册(**不能**等槽声明),仍只经 `ctx.sidebarRight.openTab/openResource/close` 与具体类上的 `openTabIn/…` 操作。
|
||||
- 宿主仍有 chain 槽(如 `conversation.composer`、`sidebar.right.tab.guide`),但本插件**从未**注册它们——插件的槽面只有三处:`conversation.session.header.utilities`(底部工作台开关)、`settings.section`、原生 `sidebar.right.pane.tab` / `.title`;唯一用过的「整行接管」型贡献(轮尾产物行)随 `turnTail` 改 list 而删除(#2)。
|
||||
- `dsh.plugin.json` **不是** DSH 契约:`git grep -n 'dsh\.plugin\.json'` 在 `dsh-v0.1.5-rc.2` 与 `dsh-v0.1.6-alpha.2` 上**均 0 命中**。它只服务第三方 registry(社区市场)通道,`lib/client-registry.js` 同理。
|
||||
|
||||
## 决策
|
||||
|
||||
1. **peer 下限必须动:`^0.1.5-rc.1` → `^0.1.6-alpha.2`**。semver 的预发布规则要求比较子与候选版本同 `[major,minor,patch]` 元组,`^0.1.5-rc.1` 在数学上**永远容纳不下任何 `0.1.6-*` 预发布版**。实测(用仓库树内的真实 `semver@7.8.5`):
|
||||
|
||||
| range \ candidate | `0.1.5-rc.2` | `0.1.6-alpha.1` | `0.1.6-alpha.2` | `0.1.6-alpha.3` | `0.1.6-rc.1` |
|
||||
|---|---|---|---|---|---|
|
||||
| `^0.1.5-rc.1` | true | false | false | false | false |
|
||||
| `^0.1.5-rc.2` | true | false | false | false | false |
|
||||
| `*`(裸) | false | false | false | false | false |
|
||||
| `^0.1.6-alpha.2` | false | false | **true** | **true** | **true** |
|
||||
|
||||
即:0.1.5 的下限不是「保守」而是**结构上不可能**容纳 0.1.6 预发布;`^0.1.6-alpha.2` 天然容纳 0.1.6 的 alpha/rc 全线并排除 0.1.5。
|
||||
2. **支持线取舍:0.1.5-rc.\* 用户停留在 v0.19.1**。rc.2 → alpha.2 有真实破坏(#1/#2),按本仓库先例(v0.19.0 抬到 0.1.5-rc.1 时直接桥掉 0.1.2 线)**不写运行时兼容层**;0.1.5-alpha.2 及更早同样停留在旧版。`engines.dsh` 与 peer 下限同源(同一个支持范围,两处不能分叉)。
|
||||
3. **终端整体让位**:删除整套 PTY 栈(见改动清单),宿主 `ui-sidebar-terminal` 成为唯一提供者。**接受模型侧代价并写进 README**:插件原有的 8 个 `terminal_*` 工具(`terminal_create/list/send/read/wait/resize/signal/close`,默认关)是模型唯一的**跨调用持久**终端,而上游等价物 `@deepseek-ai/dsh-tool-terminal` 未被任何 shipped bundle 默认挂载,删除后模型只剩一次性 `bash` / `pwsh`——这是「不维护第二套 PTY」的既定代价,不是意外,所以不补自家的替代实现。
|
||||
4. **浏览器让位,但保留 DOM 层链接接管**。宿主不做按协议分流(https 默认交系统浏览器),那是插件唯一还能提供的能力,因此 `src/client/link-intercept.ts` 与已发布的 `urlTarget` 机制(v0.13.0+,第三方消费插件按它声明)不动;接管目标改指**宿主的** `browser` kind(`ctx.sidebarRight.openTab('browser', { params: { url } })`),并保留 `window.open` 兜底——宿主没装该包时不至于点了没反应。
|
||||
5. **轮尾接管整体删除**。list 只能追加,插件再注册一行只会与宿主的产物卡**重复**;上游是有意选 list(「An additive list lets both plugins contribute without either knowing the other's data or rendering」,`.agents/notes/implemented/feature/2026-09-17-persistent-plan-cards.md:27`)。产物行交回宿主,它开文件走宿主 `openFile` → 本插件的 editor,**行为不变**。文件打开的那一半(`sidebar-file.ts`)与路径助手(`paths.ts` 的 `resolveSidebarPath`)保留,因为文件变动 tab 与编辑器宿主仍在用。
|
||||
6. **guide 条目 id 作为类型契约落进插件侧**:两个条目各自带 `id`(descriptor id / `'files'`),并加测试守护「每个 guide 条目 id 非空且互不重复」。**不做运行时兜底**(自动补 id 会把契约破坏变成永久沉默,正是这次最难查的形态)。
|
||||
7. **加一条可见的失败接缝**:`registerNativeSurface` 新增可选 `reportFailure(phase, error)`,接到 `src/client/index.tsx` 的 `fail()` 可见诊断条;单个 descriptor 注册失败不再带走其余注册。理由就是这次的症状——整个原生承载面静默空掉,除了一行缺失的指南之外没有任何提示。
|
||||
8. **卡片采用宿主胶囊配方,但字重走令牌阶梯**:几何/描边/填充/圆角照抄宿主 `GuideBody.module.css` 的 `.entry`(0.5px `--dsw-alias-border-l4` / 24px 圆角 / `--dsw-alias-bg-layer-1` / `min-height: 56px` / `padding: 14px 20px`),标签字号**不抄**宿主那里的裸 `font-size: 15px`(它背后没有令牌),改用阶梯最近一档 `--dsw-font-s-strong-14`(14px medium)。皮肤契约要求插件侧零豁免面(AGENTS.md §5)。
|
||||
9. **不接入本版新增的宿主面**:`openTabs` / `tabsIn` / `registerCloseHandler` / `preferNewPane` / keyed `guide.entry` 槽 / 全局面板——插件只用到 `multiple` 与既有面。
|
||||
|
||||
## 改动清单(子系统级)
|
||||
|
||||
| 子系统 | 文件 | 变更 |
|
||||
|---|---|---|
|
||||
| 基线 | `package.json` | `version` → `0.20.0`;15 个 `@deepseek-ai/*` peer 下限 `^0.1.5-rc.1` → `^0.1.6-alpha.2`;devDependencies 同步钉 `0.1.6-alpha.2`;移除 `@deepseek-ai/dsh-code-runtime`(上游整包消失);新增 `diff` `^9.0.0`、`simple-icons` `^16.32.0`(`ui-primitives` 裸 import,不走 devDependencies 就 `Cannot find package`) |
|
||||
| 基线 | `dsh.plugin.json` | `version` → `0.20.0`;`engines.dsh` → `>=0.1.6-alpha.2` |
|
||||
| 基线 | `pnpm-lock.yaml` | 锁文件对齐 alpha.2(646 行变动) |
|
||||
| 基线 | `.github/workflows/ci.yml` | mount 车道钉 `@deepseek-ai/dsh@0.1.6-alpha.2`;注释改为「alpha.2 是 npm 的 `alpha` dist-tag,`latest` 仍是 0.1.5 线」 |
|
||||
| 基线 | `scripts/e2e-common.sh` | 回退 npx **显式钉版**:新增 `DSH_NPX_SPEC`(缺省 `@deepseek-ai/dsh@0.1.6-alpha.2`)——不钉就会解析 `latest`,而 `latest` 仍是插件已不支持的 0.1.5 线 |
|
||||
| 基线 | `src/client/service.ts` | `SIDEBAR_SERVICE_VERSION` → `0.20.0`(`tests/service.spec.ts` 断言它 === `package.json` 的 version) |
|
||||
| 基线 | `src/client/SideChatView.tsx` | `IconSendOutline16` → `IconSendOutline14 size={16}`(保住原 16px 视觉尺寸);`TerminalBlockLabels` 补必填 `noExitCode: t('sideChatBlockNoExitCode')`;`ConnectionIndicator` 去掉 `reconnectLabel` |
|
||||
| 基线 | `src/client/locales.ts` + 19 份 `locales-*.ts`(共 20 份词典) | 新增 `sideChatBlockNoExitCode`(zh「已结束(无退出码)」/ en「Ended without an exit code」+ 各语言);`sideChatConnDisconnected` 改口径为「连接已断开,点击立即重连」——断线标签现在**自身**命名重试动作,跟随宿主文案 |
|
||||
| Tab 契约 | `src/client/native/index.ts` | 两个 guide 条目各带 `id`(descriptor id / 接管条目 `'files'`);`NativeSurfaceDeps` 新增可选 `reportFailure?: (phase: string, error: unknown) => void`,wrapper 内 try/catch 上报(单个 descriptor 失败不再带走其余注册) |
|
||||
| Tab 契约 | `src/client/index.tsx` | `fail()` 诊断条上移到 `apply()` 前部,并以 `reportFailure: (phase, error) => fail(\`native ${phase}\`, error)` 注入 `registerNativeSurface` |
|
||||
| Tab 契约 | `tests/native-surface.spec.ts` | +15 行:遍历注册到的全部 guide 条目,断言每个 `id` 是 string、总数 > 0、`new Set(ids).size === ids.length` |
|
||||
| 轮尾 | `src/client/intercept.tsx`(−131)、`src/client/produced-files.ts`(−120)、`tests/turn-tail-intercept.spec.ts`(−200)、`tests/produced-files.spec.ts`(−56) | 轮尾接管整体删除;`src/client/sidebar.module.css` 的接管样式随之删(−46) |
|
||||
| 轮尾 | `src/client/sidebar-file.ts`(新增 30)、`src/client/paths.ts`(+13) | 文件打开的那一半存活(文件变动 tab 与编辑器宿主仍用);`resolveSidebarPath` 移进同胞路径助手模块(绝对路径判定镜像宿主:POSIX 根 / 盘符 / UNC 都不拼 cwd) |
|
||||
| 终端 | 删除 `src/pty-manager.ts`(−548)、`src/agent-pty.ts`(−722)、`src/pty-deps.ts`(−240)、`src/tools.ts`(−500)、`src/client/TerminalView.tsx`(−477)、`src/client/TerminalWaitBanner.tsx`、`src/client/terminal-font.ts`(−255)、`src/client/terminal-links.ts`(−194)、`src/client/chunks/terminal.tsx`、`src/client/pinned.ts`(−177)、`src/client/sidebar/use-pinned-tabs.ts`(−147) | 整套 PTY 栈、xterm 视图、字体/链接助手、chunk 入口与跨会话固定终端全部下线 |
|
||||
| 终端 | `src/index.ts`(+14 / −560)、`src/config.ts`(−48)、`src/prefs-shared.ts`、`src/client/prefs.ts`(−26)、`src/client/api.ts`(−40)、`src/client/state.ts`(+11 / −244)、`src/client/TabBar.tsx`(+20 / −76)、`src/client/Sidebar.tsx`(+16 / −91)、`src/client/icons.tsx`(−11) | 终端设置项(`terminalShell` / `terminalShellArgs` / `terminalFontFamily` / `terminalFontSize` / `agentTerminalTools` / `bottomPanelAutoTerminal`)、宿主路由 `terminal.deps`、`clampTerminalFontSize`,以及被删特性留下的死状态(`SidebarTab.pin` / `nextTerminal` / `agentWaits` / `bottomOpenedOnce` / agent-tab helpers / pinned-tab memo key)一并清除 |
|
||||
| 终端 | `package.json` | `node-pty` `^1.1.0` 离开 `dependencies`;`@xterm/xterm`、`@xterm/addon-fit` 离开 devDependencies;`files` 去掉 `lib/client-terminal.js` |
|
||||
| 终端 | `pnpm-workspace.yaml` | 原 `allowBuilds: {node-pty: true, protobufjs: true}` 收缩(**块未整块删除**,见「收尾偏差」2) |
|
||||
| 终端 | `tsdown.config.ts`、`src/bundle-route.ts`、`src/client/chunk-loader.ts`、`scripts/package-registry.mjs`、`package.json` 的 `files`、`tests/{bundle-route,chunk-loader,chunk-artifact,manifest-consistency,lazy-chunk}.spec.*` | chunk 名由 `['editor','mermaid','locale','terminal']` 收敛为 `['editor','mermaid','locale']`(AGENTS.md §5 列出的镜像处全部同步) |
|
||||
| 终端 | 删除 13 个 spec | `agent-pty` / `agent-terminal-reconcile` / `agent-wait-badge` / `bottom-auto-terminal` / `open-when-sized` / `pinned` / `pty-deps` / `pty-helpers` / `terminal-deps-banner` / `terminal-font` / `terminal-links` / `terminal-wait-banner` / `tools` |
|
||||
| 浏览器 | 删除 `src/client/BrowserView.tsx`(−289)、`src/client/browser.ts`(−167)、`src/browser-probe.ts`(−26)、`tests/browser.spec.ts`、`tests/browser-probe.spec.ts`;`src/client/builtins/tabs.tsx`(+13 / −175)删 `browser` descriptor | 插件 `browser` tab 类型下线,宿主 `browser` kind 独占嵌入页(两条 commit 合计删 15 个 spec:终端 13 + 浏览器 2) |
|
||||
| 浏览器 | `src/index.ts`(删宿主 `browser.probe` 路由)、`src/prefs-shared.ts` / `src/client/prefs.ts`(删 `browserNoSandbox` / `browserAllowedLoopback`)、`src/config.ts` | 只配置插件自家 iframe 的两个设置项随它们喂养的宿主路由一并删除(宿主 `browser` kind 不进插件注册表) |
|
||||
| 浏览器 | `src/client/link-intercept.ts`、`src/client/sidebar/use-host-feeds.ts:128` | 保留 DOM 层 http(s) 接管,目标改宿主 `browser` kind(`ctx.sidebarRight.openTab('browser', { params: { url } })` + `window.open` 兜底);`urlTarget` 机制与匹配顺序(`matchUrlTarget`)不变 |
|
||||
| 卡片 | `src/client/sidebar.module.css` `.paneCard` | 采用宿主 guide 胶囊配方:`min-height: 56px` / `padding: 14px 20px` / `border: 0.5px solid var(--dsw-alias-border-l4)` / `border-radius: 24px` / `background: var(--dsw-alias-bg-layer-1)` / `font: var(--dsw-font-s-strong-14)` / hover 用 `--dsw-alias-interactive-bg-hover` |
|
||||
| 卡片 | `tests/theme.spec.ts`(+23) | 守护 `.paneCard` 的几何与「每条 paint 都解析到 `var(--dsw-`」,并断言**不得**出现上游专有拼写 `--dsw-alias-bg-l1` / `--dsw-alias-bg-l2`(证据:alpha.2 全树 4 处声明、**0 处定义**,全在 `packages/client/ui-sidebar-terminal/src/client/*.module.css`,故上游那 4 条 `background` 实际被丢弃) |
|
||||
| 挂载冒烟 | `tests/e2e/mount.e2e.ts` | 指南 sweep 拆成「插件自有 4 条(files / git / subagent / sidechat)+ 宿主自有 2 条(terminal / browser,各**恰好 1 条**——多 1 条即插件又在遮蔽宿主)」;说明表断言从「关 3 个 tab 类型」改「关 2 个」(宿主终端条目已不在插件注册表内) |
|
||||
| 聚合冒烟 | `scripts/e2e-aggregate-mount.sh` | 真方法哨兵从已删的 `terminal.deps` 改为 `settings.get`(理由写进脚本注释:`/sidebar/api` 是插件自己 fence 的 prefix 路由,未注册方法统统 404,`{"ok":true}` 只可能来自真实注册的 handler) |
|
||||
| 文档 | `AGENTS.md` §3 | 重写为「DSH 0.1.6-alpha.2 适配要点(0.1.6-alpha.2+ 基线)」:支持线 + 两条硬契约 + 两处让位 + 「本版还需知道的」9 条 |
|
||||
| 文档 | `README.md` | v0.20.0 段落、终端/浏览器「交还宿主」说明、支持版本徽章;终端卡片内写明模型侧代价与自行插入 `tool-terminal` 行的指引 |
|
||||
| 文档 | `docs/plans/2026-09-21-dsh-0.1.6-alpha.2-adaptation.md` | 本计划 |
|
||||
|
||||
### 收尾偏差(核对实际文件后修正的三处口径)
|
||||
|
||||
这三处不是计划项,而是**核对文件时发现的「提交信息与产物不符」**;写在这里以免被当作已完成,也给下一版留证据:
|
||||
|
||||
1. **安装脚本的 `-Repair` 模式**:`5912274` 的提交信息与 `README.md` 都称它已随终端删除,但三条 commit **都没有触碰这两个文件**——`git diff --stat HEAD~3..HEAD -- scripts/` 只有 `e2e-aggregate-mount.sh` / `e2e-common.sh` / `package-registry.mjs` 三个。`scripts/install.sh`(`--repair`,改动前行号 15/19/54/57/75/82/180)与 `scripts/install.ps1`(`-Repair`,改动前行号 22/26/51/175)当时**完整保留**了 node-pty 修复流程与 issue #140 文案。**该偏差已在本分支工作树修掉**(两个安装脚本 + `e2e-common.sh` 里 scratch profile 的 `allowBuilds` 块 + `package-registry.mjs` 的 node-pty 注释),截至本文档写作时**尚未提交**(见「落地」)。
|
||||
2. **`pnpm-workspace.yaml` 的 `allowBuilds` 块仍未收干净**:现状是 `node-pty: set this to true or false`(pnpm 自己写的待决占位行);原条目 `node-pty: true` 被换成该占位、`protobufjs: true` 被移除。块本身仍在,且仍以**已删除的依赖名**出现——**本版未处理**。
|
||||
3. **`browserNoSandboxWarning` 成了死键**:20 份 `src/client/locales*.ts`(词典 `locales.ts` + 19 份第三方语言)里仍在,而它描述的 `browserNoSandbox` 设置项已删,代码侧除词典外零消费——**本版未处理**。
|
||||
|
||||
## 验证
|
||||
|
||||
- **门禁(本版分支 HEAD `5912274`,macOS arm64)**:`pnpm install --registry=https://registry.npmjs.org`(**必须显式指定 registry**——机器默认的 npmmirror 源滞后,对 `@deepseek-ai/dsh-code-runtime` 报 404);`pnpm peers check` = `No peer dependency issues found`;`pnpm typecheck` 干净;`pnpm lint` 干净;`pnpm check:consumer-types` OK;`pnpm test` = **110 files / 1085 passed / 9 skipped**;`pnpm build` 恰好产出三个 chunk(`client-editor.js` / `client-mermaid.js` / `client-locale.js`)。
|
||||
- **写本文档时复核过的两条**:`pnpm peers check` = `No peer dependency issues found`、`pnpm test` = **110 files / 1085 passed / 9 skipped**,与上条一致(复核时工作树只有文档改动,测试文件与 HEAD 一致,因此数字对应 HEAD 的测试面;此后工作树又改动了 `tests/theme.spec.ts` 等收尾文件,**未重跑**)。`typecheck` / `lint` / `check:consumer-types` / `build` **未重跑**(本节记录的是门禁那一次的结果)。
|
||||
- **真机挂载冒烟(DSH 0.1.6-alpha.2):未执行**。`pnpm test:mount` 与 `pnpm test:mount:aggregate` 在本版分支上**没有跑过**——这是本版最大的未验证面:指南「插件 4 条 + 宿主 terminal/browser 各恰好 1 条」、聚合哨兵改 `settings.get`、卡片新几何,目前只有单测与 e2e 代码,没有真机结论。
|
||||
- **3080 实例 GUI 人工验证:未执行**。
|
||||
- **上游事实的取证方式**:本文每条 alpha.2 结论都来自 tag(`git -C /tmp/dsh-0162 diff dsh-v0.1.5-rc.2 dsh-v0.1.6-alpha.2 -- <path>` / `git show dsh-v0.1.6-alpha.2:<path>`),关键抛点原文为 `sidebarRight: duplicate guide entry id in "<id>"`(`tab-registry.ts:251`)与 `list slot "<name>" requires options.id`(`ui-slots/src/index.ts:1230`)。semver 表用仓库树内的 `semver@7.8.5` 实测。
|
||||
- **未验证的推断**(明确标注,不作为结论):「模型只剩一次性 `bash` / `pwsh`」是按两条**已验证**事实推出的——① `@deepseek-ai/dsh-tool-terminal` 未被任何 shipped bundle 挂载(`packages/bundle/` 零挂载行);② 插件删除的 `src/tools.ts` 确实注册了 8 个 `terminal_*` 工具。**没有**在真机上对模型可见工具清单做过实测。
|
||||
|
||||
## 落地
|
||||
|
||||
- 分支:`feat/dsh-0.1.6-alpha.2`,三条 commit(HEAD `5912274`)之上还有一批**未提交**的收尾改动:`AGENTS.md` §3 重写、`README.md` / `README_EN.md`、`docs/external-plugin-guide.md`、`scripts/install.sh` / `install.ps1`(删 `-Repair` / `--repair`)、`scripts/e2e-common.sh` / `e2e-aggregate-mount.sh` / `package-registry.mjs`(node-pty 残迹)、`tests/theme.spec.ts`(卡片守护加强,见「收尾偏差」1)。
|
||||
- 其余(PR、真机挂载冒烟、3080 GUI 验证、发版结论):**待回填**——本节不预填未经执行的结论。
|
||||
|
||||
## 不做
|
||||
|
||||
- 不为 0.1.5-rc.\* 写运行时/类型层兼容层(本版是**有意**的支持线断裂,与 v0.19.0 桥掉 0.1.2 线同一先例)。
|
||||
- 不把 peer 下限留在 `^0.1.5-rc.1` 或写成「宽松范围」——它在数学上容不下任何 0.1.6 预发布版(§决策 1 的表),留着只会让消费方装上一个必然崩的宿主。
|
||||
- **不补自家的终端替代实现**(不上第二套 PTY、不为模型侧持久终端另造工具);上游 `tool-terminal` 未被 bundle 挂载是上游的取舍,README 给指引即可。
|
||||
- 不恢复被删的轮尾产物行:list 槽只能追加,重新注册只会与宿主产物卡重复。
|
||||
- 不接入本版新增宿主面(`openTabs` / `tabsIn` / `registerCloseHandler` / `preferNewPane` / keyed `guide.entry` 槽 / 全局面板)。
|
||||
- 不改 DSH 源码(仓库硬约束 §1);不改 `release.yml`(0.20.0 无 prerelease 后缀 → 自动发 `latest`)。
|
||||
Reference in New Issue
Block a user