dsh-expert-group 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,11 +12,32 @@
12
12
  现有 Host 服务与客户端 Slot 注册;**最低宿主契约**是下面这些声明在 `inject` 里的服务:
13
13
  `tools`、`subagents`、`systemPrompt`、`llm`、`agents`、`settings`、`webServer`。
14
14
 
15
- 其中 **goals 是可选兼容服务**:插件通过 `ctx.get("goals")` 读取,不把它声明为 `inject` 依赖。
16
- 宿主缺少该服务时,插件主体、专家派发与其他工具仍正常加载,只有依赖真实 `pause` / `resume`
17
- 的 **goal handoff** 不可用;能力矩阵标记 `required: false`、`status: "absent"` 和
18
- `degradation: "goal-handoff-disabled"`。服务存在但缺少 `get` / `pause` / `resume` 时同样
19
- fail-closed,并标记为 `status: "incomplete"`;绝不伪造 paused ref 或假装能 resume。
15
+ 其中 **goals 是可选只读投影源**:插件通过 `ctx.get("goals")` 读取,不把它声明为 `inject` 依赖,
16
+ 且**只用于侧边栏的目标阶段展示**。插件不调用 `goals.pause` / `goals.resume`,等待语义完全由
17
+ 「保持 root 当前 turn 打开」实现(见下)。宿主缺少该服务时,插件主体、专家派发与其他工具仍正常
18
+ 加载,只是目标阶段投影为空。
19
+
20
+ 等待判定需要一条**能证明**「该 root 没有 running 后代」的 root-scoped 查询,分两档:
21
+ - **权威**:`subagents.listDescendants(rootSessionId)`——递归后代枚举,每行带
22
+ `activity: "running" | "inactive"`;整条 listing reject、出现不可读分支
23
+ (`kind: "diagnostic"`)或结构非法都按 unknown 处理。**只有**它「干净且完整」地回答
24
+ 才能证明静止,这也是等待唯一的释放依据;
25
+ - **降级**:公开 `workspace/session-activity` waterfall。它是**非专属**扩展点,
26
+ **只能报告 activity,不能证明 quiet**,两个原因都必须处理:
27
+ - 宿主 subagent provider 在空闲时是原样透传最内层返回值,因此「没有 provider」与
28
+ 「provider 报告空」在返回值上同形;插件用模块唯一的 sentinel 兜底,sentinel 命中一律
29
+ unknown;
30
+ - 即使拿到的是**非 sentinel 的新数组**,也无法识别贡献者:任意第三方 listener 都能返回
31
+ 一个全新的空数组(既不 spread 最内层值,也不检查 subagent 血缘),它与「provider
32
+ 明确报告没有 running 后代」同形但**不可信**。因此**任何空数组**(含 fresh `[]`)
33
+ 都不是静止证明。
34
+ 该档唯一可用的明确结论是**正向**的「确有 `kind: "subagent"` 的 running 后代」,
35
+ 它只用于继续等待。
36
+
37
+ 两档都不可用时能力矩阵标记 `required: false`、`status: "unavailable"` 和
38
+ `degradation: "root-turn-wait-descendant-query-unavailable"`;只有 waterfall 时标记
39
+ `status: "degraded"` 和 `root-turn-wait-descendant-listing-unavailable`。任何 unknown
40
+ 一律 fail-closed(继续等待,绝不把「查不到」当成「没有人跑」)。
20
41
 
21
42
  图片规范化额外使用可选公开 `ctx.attachments` 服务,冷会话精确读取使用可选 `ctx.sessionQuery`;
22
43
  两者缺失时只禁用受影响能力(`image-normalization-disabled` / `cold-session-read-disabled`),
@@ -29,8 +50,8 @@ fail-closed,并标记为 `status: "incomplete"`;绝不伪造 paused ref 或
29
50
  - `librarian` — 只读外部文档 / 库 API 调研
30
51
  - `oracle` — 只读架构决策 / 疑难调试 / 代码审查
31
52
  - `designer` — UI/UX 与视觉实现(可写)
