# RoundTable v1.0.0-rc.2 **宿主适配版:把插件从 DeepSeek Harness `0.1.5-rc.3` 抬到 `0.2.0-rc.2`。** 这是一次**跨大版本**的升级,宿主动了两处真正破坏兼容的地方;功能面不变, 但其中一处(设置服务重构)让插件的偏好持久化换了实现。 > **发行渠道:GitHub**(本插件**不发布到 npm** —— 作者因个人原因无法注册 npm 账户, > `@huanlin` scope 拿不到,registry 上不存在这个包)。安装请从源码: > `git clone https://github.com/9931666/dsh-plugin-roundtable` → `pnpm install` → `pnpm build` → `dsh plugin --profile web add .` > > 宿主基线:**DeepSeek Harness 0.2.0-rc.2** --- ## 0. 一句话 `0.2.0-rc.2` 与 `0.1.5-rc.3` 之间,插件源码只有 **4 个类型错误**,但其中 2 个 指向的是**宿主把「插件设置」这件事重新设计了一遍**——修法不是改一行签名,而是 换掉偏好持久化的实现。另一处是消息来源标记的扩展模型变了。 浏览器半体(`RoundTableView.tsx` / `RoundTableSettings.tsx` 等)**零改动**, `tsc -p tsconfig.client.json` 一次通过。 --- ## 1. 破坏性变更一:`settings` 服务被重构成 `SettingsForms` ### 宿主改了什么 | | 0.1.5-rc.3 | 0.2.0-rc.2 | | --- | --- | --- | | 服务形状 | `SettingsScope`:插件注册自己的命名空间 | `SettingsForms extends Service`:把 **profile 配置投影成表单** | | 关键 API | `settings.register(ns, schema, { base })` → `scope` | `configure()` / `describe()` / `update()` / `replace()` / `mutate()` | | 插件自管偏好 | `scope.get()` / `scope.update()` / `scope.watch()` | **没有对应物** —— 插件不再拥有注册命名空间的 API | | `SettingsScope` 类型 | 导出 | **已移除** | 新模型里「插件的可配置面」就是它自己的 Cordis `Config`(由 Loader YAML 提供、 宿主自动生成配置页)。而写 `Config` 会触发 Loader **重载该 entry** —— 对「在设置页改一个偏好」来说太重了:正在开的会会被打断。 ### 插件怎么改 新增 **`src/preferences.ts`**:偏好改为**自持久化**,落在 DSH home 下的 `roundtable/preferences.json`。 - `PreferenceStore`(`get` / `update` / `watch` / `ready`)保留了旧 `scope` 的语义, 所以 `rpc.ts` 的 `prefs.get` / `prefs.set` 两个端点对外**一字未变**, 客户端设置页与 `RoundTableSettings.tsx` 完全无感; - 读盘是**惰性且异步**的:`get()` 在读盘完成前返回基准值,不阻塞宿主启动, 首次加载完成后自动补同步一次 `defaultMode`; - **读失败绝不抛给调用方**:偏好文件损坏、被手改成非法 JSON,一律退回默认值 + 一条 warn。偏好坏了不能让整场会议从界面上消失; - 写入复用 `state.ts` 的 `writeTextAtomic`(宿主 `writeFileAtomic` 优先、本地 tmp + rename 兜底),并发提交串行化,最后一次写入胜出; - 净化规则(`sanitizeHiddenPanels` / `sanitizeRolePresets` / `sanitizeSquads`) 与上限常量一并搬进 `preferences.ts`,`rpc.ts` 继续 re-export —— 测试与工具的 既有导入路径不变。 `src/index.ts` 里 `ctx.inject(['settings'], …)` 那一段整体删除, `RoundTableRuntime` 从 `{ scope, stateDir, fallbackPrefs }` 收敛为 `{ prefs, stateDir }` (内存兜底不再需要:store 自己带基准值)。 顺带把默认值从**两处收敛为一处**:原先 schemastery 的 `PreferenceSchema` 与 `fallbackPrefs` 各写了一遍默认值(本来就有漂移风险),现在只有 `preferences.ts` 的 `defaultPreferences()` 一处。 ### 能力清单的变化 `settings` 不再是本插件探测的能力,三处清单同步移除: ``` compatibility.json capabilities.optional src/harness-compat.ts OPTIONAL_CAPABILITIES / CAPABILITY_SPECS ``` `@deepseek-ai/dsh-settings` 也从 `peerDependencies` / `devDependencies` / `baseline.packages` 里移除 —— 源码不再 import 它。 > **副作用(是好事)**:这条路径上**没有任何可选能力可缺**。旧实现里 > 「宿主没挂 settings 服务 → 偏好退回内存态、重启即丢」,现在不存在这个分支。 --- ## 2. 破坏性变更二:`MessageSourceMap` 不再有 catch-all 的 `plugin` kind `0.1.5-rc.3` 的宿主提供 `{ kind: 'plugin', plugin: string }`,本插件用它标注 注入给主持人的 steering 内容。`0.2.0-rc.2` 的 `@deepseek-ai/dsh-llm` 只剩四种基础 kind: ```ts export interface MessageSourceMap { user: { kind: 'user' } model: ModelMessageSource tool: ToolMessageSource 'system-prompt': SystemPromptMessageSource } ``` 其余 kind 一律**由生产者在自己的模块里声明**——宿主的 `tool-registry` / `ptc-mode` / `agent-message` / `subagent-settled` / `user-question-reply` 都是 这么做的。源码里的原文注释写得很直白: > Merge-extensible sum type — each producer declares its own `kind` in its own > module; **there is no shared catch-all `plugin` kind**. 所以 `src/members.ts` 按同一模式补上自己的条目,而不是退化成匿名的 `user`: ```ts declare module '@deepseek-ai/dsh-llm' { interface MessageSourceMap { roundtable: { kind: 'roundtable'; plugin: string } } } ``` `steerCaptain()` 的 source 相应改为 `{ kind: 'roundtable', plugin: 'dsh-plugin-roundtable' }`。 运行时无影响:`kind` 是给转录消费者做归属展示的标签,未知值一律 fall through。 --- ## 3. 清单与文档同步 | 项 | 之前 | 现在 | | --- | --- | --- | | `compatibility.json` `recommendedHost` | `0.1.5-rc.3` | `0.2.0-rc.2` | | `package.json` `roundtable.hostBaseline` | `0.1.5-rc.3` | `0.2.0-rc.2` | | devDependencies(17 个基线包) | `0.1.5-rc.3` | `0.2.0-rc.2` | | peerDependencies 区间 | `^0.1.5-rc.1` | `^0.2.0-rc.2` | | `@deepseek-ai/cordis` | `4.0.2` | `4.0.4`(与宿主同一副本) | | `@deepseek-ai/schemastery` | `3.18.2` | `3.18.4` | | `src/version.ts` `HARNESS_RANGE` | `0.1.5-rc.3+` | `0.2.0-rc.2+` | | 能力清单 | 12 个探测点 | **11 个**(`settings` 移除) | | 插件版本 | `1.0.0-rc.1` | `1.0.0-rc.2` | `peerDependencies` 只能有一个区间,而类型基线也只有一个版本,所以本版**整条切到 0.2 线**:`0.1.5-rc.3` 不再对外承诺(源码里的可选能力探测仍保留对旧形状的降级, 但没有回归验证,不作为支持声明)。 --- ## 验证记录 | 项 | 结果 | | --- | --- | | `tsc -p tsconfig.json --noEmit`(宿主,对 0.2.0-rc.2) | **通过,0 错误** | | `tsc -p tsconfig.client.json --noEmit`(浏览器) | **通过,0 错误**(零改动) | | `node --test --test-isolation=none "test/*.test.mjs"` | **150/150 通过,0 失败** | | `pnpm build`(tsdown + 双端 .d.ts) | 通过(`lib/index.js` 186.7 kB / `lib/client.js` 564.3 kB) | | `node scripts/compatibility.mjs` | **43/43 通过** | | `node scripts/verify-package.mjs` | **19/19 通过**(24 个模块全部命中) | | `node scripts/release.mjs` | **10/10 通过**,产物已出指纹 | | `node scripts/doctor.mjs` | 宿主 0.2.0-rc.2;混装 ✓;cordis 同一性 ✓ | | 候选产物 | 53 个文件,425,405 B 打包 / 1,024,000 B 解包(自建打包器) | > 测试数从 rc.1 的 132 涨到 150,是因为前置版本里有两个测试文件因导出搬移而 > 加载失败(`rpc.test.mjs` / `squads.test.mjs`),本轮修好 re-export 后它们 > 的全部用例都跑起来了。 ## 已知边界 - **只有 `0.2.0-rc.2` 一条支持线**。矩阵不再保留 `0.1.5-rc.3` 条目(peer 区间 只能一条、类型基线只能一个版本)。 - **`pnpm-lock.yaml` 未在本机刷新**:它仍是 `0.1.5-rc.3` / cordis `4.0.2` 时代的 记录。本机 pwsh 沙箱禁止写工作区、且 pnpm store 数据库损坏(历史已知问题), 所以无法重新生成。**首次安装请直接 `pnpm install`**(默认会按新的 `package.json` 重新解析并刷新 lockfile);能用 pnpm 的机器上跑一次后请把 刷新后的 lockfile 提交回来。CI 不跑安装,不受影响。 - **前端测试仍为零**:`RoundTableView.tsx` 没有测试覆盖(历史欠账,不影响本版)。 - 历史会议记录(`.roundtable/`)的数据格式**本版没有变动**, `meeting.json` 的 `schemaVersion` 与迁移通道照旧。 ## 生效方式 - **浏览器半体**:重建后刷新页面即可(本版无改动,但 `lib/client.js` 会随构建刷新)。 - **宿主半体**(`inject` / 事件 / RPC 通道 / 偏好存储):需要**重启 DSH**。 - **偏好位置变了**:从宿主的 `settings.yaml` 的 `roundtable` 命名空间搬到 `/roundtable/preferences.json`。旧命名空间里的值**不会自动迁移**, 设置页重新设一次即可(默认值与旧版一致,不设也能正常用)。