dsh-plugin-experts 0.1.0
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 +513 -0
- package/cordis.patch.yml +14 -0
- package/dist/client.js +3072 -0
- package/dist/host.js +1298 -0
- package/locale/en.json +4 -0
- package/locale/zh.json +4 -0
- package/package.json +44 -0
- package/resources/skills/expert-manager/LICENSE.workbuddy.txt +191 -0
- package/resources/skills/expert-manager/NOTICE.md +20 -0
- package/resources/skills/expert-manager/SKILL.md +54 -0
- package/resources/skills/expert-manager/references/agent-md-spec.md +186 -0
- package/resources/skills/expert-manager/references/authoring-api.md +113 -0
- package/resources/skills/expert-manager/references/avatar-spec.md +169 -0
- package/resources/skills/expert-manager/references/plugin-json-spec.md +190 -0
- package/resources/skills/expert-manager/references/team-spec.md +127 -0
- package/resources/skills/expert-manager/runtime/team-lead.md +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# dsh-plugin-experts
|
|
2
|
+
|
|
3
|
+
一个**专家**是可复用的角色设定:名称、描述、角色说明(进入模型上下文的指令文本),
|
|
4
|
+
以及一组可选的首选技能名。本包提供专家资产的编写与采用,不提供任何执行编排。
|
|
5
|
+
|
|
6
|
+
一个**专家团**(`专家团`)是一份具名的、有序的**已有专家**名册:2–16 位成员,其中
|
|
7
|
+
至多一位主理人。团队只**引用**专家,不复制其内容——编辑一位专家,所有列出它的
|
|
8
|
+
专家团立刻看到最新内容。
|
|
9
|
+
|
|
10
|
+
- Host:`POST /api/dsh-skills/experts`(一个路由、一个 `{ ok, value | error }` 信封)
|
|
11
|
+
- Host 工具:`expert_list` / `expert_get` / `expert_create` / `expert_update` / `expert_publish`
|
|
12
|
+
(模型侧入口,走同一个 `ExpertManager`)
|
|
13
|
+
- Host 技能:内置 `expert-manager`,即命令 `/expert-manager`
|
|
14
|
+
- Client:`main`(key `dsh-plugin-experts`)、`sidebar.panellist`(order 40,
|
|
15
|
+
`专家 · 技能 · 连接器`:专家/技能/连接器共用的那一个侧栏入口)、
|
|
16
|
+
`conversation.input.left`(order 32,会话输入框左侧的专家选择器)
|
|
17
|
+
- 运行时:被采用的专家,其**已发布**的角色说明进入该会话的模型上下文
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
前置条件:DSH 0.2.0-rc.2+。
|
|
22
|
+
|
|
23
|
+
- **npm 包**:在 DSH 的插件市场/插件管理里安装 `dsh-plugin-experts`,或在 profile 的 `package.json` 的 `dependencies` 里加 `"dsh-plugin-experts": "^0.1.0"`
|
|
24
|
+
- **tarball**:`dependencies` 里写 `"dsh-plugin-experts": "file:./dsh-plugin-experts-0.1.0.tgz"`,然后重启 DSH 触发安装
|
|
25
|
+
|
|
26
|
+
安装后**完全退出并重启 DSH**(Host 半边只在启动时加载)。验证:重启后插件列表里本包显示「已启用」,且对应面板/工具可用。
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## 为什么是概念级移植,而不是逐行移植
|
|
30
|
+
|
|
31
|
+
WorkDSH 的 `plugins/experts` 是 6646 行(Host 4588 / Client 2058),它的 `inject` 是:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
loader, storageDomain, agentPresets, sessionController,
|
|
35
|
+
workdshIdentity, workdshAccess, workdshAudit, workdshSessionAccess,
|
|
36
|
+
workdshSkills, connection, tools, skills, agents, agentTeams, sessionQuery, fs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
也就是说,它依赖 WorkDSH **自己的治理与执行栈**(身份、访问控制、审计、会话访问、
|
|
40
|
+
技能修订保留、agent preset 编译、加载器、会话查询)。本仓库在
|
|
41
|
+
[docs/PLAN.md](../../docs/PLAN.md) 的「不做」里明确没有移植 `identity-local` /
|
|
42
|
+
`access` / `audit`,也没有自研执行器(官方 Agent Teams 已覆盖)。逐行照搬会得到一个
|
|
43
|
+
在 0.2.0 里根本装配不起来的包,所以这里只保留其中**不依赖那套栈**的资产半边,
|
|
44
|
+
这也是本包范围较小的诚实原因。
|
|
45
|
+
|
|
46
|
+
**明确不做**(在本包里没有代码路径):
|
|
47
|
+
|
|
48
|
+
- 任何执行装配:官方 Agent Teams、`spawn_teammate`、persona、agent preset;
|
|
49
|
+
- WorkBuddy 风格 `plugin.json` 包的导入 / 导出;
|
|
50
|
+
- `workdsh-contracts` 的 DTO 兼容(`ExpertDefinition`、`ExpertRevision` 等类型不复刻)。
|
|
51
|
+
|
|
52
|
+
**专家团是保留的**(2026-10 补齐)。上游把团队建在**执行栈**上:每位成员被物化成
|
|
53
|
+
一条 `teamParentId` 指向团长的子专家行,发布时把 `teamMembers`(成员 key → 修订引用)
|
|
54
|
+
写进不可变修订,再交给 `compileExpertPreset` 编译成 preset。本仓库没有那套栈,
|
|
55
|
+
所以只保留**资产半边**的名册:成员是**专家 id 引用**,没有子专家行、没有 preset 编译、
|
|
56
|
+
没有协作场景。见下面「专家团的数据模型」与偏离日志第 8、22–25 条。
|
|
57
|
+
|
|
58
|
+
## 行为
|
|
59
|
+
|
|
60
|
+
### 草稿 → 发布
|
|
61
|
+
|
|
62
|
+
- `create` 只创建专家与其**草稿**,不产生发布修订。
|
|
63
|
+
- `update-draft` 只改草稿,带乐观并发检查;已发布的修订永不改变。
|
|
64
|
+
- `publish` 校验当前草稿,冻结成一个**新的、带编号的不可变修订**(1、2、3……),
|
|
65
|
+
并把专家指向它。
|
|
66
|
+
- `copy` 从「指定修订 → 已发布修订 → 草稿」依次取源,生成一个未发布的新专家
|
|
67
|
+
(名称加 ` 副本` 后缀)。
|
|
68
|
+
- `delete` 删除专家、草稿和它拥有的全部修订;引用它的采用记录一并删除,
|
|
69
|
+
返回值里给出被释放的会话 id,Host 会立刻移除这些会话的提示词上下文。
|
|
70
|
+
**它同时维护专家团**(见下)。
|
|
71
|
+
|
|
72
|
+
### 专家团(teams)
|
|
73
|
+
|
|
74
|
+
- `create-team` 用**已有专家**的 id 组一个团:名称(≤120)必填,简介(≤2000)与
|
|
75
|
+
主理人职责(≤60000)可选,成员 2–16 位。成员是**引用**,不是快照。
|
|
76
|
+
- 成员形如 `{ key, expertId, lead }`:`key` 沿用上游的
|
|
77
|
+
`/^[a-z][a-z0-9-]{0,39}$/`(`lead` 保留给主理人),用于在同一团内区分成员;
|
|
78
|
+
`expertId` 必须是一位**真实存在**的专家,且同一位专家不能在同一个团里出现两次。
|
|
79
|
+
至多一位成员 `lead: true`。
|
|
80
|
+
- `update-team` 整体替换名册,带**乐观修订检查**:`revision` 是全部书写字段
|
|
81
|
+
(名称、简介、职责、有序名册)的内容摘要,所以增删成员或调整顺序都会换修订。
|
|
82
|
+
过期保存返回 `experts/team-conflict`,不会覆盖别人的名册。
|
|
83
|
+
- 专家团**没有草稿 / 发布之分**。上游的草稿→发布是为 preset 编译服务的:发布要把
|
|
84
|
+
成员快照、技能版本和编译指纹一起冻结。本仓库没有那套编译,也就没有可冻结的东西,
|
|
85
|
+
因此一个团保存即生效(`revision` 只是并发令牌,不是版本历史)。
|
|
86
|
+
- `delete-team` 只删除团本身;它列出的专家一概不动(团只引用过它们)。
|
|
87
|
+
- **成员专家被删除时,团会跟着保持一致**(这一条是有意为之的设计决定):
|
|
88
|
+
- 从每个列出该专家的团里**摘掉这个成员**,并重算 `revision`;
|
|
89
|
+
- 如果摘掉后成员不足 2 位,**整个团一并删除**——它已经无法再被保存或表达;
|
|
90
|
+
- `delete` 的返回值里给出 `updatedTeams` 与 `removedTeams`,面板据此提示用户
|
|
91
|
+
「已删除 X,并更新了专家团:……」,而不是让用户下次自己发现。
|
|
92
|
+
- 为什么不是留悬空引用:上游把成员物化成子专家行、由它自己管理那行的生命周期;
|
|
93
|
+
本仓库没有子专家行,若保留悬空引用,`update-team` 会因整份名册重新校验而
|
|
94
|
+
立刻失败——那是个陷阱而不是特性。因此选择「跟着改」,并且宁可删团也不留不可用的团。
|
|
95
|
+
|
|
96
|
+
### 采用(adoption)
|
|
97
|
+
|
|
98
|
+
- 用户为**某个会话**采用一位专家;采用会把该专家当前已发布修订的 id **钉住**
|
|
99
|
+
(`revisionId`),之后编辑草稿或发布新修订都不会改动已有采用。
|
|
100
|
+
- 未发布的专家不能被采用(`experts/not-published`)。
|
|
101
|
+
- 采用变更会立即同步到**活着的**会话:新采用会替换该会话已注册的角色上下文,
|
|
102
|
+
取消采用会移除它。这比 `packages/projects` 的「任务创建时钉住」多一步,
|
|
103
|
+
因为采用可以在会话中途改变。
|
|
104
|
+
|
|
105
|
+
### 提示词上下文
|
|
106
|
+
|
|
107
|
+
`AssembleContext` 只有 `{ scope, signal }`,没有会话身份(已用
|
|
108
|
+
`cordis_inspect_query` 的 Host `Service` provider 核对),所以角色说明**按 agent**
|
|
109
|
+
在 `agent/created` 时注册到 `agent.ctx.systemPrompt.context({ name, order, text })`:
|
|
110
|
+
|
|
111
|
+
- `name`: `dsh-skills:expert-role`
|
|
112
|
+
- `order`: `126`(`packages/projects` 用 125,两者可共存)
|
|
113
|
+
- `text`: 注册时渲染好的常量快照
|
|
114
|
+
|
|
115
|
+
注销路径有三条:`agent/disposed`、取消采用 / 删除专家时的主动同步、以及插件卸载时
|
|
116
|
+
`ctx.effect` 的统一清理。注册过程 fail-open:任何查找失败只写一条 logger warning,
|
|
117
|
+
不会中断会话。
|
|
118
|
+
|
|
119
|
+
## 数据布局
|
|
120
|
+
|
|
121
|
+
单一 `ctx.storageDomain` 域 `dsh_skills_experts`(`layout: 'per-record'`),
|
|
122
|
+
表 `states`,键为常量 `local`(本 profile 单用户;见 `packages/projects/src/domains.js`
|
|
123
|
+
的同一取舍)。一条记录:
|
|
124
|
+
|
|
125
|
+
```jsonc
|
|
126
|
+
{
|
|
127
|
+
"schemaVersion": 1,
|
|
128
|
+
"experts": { "<expertId>": { "id", "draftRevision", "publishedRevisionId?", "createdAt", "updatedAt" } },
|
|
129
|
+
"drafts": { "<expertId>": { "expertId", "name", "description", "role", "preferredSkills",
|
|
130
|
+
"revision" /* 四个字段的内容摘要,用作乐观修订 */, "createdAt", "updatedAt" } },
|
|
131
|
+
"revisions": { "<revisionId>": { "id", "expertId", "number", "name", "description", "role",
|
|
132
|
+
"preferredSkills", "createdAt" } },
|
|
133
|
+
"adoptions": { "<sessionId>": { "sessionId", "expertId", "revisionId", "adoptedAt" } },
|
|
134
|
+
"teams": { "<teamId>": { "id", "name", "description", "mandate",
|
|
135
|
+
"members": [ { "key", "expertId", "lead" } ] /* 有序 */,
|
|
136
|
+
"revision" /* 全部书写字段的内容摘要 */, "createdAt", "updatedAt" } }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`drafts` 以 `expertId` 为键(一位专家一份草稿),`revisions` 以 `revisionId` 为键
|
|
141
|
+
(内容不可变),`adoptions` 以 `sessionId` 为键(一个会话至多采用一位专家),
|
|
142
|
+
`teams` 以 `teamId` 为键(一个团一份名册)。状态在第一次读取后常驻内存,
|
|
143
|
+
这正是 `adoptionContext(sessionId)` 可以同步回答的原因(提示词注册处只接受同步字符串)。
|
|
144
|
+
|
|
145
|
+
`teams` 与 `experts` 放在**同一条记录**里,而不是第二个域:一个团的全部含义就是
|
|
146
|
+
它对专家的引用,两者必须在同一把锁下写入,否则会出现「团引用了刚被删掉的专家」
|
|
147
|
+
这种中间态。读接口返回的成员是**解析过的**:`{ key, expertId, lead, name, description,
|
|
148
|
+
published, missing }`——`name`/`description` 来自被引用的专家,`missing: true` 表示
|
|
149
|
+
引用已经失效(正常情况下不会出现,因为 delete 会同步维护;保留它是为了老数据可读)。
|
|
150
|
+
|
|
151
|
+
## 路由端点
|
|
152
|
+
|
|
153
|
+
请求体 `{ endpoint, payload }`,应答 `{ ok: true, value }` 或 `{ ok: false, error: { code, message } }`。
|
|
154
|
+
|
|
155
|
+
| endpoint | payload | 返回 |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| `list` | `{ query? }` | `ExpertSummary[]`(按 `updatedAt` 倒序) |
|
|
158
|
+
| `get` | `{ expertId }` | `{ expert, draft, revisions, publishedRevision }` |
|
|
159
|
+
| `create` | `{ name, description, role, preferredSkills? }` | 同上 |
|
|
160
|
+
| `update-draft` | `{ expertId, patch, expectedRevision }` | 同上 |
|
|
161
|
+
| `publish` | `{ expertId, expectedRevision? }` | `{ expert, revision }` |
|
|
162
|
+
| `copy` | `{ expertId, revisionId? }` | 同上(新专家的 detail) |
|
|
163
|
+
| `delete` | `{ expertId }` | `{ removed: true, releasedSessions: string[], updatedTeams, removedTeams }` |
|
|
164
|
+
| `set-adoption` | `{ sessionId, expertId \| null }` | `adoption \| null` |
|
|
165
|
+
| `adoption` | `{ sessionId }` | `adoption \| null` |
|
|
166
|
+
| `list-teams` | `{ query? }` | `TeamSummary[]`(按 `updatedAt` 倒序,成员已解析) |
|
|
167
|
+
| `get-team` | `{ teamId }` | `TeamSummary` |
|
|
168
|
+
| `create-team` | `{ name, description?, mandate?, members: [{ key, expertId, lead? }] }` | `TeamSummary` |
|
|
169
|
+
| `update-team` | `{ teamId, patch, expectedRevision? }` | `TeamSummary` |
|
|
170
|
+
| `delete-team` | `{ teamId }` | `{ removed: true }` |
|
|
171
|
+
|
|
172
|
+
`TeamSummary` = `{ id, name, description, mandate, members[], revision, createdAt, updatedAt }`,
|
|
173
|
+
其中 `members[]` = `{ key, expertId, lead, name, description, published, missing }`。
|
|
174
|
+
|
|
175
|
+
## 模型工具
|
|
176
|
+
|
|
177
|
+
五个工具,全部薄封装在 `ExpertManager` 之上——和面板走的是同一批方法,所以两边的
|
|
178
|
+
校验、令牌检查和失败码不可能分叉。参数用 snake_case,返回体也投影成 snake_case
|
|
179
|
+
(`expert_id` / `draft_revision` / `published_revision_id`),因为模型必须原样回填令牌。
|
|
180
|
+
|
|
181
|
+
| 工具 | 入参 | 走的方法 |
|
|
182
|
+
| --- | --- | --- |
|
|
183
|
+
| `expert_list` | `query?` | `list()` |
|
|
184
|
+
| `expert_get` | `expert_id` | `get()` |
|
|
185
|
+
| `expert_create` | `name`, `description`, `role`, `preferred_skills?` | `create()` |
|
|
186
|
+
| `expert_update` | `expert_id`, `expected_revision`, 可选 `name`/`description`/`role`/`preferred_skills` | `updateDraft()` |
|
|
187
|
+
| `expert_publish` | `expert_id`, `expected_revision?` | `publish()` |
|
|
188
|
+
|
|
189
|
+
**草稿与发布是两个工具**,不是一个带 `publish` 开关的保存:`expert_create` /
|
|
190
|
+
`expert_update` 只写可变草稿,永远不发布;`expert_publish` 是唯一不可撤销的一步,
|
|
191
|
+
名字必须让模型能对用户说清它即将发生。
|
|
192
|
+
|
|
193
|
+
**工具失败永远是 `FeatureError`**:manager 的稳定码原样带出(`experts/invalid-name`、
|
|
194
|
+
`experts/draft-conflict`、`experts/not-found` …),任何其它异常收敛成
|
|
195
|
+
`experts/tool-failed` 并保留原始 message,绝不把裸异常抛给模型。
|
|
196
|
+
|
|
197
|
+
## 内置技能 `expert-manager`
|
|
198
|
+
|
|
199
|
+
`/expert-manager` 能解析,靠的不是命令注册表,而是两条官方机制:
|
|
200
|
+
|
|
201
|
+
1. `ctx.skills.register({ name, description, whenToUse, source, content, resourceBase })`
|
|
202
|
+
把一份**运行时技能**登记进当前 context 的层(`packages/experts/src/skill.js`);
|
|
203
|
+
2. 官方 `dsh-tool-skill` 在 `agent/pre-step` 扫描每条用户文本里的
|
|
204
|
+
`/(^|\s)\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\s|$)/`,用捕获到的名字查
|
|
205
|
+
`ctx.skills.get(name, …)`;存在且 `userInvocable` 时把该技能的 `<skill_content>`
|
|
206
|
+
块作为一条用户消息注入这一步。
|
|
207
|
+
|
|
208
|
+
所以**技能的 `name` 就是命令**:`SKILL.md` frontmatter 里的
|
|
209
|
+
`name: expert-manager` 让 `/expert-manager` 解析,`resourceBase` 指向的
|
|
210
|
+
目录让正文里 `references/...`、`runtime/...` 的相对路径可解析。`invocation` 不显式设置,
|
|
211
|
+
走注册表的默认值(模型与用户都可调用)——显式写 `userInvocable: false` 会让这条命令变成静默空操作。
|
|
212
|
+
|
|
213
|
+
资源在 `resources/skills/expert-manager/`(9 个文件,约 64K):
|
|
214
|
+
|
|
215
|
+
| 文件 | 来源 |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| `SKILL.md` | 上游原文改写:工具名与流程映射到本系统 |
|
|
218
|
+
| `references/authoring-api.md` | 本仓库重写:本系统的四个字段、五个工具、草稿/发布两态、没有的能力 |
|
|
219
|
+
| `references/{agent-md-spec,team-spec,plugin-json-spec,avatar-spec}.md` | WorkBuddy 原版规范,正文未改写,仅在标题下加了一段说明「这里提到的工具名本系统没有」 |
|
|
220
|
+
| `runtime/team-lead.md` | 上游原文改写:补上「本系统没有 Host 侧成员挂载」这一事实 |
|
|
221
|
+
| `NOTICE.md` / `LICENSE.workbuddy.txt` | 署名与 Apache-2.0 许可证,随内容分发 |
|
|
222
|
+
|
|
223
|
+
## 错误码
|
|
224
|
+
|
|
225
|
+
| 码 | 触发 |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| `experts/invalid-request` | 缺少必填字段、`preferredSkills` 不是数组、`patch` 不是对象、成员缺 `expertId` |
|
|
228
|
+
| `experts/invalid-name` | 名称为空或超过 120 字符 |
|
|
229
|
+
| `experts/invalid-description` | 描述为空或超过 2000 字符 |
|
|
230
|
+
| `experts/invalid-role` | 角色说明为空或超过 60000 字符 |
|
|
231
|
+
| `experts/role-budget-exceeded` | 角色说明超过 8000 token 的预留预算(中英混排估算) |
|
|
232
|
+
| `experts/invalid-skill-name` | 首选技能名为空或超过 128 字符 |
|
|
233
|
+
| `experts/too-many-skills` | 首选技能超过 12 个 |
|
|
234
|
+
| `experts/not-found` | 专家不存在(或草稿/发布修订缺失) |
|
|
235
|
+
| `experts/revision-not-found` | `copy` 指定的修订不属于该专家 |
|
|
236
|
+
| `experts/draft-conflict` | 草稿已被改动,`expectedRevision` 过期 |
|
|
237
|
+
| `experts/not-published` | 采用一个还没有发布修订的专家 |
|
|
238
|
+
| `experts/invalid-team-name` | 专家团名称为空或超过 120 字符 |
|
|
239
|
+
| `experts/invalid-team-description` | 专家团简介超过 2000 字符 |
|
|
240
|
+
| `experts/invalid-team-mandate` | 主理人职责超过 60000 字符 |
|
|
241
|
+
| `experts/invalid-team-members` | `members` 不是数组,或某个成员不是对象 |
|
|
242
|
+
| `experts/team-members-out-of-range` | 成员少于 2 位或多于 16 位 |
|
|
243
|
+
| `experts/invalid-member-key` | 成员标识不符合 `/^[a-z][a-z0-9-]{0,39}$/`,或用了保留字 `lead` |
|
|
244
|
+
| `experts/duplicate-member-key` | 同一个团里成员标识重复 |
|
|
245
|
+
| `experts/unknown-member` | 成员引用的专家不存在(**引用完整性**) |
|
|
246
|
+
| `experts/duplicate-member` | 同一位专家在同一个团里出现两次 |
|
|
247
|
+
| `experts/multiple-leads` | 一个团里有多位主理人 |
|
|
248
|
+
| `experts/team-not-found` | 专家团不存在 |
|
|
249
|
+
| `experts/team-conflict` | 专家团已被改动,`expectedRevision` 过期 |
|
|
250
|
+
| `experts/not-ready` | 存储尚未就绪(正常情况下不会出现) |
|
|
251
|
+
| `unknown-endpoint` / `malformed-request` / `handler-failed` | 共享路由约定,非本包所有 |
|
|
252
|
+
|
|
253
|
+
## 校验与限制
|
|
254
|
+
|
|
255
|
+
- 名称 ≤ 120 字符;描述 ≤ 2000;角色说明 ≤ 60000 字符且 ≤ 8000 token(CJK 1 字 1 token,
|
|
256
|
+
ASCII 4 字符 1 token)。
|
|
257
|
+
- 首选技能:最多 12 个,去重保序;只作为给模型的提示,**不会**自动调用或绑定执行。
|
|
258
|
+
- 复制时名称截断到 116 字符再加 ` 副本`,保证不超过 120。
|
|
259
|
+
- 草稿修订是四个字段的内容摘要(sha256),所以「改回原样」会得到同一个修订号,
|
|
260
|
+
这是有意的内容寻址语义(同 `packages/skills` 的 `revision`)。
|
|
261
|
+
- 专家团:名称 ≤ 120(与专家同名上限,不引入第二套数字);简介 ≤ 2000;
|
|
262
|
+
主理人职责 ≤ 60000;成员 2–16 位(**上游 `validateDefinition` 的同一组数字**:
|
|
263
|
+
`members.length < 2 || members.length > 16`,主理人另计)。
|
|
264
|
+
- 成员的 `key` 由客户端从专家名生成(`memberKeyFor`):取小写 a–z/0–9/-、截断到 40、
|
|
265
|
+
冲突时加 `-2`、纯中文等无法成 key 的名字回退成 `member`;Host 只负责校验。
|
|
266
|
+
|
|
267
|
+
## 构建与校验
|
|
268
|
+
|
|
269
|
+
```sh
|
|
270
|
+
node scripts/build-client.mjs experts
|
|
271
|
+
node scripts/check.mjs experts
|
|
272
|
+
node scripts/smoke.mjs experts
|
|
273
|
+
node scripts/render-check.mjs experts
|
|
274
|
+
node --test "tests/experts.test.mjs"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`tests/experts.test.mjs` 走**真实 Host 入口**(`apply` + 注册出来的路由),
|
|
278
|
+
并用 `scripts/harness/host-context.mjs` 的替身 context 驱动。它覆盖:创建与各条校验拒绝、
|
|
279
|
+
草稿编辑不影响已发布修订、发布递增修订号、采用钉住修订、提示词上下文只给采用的会话注册、
|
|
280
|
+
取消采用会移除、复制、删除、以及未知端点 / 缺字段的信封应答。
|
|
281
|
+
|
|
282
|
+
专家团部分另外覆盖:创建 / 列表 / 读取 / 更新 / 删除的完整往返、成员引用**真实专家**、
|
|
283
|
+
引用不存在专家被拒(`experts/unknown-member`)、同一专家重复入团被拒
|
|
284
|
+
(`experts/duplicate-member`)、成员标识重复被拒(`experts/duplicate-member-key`)、
|
|
285
|
+
保留字 `lead` 被拒、多位主理人被拒、成员数量越界被拒、名称校验、
|
|
286
|
+
`update-team` 的乐观修订冲突、以及**删除成员专家后团保持一致**
|
|
287
|
+
(三减一 → 摘掉成员并换修订;二减一 → 团团一并删除;没有被任何团引用的专家 → 团完全不动)。
|
|
288
|
+
另有 `node scripts/render-check.mjs experts` 证明三个注册组件在空数据与永不响应的路由下
|
|
289
|
+
都能渲染(不再只是「能加载」)。
|
|
290
|
+
|
|
291
|
+
## 客户端复刻(对齐上游)
|
|
292
|
+
|
|
293
|
+
面板、详情对话框、草稿编辑器与发布确认对话框的**标记、类名、文案与 `aria-*`**
|
|
294
|
+
来自上游 `plugins/experts/src/client/`,样式表整块移植;不能逐字对齐的地方全部列在
|
|
295
|
+
下面的偏离日志里。**未做浏览器验证**(见「未验证」)。
|
|
296
|
+
|
|
297
|
+
| 上游 | 本包 |
|
|
298
|
+
| --- | --- |
|
|
299
|
+
| `client/ExpertsPanel.tsx` | `src/panel.tsx` |
|
|
300
|
+
| `client/ExpertDetailModal.tsx` | `src/detail.tsx` |
|
|
301
|
+
| `client/ExpertDraftEditor.tsx` + `client/PublishConfirmDialog.tsx` | `src/editor.tsx` |
|
|
302
|
+
| `client/styles.ts` | `src/styles.ts`(`@layer workdsh-business` 规则块与媒体查询逐字节相同) |
|
|
303
|
+
| `packages/ui/src/styles/modal.ts` 的 `modalCss` | `src/dialog-css.ts`(逐字节相同) |
|
|
304
|
+
| `packages/ui/src/components/Icon.tsx` 的 `experts`/`skills`/`connectors` 图标 | `src/icons.tsx`(path 与 SVG 属性逐字节相同) |
|
|
305
|
+
| `client/ExpertUsagePreview.tsx` 的 `.usage-preview` | 折叠进 `src/editor.tsx`(按本 Host 的字段缩减) |
|
|
306
|
+
| `client/TeamOverview.tsx` | `src/team.tsx` 的 `TeamOverview`(成员网格逐字对齐;`identity()` 的取数层改写) |
|
|
307
|
+
| `client/TeamContent.tsx` | `src/team.tsx` 的 `TeamContent`(标记与文案逐字对齐;字段减到一项) |
|
|
308
|
+
|
|
309
|
+
### 偏离日志
|
|
310
|
+
|
|
311
|
+
1. **`workdsh-ui` 组件 → 原生元素。** `Button`/`Input`/`Select`/`Textarea` 换成携带
|
|
312
|
+
同样类名的 `<button>`/`<input>`/`<select>`/`<textarea>`;只影响 `workdsh-ui`
|
|
313
|
+
自身外观的 `variant`/`size` 属性被去掉(它们不在移植的样式表里)。渲染出的类名、
|
|
314
|
+
文案、`aria-*` 与上游一致。
|
|
315
|
+
2. **`Modal` → `shared/ui` 的 `Modal`。** 传入 `open` 与 `className`,并让容器同时
|
|
316
|
+
带上上游的 `wd-dialog`,因此渲染出的 `class` 与上游一致(`wd-dialog <上游 className>`:
|
|
317
|
+
`expert-dialog` / `editor-dialog` / `confirm-dialog publish-dialog`)。上游 `Modal`
|
|
318
|
+
自带的右上角关闭按钮(`.wd-dialog-close`)由我们在容器内渲染,标记相同。
|
|
319
|
+
3. **对话框外壳 CSS 的来源。** 上游 `styles.ts` 头部插值 `${modalCss}`(来自
|
|
320
|
+
`workdsh-ui`)。`shared/ui` 的 `modalCss` 描述的是另一个容器(`.dshs-modal`),
|
|
321
|
+
所以把上游那份逐字节抄进 `src/dialog-css.ts`。遮罩仍由我们的 `Modal` 提供
|
|
322
|
+
(`.dshs-overlay`,不是上游的 `.wd-dialog-backdrop`):底色的 `color-mix` 与
|
|
323
|
+
`z-index` 因此与上游不同。
|
|
324
|
+
4. **令牌别名。** `tokenAliasCss` 前置到面板与选择器样式表,`wd-scope` 加在面板
|
|
325
|
+
`section` 与选择器根 `div` 上(对话框是面板的子节点,自定义属性照常继承)。
|
|
326
|
+
上游样式用到四个 DSH 0.2.0 没有的 token(`--dsw-alias-label-tertiary`、
|
|
327
|
+
`--dsw-alias-interactive-bg-hover`、`--dsw-alias-button-primary-fill`、
|
|
328
|
+
`--dsw-alias-label-primary-inverted`),由它定义。颜色、圆角、渐变与间距没有改动。
|
|
329
|
+
5. **去掉尾部的 `${controlsCss}`。** 它来自 `workdsh-ui`,给 `[data-wd-control]` 与
|
|
330
|
+
`.wd-form-*` 用;移植后的原生元素不带这些属性,规则块本身已经承担了按钮/输入的
|
|
331
|
+
外观,留着只会争优先级。
|
|
332
|
+
6. **`label` → `title`。** 我们的 `Modal` 用 `title` 接收上游 `label` 的字符串,
|
|
333
|
+
渲染出的 `aria-label` 与上游相同。
|
|
334
|
+
7. **能力切换键名。** 上游 `workdsh-experts|workdsh-skills|workdsh-connectors` →
|
|
335
|
+
本家族的 `dsh-plugin-experts|dsh-plugin-skills|dsh-plugin-connectors`。
|
|
336
|
+
8. **专家团已接通(原先的「省略数据通路」已撤销)。** 2026-10 之前本 Host 没有专家团,
|
|
337
|
+
`work-types` 的「专家团」页签与「制作专家」→「创建专家团」**保留但 disabled 并给出
|
|
338
|
+
`title` 原因**。现在两者都已**删除 disabled 占位**并按上游接线:
|
|
339
|
+
- `kind`(`agent` | `team`)状态、`expert-kind` URL 参数、两个页签的
|
|
340
|
+
`aria-pressed`/`active` 与 `我的` 视图下的计数,都按上游 `ExpertsPanel.tsx` 写;
|
|
341
|
+
- 列表、卡片、空态文案(`暂无可用专家团` / `还没有自己的专家团`)按上游分支写。
|
|
342
|
+
|
|
343
|
+
仍然不同的地方:
|
|
344
|
+
- **成员是引用,不是内联快照。** 上游 `team.members[].definition` 是整份
|
|
345
|
+
`ExpertDefinition`;本包只存 `{ key, expertId, lead }`,读时解析。
|
|
346
|
+
- **`TeamOverview` 的 `identity()` 改写。** 上游用 `yaml` 解析 `agentDocument`
|
|
347
|
+
前置元数据取 `displayName`/`profession`,并从
|
|
348
|
+
`packageDocuments['.workdsh-expert/plugin.json']` 的 `members[].role === 'lead'`
|
|
349
|
+
里取主理人的 `avatar`;本 Host 没有包文档与资源,所以直接取被引用专家的
|
|
350
|
+
`name`/`description`,头像用姓名首字。渲染出的类名(`.member-avatar` /
|
|
351
|
+
`.member-identity` / `.member-lead` / `.member-responsibility` / `.team-person`)
|
|
352
|
+
与元素层级与上游一致,**样式表未改**。
|
|
353
|
+
- **`role` 文本要另外取。** 上游的成员快照自带 `role`;本包的名册行只有 id,
|
|
354
|
+
所以详情打开后逐个 `get` 一次成员的 `role` 填进 `.member-responsibility`。
|
|
355
|
+
这是唯一的额外往返,也是「引用而非快照」的直接代价。
|
|
356
|
+
- **主理人是名册里的一位成员**(`lead: true`),不是团长自己。上游把团长当
|
|
357
|
+
lead、成员另计;本 Host 的团没有「自己」这一实体,所以 `members[0].lead`
|
|
358
|
+
表示主理人,卡片页脚显示「主理人 X」。
|
|
359
|
+
|
|
360
|
+
9. **省略协作场景(`team.workflows`)。** 上游的 `ExpertTeamDefinition.workflows`
|
|
361
|
+
是 1–12 个具名场景,每个场景有 `title` / `trigger` / `deliverable` 和一组
|
|
362
|
+
`stages[{ id, worker, reviewer, dependsOn }]`,发布时由 `validateTeamWorkflow`
|
|
363
|
+
做环检测、成员存在性与「评审不能是执行人」校验,最终进 preset 编译。本仓库没有
|
|
364
|
+
执行编排(官方 Agent Teams 覆盖),这些阶段没有任何东西可驱动,所以
|
|
365
|
+
`TeamOverview` 里的 `.team-workflows` 折叠块与 `TeamContent` 里的「协作场景」
|
|
366
|
+
一节**整块不渲染**,而不是渲染一个永远空着的壳。
|
|
367
|
+
样式表里 `.expert-dialog .team-workflows article` 两条规则**保留**(上游逐字节),
|
|
368
|
+
只是因为不再有元素用到而暂时无效果。
|
|
369
|
+
10. **省略成员级治理与发布快照。** 上游每位成员会被物化成一条带 `teamParentId` 的
|
|
370
|
+
子专家行(`origin`/`availability`/`canEdit`/`canManage`),发布修订上还带
|
|
371
|
+
`teamMembers`(key → `{ expertId, revisionId }`)、`dependencyLock`、
|
|
372
|
+
`compilerVersion`、`compositionDigest`,另外还有 `upgradePublishedTeam` 这种
|
|
373
|
+
「用当前运行时重编译旧团」的路径。本仓库没有子专家行、preset 编译或修订保留策略,
|
|
374
|
+
这些字段一律不存在;专家团因此**没有草稿 / 发布之分**,保存即生效。
|
|
375
|
+
11. **省略导入 / 导出。** 本 Host 没有 WorkBuddy `plugin.json` 包的暂存、预览、
|
|
376
|
+
提交与导出端点,`section-actions` 里只保留「刷新」,`导入专家` 对话框不渲染。
|
|
377
|
+
12. **省略预设菜单(`PresetMenu.tsx`)。** 上游要接管
|
|
378
|
+
`conversation.hero.agentPreset` 这一席位并过滤 `wd-exp-*` 预设;本 Host 没有
|
|
379
|
+
preset 服务,客户端也不请求任何 Harness 模块,因此不注册。
|
|
380
|
+
13. **省略召唤 / 可用性 / 置顶。** 本 Host 只有「采用」(`set-adoption`),没有
|
|
381
|
+
执行装配、`availability`(enabled/disabled/archived)或 `preference`。因此:
|
|
382
|
+
- 详情头部的主动作是**为本次对话采用 / 取消采用**(沿用上游 `.summon` 主按钮样式),
|
|
383
|
+
不渲染「召唤专家」与示例任务列表;专家团详情的主按钮换成了「编辑专家团」,
|
|
384
|
+
因为一个团没有可采用的修订;
|
|
385
|
+
- 卡片与详情菜单不渲染 停用 / 启用 / 归档;
|
|
386
|
+
- 卡片页脚的置顶星标(`.pin`)不渲染(置顶样式仍在移植表里,未使用)。
|
|
387
|
+
14. **省略示例任务、制作文件、技能状态与扩展能力。** 本 Host 的专家只有
|
|
388
|
+
名称/简介/角色说明/首选技能四项:详情不渲染「试试这样问我」「扩展能力」,
|
|
389
|
+
配备技能一行只给名称(没有版本与可用性状态),编辑器按
|
|
390
|
+
`.list-editor`/`.equipped-row` 增删技能名,不做 `SkillPicker` 的已安装目录查询。
|
|
391
|
+
15. **发布是单次调用。** 上游的三步信任链(请求确认 → 确认 → 发布)、定义摘要、
|
|
392
|
+
依赖锁定摘要与一次性凭据在本 Host 不存在,`publish` 一次调用即冻结修订。
|
|
393
|
+
`.confirm-dialog publish-dialog` / `.usage-preview` 的标记保留,内容缩减为本
|
|
394
|
+
Host 的四个字段;成功页去掉「去试试」(没有召唤),只留「返回我的专家」。
|
|
395
|
+
16. **「我的专家 / 默认」是投影,不是 `origin` 字段。** 本 Host 没有内建/自建之分:
|
|
396
|
+
每一位专家都是本机编写的。因此 `默认` = 已发布(存在冻结修订),
|
|
397
|
+
`我的` = 仅有草稿,`我的专家 N` 数的是草稿数;`mine` 视图不再渲染上游的
|
|
398
|
+
状态筛选行(草稿/已发布/已停用/已归档里的后两个本 Host 没有)。
|
|
399
|
+
专家团页签下的 `我的专家 N` 数的是团的总数(一个团没有草稿态,只有存在与否)。
|
|
400
|
+
17. **卡片页脚的三枚药丸。** 上游是 `专家` + readiness + `默认`(origin)。本包是
|
|
401
|
+
`专家` + `可用`(绿色,已发布) / `未发布` + `默认修订 N` / `草稿`;卡片副标题
|
|
402
|
+
(上游的 frontmatter `profession`)改为简介首行,完整简介仍由 `.desc` 承担。
|
|
403
|
+
专家团卡片对应的是 `专家团` + `N 位成员` + `主理人 X`(或 `未指定主理人`)。
|
|
404
|
+
18. **「制作专家」的第一步是本包自有的对话框。** 上游会新建一个会话并填入
|
|
405
|
+
`/expert-manager` 引导草稿;本 Host 的 `create` 直接收四个字段,所以
|
|
406
|
+
用一个对话框收齐(沿用移植的 `.editor-dialog`/`.group`/`.field` 标记与样式)。
|
|
407
|
+
这是本包新增、上游没有的界面。**「创建专家团」同理**:上游走
|
|
408
|
+
`createExpertTask('team')` 开一个引导会话,本包用 `CreateTeamDialog`
|
|
409
|
+
直接收(名称 / 简介 / 主理人职责 / 成员名册),也是本包新增的界面。
|
|
410
|
+
19. **删除是本包新增的入口。** 上游面板不暴露删除;本包的 `delete` 路由必须可达,
|
|
411
|
+
所以卡片菜单与详情菜单各有一项(`.danger`),并带一个 `.confirm-dialog` 确认框。
|
|
412
|
+
专家团同样如此(`DeleteTeamDialog`)。删除成员专家时面板会额外提示被连带更新
|
|
413
|
+
或删除的团名,见「专家团」一节。
|
|
414
|
+
20. **选择器没有上游对应物。** 上游没有会话级专家选择器(召唤即绑定),
|
|
415
|
+
`picker.tsx` 因此沿用同族已移植选择器的类名与度量(`wd-scope`、
|
|
416
|
+
`tokenAliasCss`、`.picker-*`),功能保留会话总线协议与「采用/取消采用」。
|
|
417
|
+
21. **`data-testid` 保持上游的 `workdsh-experts`**,没有改成本家族命名。
|
|
418
|
+
22. **客户端接线。** 上游 `inject` 是 `['slots','layout','sessions','workspaces','remote',
|
|
419
|
+
'remote.session','connection','uiWorkspace']`;本包走 `fetch` 的 `ExpertsApi`,
|
|
420
|
+
只读 `ctx.slots` 与 `ctx.layout`,所以 `inject` 是 `['slots','layout']`,能力切换
|
|
421
|
+
仍经 `shared/ui` 的 `createNavFace`(`scripts/check.mjs` 会静态检查 `ctx.*` 与 `inject`)。
|
|
422
|
+
专家团不引入任何新的 `ctx` 读取,因此 `inject` 不变。
|
|
423
|
+
23. **`dist/client.js` 里的多行 CSS 每行多 4 个空格。** `scripts/build-client.mjs`
|
|
424
|
+
缩进打包后的源码,多行模板字符串会一起被缩进;源文件与上游逐字节相同,这里只
|
|
425
|
+
影响构建产物里的空白(CSS 空白无语义)。全家族共有现象,不是本包改动。
|
|
426
|
+
24. **成员名册编辑器是本包自有的界面。** 上游的团队编辑发生在会话内的
|
|
427
|
+
`ExpertDraftEditor`,成员是随整份定义走的内联快照。本包的
|
|
428
|
+
`EditTeamDialog` / `CreateTeamDialog` 复用移植的 `.editor-dialog` /
|
|
429
|
+
`.group` / `.field` / `.list-editor` / `.equipped-row` 标记与文案风格,
|
|
430
|
+
名册用「选择已有专家 → + 添加成员」而非自由文本,这是本包新增的界面。
|
|
431
|
+
25. **成员标识由客户端生成。** 上游的 `key` 是作者手写的(`analyst`、`reviewer`)。
|
|
432
|
+
本包的用户只从下拉里挑专家,所以 `memberKeyFor` 从专家名派生一个符合
|
|
433
|
+
`/^[a-z][a-z0-9-]{0,39}$/` 的 key(冲突加 `-2`,无法成 key 的中文名回退
|
|
434
|
+
`member`)。Host 仍按上游规则校验,客户端只是保证自己发出去的东西合法。
|
|
435
|
+
|
|
436
|
+
## 已核对(`cordis_inspect_query`)
|
|
437
|
+
|
|
438
|
+
- Host `Service` `systemPrompt`:`context(context: PromptContext): () => void` 存在;
|
|
439
|
+
`PromptContext = { name, order, text: string | ((context) => string) }`;
|
|
440
|
+
`AssembleContext = { scope?, signal? }` —— **没有会话身份**,所以必须按 agent 注册。
|
|
441
|
+
- Host `Service` `agents`:`get(id)` 返回活着的 agent(用于采用变更时定位会话)。
|
|
442
|
+
- Host `Event` `agent/created`:`{ agent, source, signal? }`,serial。
|
|
443
|
+
- Host `Event` `agent/disposed`:`{ agent }`,emit。
|
|
444
|
+
|
|
445
|
+
## 未验证(Not verified)
|
|
446
|
+
|
|
447
|
+
- **没有装进任何 profile。** 本包没有执行 `plugin_manager install_bundle`,没有重启应用,
|
|
448
|
+
没有触碰 `~/.dsh`。真机安装与页面渲染由 Lead 负责。
|
|
449
|
+
- **Client 侧插槽契约没有现场核对。** 写这个包时 Client `Slots` 的 inspect 查询
|
|
450
|
+
(`listSubTree`)连续超时,没有拿到 `main` / `conversation.input.left` /
|
|
451
|
+
`sidebar.panellist` 的实时目录。这里用的是 `docs/PORTING-NOTES.md` §5 记录过的契约
|
|
452
|
+
与 `packages/connectors` 已装通的写法:`main` 用 `{ name, key, inject }`,
|
|
453
|
+
输入框插槽用 `{ name, id, order, inject }` 并用插槽传入的 `sessionId`。
|
|
454
|
+
- **面板的「为本次对话采用」如何拿到会话 id 没有现场验证。** `main` 插槽不提供会话 id,
|
|
455
|
+
所以面板采用**会话总线**:输入框选择器把它从插槽拿到的 `sessionId` 发布出来,面板采用
|
|
456
|
+
这一个。总线保留**最后一次**被寻址的会话(不在选择器卸载时清空),因为打开专家面板会
|
|
457
|
+
把会话视图从 `main` 换掉、选择器随之卸载;清空会让采用控件在最需要它的时候失去目标。
|
|
458
|
+
这里没有去猜 shell 的 session hook 形状。如果实时契约与 PORTING-NOTES §5 不同,
|
|
459
|
+
需要按现场结果调整。副作用:会话关闭后总线仍保留该 id,采用会写到一个暂时不存在的
|
|
460
|
+
会话上(该会话恢复时上下文会重新注册)。
|
|
461
|
+
- **`systemPrompt.context` 的真实装配结果没有跑过**:离线替身只记录注册调用,
|
|
462
|
+
证明不了真实装配器把这段文字放进了模型请求,也证明不了多个插件同时注册不同 order 时
|
|
463
|
+
的真实排序(只验证了两者 order 数值不同)。
|
|
464
|
+
- **存储设施没有用真实 zod 校验过**:域 spec 走的是替身,真实
|
|
465
|
+
`@deepseek-ai/dsh-storage-domain` 是否接受这份 loose schema、以及真实 `local` 记录的
|
|
466
|
+
读写,都没有验证。
|
|
467
|
+
- **`ctx.agents.get(sessionId)` 返回的对象是否带 `ctx.systemPrompt`** 没有在真机验证。
|
|
468
|
+
代码对此 fail-open:拿不到 agent 或拿不到 `context()` 时只跳过注册(会话创建路径
|
|
469
|
+
仍然通过 `agent/created` 事件载荷工作)。
|
|
470
|
+
- **专家团的视觉没有验证。** 只有 jsdom 断言(仓库外的一次性探针,不提交):
|
|
471
|
+
它挂载**构建产物** `dist/client.js`、用真实 React 驱动、点通了
|
|
472
|
+
创建 → 详情 → 编辑 → 删除,并断言 `.team-person` / `.member-avatar` /
|
|
473
|
+
`.member-identity` / `.member-lead` 等移植类名真的渲染出来、以及所有挂载的
|
|
474
|
+
`<Modal>` 都是 `open`。**没有像素级验证**:没有浏览器、没有布局、没有样式计算、
|
|
475
|
+
没有真实点击坐标,`.team-member-grid` 的三列栅格与上游看上去是否一致,
|
|
476
|
+
要靠真机页面确认。
|
|
477
|
+
- **`schemaVersion` 仍是 1,旧记录靠 `init` 补齐。** `teams` 是同一条记录里新增的
|
|
478
|
+
集合键,属于增量而非迁移,所以没有升版。`init` 会把缺 `teams` 键的旧记录补成
|
|
479
|
+
`{ ...stored, teams: {} }` 并写回,因此一份**旧版本写下的**记录不会在第一次读取时
|
|
480
|
+
炸掉(`tests/experts.test.mjs` 有一条测试直接种一条没有 `teams` 的记录来证明这一点)。
|
|
481
|
+
替身 harness 的 schema 是 passthrough,所以**无法证明**真实的
|
|
482
|
+
`@deepseek-ai/dsh-storage-domain` 会接受这份带 `teams` 的记录;那是安装后第一次
|
|
483
|
+
启动才能确认的事。
|
|
484
|
+
|
|
485
|
+
## 许可
|
|
486
|
+
|
|
487
|
+
MIT。设计参照 WorkDSH `packages/plugins/experts`(MIT),但为概念级重写,未复制其源码。
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## 更正(同日):`tokenAliasCss` 已被移除
|
|
492
|
+
|
|
493
|
+
复刻时我判断 DSH 0.2.0 缺少上游 CSS 用的四个 token
|
|
494
|
+
(`--dsw-alias-label-tertiary`、`--dsw-alias-interactive-bg-hover`、
|
|
495
|
+
`--dsw-alias-button-primary-fill`、`--dsw-alias-label-primary-inverted`),
|
|
496
|
+
于是在 `shared/ui` 里加了一层 `color-mix` 近似别名并前置到样式表。
|
|
497
|
+
|
|
498
|
+
**这个判断是错的。** `@deepseek-ai/dsh-client-ui-theme` 在明暗两套主题里都定义了这四个:
|
|
499
|
+
|
|
500
|
+
```
|
|
501
|
+
--dsw-alias-button-primary-fill:var(--dsw-alias-brand-primary)
|
|
502
|
+
--dsw-alias-label-tertiary:var(--dsw-static-neutral-bluish-600)
|
|
503
|
+
--dsw-alias-interactive-bg-hover:#2631480f
|
|
504
|
+
--dsw-alias-label-primary-inverted:var(--dsw-static-neutral-bluish-800)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Client 的 `Theme.listTokens` inspect 只报告 14 个 token,是个**更窄的公开子集**,
|
|
508
|
+
我据它就下了结论。别名定义在作用域上会**覆盖真实值** —— 最明显的是主按钮
|
|
509
|
+
(+ 添加 MCP / 保存并连接 / 编辑)从品牌蓝变成了 label 色。
|
|
510
|
+
|
|
511
|
+
现已删除 `tokenAliasCss` 及其在样式表与选择器里的前置,`wd-scope` 类保留但不再定义任何变量。
|
|
512
|
+
同样地,本包(以及 `packages/projects`、`packages/library`)的顶部**不再渲染能力切换标签** ——
|
|
513
|
+
上游只在 专家 / 技能 / 连接器 三处渲染它,项目与资料库是独立的侧栏目的地。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# dsh-skills experts bundle patch.
|
|
2
|
+
#
|
|
3
|
+
# One insert row. The Host half owns POST /api/dsh-skills/experts, keeps the
|
|
4
|
+
# expert records (draft + immutable published revisions) and contributes an
|
|
5
|
+
# adopted expert's published role to the conversation's prompt. The Client half
|
|
6
|
+
# (declared through package.json's dsh.client) registers the rail entry, the
|
|
7
|
+
# expert center and the composer picker.
|
|
8
|
+
#
|
|
9
|
+
# No execution wiring is installed here on purpose: official Agent Teams owns
|
|
10
|
+
# member execution, and this repo does not port WorkDSH's own governance or
|
|
11
|
+
# execution stack (see packages/experts/README.md).
|
|
12
|
+
- insert:
|
|
13
|
+
- id: dsh-plugin-experts
|
|
14
|
+
name: 'dsh-plugin-experts'
|