32
- - `fixer` — 边界清晰的多文件实现与测试补齐(可写)
33
- - **视觉代理** `observer`:只读视觉分析专家,负责图片 / 截图 / UI 稿 / 图表 / PDF 的解读。裸 Config 默认关闭;当前 `cordis.patch.yml` 显式把 bundle override 设为开启。图片路由会把 Host 创建的 `image/png` durable attachment ContentBlock 直接交给 Observer;自动图片入口只派发有界的纯视觉任务,用户原始任务仍由主代理持有并综合视觉报告。
53
+ - `fixer` — 已定方案的实现、重复修改、迁移与测试补齐,含单文件的大量工作(可写)
54
+ - **视觉代理** `observer`:只读视觉分析专家,负责 Host 提供的真实 durable 图片附件的解读,包括截图、UI 稿、图表和已呈现为图片的 PDF 页面;不接受任意本地路径。裸 Config 默认关闭;当前 `cordis.patch.yml` 显式把 bundle override 设为开启。图片路由会把 Host 创建的 `image/png` durable attachment ContentBlock 直接交给 Observer;自动图片入口只派发有界的纯视觉任务,用户原始任务仍由主代理持有并综合视觉报告。
34
55
  - 插件工具使用简短、无冲突的角色工具名(`explorer`、`librarian` 等),注册在当前 DSH 的 `ctx.tools`;
35
56
  主代理不得把 Harness 外层 `subagent_<role>`、PTC 的 `run_code` 或通用 subagent 入口当作插件委派替代。
36
57
  - 每个专家委派始终是可继续(continuable)子代理:默认后台运行立即返回子代理 id;
@@ -42,9 +63,20 @@ fail-closed,并标记为 `status: "incomplete"`;绝不伪造 paused ref 或
42
63
  - 子代理的 `reasoningEffort` 通过子代理 `agent/request` waterfall 注入,绕开 `dsh-tool-subagent`
43
64
  `AgentOptions` 尚未公开该字段的限制。
44
65
 
45
- ## 安装
66
+ ## 安装与平台分工
46
67
 
47
- 本插件发布到 npm 公共源,按包名安装:
68
+ 本插件发布到 npm 公共源,按包名安装。**CLI/web 与桌面版是两套管理面,命令不通用**:
69
+ CLI/web 的完整流程见 [安装说明.txt](安装说明.txt) 与 [web-install.ps1](web-install.ps1) / [web-uninstall.ps1](web-uninstall.ps1)(这两个脚本只处理 web profile);
70
+ 桌面版见 [desktop-install.ps1](desktop-install.ps1)(构建 → 测试 → 打包 → 备份 → 安装 → 校验的快速部署流程,脚本头部注释即流程说明)。
71
+
72
+ | 目标 | 安装入口 | 卸载入口 | 生效方式 |
73
+ | --- | --- | --- | --- |
74
+ | **CLI / `dsh web`** | `dsh plugin --profile web add dsh-expert-group` | `web-uninstall.ps1`(等价于 `dsh plugin --profile web remove dsh-expert-group`) | 重启 DSH Web |
75
+ | **桌面版(Electron 客户端)** | 桌面版**应用自带**的 `dsh.cmd`(`plugin --profile desktop`,见下),或 GUI「插件 → 添加插件」 | `desktop-uninstall.ps1`(内部调用应用自带 CLI,见下) | 重启桌面版 |
76
+
77
+ 两个卸载脚本各自只处理自己的 profile,**不可混用**:`web-uninstall.ps1` 操作 `profiles\web`,`desktop-uninstall.ps1` 操作 `profiles\desktop`。
78
+
79
+ ### CLI / `dsh web`
48
80
 
49
81
  ```powershell
50
82
  dsh plugin --profile web add dsh-expert-group
@@ -54,6 +86,64 @@ dsh plugin --profile web add dsh-expert-group
54
86
 
55
87
  要求:DSH Web 可运行(`dsh web`)、Node.js ≥ 22、PATH 上有 `pnpm`。**本插件不内置任何角色默认模型**——安装后需先在「设置 → dsh-expert-group」为各角色选择模型并保存。
56
88
 
89
+ ### 桌面版(Electron 客户端)
90
+
91
+ 桌面版的 profile 是 `desktop`,由 Electron 应用管理;**不要用 PATH 上的 npm 全局 `dsh` 去操作它**:
92
+ 启动器里的开关 `manageDesktopProfile` **只作用于 `plugin` 子命令**,
93
+ boot / `--dump-config` 路径是无条件拒绝的——**应用自带 CLI(rc.2)同样拒**:
94
+
95
+ ```text
96
+ error: profile "desktop" is managed exclusively by the Electron application
97
+ ```
98
+
99
+ 三条可用入口:
100
+
101
+ | 入口 | 适用场景 | 说明 |
102
+ | --- | --- | --- |
103
+ | **应用自带 CLI** | 安装 / 升级 / 卸载的默认路径 | `0.2.0-rc.2` 起发行包带 `resources\runtime\cli\bin\dsh.cmd`(`0.2.0-rc.1` 上不存在);只有它放行 `plugin --profile desktop` |
104
+ | **GUI「插件 → 添加插件」** | 图形化安装 | 与版本无关;**按 spec 检查同名**——包名 / 本地目录形式的 spec 命中已安装依赖时提示「该插件已安装。如需升级,请卸载后重新安装」,**tgz spec 的实际行为本轮未验证**;也不提供版本豁免入口 |
105
+ | **手工维护 profile 文件** | 无 CLI、无 GUI 的兜底 | 手工建 junction / 复制副本**不是默认升级方式**,会绕过 pnpm 的锁与 integrity 校验 |
106
+
107
+ ```powershell
108
+ $cli = Join-Path '<DSH 安装目录>' 'resources\runtime\cli\bin\dsh.cmd' # 应用自带 CLI
109
+ $tgz = '<候选包 tgz 的绝对路径>' # 也接受包名 / 本地目录绝对路径
110
+
111
+ & $cli plugin --profile desktop add $tgz
112
+ if ($LASTEXITCODE -ne 0) { throw "add 失败(exit=$LASTEXITCODE),先查落盘状态再决定回滚" }
113
+
114
+ & $cli plugin --profile desktop list
115
+ if ($LASTEXITCODE -ne 0) { throw "list 失败(exit=$LASTEXITCODE)" }
116
+
117
+ # 卸载:可直接运行仓库根的 .\desktop-uninstall.ps1(内部就是下面的 remove);
118
+ # 手工执行等价于把 add 换成 remove;版本豁免:version-exemptions / allow-version / revoke-version
119
+ ```
120
+
121
+ **生效与验收:命令执行者是主代理,最后一步重启由用户完成。** 构建 → 测试 → 打包 → 备份 → 安装
122
+ → 校验的完整流程(含判据与回滚)见 **[desktop-install.ps1](desktop-install.ps1)**(流程与判据在脚本头部注释与各阶段日志中)。
123
+
124
+ > 在线(桌面版运行中)安装属于受控测试:CLI 只检查 profile 是否初始化(提示 `... then fully quit it before
125
+ > running dsh plugin --profile desktop.`),**没有进程硬拦截**,因此可能出现内存里旧模块 + 磁盘上新模块混用。
126
+ > **磁盘安装成功 ≠ 内存生效、更 ≠ 功能验收**——真正验收要等用户重启后读证据(卸载入口见 [desktop-uninstall.ps1](desktop-uninstall.ps1))。
127
+
128
+ **验证安装登记与目标文件**(直接检查目标 profile,真实目录、Junction 和 pnpm 链接均适用):
129
+
130
+ ```powershell
131
+ $ErrorActionPreference = 'Stop'
132
+ $home_dsh = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $env:USERPROFILE '.dsh' }
133
+ $d = Join-Path $home_dsh 'profiles\desktop'
134
+ $pkg = Get-Content -LiteralPath (Join-Path $d 'package.json') -Raw -Encoding UTF8 | ConvertFrom-Json
135
+ if (-not $pkg.dependencies.'dsh-expert-group' -or 'dsh-expert-group' -notin @($pkg.dsh.profile.bundles)) {
136
+ throw '目标 profile 未完整登记 DEG 依赖与 bundle'
137
+ }
138
+ $installedManifest = Join-Path $d 'node_modules\dsh-expert-group\package.json'
139
+ if (-not (Test-Path -LiteralPath $installedManifest -PathType Leaf)) { throw '目标 profile 中没有 DEG 包清单' }
140
+ $installed = Get-Content -LiteralPath $installedManifest -Raw -Encoding UTF8 | ConvertFrom-Json
141
+ if ($installed.name -ne 'dsh-expert-group') { throw '目标包名不符' }
142
+ [pscustomobject]@{ Path = $installedManifest; Name = $installed.name; Version = $installed.version }
143
+ ```
144
+
145
+ 这只是快速检查;同版本替换还须按详细指南比对实际文件与候选 tgz 的哈希,不能只看版本号。
146
+
57
147
  **可选图像增强**:`sharp` 是 `optionalDependencies`(约 30 MB 原生二进制),用于把 JPEG / WebP / GIF 转码为 PNG;拉取失败或未安装都不影响插件加载,仅会拒绝非 PNG 图片的转码,其余功能全部正常。
58
148
 
59
149
  ## 界面总览
@@ -171,7 +261,7 @@ DEG 没有模型可调用的通知工具,也不在会话标题栏或右侧栏
171
261
  | 弹窗停留时间(下拉) | `5s` | `5s / 10s / 30s / 60s / 5min / 10min / 永久` | `dsh-expert-group.notificationHoldMs` |
172
262
  | 通知音量(滑杆 + 数字框) | `100` | `0 – 100`,步进 `1` | `dsh-expert-group.notificationVolume` |
173
263
 
174
- - 「通知弹窗」关闭后 Host **完全不发送通知**,也不补发;它只影响投递,不影响任务快照、goal handoff 或恢复语义。
264
+ - 「通知弹窗」关闭后 Host **完全不发送通知**,也不补发;它只影响投递,不影响任务快照、root turn 等待或恢复语义。
175
265
  - 音量 `100` 表示不做任何衰减,只受 Windows 主音量影响;滑杆与数字框同值双向绑定。
176
266
  - 三项都是延迟保存:改完要点页脚「保存」;非法值会回落到默认(停留时间 → `5s`,音量 → `100`)。
177
267
 
@@ -353,3 +443,34 @@ Expert Group 是 DEG 注册在右侧栏的任务视图:它把**当前会话所
353
443
  - 首次形成两个 pane 后:Expert Group 占右栏 40%(视口约 20%),子代理 pane 占右栏 60%(视口约 30%);最后一个被跟踪的 pane 关闭后恢复约 20% 的默认宽度。
354
444
 
355
445
  **能力缺失时的降级**:宿主没有 `sidebarRight.openResource` 时箭头不渲染、其余行内容完整;缺少关闭确认能力时保留静态 `>>`;整条侧栏接缝不可用时 DEG 只注册页面,不猜测打开状态。
446
+
447
+ ## 只读能力矩阵与运行时诊断
448
+
449
+ Host RPC 的 `meta` 动作返回两份**只读投影**(设置页诊断块与客户端归一化都消费它们):
450
+
451
+ - `capabilities`:宿主服务可达性与固定降级标记,带 schema 版本 `version`。
452
+ 每个能力条目只允许固定字段(`required` / `status` / `degradation` / `methods` / `probe` /
453
+ `transport` / `failure` / `binding`),不含任何自由文本。
454
+ - 硬依赖(`tools` / `subagents` / `agents` / `settings` / `webServer` / `systemPrompt` / `llm`)
455
+ 在 `inject` 中声明,缺失时插件不加载;
456
+ - `rootTurnWait` 是可选宿主 seam:权威档是 `subagents.listDescendants`(**唯一**能证明
457
+ 「该 root 没有 running 后代」的 seam,且必须干净且完整),降级档是公开的
458
+ `workspace/session-activity` waterfall(只能报告 activity,不能证明 quiet)。
459
+ 只有 waterfall 时为 `degraded` + `root-turn-wait-descendant-listing-unavailable`;
460
+ 两档都没有时为 `unavailable` + `root-turn-wait-descendant-query-unavailable`,
461
+ 此时等待一律 fail-closed(继续等待);
462
+ - `connectionRpc` / `storageDomain` / `attachments` / `sessionQuery` 各自报告
463
+ `available` / `degraded` / `absent` / `unknown`,缺失时只禁用对应能力。
464
+ - `diagnostics`:children / fanout / recovery / settings / storage / handoff / notifications /
465
+ rpc 的**计数与固定 token**,同样带 `version`。
466
+
467
+ 敏感性边界:两份投影不含 prompt、原始请求体、token、Cookie、authorization、文件路径、
468
+ 会话/child 标识或错误消息正文;能力状态与降级说明都从固定枚举取值,任何超出字符面的输入
469
+ 在归一化时被丢弃。客户端遇到未知 `version` 时整体忽略该块,绝不猜测字段含义。
470
+
471
+ 系统通知(Windows Toast)的诊断字段与固定 token 同样在这一份 `diagnostics` 里:
472
+ `notifications.systemToast` 报告 `status` / `version` / `platform` / `aumidResolved` /
473
+ `aumidReason` / `inflight` / `queued` / `sent` / `failed` / `dropped` / `lastReason`;
474
+ 解释器不可用时 `status` 为 `absent` 且固定 `reason` 为 `powershell-unavailable`,
475
+ AUMID 解析失败的固定 token 为 `shortcut-read-failed`。这些字段只报状态与计数,不含命令行、
476
+ 路径或通知正文。