deepseek-coordination 0.1.9 → 0.10.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/LICENSE +21 -21
- package/README.md +349 -1083
- package/README.zh-CN.md +349 -697
- package/cordis.patch.yml +5 -74
- package/lib/client.js +33772 -0
- package/lib/index.d.mts +252 -0
- package/lib/index.mjs +27374 -0
- package/package.json +98 -114
- package/client.js +0 -338
- package/src/host/bin/dsh-os.bat +0 -19
- package/src/host/bin/dsh-os.ps1 +0 -5039
- package/src/host/bin/dsh-os.sh +0 -3385
- package/src/host/bin/dsh-os7.ps1 +0 -5518
- package/src/host/command.txt +0 -136
- package/src/host/docs/command-original.txt +0 -26
- package/src/host/docs/os-spec.md +0 -528
- package/src/host/docs/permission-model.md +0 -206
- package/src/host/spec/charsets.tsv +0 -28
- package/src/host/spec/verbs.tsv +0 -120
- package/src/permission/index.js +0 -546
- package/src/plugin/index.js +0 -226
- package/src/plugin/invoke.js +0 -306
- package/src/plugin/settings.js +0 -349
- package/src/plugin/takeover.js +0 -436
- package/src/plugin/workflow.js +0 -1494
- package/tools/measure-cache.mjs +0 -223
package/README.zh-CN.md
CHANGED
|
@@ -1,697 +1,349 @@
|
|
|
1
|
-
# deepseek-coordination
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
`
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
`
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
|
239
|
-
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
|
252
|
-
|
|
253
|
-
|
|
|
254
|
-
|
|
|
255
|
-
|
|
|
256
|
-
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
|
324
|
-
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
动词深度为一到三段——`selfcheck`、`file charset`、`file verify charset`——解析器只在扩展形状确实已知时才吞掉下一个 token,因此一个恰好以单词开头的路径参数不会被当成动词的一部分吃掉。
|
|
351
|
-
|
|
352
|
-
### 编码、BOM 与行尾
|
|
353
|
-
|
|
354
|
-
`src/host/spec/charsets.tsv` 定义了 15 个规范编码名,各含别名、.NET 码页、`iconv` 名与 BOM 字节:
|
|
355
|
-
|
|
356
|
-
`utf-8`、`utf-8-bom`、`utf-16le`、`utf-16be`、`utf-32le`、`utf-32be`、`gbk`、`gb18030`、`big5`、`shift_jis`、`euc-jp`、`euc-kr`、`latin1`、`windows-1252`、`ascii`。
|
|
357
|
-
|
|
358
|
-
探测顺序是先嗅 BOM,再试**严格** UTF-8,然后是 GBK,最后 latin1。读取时剥离 BOM 并把 `\r\n` 归一为 `\n` 用于输出;**任何写回操作都还原文件原有的 BOM 存在性与行尾约定。** 偏移是半开区间 `[start, end)` 的字节偏移,必须落在字符边界上,否则以 `E_BOUNDARY` 失败,而不是把一个字符切碎。
|
|
359
|
-
|
|
360
|
-
二进制判定是载荷前 8 KiB 内出现 NUL 字节,报 `E_BINARY_CONTENT`,而不是给出乱码。
|
|
361
|
-
|
|
362
|
-
---
|
|
363
|
-
|
|
364
|
-
## 动词面
|
|
365
|
-
|
|
366
|
-
`src/host/spec/verbs.tsv` 中**声明了 86 个动词**,它是单一真源。其中一部分只声明未实现;在依赖某个动词之前,请先看[实际可用范围](#实际可用范围)。
|
|
367
|
-
|
|
368
|
-
| 组 | 动词 |
|
|
369
|
-
|---|---|
|
|
370
|
-
| `file` | `create` `charset` `encode` `length` `information` `list` `delete` `move` `copy` `mkdir` `append` `insert` `view` `search` |
|
|
371
|
-
| `file verify` | `charset` `encoding` `completion` |
|
|
372
|
-
| `execute` | `command` `synchronous` `asynchronous` `query` `kill` |
|
|
373
|
-
| `system` | `brand` `cpu` `gpu` `version` `info` |
|
|
374
|
-
| `task` | `list` `query` `show` `kill` `launch` |
|
|
375
|
-
| `service` | `list` `query` `start` `stop` `restart` |
|
|
376
|
-
| `environment` | `list` `get` `add` `set` `delete` |
|
|
377
|
-
| `environment attribute` | `list` `get` `add` `set` `delete` |
|
|
378
|
-
| `permission` | `query` `list` `audit` `grant` `revoke` `acl init` |
|
|
379
|
-
| `window` | `find` `list` `active` `close` `top` `bottom` `move` `resize` `minimize` `maximize` `restore` |
|
|
380
|
-
| `mouse` | `click` `context` `over` `wheel` `down` `up` `cursor` |
|
|
381
|
-
| `keyboard` | `down` `up` `press` `combination` `type` `codes` |
|
|
382
|
-
| `clipboard` | `read` `write` `paste` |
|
|
383
|
-
| `wait` | `window` `ms` |
|
|
384
|
-
| 观察面 | `screenshot` `text` `structure` |
|
|
385
|
-
| 自检 | `selfcheck` |
|
|
386
|
-
|
|
387
|
-
几个值得先知道的设计点:
|
|
388
|
-
|
|
389
|
-
* **`file verify.*` 是断言,不是抛错。** `file verify charset` 会在返回观测值的同时给出 `match: false`,让调用方能区分"文件不对"与"我要求错了"。`file verify completion` 在你没给任何预期时返回 `complete: null`——这是刻意不写成 `false`。
|
|
390
|
-
* **`file insert` 的 `--charset` 是标志,不是位置参数。** 夹在 `content` 与 `offset` 之间时它会**静默顶掉 offset**。
|
|
391
|
-
* **`file.delete` 在 Windows 上默认进回收站。** 在 POSIX 上需要 `trash-put`,否则报 `E_NOT_SUPPORTED`——它**不会**静默地真删。
|
|
392
|
-
* **`environment` 有两层。** 变量持有一个字符串;**属性**(PATH、PATHEXT、LIB、INCLUDE)持有一个有序列表。写入会返回 `visibleToRunningProcesses`,因为 `user` 或 `machine` 级写入对已经在运行的进程不可见。
|
|
393
|
-
* **`keyboard type` 以 Unicode 为先。** 非 ASCII 文本走剪贴板粘贴,因为在有输入法的情况下 `KEYEVENTF_UNICODE` 并不可靠。
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
## 危险权限
|
|
398
|
-
|
|
399
|
-
在授予任何权限之前,这一章是必读的。本交互层刻意做得很有能力,而它给出的保证比功能列表看起来要窄。
|
|
400
|
-
|
|
401
|
-
### 一个被完全授权的 agent 能做什么
|
|
402
|
-
|
|
403
|
-
| 能力 | 涉及动词 | 授权要求 |
|
|
404
|
-
|---|---|---|
|
|
405
|
-
| **读取你的屏幕内容** | `screenshot`、`text`、`structure` | **只读,永不弹窗** |
|
|
406
|
-
| **读写你的剪贴板** | `clipboard.read`、`clipboard.write` | `read` 永不弹窗;`write` 需 `--allow` |
|
|
407
|
-
| **合成输入**——点击、拖拽、打字、组合键 | `mouse.*`、`keyboard.*` | `--allow` |
|
|
408
|
-
| **操纵你的窗口**——移动、改尺寸、关闭、置前 | `window.*` | `--allow` |
|
|
409
|
-
| **启动与结束进程** | `task.kill`、`execute.*` | `--allow` |
|
|
410
|
-
| **执行任意命令行** | `execute.synchronous`、`execute.asynchronous` | `--allow` |
|
|
411
|
-
| **删除、移动、覆盖文件** | `file.delete`、`file.move`、`file.copy` | `--allow` |
|
|
412
|
-
| **持久修改你的环境变量** | `environment.set`、`.add`、`.delete`、`attribute.*` | `--allow` |
|
|
413
|
-
| **控制系统服务**(已声明,未实现) | `service.start`、`.stop`、`.restart` | `--allow` + 提权 |
|
|
414
|
-
| **读取进程与系统清单** | `task.*`、`system.*` | **只读,永不弹窗** |
|
|
415
|
-
|
|
416
|
-
说得直白一些:`mouse.*` 与 `keyboard.*` 意味着一个被授权的 agent 能以你的权限操作**任何**你能操作的程序。又因为它能读屏,它还能观察自己刚刚做了什么。桌面之内没有沙箱。
|
|
417
|
-
|
|
418
|
-
### 授权实际上是怎么运作的
|
|
419
|
-
|
|
420
|
-
**B 层——结构强制(始终生效,跨平台)。**
|
|
421
|
-
|
|
422
|
-
* 每个变更类动词都要求 `--allow`。缺失即 `E_PERMISSION`,且**什么都不会发生**。
|
|
423
|
-
* **`permission.json` 的三个写入动词存在,但它们只走委托,不走脚本自身的授权。** 动词表里声明了 `permission grant`、`permission revoke`、`permission acl.init`(三个都是 `W` 类),而 `permission query`、`permission list`、`permission audit` 是只读的。`grant` 与 `revoke` 的 flags 只接受 `delegate`(外加 `allow`),也就是**只能由同意写入委托调用**;脚本本身不能凭 `--allow` 创建持久授权,缺委托时报 `E_PERMISSION_DELEGATE_ONLY`。
|
|
424
|
-
* 一条硬性的**受保护路径边界**拒绝任何对"决定本层能做什么"的文件的修改:`permission.json`(及 `.bak`)、`src/host/command.txt`、**四个** `src/host/bin/dsh-os.*`(`.ps1`、`dsh-os7.ps1`、`.sh`、`.bat`)、`src/host/spec/verbs.tsv` 与 `src/host/spec/charsets.tsv`,以及任何可能在其旁边暂存内容的同名临时文件。违反者报 `E_PERMISSION_PROTECTED_PATH`,且无任何副作用。读取仍然允许,以便你审阅规则。POSIX 侧的受保护清单有 8 项——同样的名单但**不含 `dsh-os7.ps1`**。
|
|
425
|
-
* 该检查作用于**解析并归一化之后**的路径,逐段比较——而不是正则匹配,因为 `..`、8.3 短名或结尾的句点都能绕过正则。`file.move` 与 `file.copy` **两端都检查**,因为移动受保护文件同样是一种破坏方式。
|
|
426
|
-
|
|
427
|
-
**A 层——内核 ACL(Windows,需显式开启,需提权)。**
|
|
428
|
-
|
|
429
|
-
`permission acl init` 会给 `permission.json` 的 DACL 追加一条针对 `Everyone`(`S-1-1-0`)的显式 **DENY**,覆盖 `FILE_GENERIC_WRITE | DELETE | WRITE_DAC | WRITE_OWNER`。该动作幂等;无法执行时报 `E_ELEVATION_REQUIRED`,而不是含糊地失败。
|
|
430
|
-
|
|
431
|
-
`selfcheck` 报告 `aclEnforced` 的方式是**读取文件真实的安全描述符**并遍历 DACL。早期版本是从一个状态文件里读标志位——那会声称一种并未生效的强制;该实现已被删除。`aclReason` 会解释 `false` 的原因,使其可被处置而不只是被疑惑。
|
|
432
|
-
|
|
433
|
-
### 四条需要如实说明的限制
|
|
434
|
-
|
|
435
|
-
这些在 `src/host/docs/permission-model.md` 中有记录,在此重述是因为它们改变了上述保证的含义。
|
|
436
|
-
|
|
437
|
-
1. **持久规则被记录了,但没有在动词层参与执法。** `permission.json` 的规则由同意写入委托及其 `os_permission_query` 工具来匹配;脚本读取该文件**仅用于展示**(`permission list/query/audit`)。没有任何动词在行动前查规则:真正的门禁是 `--allow`、受保护路径检查,以及(若已开启的)A 层。一条 `deny-always` 规则目前**并不能**拦住一个带着 `--allow` 到来的动词。
|
|
438
|
-
2. **`E_PERMISSION_DENIED_BY_RULE` 已登记但从未被抛出。** 它在错误表里并映射到退出码 3,但没有任何代码路径产生它——与第 1 条一致。
|
|
439
|
-
3. **脚本内的规则读取器没有实现 deny 优先。** 脚本侧的 `permission query` 取的是**第一条**匹配规则,不区分其决定是允许还是拒绝。委托侧正确实现了 deny 优先并有测试覆盖;因此两个读取器可能给出不同答案,而脚本侧是更弱的那个。
|
|
440
|
-
4. **`execute` 是绕过受保护路径边界的一条通道。** 该边界守卫的是 `file.*` 与 `environment.*`。它无法约束子进程:`execute.synchronous` 在 Windows 上把命令行交给 `cmd.exe /d /s /c`,在 POSIX 上交给 `sh -c`,因此被启动的程序可以写任何它自身权限允许的东西,包括 `permission.json`。只有 A 层能阻止这一点。当 `aclEnforced: false` 时,B 层做不到。
|
|
441
|
-
|
|
442
|
-
同意写入委托还有一个**设计缺口**需要直说:委托注册了五个工具(`os_permission_query`、`os_permission_grant`、`os_permission_revoke`、`os_permission_list`、`os_permission_audit`),但**没有注册 `approval/request` answerer**。它自己的设计文档写明应当注册,并且 `permission grant` **必须**经过用户确认的弹窗。就代码现状而言,强制手段是一段**工具描述**在告诉模型"只在用户选择之后才调用 `os_permission_grant`";代码并不校验弹窗是否真的发生过。请把该委托当作可信基础设施,而不是对 agent 的检查。
|
|
443
|
-
|
|
444
|
-
关于 A 层还有两点:它要求 harness 与委托以**不同身份**运行(否则退化为 B 层);其 POSIX 等价物更弱——一次改权限模式,同一用户即可撤销。`permission acl init` 在自己的返回结果里也这么说。
|
|
445
|
-
|
|
446
|
-
### 审阅已被授予了什么
|
|
447
|
-
|
|
448
|
-
```sh
|
|
449
|
-
dsh-os permission list # 规则、默认值,以及【实测】的 aclEnforced
|
|
450
|
-
dsh-os permission query file.delete "R:\project\**"
|
|
451
|
-
dsh-os permission audit --limit 50 # 追加式尾部;委托裁剪到 200 条
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
若 `permission.json` 的 `updatedBy` 不是 `consent-delegate`,它会被视为**不可信**:规则被忽略,一切退回默认值,并报告原因。也就是说,手工编辑该文件塞进去的恶意规则**不会**生效——它会被忽略,而且你会被告知。
|
|
455
|
-
|
|
456
|
-
要**创建**持久决定,必须使用委托的工具。通配目标(`target.value === "*"`)会被拒绝,除非显式传入 `acknowledgeBroad: true`——因为"对一切永久同意"必须是一个独立的决定,而不能是某次点击的副作用。
|
|
457
|
-
|
|
458
|
-
---
|
|
459
|
-
|
|
460
|
-
## 实际可用范围
|
|
461
|
-
|
|
462
|
-
统计方式:抽出各平台的分发表,与声明的动词表逐一比对。未实现的动词**从不静默失败**:它会返回一个指名道姓的 `E_NOT_SUPPORTED` 信封。
|
|
463
|
-
|
|
464
|
-
| 平台 | 已声明 | 已实现 |
|
|
465
|
-
|---|---|---|
|
|
466
|
-
| Windows PowerShell 5.1(`src/host/bin/dsh-os.ps1`) | 86 | **82** |
|
|
467
|
-
| PowerShell 7.6(`src/host/bin/dsh-os7.ps1`) | 86 | **82** |
|
|
468
|
-
| POSIX(`src/host/bin/dsh-os.sh`) | 86 | **61** |
|
|
469
|
-
|
|
470
|
-
两套 PowerShell 运行时的已实现集合**完全相同**(逐条比对两张分发表得出,不是目测)。
|
|
471
|
-
|
|
472
|
-
### Windows(5.1 与 7.6 相同)
|
|
473
|
-
|
|
474
|
-
**已实现(82):** 全部 `file.*`(含 `file.encode`);`file verify.*`;`file.search`;五个 `execute` 动词(`command`、`synchronous`、`asynchronous`、`query`、`kill`);`system.*`;全部 `task.*`(含 `task.launch`);`service.list/query`;整个 `environment` 面(含 `attribute.*`);整个 `permission` 面(含 `grant`、`revoke`、`acl.init`);整个 `window` 面;`screenshot`;`clipboard.read/write`;`wait.window/ms`;整个 `mouse` 与 `keyboard` 面;`text`;`structure`;`selfcheck`。
|
|
475
|
-
|
|
476
|
-
**未实现(4):** `clipboard.paste`、`service.start`、`service.stop`、`service.restart`。
|
|
477
|
-
|
|
478
|
-
### POSIX
|
|
479
|
-
|
|
480
|
-
**已实现(61):** 除 `file.encode` 外的整个 `file` 面;`file verify.*`;`file.search`;五个 `execute` 动词(含 Windows 侧同样具备的 `execute.command`);`system.*`;`task.list`/`query`/`show`/`kill`(**不含 `task.launch`**);整个 `environment` 变量层(含持久写入)与 `attribute.*` 五个动词;整个 `permission` 面;`wait.ms`;整个 `mouse` 面(7 个)与 `keyboard` 面(6 个);`selfcheck`。
|
|
481
|
-
|
|
482
|
-
这 61 个里,**13 个需要 X display**(`mouse.*` 7 个 + `keyboard.*` 6 个),其余 48 个不需要。
|
|
483
|
-
|
|
484
|
-
**未实现(25):** `file.encode`;全部 `window.*`(11 个);`screenshot`、`text`、`structure`;`clipboard.read`、`clipboard.write`、`clipboard.paste`;`wait.window`;`task.launch`;全部 `service.*`(5 个:`list`、`query`、`start`、`stop`、`restart`)。这些会经分发表的默认分支返回**带类型的** `E_NOT_SUPPORTED`,而不是静默缺失。
|
|
485
|
-
|
|
486
|
-
> [!NOTE]
|
|
487
|
-
> POSIX 没有持久化变量的内核 API,因此 `environment add/set/delete --scope user` 是通过一个**托管块**去编辑 `${HOME}/.profile` 的,块由 `# >>> dsh-os managed environment >>>` 与 `# <<< dsh-os managed environment <<<` 标记界定。每次改动都会重新生成标记之间的全部内容,因此这些操作是幂等的,用户也可以直接删掉整个块来一次性撤销。若直接往文件里写裸赋值,第二次调用就会重复追加,而且无法撤销。`--scope machine` 不可用,会报 `E_NOT_SUPPORTED`。
|
|
488
|
-
>
|
|
489
|
-
> `environment attribute add/set/delete` 则是**仅进程作用域**,持久作用域一律以 `E_NOT_SUPPORTED` 拒绝。这是刻意的:POSIX 没有列表型变量的内核 API,要持久化就得重写 shell 启动文件,而那里一旦重建错 `PATH`,整台机器的 shell 都可能不可用。
|
|
490
|
-
|
|
491
|
-
> [!IMPORTANT]
|
|
492
|
-
> **POSIX 只有 `mouse.*`(7 个)与 `keyboard.*`(6 个)需要 X display。** 没有 `DISPLAY` 时它们返回**带类型的** `E_NO_DISPLAY`;装了显示但没有 `xdotool` 时返回 `E_NOT_SUPPORTED` 并点名缺失的工具。
|
|
493
|
-
>
|
|
494
|
-
> **`wait.ms` 不需要**——它只是一次 sleep,因此本机可以完整验收。`environment attribute.*` 五个动词同样不需要(它们动的是环境变量,不是桌面)。
|
|
495
|
-
>
|
|
496
|
-
> 本机(无 `DISPLAY`、无 xdotool)的黄金测试因此把它们断言成 `E_NO_DISPLAY`:**这断言的是"缺失路径",不是"它们能用"。** 注入路径由 `tests/smoke-desktop-sh.sh` 用替身工具覆盖,见[测试](#测试)。
|
|
497
|
-
|
|
498
|
-
> [!NOTE]
|
|
499
|
-
> POSIX 上仍**结构性缺失**的是 `window.*`(11 个)、`screenshot`、`text`、`structure`、`clipboard.*`、`wait.window` 与全部 `service.*`。`native.sh`(515 字节)与 `service.sh`(333 字节)是刻意留空的桩,其文件头解释了原因:POSIX 上的对应物是外部程序(`wmctrl`、`import`、`xclip`、`scrot`),而不是可链接的库。
|
|
500
|
-
>
|
|
501
|
-
> **`window.sh` 不再是桩**(26 KB):它现在实现了 `wait.ms`、全部 `mouse.*` 与 `keyboard.*`,通过 `xdotool` 驱动 X11,并在动手之前先做显示与工具的能力检查。其余桌面动词仍从分发表的默认分支返回 `E_NOT_SUPPORTED`。
|
|
502
|
-
|
|
503
|
-
### 两套实现并不对等
|
|
504
|
-
|
|
505
|
-
同名动词、同一调用约定,但在若干处**行为不同**。重要的那些:
|
|
506
|
-
|
|
507
|
-
| 方面 | Windows | POSIX |
|
|
508
|
-
|---|---|---|
|
|
509
|
-
| `execute.command` | 未实现 | 已实现(真 argv,不经 shell) |
|
|
510
|
-
| `file.encode` | 已实现 | 未实现 |
|
|
511
|
-
| `file.delete` 默认 | 进回收站 | 无 `trash-put` 则失败 |
|
|
512
|
-
| `file.search` 字面匹配 | 大小写不敏感 | 大小写敏感 |
|
|
513
|
-
| `file.view` 字符边界 | 强制校验(`E_BOUNDARY`) | **完全不校验** |
|
|
514
|
-
| `file.view --max-bytes` | 生效 | 被忽略(`truncated` 恒为 `false`) |
|
|
515
|
-
| 超过 32 MiB 的文件 | `E_TRUNCATED` | 无上限 |
|
|
516
|
-
| 创建/追加时的 `--eol crlf` | 精确字节 | 会多出一个结尾 CRLF |
|
|
517
|
-
| 追加时遇到混合行尾文件 | 原有行尾不动 | 整个文件被重新归一 |
|
|
518
|
-
| 无法表示的字符 | 静默写成 `?` | `E_ENCODING_INVALID` |
|
|
519
|
-
| `--pretty` / `--quiet` | 生效 | 接受但被忽略 |
|
|
520
|
-
| `--` | `E_USAGE` | 选项结束标记 |
|
|
521
|
-
| 受保护路径比较 | 大小写不敏感、已归一化 | 大小写敏感、按原参数 |
|
|
522
|
-
| 未登记错误码 | 退出码 7 | 退出码 5 |
|
|
523
|
-
| 失败时的 `meta.durationMs` | 真实耗时 | `0` |
|
|
524
|
-
|
|
525
|
-
`file.search` 的 `--type` 在两套实现上都是一个潜在缺陷:实现会读它,但它并未登记为已知标志,因此行为取决于解析器的兜底路径。
|
|
526
|
-
|
|
527
|
-
---
|
|
528
|
-
|
|
529
|
-
## 安装现状与已知缺陷
|
|
530
|
-
|
|
531
|
-
直说,以免有人措手不及。
|
|
532
|
-
|
|
533
|
-
### bundle 的装载路径已由三个校验器覆盖
|
|
534
|
-
|
|
535
|
-
此处曾记录:loader 端到端路径**未**验证。该缺口**已补齐**,方式是三个校验器,且它们找出了一个真实缺陷:
|
|
536
|
-
|
|
537
|
-
| 校验器 | 证明什么 | 用例 |
|
|
538
|
-
|---|---|---|
|
|
539
|
-
| `tools/verify-bundle.mjs` | loader 的**解析**契约:manifest 头、patch 文件、每一行都能从一个 profile 形态的 `node_modules` 解析、每个被解析模块都导出 loader 会调用的 Cordis 形状(`apply`/`inject`/`name`)、两行解析到**不同**模块(共享说明符会导致同一个工具注册两次并让整个入口中止)、以及版本闸门 | 10 |
|
|
540
|
-
| `tools/verify-plugin-load.mjs` | 真实的 `apply()` 路径:宿主插件以**根类型为 `object`** 的参数 schema 注册 `dsh_os`,授权插件注册其五个工具,并且**注册后的 handler 真的 spawn 了脚本**并返回真实信封(`selfcheck` → ok,`verbCount` 86);不带 `allow` 的变更类动词以带类型的 `E_PERMISSION` 被拒,而不是抛异常。另含工作流一节:工具与提示词小节确实注册,且 `start → plan → choose → run` 经真实 handler 跑通、阶段门依然成立 | 30 |
|
|
541
|
-
| `tools/verify-editions.mjs` | 版本选择:默认仍是 PowerShell 5.1;`powershellEdition: '7.6'` 与 `DSH_OS_POWERSHELL_EDITION` 选中 7.6 产物;`'auto'` 仅在产物与可用的 `pwsh` **同时**存在时才用 7.6;配置优先于环境变量;显式 `scriptPath` 完全禁用该选择。`-ExecutionPolicy` 只传给 5.1 可执行文件 | 14 |
|
|
542
|
-
|
|
543
|
-
bundle 解析校验把包以 junction 链接进一个一次性的 profile 沙箱,因此解析行为与真实 profile 一致,而**活动 profile 从不被写入**。
|
|
544
|
-
|
|
545
|
-
它找出的缺陷是:`bin/dsh-os7.ps1` 此前**从插件侧不可达**——它存在、能构建、能测试,但 `resolveInvocation` 只找 `dsh-os.ps1`,于是全部 7.6 工作从插件角度看都是死代码。现已可选,且**默认不变**(仍为 5.1),因为一个已经工作的部署不应仅仅因为插件升级就改变行为。
|
|
546
|
-
|
|
547
|
-
**仍未证明、也不主张:** harness **自身的 loader** 真的会在活动 profile 里组合这些 bundle。那需要把本包装进用户的桌面活动 profile 并重启 harness,这一步没有做。
|
|
548
|
-
|
|
549
|
-
### Windows 黄金测试的顺序依赖已修复
|
|
550
|
-
|
|
551
|
-
此处曾记录:在干净检出上实测 `passed: 47 failed: 1`,失败用例是 `parser/one-two-three-token-verbs`,因为它断言 `file length` 作用于一个由**更晚**的用例才创建的夹具目录。
|
|
552
|
-
|
|
553
|
-
该缺陷**已修复**:`tests/golden.ps1` 的解析器用例现在自己创建夹具(`$fixtureDir = Join-Path $sandbox 'parser\verify'`,随后 `New-Item` 与 `Set-Content`),再对其断言。当前实测为 **50 通过,0 失败**。
|
|
554
|
-
|
|
555
|
-
### `tests/smoke-desktop-sh.sh` 查出的两个 POSIX 真实缺陷
|
|
556
|
-
|
|
557
|
-
这两个都在**写完之后、review 看不出来**——因为本机没有 `DISPLAY`,它们平时只表现为"反正都是 `E_NO_DISPLAY`"。用替身工具把注入路径打开后才暴露:
|
|
558
|
-
|
|
559
|
-
1. **校验发生在注入之后。** `mouse click 10 20 --count abc` **返回了错误,但指针已经被移走**——一次"失败"的调用已经改变了机器状态。Windows 侧是**先校验全部参数再注入**,POSIX 侧现已对齐。(`mouse over abc 20` 同理。)
|
|
560
|
-
|
|
561
|
-
2. **`$(...)` 里的 `fail` 不会终止动词。** 坐标校验器调用 `fail`,但调用方写的是 `x="$(window_int_arg ...)"`,于是 `exit` **只结束了子 shell**:动词带着空值继续往下走并**照常注入**——`mouse over abc 20` **移动了指针并返回 `ok`**。现在三个会拒绝输入的校验器改为**赋值变量**(`WINDOW_INT`、`WINDOW_BUTTON`、`WINDOW_KEY`),并在**调用者自己的 shell** 中读取。
|
|
562
|
-
|
|
563
|
-
**这个陷阱本仓库的 `environment` 模块早已在注释里记录过**(`env_resolve_scope` 专门讲过为什么它赋值而不打印),而它仍然在这里被重复了一次。这两个缺陷现在由 3 个断言固化:非法入参必须**被拒绝且未注入任何东西**。
|
|
564
|
-
|
|
565
|
-
### 其他缺口
|
|
566
|
-
|
|
567
|
-
* **`tests/conformance.ps1` 曾经并不存在**,尽管 `spec/verbs.tsv` 的文件头声称它存在、并会校验与 `command.txt` 的一致性。**该文件现已创建**(见下文「两张表的一致性」),文件头的声明因此成为事实。一个"指名了守卫、而守卫并不存在"的注释比没有注释更糟,因为它会被当作保证来读。
|
|
568
|
-
* **同意写入委托从未注册审批 answerer**,如前所述。
|
|
569
|
-
* **委托在其 `query` 结果里无条件报告 `aclEnforced: false`**,而不是像脚本那样去实测 ACL。因此在 A 层已开启时,它会**低报**防护强度。
|
|
570
|
-
* **此处曾记载「`--shell`、`--env`、`--stdin`、`--background` 被接受但从不被读取」,以及「Windows 上 `--encoding` 构造了从未被应用的值」——两处记载均已过时,且曾经是真实缺陷,现已修复:**
|
|
571
|
-
* **Windows 侧**:`--shell`、`--env`、`--stdin`、`--background` 都确实被读取(`execute.ps1` 中有对应分支)。`--encoding` 曾经**真的是惰性的**——它构造了 `$enc` 却从未使用,`StandardOutputEncoding` 在全套模块中一次都没出现,因此子进程输出始终按运行时默认解码。现已接上输出/错误流编码。**实测**:让子进程输出 GBK 字节 `D6 D0 CE C4`,默认解码得到 `����`(4 个替换字符),`--encoding gbk` 得到 `中文`(2 个字符)。
|
|
572
|
-
* **POSIX 侧**:这些标志**确实从未被读取**。现已改为**显式拒绝**——`--shell`、`--env`、`--stdin`、`--background`、`--encoding` 一律返回 `E_NOT_SUPPORTED`(退出码 4),而不是静默忽略。这正是本项目自己的契约所要求的:**不可用的能力必须是显式错误,绝不能是空成功**;一个传了 `--stdin` 却没被喂入任何输入、却收到成功信封的调用方,是被误导了。
|
|
573
|
-
* **若干已声明的错误码确实不可达。** 在两侧实现中全文检索后确认,**零次出现**的有 `E_ENCODING_INVALID`(`file.encode`)、`E_IS_A_DIR`(`file.delete`)与 `E_ABORTED`。与此相对,`E_CHARSET_MISMATCH`(3 处)、`E_BINARY_CONTENT`、`E_TRUNCATED` 都有真实抛出点——因此这张表并非普遍不可信,但上述三个应视为尚未实现的意图,而不是可依赖的返回码。
|
|
574
|
-
* **此处曾记载"`E_TIMEOUT` 从未被任何一侧抛出"——该记载已过时且错误。** 它在 `execute.ps1`(`execute command` 超时)与 `window.ps1`(`wait window` 超时)中都会被抛出。实测:
|
|
575
|
-
`execute command cmd.exe /c ping -n 10 127.0.0.1 --timeout 300 --allow` 返回 `{"ok":false,...,"code":"E_TIMEOUT",...,"hint":"captured so far: stdout=86 bytes, stderr=0 bytes"}`,退出码 **6**。POSIX 侧同样有 `E_TIMEOUT`(`json.sh` 映射到退出码 6)。
|
|
576
|
-
* **`src/client/` 是未改动的 Vite 脚手架。** 它没有与宿主接线,`index.html` 引用 `./src/main.ts` 而实际文件在 `./src/client/main.ts`,且 `node_modules` 为空——因此 `npm run build` 与 `npm run dev` 目前没有意义。
|
|
577
|
-
* **`git` 不是必需依赖,但也并非每个目标环境都有**,因此没有任何验收步骤依赖它。
|
|
578
|
-
|
|
579
|
-
### 尚未证实的部分
|
|
580
|
-
|
|
581
|
-
* DSH loader 会组合这个 bundle,并让 `dsh_os` 出现在活动会话中。
|
|
582
|
-
* 两个平台之间的动词完全对等——两个方向都没有。
|
|
583
|
-
* 在 harness 与委托共用同一身份的机器上 A 层的强制效果——按构造那种情况会退化为 B 层。
|
|
584
|
-
* 关于 macOS 或非 Linux 的 POSIX,除代码审阅之外的一切:POSIX 测试套件是为 GNU 用户态写的,此处未运行。
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
## 测试
|
|
589
|
-
|
|
590
|
-
```console
|
|
591
|
-
npm run test:all # 按顺序跑下面全部
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
`test:all` 依次串联 `test:conformance`、`test:encoding`、`test`(Windows 黄金)、`test:plugin`、`test:takeover`、`test:permission`、`test:workflow`、`test:bundle`(`tools/verify-bundle.mjs`)、`test:package`(`tools/verify-package.mjs`)、`test:load`(先 `tools/verify-plugin-load.mjs`,再 `tools/verify-editions.mjs`)与 `test:desktop`(`bash tests/smoke-desktop-sh.sh`)。
|
|
595
|
-
|
|
596
|
-
| 套件 | 命令 | 用例数 | 实测结果 |
|
|
597
|
-
|---|---|---|---|
|
|
598
|
-
| 两表一致性 | `powershell -File tests/conformance.ps1` | 5 | **5 通过,0 失败** |
|
|
599
|
-
| 编码契约 | `powershell -File tests/encoding.ps1` | 32 个构建模块 | **全部契约成立** |
|
|
600
|
-
| Windows 黄金 | `powershell -File tests/golden.ps1` | 50 | **50 通过,0 失败** |
|
|
601
|
-
| POSIX 黄金 | `bash tests/golden-sh.sh` | 116 | **116 通过,0 失败** |
|
|
602
|
-
| POSIX 桌面注入 | `bash tests/smoke-desktop-sh.sh` | 52 | **52 通过,0 失败** |
|
|
603
|
-
| 插件 | `node tests/plugin.test.mjs` | 13 | **13 通过,0 失败** |
|
|
604
|
-
| 接管拦截 | `node tests/takeover.test.mjs` | 41 | **41 通过,0 失败** |
|
|
605
|
-
| 授权委托 | `node tests/permission.test.mjs` | 18 | **18 通过,0 失败** |
|
|
606
|
-
| 工作流 | `node tests/workflow.test.mjs` | 31 | **31 通过,0 失败** |
|
|
607
|
-
| bundle 解析 | `node tools/verify-bundle.mjs` | 16 | **16 通过,0 失败** |
|
|
608
|
-
| 打包完整性 | `node tools/verify-package.mjs` | 21 | **21 通过,0 失败** |
|
|
609
|
-
| 插件装载 | `node tools/verify-plugin-load.mjs` | 30 | **30 通过,0 失败** |
|
|
610
|
-
| 版本选择 | `node tools/verify-editions.mjs` | 14 | **14 通过,0 失败** |
|
|
611
|
-
| 构建漂移 | `powershell -File tools/build.ps1 -Check` | 3 个生成物 | **无漂移** |
|
|
612
|
-
| 前缀缓存(`npm run measure:cache`) | `node tools/measure-cache.mjs` | 2 项断言 | **PASS——四次工作流调用均值命中 96.6%** |
|
|
613
|
-
|
|
614
|
-
`measure:cache` **不在** `test:all` 里:它直连真实 provider,因此需要 `DEEPSEEK_API_KEY`(从 `$DSH_HOME/.credentials.yaml` 读取)并会消耗 token。只有当某项被测量的断言不成立时它才以非零码退出。
|
|
615
|
-
|
|
616
|
-
> [!NOTE]
|
|
617
|
-
> **`test:desktop` 需要 `bash`,而本机没有。** 因此在没有 Git Bash 或 WSL 的 Windows 主机上,`npm run test:all` 会以 `'bash' is not recognized` 结束。这是运行环境缺口而非断言失败:同一次调用中位于它之前的所有套件都跑过并通过。POSIX 套件在这里依旧未被验证,原因与过去相同——它们需要一台 POSIX 主机。
|
|
618
|
-
|
|
619
|
-
### 两张表的一致性
|
|
620
|
-
|
|
621
|
-
`spec/verbs.tsv` 的文件头一直声称「`tests/conformance.ps1` 会校验它与 `command.txt` 一致」,而那个文件**并不存在**——**现已补上**,声明因此成为事实。该守卫按「**最长的、能在词表中命中某个动词的词前缀**」把 `command.txt` 的每一行解析成动词(所以 `file verify charset path charset` 解析为 `file.verify.charset`,而不会误认成不存在的 `file.verify`),然后校验:两表动词集合互相包含、数量相等、R/W 类别一致、位置参数名按序一致。
|
|
622
|
-
|
|
623
|
-
**写它的过程中查出 5 处真实不一致**,除一处外都在 `command.txt`(它是权威契约):
|
|
624
|
-
|
|
625
|
-
| 动词 | 原声明 | 按实现应有的样子 |
|
|
626
|
-
|---|---|---|
|
|
627
|
-
| `file.append` | `path content charset` | charset 是**可选**的第三个位置参数,原写法让人以为必需 |
|
|
628
|
-
| `file.insert` | `path content charset offset` | charset 走 `--charset`,**刻意不是**位置参数——若它是位置参数,省略它会让 `offset` 静默移位 |
|
|
629
|
-
| `wait.ms` | 无位置参数 | 接受 `ms` |
|
|
630
|
-
| `keyboard.combination` | 两行 `... down a b c d` / `... up a b c d` | 是**一个**动词,含 `phase` 位置参数;两行写法读起来像两个动词 |
|
|
631
|
-
| `execute.command` | tsv 写作 `command\|arguments` | 整个位置参数列表就是 argv,不存在单独的 `command` |
|
|
632
|
-
|
|
633
|
-
其中四处改在 `command.txt`,`verbs.tsv` 的 `execute.command` 行改为 `arguments...`。**运行面没有任何改变**——变的只是两份文档的说法,而这正是这种一致性守卫存在的意义。
|
|
634
|
-
|
|
635
|
-
`tests/encoding.ps1` 守卫的是让其余一切有意义的那些契约:`.ps1` 生成物带 BOM、`.sh` 生成物不带、行尾遵循 `.gitattributes`、`command.txt` 与 `.tsv` 表保持纯 ASCII + LF,以及 **stdout 永不携带 BOM**。
|
|
636
|
-
|
|
637
|
-
**此前记载的「4 项编码违规」现已消失,但这不是"放宽了守卫"。** 规则本身从未松动,改的是它的**适用范围**:原先它要求 `src/host/lib/` 下**每个** `*.ps1` 都不带 BOM 且纯 ASCII,而其中两个 `git.ps1` 是**独立工具**——不会被拼进任何生成物,且必须带 BOM 才能在 Windows PowerShell 5.1 下正确解析其中文提示字面量。对"构建从不读取的文件"套用该规则属于**误报**。现在守卫只校验 `tools/build.ps1` 实际拼接的 **32 个模块**,并**打印出它跳过了哪些文件**,使范围的收窄是可见的而不是静默的。
|
|
638
|
-
|
|
639
|
-
> [!NOTE]
|
|
640
|
-
> **POSIX 桌面动词的「116 通过」与「52 通过」都不是「驱动真实窗口管理器」的证据。** 两套测试从两个方向覆盖同一批动词,而本机没有 X server:
|
|
641
|
-
> * `tests/golden-sh.sh` 断言**缺失路径**——没有 `DISPLAY` 时,每个桌面动词都必须返回带类型的 `E_NO_DISPLAY`,而不是空成功。
|
|
642
|
-
> * `tests/smoke-desktop-sh.sh` 断言**注入路径**——它把会记录调用的 `tests/fake-xdotool` 放进 `PATH` 并设置 `DISPLAY`,于是动词走真实代码路径,断言落在**交给工具的确切参数向量**上:`--button right` 是否变成按键 `3`、`Enter` 是否变成 keysym `Return`、表外的 `XF86AudioMute` 是否被原样送达、`--clearmodifiers` 与 `--repeat` 是否在正确位置、以及 xdotool 失败是否报成 `E_IO` 而不是假成功。
|
|
643
|
-
>
|
|
644
|
-
> **正是这个测试台查出了 POSIX 实现里的两个真实缺陷**(见上文「实际可用范围」与「已知缺陷」)。没有任何一套证明它们真的操作了桌面。
|
|
645
|
-
|
|
646
|
-
> [!WARNING]
|
|
647
|
-
> **两套黄金测试必须串行运行。** 两套实现都会把失败信封暂存到一个**固定路径**的临时文件——`.temporary/dsh-os.err`,既无 PID 也无锁——因此并发调用会互相覆盖对方待取的结果。实测并行运行 `golden.ps1` 与 `golden-sh.sh` 时报出了 2 个**假**失败:退出码与信封自身的 `error.code` 相互矛盾;改为串行后二者全部消失。请把它当作缺陷而不是随机抖动:该暂存路径在构造上就是竞态的。
|
|
648
|
-
|
|
649
|
-
---
|
|
650
|
-
|
|
651
|
-
## 参与贡献
|
|
652
|
-
|
|
653
|
-
`src/host/lib/powershell5.1/*`、`src/host/lib/powershell7.6/*` 与 `src/host/lib/*.sh` 是真源。**绝不要手改 `src/host/bin/dsh-os.ps1`、`dsh-os7.ps1` 或 `.sh`**——它们是生成物。请改模块,然后:
|
|
654
|
-
|
|
655
|
-
```console
|
|
656
|
-
powershell -File tools/build.ps1 # 重新生成三个脚本
|
|
657
|
-
powershell -File tools/build.ps1 -Check # 已提交脚本一旦漂移即失败
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
三个生成物是分开的而不是"一份脚本加运行时分支",因为两个运行时差异无法用分支干净地抹平:.NET Framework 没有 `ProcessStartInfo.ArgumentList`,而 .NET 10 没有理由继续背负这套项目为绕开它而手写的 Windows 命令行转义。
|
|
661
|
-
|
|
662
|
-
`git.ps1` / `git.sh` **不在** `tools/build.ps1` 的模块清单内,因此它们不被拼进任何生成物;它们是各自独立、自包含的交互式工具,详见上文"独立工具"。
|
|
663
|
-
|
|
664
|
-
`.gitattributes` 是契约的一部分而不是偏好:`command.txt` 与 `.tsv` 表必须是 LF,两套实现才能解析逐字节一致的输入;`dsh-os.bat` 必须是 CRLF 且仅 ASCII,否则 `cmd.exe` 会解析错误;`dsh-os.ps1` 必须是 CRLF 且 UTF-8 **带** BOM,否则 PowerShell 5.1 会按 ANSI 码页读取其中的非 ASCII 字面量。
|
|
665
|
-
|
|
666
|
-
两条在这个仓库里踩出来的教训,值得一并继承:
|
|
667
|
-
|
|
668
|
-
* **依赖路径推导的安全边界必须 fail-closed。** 一次重构曾让仓库根多算上一层,于是没有任何受保护路径匹配。其失败形态不是报错,而是**保护静默失效**——受保护的文件真的被删掉了。
|
|
669
|
-
* **临时文件统一放在 `.temporary/` 下**,黄金测试夹具也在那里。
|
|
670
|
-
|
|
671
|
-
各模块文件头仍引用已不存在的 `tools/split-once.ps1`;当前的拼装器是 `tools/build.ps1`。
|
|
672
|
-
|
|
673
|
-
---
|
|
674
|
-
|
|
675
|
-
## 独立工具
|
|
676
|
-
|
|
677
|
-
`src/host/lib/git.sh`、`src/host/lib/powershell5.1/git.ps1` 与 `src/host/lib/powershell7.6/git.ps1` 是交互式 git 运行器。它们**刻意不属于动词面**:不在 `tools/build.ps1` 的 `$modulesPs1`/`$modulesSh` 清单里,因此永远不会被拼进 `bin/dsh-os.ps1`、`bin/dsh-os7.ps1` 或 `bin/dsh-os.sh`,也不输出 JSON 信封。三者各自是单个自包含文件、彼此字节独立,靠人工评审保持一致而不是靠生成器。
|
|
678
|
-
|
|
679
|
-
它们按三阶段定位 git:`GIT_EXE`/`GIT_HOME`/`PATH` → 已知安装位置(含 Git for Windows 注册表键)→ 对每个挂载点做**有深度与时限约束**的全盘遍历;全都找不到时列出搜索过的位置并以 127 退出。参数以独立 argv 元素送达 git,绝不拼接 shell 字符串。`git add` 的输入为空(直接回车)即等价于 `git add .`。三者的编码各自服从其运行时的契约——这正是 `git.sh` 纯 ASCII(英文提示)而两个 `git.ps1` 不是的原因。
|
|
680
|
-
|
|
681
|
-
---
|
|
682
|
-
|
|
683
|
-
## 许可
|
|
684
|
-
|
|
685
|
-
**MIT**,在 `package.json` 中声明(`"license": "MIT"`,作者 Eloy Kim),并以独立的 [`LICENSE`](LICENSE) 文件随仓库分发。完全开源:整个动词面、规范文档与测试套件都在仓库里。无遥测、无需账号、运行时无网络访问。
|
|
686
|
-
|
|
687
|
-
```
|
|
688
|
-
MIT License
|
|
689
|
-
|
|
690
|
-
Copyright (c) 2026 Eloy Kim
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
许可证文件为纯 ASCII、LF 行尾、无 BOM,与仓库其余的编码契约保持一致。需要更正一处旧描述:它**并未**列入 `package.json` 的 `files` 白名单(该白名单共 13 项,不含 `LICENSE`,也不含 `README.zh-CN.md` 与 `dsh-os7.ps1`),因此它留在检出目录里,但不会随发布的包一起分发。
|
|
694
|
-
|
|
695
|
-
动词契约与规范文档属于源码的一部分:行为变更应当同步反映到 `src/host/spec/verbs.tsv`、`src/host/command.txt` 与 `src/host/docs/os-spec.md`,而不只是改代码。
|
|
696
|
-
|
|
697
|
-
另需说明:规范文档(`src/host/docs/os-spec.md`、`src/host/docs/permission-model.md`、`src/host/command.txt`)以**中文**撰写,而本 README 与动词表为英文。
|
|
1
|
+
# deepseek-coordination
|
|
2
|
+
|
|
3
|
+
[English](README.md) · **简体中文** · [更新日志](CHANGELOG.zh-CN.md) · [English changelog](CHANGELOG.md)
|
|
4
|
+
|
|
5
|
+
一个面向 DeepSeek Harness 的双端 Cordis 插件:宿主端持有协调 SQLite 数据库、7 个模型可见工具和一组 HTTP 路由;浏览器端在输入框挂载五个下拉框,并提供设置目录与服务级接管对话框。
|
|
6
|
+
|
|
7
|
+
**当前版本:v0.10.0(2026-10-18)。** 它在 v0.9.2 的 tooltip 修复之后,加入设置目录、持久文件层、Turn 流水线、阶段冻结和数据结构采用。包版本、更新日志和发布 tarball 来自同一棵树;下文按源码核对,不以设计文档代替实现。
|
|
8
|
+
|
|
9
|
+
| 状态 | 含义 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **已接线** | 由 `src/index.ts` 注册,或被已组合的 service、路由、工具实际调用。 |
|
|
12
|
+
| **仅库** | 模块已实现并有测试,但当前 Turn 生命周期不驱动它,也没有路由/工具暴露。 |
|
|
13
|
+
| **设计/计划** | 只写在文档、配置或 schema 中,尚无运行时消费者。 |
|
|
14
|
+
|
|
15
|
+
## 这是什么
|
|
16
|
+
|
|
17
|
+
| 组成 | 位置 | 作用 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Bundle | `cordis.patch.yml`、`package.json` 的 `dsh.bundle` | 把一行 `deepseek-coordination` 插入 DSH profile 的 bundle 列表。 |
|
|
20
|
+
| 宿主端 | `lib/index.mjs`(ESM,Node) | `CoordinationService`、`coordination` SQLite 库、7 个工具、14 条路由路径和 agent 钩子。 |
|
|
21
|
+
| 浏览器端 | `lib/client.js`(CJS,浏览器,`__ModuleLoader__` 包装) | React 18 + Material UI 9:输入框下拉框、设置页、接管对话框。 |
|
|
22
|
+
| 数据库 | 默认 `%DSH_HOME%\coordination\coordination.db` | 插件自有 SQLite 文件;63 张表、7 个触发器、63 个索引,schema 版本 24。从不打开 DSH 的 `harness` 库。 |
|
|
23
|
+
|
|
24
|
+
插件不替代 DSH 的 agent 循环。它记录协调状态、为每个任务锁定一个职能、提供输入框选择与文件元数据、在上下文压力越过阈值时请求 DSH 压缩,并把审批/提问转给人工对话框。
|
|
25
|
+
|
|
26
|
+
五个输入框控件是 **权限、职能、交互、情景、验证**。命令面(`/goal`、`/plan`、`/feedback`、`/compact`、`/permission`)是消息前缀,不占第六个下拉位。
|
|
27
|
+
|
|
28
|
+
## 安装与构建
|
|
29
|
+
|
|
30
|
+
| 要求 | 说明 |
|
|
31
|
+
|---|---|
|
|
32
|
+
| Node.js | `package.json` 声明 `>=18`,但宿主端使用 `node:sqlite`,必须使用带该模块的 Node 构建。本次验证使用 Node 24.21.0。 |
|
|
33
|
+
| npm | npm 11(随 Node 24 提供)按 `package-lock.json` 安装依赖,并驱动 scripts 与发布工作流。 |
|
|
34
|
+
| DSH | 装有 `dsh` CLI 的 DSH,且 profile 含 `@deepseek-ai/dsh-base` 与 `@deepseek-ai/dsh-web-app`。 |
|
|
35
|
+
| Peer 包 | 见下表;它们由 DSH 安装提供,不由本包携带。 |
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
cd deepseek-coordination
|
|
39
|
+
npm ci # 全新 checkout 也可用 `npm install`
|
|
40
|
+
npm run typecheck # tsc -p tsconfig.json --noEmit
|
|
41
|
+
npm test # tsdown 测试构建 + node --test
|
|
42
|
+
npm run build # 生成 lib/index.mjs、lib/index.d.mts、lib/client.js
|
|
43
|
+
npm pack # 先执行 prepack,生成可安装 tarball
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`npm run test:tsc` 检查测试树;`npm run pack:check` 校验 tarball 中的运行时文件并检查宿主入口语法。仓库提交了 `package-lock.json`,`npm ci` 会精确复现它。
|
|
47
|
+
|
|
48
|
+
| Peer 包 | 范围 | 端 |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `@deepseek-ai/cordis` | `^4.0.1` | 宿主运行时 |
|
|
51
|
+
| `@deepseek-ai/dsh-host-webserver` | `^0.1.0-rc.7 \|\| ^0.2.0-rc.0` | 宿主路由 |
|
|
52
|
+
| `@deepseek-ai/dsh-client-runtime`、`-locale`、`-ui-slots`、`-ui-settings` | `^0.1.0-rc.7 \|\| ^0.2.0-rc.0` | 浏览器模块表 |
|
|
53
|
+
| `react` | `^18.2.0` | 浏览器模块表;不会被打包 |
|
|
54
|
+
|
|
55
|
+
### 安装到 profile
|
|
56
|
+
|
|
57
|
+
安装打包后的 tarball,不要装 GitHub 源码:
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
npm pack
|
|
61
|
+
dsh plugin --profile <name> add .\deepseek-coordination-0.10.0.tgz
|
|
62
|
+
dsh --profile <name> --dump-config # 应出现 deepseek-coordination 层
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**不支持从 `github:eloyhere/deepseek-coordination` 直接安装。** `lib/` 被 gitignore,而 bundle 只在 `prepack`(或显式 `npm run build`)时构建,npm 安装 git 依赖时不会执行这一步,安装后的包在激活时会因找不到 `lib/index.mjs` 报 `ERR_MODULE_NOT_FOUND`。打 tag 时触发的 GitHub Actions(`.github/workflows/release.yml`)会构建并把校验过的 tarball 附到 Release,请安装该产物。
|
|
66
|
+
|
|
67
|
+
**npm 上的 latest 仍是旧的 `0.1.9` dsh-os 线。** 目前不要按包名安装。本地 checkout 在 `npm run build` 之后可以用 `dsh plugin --profile <name> add .` 链接。
|
|
68
|
+
|
|
69
|
+
`dsh plugin add` 会写入依赖和 bundle 行。如果 profile 里仍记录着别的包 spec,请替换它,不要假定新代码已经加载;用 `dsh --profile <name> --dump-config` 和已安装的 `package.json` 版本核对。
|
|
70
|
+
|
|
71
|
+
| 产物 | 格式 | 本次构建大小 |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `lib/index.mjs` | 宿主 ESM,`platform: node` | 940,726 B |
|
|
74
|
+
| `lib/index.d.mts` | 宿主类型声明 | 9,095 B |
|
|
75
|
+
| `lib/client.js` | 浏览器 CJS,包在 `__ModuleLoader__.load()` 中 | 1,170,887 B |
|
|
76
|
+
|
|
77
|
+
Material UI、emotion 与 `react-dom` 被打进 `lib/client.js`,React 本身来自宿主模块表。内联的 `react-dom` 是 18.3.1 线,与宿主的 React 18 匹配——npm 不会为 React 18 的 `react` 自动配一个 React 19 的 `react-dom`。客户端体积大是因为内联了 MUI 与 `react-dom`,不是因为多带了一份 React。
|
|
78
|
+
|
|
79
|
+
## 架构
|
|
80
|
+
|
|
81
|
+
宿主端(`src/index.ts`):
|
|
82
|
+
|
|
83
|
+
- 导出 `name`、`inject = ['webServer', 'tools']` 和 `Config`;`apply()` 解析配置、创建 service、恢复被中断的 Turn,并通过 `ctx.effect` 安装所有 effect。
|
|
84
|
+
- `CoordinationService`(`src/host/coordination.ts`)是 Cordis 的 `coordination` service,持有数据库、module host 和已组合模块:session、selection、turn、profession、plan、goal、compact、directive、permission、takeover/gate、provider、cache、command。
|
|
85
|
+
- SQLite 使用 `node:sqlite` 的 `DatabaseSync`,开启外键、5 秒 busy timeout,非 `:memory:` 时使用 WAL。触发器拒绝修改已锁定职能,`orchestration_trace` 只允许追加。
|
|
86
|
+
- `/deepseek-coordination` 前缀路由返回 JSON `{ ok, value | error }`;请求体上限 1 MiB;未知路径返回 404 路由信封。
|
|
87
|
+
- 7 个工具通过 `ctx.tools` 注册;三个输入框工具在 `POST /locale` 后重新注册,因为工具描述属于模型提示词。
|
|
88
|
+
- 钩子:`agent/pre-step` 打开协调 Turn 并运行压缩治理器;`agent/turn-stopping` 以 `done` 关闭;`agent/error` 以失败关闭;`approval/request` 与 `user-questions/request` 进入接管闸门。60 秒定时器清理过期 lock 行。
|
|
89
|
+
|
|
90
|
+
浏览器端(`src/client/index.ts`):
|
|
91
|
+
|
|
92
|
+
- `inject = ['slots', 'locale']`;`package.json` 的 `dsh.client.inject` 另行列出四个模块表包。
|
|
93
|
+
- `settings.section`(order 50)挂载 `CoordinationSettings`;`conversation.input.left`(order 40)挂载输入框控件,接管对话框作为兄弟节点。
|
|
94
|
+
- 语言跟随 `ctx.locale.getLocale().active`,把 `zh*`/`en*` 归一为 `zh-CN`/`en-US`,并上报 `POST /locale`;宿主据此重建本地化工具描述。
|
|
95
|
+
- 客户端 bridge 使用同源 `fetch`:选择接口 3 秒超时,接管/语言接口 2 秒超时。宿主不可达时,五个选择回退到每会话的 `localStorage`;权限预设永不离线写入。
|
|
96
|
+
- 输入框组件只按 `sessionId` memo;slot 每次击键也会传 `input`,若按默认浅比较重渲染,emotion 会触碰 `<head>`,导致宿主的弹层关闭。
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
deepseek-coordination/
|
|
100
|
+
├─ src/
|
|
101
|
+
│ ├─ index.ts # 宿主组合根:service、路由、工具、钩子
|
|
102
|
+
│ ├─ host/
|
|
103
|
+
│ │ ├─ coordination.ts # CoordinationService(Cordis service "coordination")
|
|
104
|
+
│ │ ├─ config.ts # Schemastery schema + resolveConfig()
|
|
105
|
+
│ │ ├─ database.ts # SQLite schema(24),63 表,触发器
|
|
106
|
+
│ │ ├─ dsh.ts # 窄 DSH port + Turn 生命周期
|
|
107
|
+
│ │ ├─ tools.ts # 7 个模型可见工具
|
|
108
|
+
│ │ ├─ chain.ts # 有界 Chain 投影引擎
|
|
109
|
+
│ │ ├─ node-registry.ts # NodeCard 注册表与 describe()
|
|
110
|
+
│ │ ├─ parse-service.ts # 工作区内的解析与读取服务
|
|
111
|
+
│ │ ├─ token.ts # TokenGovernor 与纯策略
|
|
112
|
+
│ │ ├─ takeover-gate.ts # 审批/提问瀑布闸门
|
|
113
|
+
│ │ ├─ selection.ts turn.ts profession.ts … # 选择、锁定、生命周期
|
|
114
|
+
│ │ ├─ bayesian.ts comprehension.ts organization.ts … # 库级引擎
|
|
115
|
+
│ │ └─ text/ # P0 解析器:JS/TS、CSS/HTML/MD、TeX、公式、依赖图
|
|
116
|
+
│ ├─ client/
|
|
117
|
+
│ │ ├─ index.ts # slots、语言 store、导航图标、接管 CSS
|
|
118
|
+
│ │ ├─ controls.ts # 五个输入框下拉框、tooltip、离线重放
|
|
119
|
+
│ │ ├─ settings.ts # 13 组、151 控件的设置目录页
|
|
120
|
+
│ │ ├─ takeover.ts # 审批/提问对话框
|
|
121
|
+
│ │ ├─ bridge.ts store.ts i18n.ts theme.ts selection-cache.ts
|
|
122
|
+
│ └─ shared/ # entity/enum/selection/i18n/error + 数据结构
|
|
123
|
+
├─ test/ # 24 个 *.test.ts + helpers.ts
|
|
124
|
+
├─ lib/ # 构建产物;gitignore,不手改
|
|
125
|
+
├─ cordis.patch.yml # 插入 profile 的 bundle 行
|
|
126
|
+
├─ scripts/check-pack.mjs # tarball 运行时文件校验
|
|
127
|
+
└─ .github/workflows/release.yml
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 输入框控件
|
|
131
|
+
|
|
132
|
+
| 控件 | 选项 | 行为 |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| 权限 | DSH 预设目录,或中性的“跟随 DSH” | 通过 `ctx.permissionPresets` 立即写入 DSH,不写入协调库。仅在本机“权限接管”开关打开时显示。 |
|
|
135
|
+
| 职能 | 决策 / 成果 / 资源 / 人员 / 任职 / 执行 | 活动任务内锁定;选择其他值会返回 `PROFESSION_LOCKED`,并打开重建/丢弃对话框。 |
|
|
136
|
+
| 交互 | 感情 / 思维 / 启发 / 收束 / 统筹 | 写入 `selection` 行,供下一 Turn 使用;活动 Turn 有未清除的目标时冻结。 |
|
|
137
|
+
| 情景 | 对话 / 写作 / 办公 / 研究 / 工程 / 迭代 | 写入下一 Turn;`writing` 位于“对话”和“办公”之间;情景策略由 Turn 流水线读取。 |
|
|
138
|
+
| 验证 | low / medium / high | 写入下一 Turn;活动 Turn 有 draft/scored/active 计划时冻结;三档都会检索。 |
|
|
139
|
+
|
|
140
|
+
- 每个控件和每个选项都有 tooltip。`tooltipVisibility` 单所有权 store 保证同一时刻只有一个输入框 tooltip 可见;位置在左/右,退出为 160 ms 淡出,淡出中的 popper 不接收指针事件。
|
|
141
|
+
- 下拉框第一次点击就打开菜单;pointerdown/mousedown 不改变状态,因此不会吞掉 Select 的点击。
|
|
142
|
+
- 锁定的职能触发器被禁用,其无障碍名称带锁定解释;选项 `MenuItem` 带标签和提示,键盘与读屏用户无需悬停即可获得说明。
|
|
143
|
+
- 阶段冻结每轮从主机重新推导:Turn → 职能,Plan → 验证,Goal → 交互。下拉框禁用但仍可聚焦,aria-label/tooltip 说明恢复时机;服务端对冻结字段的改值返回 `INVALID_STATE`(重复写原值允许)。
|
|
144
|
+
- tooltip 受宿主 `ui.showTooltip` 总开关约束;本机“显示提示”只能收窄,不能恢复宿主已关闭的 tooltip。
|
|
145
|
+
- 保存是乐观的,并做 120 ms 防抖。保存被拒绝时回退到最近一次宿主确认的状态;离线编辑在宿主恢复后重放。完全权限预设需要第二次显式确认。
|
|
146
|
+
- 宿主状态加载中时下拉框保持禁用并显示 loading;权限目录缺失就显示缺失,不编造预设。
|
|
147
|
+
|
|
148
|
+
验证预算由 Turn 流水线实际读取:
|
|
149
|
+
|
|
150
|
+
| 档位 | 检索预算(次数 × 条数) | 证据范围 | 交互范围 |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| low | 1 × 3 | 仅当前 Turn | 当前选择的交互 |
|
|
153
|
+
| medium | 2 × 5 | 当前会话 | 当前选择的交互 |
|
|
154
|
+
| high | 4 × 8 | 本地全部内容 | 全部交互 |
|
|
155
|
+
|
|
156
|
+
预算定义在 `src/host/comprehension.ts` 和共享的 `ValidationBudgetByLevel` 表。`command=none` 的 Turn 现在由 `CoordinationService` 驱动 Comprehension → Organization → Plan/score;检索仍受 `allowWeb` 和缺失 web 端口约束。
|
|
157
|
+
|
|
158
|
+
### 情景与写作档
|
|
159
|
+
|
|
160
|
+
| 情景 | 思考 / effort | 计划 | 大纲 | 修订 | 评分 | Bash | 联网 | 最大轮次 |
|
|
161
|
+
|---|---|---|---|---|---|---|---|---|
|
|
162
|
+
| 对话 | 关闭 / medium | – | – | – | – | 否 | 是 | 1 |
|
|
163
|
+
| 写作 | 开启 / high | 是 | 是 | 是 | 是 | 否 | 是 | 3 |
|
|
164
|
+
| 办公 | 关闭 / medium | 是 | – | – | – | 否 | 是 | 1 |
|
|
165
|
+
| 研究 | 开启 / high | 是 | – | – | – | 否 | 是 | 3 |
|
|
166
|
+
| 工程 | 开启 / high | 是 | – | – | – | 是 | 是 | 3 |
|
|
167
|
+
| 迭代 | 开启 / high | – | – | – | 是 | 否 | 是 | 3 |
|
|
168
|
+
|
|
169
|
+
“写作”要求大纲和至少一轮修订,必须有质量评分,不允许 bash,允许联网,最多三轮。通过阈值为 `0.6`,`plan.ts` 使用该写作阈值。Turn 流水线读取 reasoning effort、require-* 标志、引用/证据、`allowWeb` 与 `sandboxOnly`;`thinking`、`allowBash`、`maxIteration` 和非写作阈值仍是没有 DSH 端口/消费者的延迟诊断项。
|
|
170
|
+
|
|
171
|
+
## 职能锁定与 Turn 生命周期
|
|
172
|
+
|
|
173
|
+
- 一个任务只有一个职能。`turn.create` 写入 `profession NOT NULL`、`locked = 1`,并写一条职能记录和一条 `lock` 追踪事件。
|
|
174
|
+
- 只要存在未终结 Turn,`SelectionModule.set` 就以 `ProfessionLockedError` 拒绝改职能;HTTP 层返回 409 `PROFESSION_LOCKED`,选择不会被修改。
|
|
175
|
+
- `POST /profession/rebuild` 把活动任务以 `abandoned` 结束,创建带新序号、新职能的替代 Turn,复制 prompt/command,并写 `rebuild` + `lock` 追踪事件。它要求存在活动 Turn。
|
|
176
|
+
- `POST /profession/abandon` 结束活动任务但不创建替代;下一个任务可重新选择。两个重选开关都由 `resolveConfig` 强制为 true。
|
|
177
|
+
- SQLite 再兜一层:`trg_turn_profession_lock`、`trg_profession_record_lock`、`trg_subtask_profession_lock` 在锁定时拒绝改职能;`trg_turn_profession_unlock`、`trg_subtask_profession_unlock` 拒绝清除锁定标记。
|
|
178
|
+
|
|
179
|
+
| 钩子 | 作用 |
|
|
180
|
+
|---|---|
|
|
181
|
+
| `agent/pre-step` | 确保协调 session,打开(或复用)当前 DSH Turn 对应的协调 Turn;同时在 `next()` 前运行 Token 治理器。 |
|
|
182
|
+
| `agent/turn-stopping` | 以 `done` 关闭 Turn,下一 Turn 解锁职能。 |
|
|
183
|
+
| `agent/error` | 以 `failed` 关闭 Turn。 |
|
|
184
|
+
| 宿主启动 | `recoverInterrupted()` 把所有未终结 Turn 标为 failed,原因 `turn was interrupted by a host restart`。 |
|
|
185
|
+
| 每 60 秒 | 删除超过 `defaultLease` 的已结束 lock 行。 |
|
|
186
|
+
|
|
187
|
+
`done`、`failed`、`cancelled`、`abandoned` 是不可逆终态。Turn 钩子只记录 Turn 元数据和一条空 prompt 行,不把 DSH 对话镜像进协调库。
|
|
188
|
+
|
|
189
|
+
## 设置与配置
|
|
190
|
+
|
|
191
|
+
设置页由 `src/shared/selection.ts` 的 `SETTINGS_CONTROLS` 生成:**13 组、151 个控件、134 个可写宿主行,覆盖 155 个非秘密配置叶子**。18 个顶层配置键包含 `database` 与 `databasePath`,这两个键故意不投影、不可写。
|
|
192
|
+
|
|
193
|
+
| 组 | 默认值 / 范围 | 写入路径 |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `session` | `maxTurnPerSession=1000`、`defaultLease=30000 ms`、`clarifyTimeout=30000 ms` | 宿主行,经 `/config` |
|
|
196
|
+
| `profession` | enabled/lock/rebuild/abandon 强制 `true`;default 镜像 `defaults.profession`;audit/errorHint `true` | 强制项只读;audit/errorHint 为宿主行(audit 无消费者) |
|
|
197
|
+
| `plan` | threshold `0.5`、maxRebuild `3`、8 个权重和为 1、8 个维度阈值 | 宿主行;保存后权重重新归一 |
|
|
198
|
+
| `orchestration` | maxReject `2`、maxSubTask `32`、groundTruth `true`、timeout `5000 ms`、shell ground truth `false`、modelRouting `true` | 宿主行;`groundTruthTimeout` 无消费者 |
|
|
199
|
+
| `cache` | enabled `true`、ttl `60000 ms`、fingerprintWindow `50`、effortWarning `true` | 宿主行;`fingerprintWindow` 无消费者 |
|
|
200
|
+
| `bayesian` | prior `0.5`、stopEntropy `0.05`、infoGain `0.01`、maxRoutes `12`、calibrationFloor `0.1` | 宿主行;`calibrationFloor` 无消费者 |
|
|
201
|
+
| `comprehension` | stageTimeout `30000 ms`、searchEnabled `true`、maxEvidence `8`、highHistoryWindow `50` | 宿主行 |
|
|
202
|
+
| `compaction` | auto `true`、soft `0.70`、hard `0.88`、reserve `2048`、recentTurns `6`、cooldown `30000 ms`、maxPerSession `20` | auto/soft/hard 为宿主行;结构字段只读 |
|
|
203
|
+
| `ui` | toolbar `true`(废弃别名)、showTooltip `true`、takeover `false`、refreshInterval `5000 ms`、compact `true`、locale `auto` | 宿主行 + 两个本机开关;toolbar/locale 只读 |
|
|
204
|
+
| `scenes` | 六个情景各 13 个字段;写作阈值 `0.6` | 宿主行;只有写作评分阈值被 `plan.ts` 读取 |
|
|
205
|
+
| `parser` | enabled `true`、1 MiB、200k tokens、depth `256`、2 s、cache `256`、graph 20k/100k | enabled + 四个上限为宿主行;depth 与 graph 上限仍是计划 |
|
|
206
|
+
| `chain` | depth `8`、links `50`、tokenBudget `1500` | 宿主行 |
|
|
207
|
+
| `tools` | maxResultBytes `0` | 计划/只读;未被消费 |
|
|
208
|
+
| `defaults` | 职能 `execution`、交互 `think`、情景 `dialogue`、验证 `medium`、命令 `none` | 宿主行 |
|
|
209
|
+
|
|
210
|
+
`GET /deepseek-coordination/config` 返回 `{ values, defaults }`;`defaults` 来自 `resolveConfig(undefined)`,页面不复制默认字面量。`POST /config` 接收扁平点路径补丁(如 `{ "plan.threshold": 0.7 }`),按目录和真实 Schemastery schema 校验,写入活动配置,重新执行不变量(权重归一、别名镜像、强制开关、压缩阈值夹取),在压缩变化时重建 `TokenGovernor`,最后尽力通过 `ctx.settings.update` 持久化。
|
|
211
|
+
|
|
212
|
+
持久化是显式的:只有 DSH 接受设置写入时 `persisted` 才为 true;否则修改只在当前进程生效,页面会如实说明。未知键、`databasePath`、`database` 会以 `INVALID_ARGUMENT` 拒绝;已知但不可写的键进入 `ignored`,绝不写入。`POST /config/compaction` 保留为旧的 auto/soft/hard 兼容小路由。
|
|
213
|
+
|
|
214
|
+
设置页整体使用 Material UI:每组一个默认展开的 `Accordion`,行放在 `List` 中;六个情景各一张 `Table`(页面没有情景下拉框);布尔用 `Checkbox`,枚举用 `RadioGroup`,比例型数字用 `Slider`,计数/字节/毫秒用 `TextField type="number"`;`Chip` 只表示 `主机 / 本机 / 强制 / 只读 / 计划中` 状态和连接状态。没有保存按钮,控件在 change/blur 时提交。强制行画禁用控件,只读行显示值,计划行明确说明当前运行时尚未读取。设置导航图标是宿主 shell 的限制所致:section 只接受 `{ id, order, label }`,插件只能用 CSS mask 给导航按钮补一个 Material `sync` 图标。
|
|
215
|
+
|
|
216
|
+
## 服务级接管
|
|
217
|
+
|
|
218
|
+
- 打开 `ui.takeover` 后,`TakeoverGate` 监听 DSH 的 `approval/request` 与 `user-questions/request` 瀑布。它写一条持久 pending 行、发出 `takeover/pending`,等待客户端路由送来人工答案。
|
|
219
|
+
- 超时、请求 signal 中止、显式取消或插件卸载都会调用 `next()`;闸门绝不把“没人回答”变成 `allowed-once` 或编造的问题答案。`unavailable` 是宿主 fail-closed 的结果,不作为用户决定接受。
|
|
220
|
+
- 对话框只在以下条件同时成立时每 1 秒轮询 `/takeover/pending`:本机“权限接管”开关打开、session id 真实、页面可见。它渲染第一条 pending 请求;队列多于一条时显示 `1 / N` 徽标。
|
|
221
|
+
- 审批提供 仅本次允许 / 拒绝 / 取消;只提交 `allowed-once` 与 `rejected`,取消/Esc/点背景走 cancel 路由。提问按单选渲染 radio、多选渲染 checkbox,并允许每题自由文本;所有问题答完前提交按钮禁用。
|
|
222
|
+
- **两个开关都需要**:宿主 `ui.takeover`(默认 `false`)负责打开闸门,本机“权限接管”负责让对话框轮询。宿主开关关闭时闸门立即委托,没有窗口可显示。
|
|
223
|
+
|
|
224
|
+
## 自动压缩与 Token 治理器
|
|
225
|
+
|
|
226
|
+
`TokenGovernor` 估算上下文(CJK 感知的 O(n) 估算器,带 256 项有界 memo),按 `contextWindow - reserve` 规划预算,并在 `agent/pre-step` 利用率越过阈值时请求 DSH 的 `compactIfNeeded`。默认:soft `0.70`、hard `0.88`、reserve `max(2048, 窗口的 8%)` 且不超过窗口 25%、recentTurns `6`、cooldown `30 s`、maxPerSession `20`。
|
|
227
|
+
|
|
228
|
+
soft 阈值可按 0.30–0.90 的比例编辑;hard 为 0.30–0.98,且必须严格高于 soft。旧界面里的 60/70/80/88% 仍是合法取值(`0.60`、`0.70`、`0.80`、`0.88`),只是目录页现在用 slider,而不是四项选择器。`compaction.auto=false` 只关闭自动压缩,不约束显式 compact 命令。
|
|
229
|
+
|
|
230
|
+
失败封闭是刻意设计:钩子绝不阻塞 step;压缩 port 缺失、返回 `null`/`undefined`、超时、抛错、每会话额度用尽或 cooldown 未到,全部按 no-op 处理,agent 继续执行 `next()`。治理器在构造时快照策略,所以设置热更新会同时重建它。实际安全范围和替换由 DSH 负责,本插件只决定何时值得请求。
|
|
231
|
+
|
|
232
|
+
## 文件工具、解析器与 Chain
|
|
233
|
+
|
|
234
|
+
文件层以四个模型可见工具接线,加上三个输入框/职能工具,共 7 个:
|
|
235
|
+
|
|
236
|
+
| 工具 | 用途 |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `coordination_parse` | 解析工作区文件或给定文本。`path`/`text` 二选一;返回引用、诊断、字符集、截断标记和节点卡片,绝不返回文件正文。 |
|
|
239
|
+
| `coordination_describe` | 按 handle 返回一个 `NodeCard`,可选 `summary`/`description`/`usage`/`purpose`;不返回正文。 |
|
|
240
|
+
| `coordination_chain` | 查询一条有界关系链:dependencies、dependents、satisfies、satisfied_by、baseline、prerequisites、unmet、path、cycle。深度 ≤ 8、链接 ≤ 50、约 1,500 tokens;不返回 AST 或图对象。 |
|
|
241
|
+
| `coordination_read` | 唯一返回文件内容的工具。读字节范围(end 不含)或行范围(1 基,end 含),单次上限 256 KiB / 2,000 行 / 16k tokens。 |
|
|
242
|
+
|
|
243
|
+
P0 解析器是词法级、确定性的:JS/TS/JSX(import/export/`require`/动态 import/符号/注释)、CSS/HTML/Markdown(`@import`/`url()`、script/link/img、链接/图片/标题/front matter/fenced div/公式)、TeX(定理类环境、证明、label/ref、数学、`\input`/`\include`)、通用公式(Markdown/TeX/表格/MathML/OMML/unknown;不求值)、词法依赖图(外部过滤、拓扑序、环、层级、孤立点)。畸形输入产生诊断和 `truncated`,不崩溃。
|
|
244
|
+
|
|
245
|
+
每个解析节点都是**蓝图式元数据**:`path`、UTF-8 `byteStart`/`byteEnd`、1 基 `lineStart`/`lineEnd`、`sha256:<hex>` 指纹和 `charset`;没有 `text`、`body`、`content` 或 AST。`NodeCard` 与 `ChainLink` 同样如此。`coordination_read` 是唯一内容路径;它拒绝绝对路径、UNC、目录穿越和符号链接逃逸,拒绝 NUL/二进制/不可解码内容,并对返回内容做哈希。
|
|
246
|
+
|
|
247
|
+
| 上限 | 值 |
|
|
248
|
+
|---|---|
|
|
249
|
+
| 单次解析源文本 | 1 MiB;只解析前缀并标 `truncated` |
|
|
250
|
+
| 单次解析 token | 200,000 |
|
|
251
|
+
| AST 深度 | 256 |
|
|
252
|
+
| 解析墙钟时间 | 2,000 ms |
|
|
253
|
+
| 诊断保留 | 100 条 |
|
|
254
|
+
| 单次解析节点元数据 | 20,000 |
|
|
255
|
+
| 依赖图 | 20,000 节点 / 100,000 边,超限 fail-closed |
|
|
256
|
+
| 公式 | 单次扫描 5,000 个 / 512 条引用 |
|
|
257
|
+
| 解析缓存 | 256 文件,按 `path+mtime+size+parserId` 做 LRU |
|
|
258
|
+
| 读取范围 | 256 KiB / 2,000 行 / 16,000 估算 token |
|
|
259
|
+
| Chain 投影 | 深度 ≤ 8 / 链接 ≤ 50 / 约 1,500 tokens;50k 节点 / 200k 边 fail-closed |
|
|
260
|
+
|
|
261
|
+
`parser.enabled` 在注册文件工具时读取;parser 各上限与 `chain.depth`/`links`/`tokenBudget` 在惰性工具运行时创建 `ParseService`/`ChainEngine` 时捕获。因此 `/config` 的热改不会调整已创建的运行时,要到下一次注册(目前是语言切换或重启)才生效。工作区根默认回退到 `process.cwd()`,因为插件入口没有拿到 DSH 的 workspace root;cwd 不是工作区的宿主必须通过工具选项传入根目录。
|
|
262
|
+
|
|
263
|
+
**没有写入或刷新工具。** v24 的写入契约需要 `expectedFingerprint` 冲突检测、事务式重解析与范围重算、显式 charset/BOM 变更以及 watcher/rename 策略;这些都没有实现,贸然提供 writer 会覆盖并发编辑或返回过期范围。`coordination_write` 与 `coordination_refresh` 属于设计/计划。
|
|
264
|
+
|
|
265
|
+
## 数据结构
|
|
266
|
+
|
|
267
|
+
`src/shared/` 的共享库是纯 ES2022:hash/LRU、节点、二叉树/B+ 树/红黑树、堆、栈/双端队列/队列、有序 map/set/skip list、AST/TokenStream、有向图、Trie、稀疏网格。当前运行时实际使用:
|
|
268
|
+
|
|
269
|
+
- `LruList`:token 估算 memo、解析缓存、节点注册表、Chain 投影缓存。
|
|
270
|
+
- `BinaryHeap`:`selectContext`,保持原全序(value 降序 → created 升序 → id 字节升序 → 输入下标)。
|
|
271
|
+
- `RedBlackTree` 前驱/后继:数值一致性(numeric agreement);精确 argmax 证明在采用文档中。
|
|
272
|
+
- `OrderedSet`:comprehension/emotion/permission 的首次出现去重;`Queue`:权限命令 tokenizer。
|
|
273
|
+
- `DirectedGraph`:依赖图,以及每个 Chain relation 一张图。
|
|
274
|
+
|
|
275
|
+
第二轮采用有随机化等价测试,也有一个诚实 caveat:在 20k chunk 的 **wide** 合成用例里,堆并不比 V8 sort 快(17.7 ms 对 15.9 ms);它在预算提前填满时占优,且结果不劣于 sort。`SparseGrid` 与 `BPlusTree` 只在共享库里有回归测试,**没有接到运行时路径**;不要据此认为它们在用。
|
|
276
|
+
|
|
277
|
+
## 工具与路由
|
|
278
|
+
|
|
279
|
+
| 工具 | 返回 |
|
|
280
|
+
|---|---|
|
|
281
|
+
| `coordination_selection` | 读取或修改某 session 的五个选择(含 `command` 与 DSH `preset`);返回带锁、预设和配置投影的 `SelectionState`。 |
|
|
282
|
+
| `coordination_profession` | 读取当前职能记录;仅在活动任务之前/不存在时设置职能。任务已锁时返回 `PROFESSION_LOCKED`,不改选择。 |
|
|
283
|
+
| `coordination_professionlock` | 读取活动 Turn 的锁定状态/职能,或指定序号 Turn 的状态。 |
|
|
284
|
+
| `coordination_parse` | 无正文的解析投影(引用、诊断、字符集、截断、卡片)。 |
|
|
285
|
+
| `coordination_describe` | 无正文的 `NodeCard` 和请求的元数据字段。 |
|
|
286
|
+
| `coordination_chain` | 只返回有界 `ChainProjection`。 |
|
|
287
|
+
| `coordination_read` | 唯一返回内容的工具;有上限的字节/行范围。 |
|
|
288
|
+
|
|
289
|
+
所有路由都在 `/deepseek-coordination` 下,共 **14 条路径 / 16 个方法处理器**:
|
|
290
|
+
|
|
291
|
+
| 方法 | 路径 | 作用 |
|
|
292
|
+
|---|---|---|
|
|
293
|
+
| GET | `/ping` | 存活检查;返回插件名、`pong`、当前语言。 |
|
|
294
|
+
| POST | `/echo` | 回显 JSON body(诊断用)。 |
|
|
295
|
+
| GET, POST | `/selection` | 读 `SelectionState`;修改五个选择。 |
|
|
296
|
+
| GET | `/presets` | 投影 DSH 权限预设目录。 |
|
|
297
|
+
| POST | `/permission` | 立即应用一个 DSH 预设。 |
|
|
298
|
+
| GET, POST | `/config` | 读 `{ values, defaults }`;应用扁平配置补丁。 |
|
|
299
|
+
| POST | `/config/compaction` | `auto`/`soft`/`hard` 的兼容补丁。 |
|
|
300
|
+
| GET | `/takeover/pending` | 某 session 的待处理审批/提问窗口。 |
|
|
301
|
+
| POST | `/takeover/answer` | 提交合法人工答案;非法答案保持窗口。 |
|
|
302
|
+
| POST | `/takeover/cancel` | 取消窗口并交还宿主默认处理。 |
|
|
303
|
+
| POST | `/profession/rebuild` | 结束活动任务,用新职能创建替代 Turn。 |
|
|
304
|
+
| POST | `/profession/abandon` | 结束活动任务,不创建替代。 |
|
|
305
|
+
| POST | `/locale` | 上报客户端语言;重新注册本地化工具描述。 |
|
|
306
|
+
| GET | `/state` | 选择 + 协调指标(`session`、`turn`、`plan`、`subtask`、`trace`、`takeover`、`schema`)。 |
|
|
307
|
+
|
|
308
|
+
没有 `coordination_write`、`coordination_refresh`、`outline`、`findSymbol`、`searchSummary`、`commentsOf` 工具。四个文件工具的说明目前是内联中英双语,因为共享 `ToolName` 表还没有它们的键;三个输入框/职能工具使用共享 copy 表。
|
|
309
|
+
|
|
310
|
+
## 数据、隐私与安全
|
|
311
|
+
|
|
312
|
+
- 插件使用独立 SQLite 库,从不打开 DSH 的 `harness` 库。默认路径是 `%DSH_HOME%\coordination\coordination.db`;`database`/`databasePath` 只属于部署配置,既不投影也不允许 `/config` 写入。
|
|
313
|
+
- 解析节点、`NodeCard`、`ChainLink` 及其工具结果都不带文件正文、附件或 AST。只有 `coordination_read` 返回文本,而且只返回请求的范围。
|
|
314
|
+
- 文件路径必须是工作区相对路径;绝对路径、盘符路径、UNC、`..` 逃逸与符号链接逃逸在读取前拒绝。二进制/NUL/不可解码内容直接拒绝,不猜。
|
|
315
|
+
- Turn 钩子只写 Turn 元数据和一条空 prompt 行,不镜像 DSH 对话,也不把文件内容复制进协调库。
|
|
316
|
+
- 权限预设立即写给 DSH(完全权限需二次确认),从不进入离线 localStorage;离线重放只覆盖五个已存选择字段。
|
|
317
|
+
- 接管绝不自动批准。超时、中止、取消、卸载都以 `next()` 委托;只有通过 answer 路由送达的人工决定才会结算窗口。
|
|
318
|
+
- 失败封闭是明确的:非法接管答案保持窗口;`PROFESSION_LOCKED` 不改选择;解析不确定标 `unverifiable`;解析/图溢出标 `truncated`;压缩失败是 no-op;Chain 的 `unknown` 不会自动通过。
|
|
319
|
+
- 协调库保存任务/审计行(包括 `prompt` 与追踪行),不保存凭据或 provider secret。配置投影中没有 DSH 凭据、token 或 provider key。
|
|
320
|
+
|
|
321
|
+
## 测试、质量与限制
|
|
322
|
+
|
|
323
|
+
| 检查 | 结果 |
|
|
324
|
+
|---|---|
|
|
325
|
+
| `npm test` | **473 tests,472 pass,1 skipped(仅 GC),0 fail**,Node 24.21.0 上约 3.3 s |
|
|
326
|
+
| `npm run typecheck` / `npm run test:tsc` | 通过 |
|
|
327
|
+
| `npm run build` | 宿主 + 客户端 bundle 通过;MUI 的 `"use client"` directive 警告不致命 |
|
|
328
|
+
| `npm run pack:check` | OK:`deepseek-coordination-0.10.0.tgz`(538,358 B),四个运行时文件齐全 |
|
|
329
|
+
| 发布 CI | `.github/workflows/release.yml` 运行 typecheck、test:tsc、测试、pack:check,并在 `v*` Release 附上 tarball |
|
|
330
|
+
|
|
331
|
+
解析器与结构的数字是守卫,不是承诺:100k 字符解析测试保持在 3 秒内;数据结构报告记录 B+ 树 10k 插入 8.3/9.8 ms、skip list 10k 插入 6.4 ms、红黑树 10k 随机 set/delete 13.5 ms、稀疏网格 10k 单元 5.1 ms、`hashString` 100k 次 4.3 ms;复杂度审计记录 Turn 序号从 `COUNT(*)` 改为 `MAX(ordinal)`(2,000 条语句约 65–69 ms → 约 12–14 ms)以及路由排序(200 × 12 条路由的 2,400 次 posterior 查询降到 200 次)。
|
|
332
|
+
|
|
333
|
+
已知限制,直接列出而不是隐藏:
|
|
334
|
+
|
|
335
|
+
1. **持久图有边界。** `node`/`edge`/`snapshot` 已是 schema 24,跨运行时保留;但重水化只取最新 10k 节点 / 50k 边,删除的符号不会清理,超出窗口的旧 handle 返回 `NOT_FOUND`。
|
|
336
|
+
2. **领域提取在解析器层,关系链投影仍不完整。** Book/Article/Part/Chapter/Section/Paragraph、Person/Venue、Condition、Sheet/Cell/Range、TeX 证明状态和 CSV/TSV 表格已产出并投影到节点卡;`partOf`/`condition` 边还不能通过 `coordination_chain` 查询。xlsx/docx/pptx、Sign 提升、Lean/Coq/Isabelle 状态和跨文件 Book 组合仍未实现。
|
|
337
|
+
3. **核心 Turn 流水线已接线,更宽的引擎没有。** `command=none` 的 Turn 会运行 Comprehension → Organization → Plan/score,以及需要的 Predict 行;有端口的 scene 策略会生效。独立 Emotion/Bayesian/Cognition/Orchestration/Learning/Resource/Score、计划重建循环和 decompose/dispatch/verify/synthesize 仍是库级。`thinking`、`allowBash`、`maxIteration` 和非写作阈值记录为延迟诊断,不伪造。
|
|
338
|
+
4. **配置上限已生效。** `parser.maxAstDepth`、两个依赖图上限和 `tools.maxResultBytes` 都有消费者;parser/chain 上限会 retune 已存在运行时,不再留下陈旧运行时。
|
|
339
|
+
5. **工作区根已解析,不再硬编码。** 工具参数 → service 根 → host 根 → sandbox policy 根 → 最后才回退 `process.cwd()`。
|
|
340
|
+
6. **`parser.enabled` 仍需重新注册。** 其他文件层上限会即时 retune;关闭 parser 会立即失败关闭,但从工具列表增删四个工具仍要等下一次注册(重启)。
|
|
341
|
+
7. **没有浏览器 E2E。** UI 由类型、构建、settings-catalog 测试以及 takeover/bridge/集成测试覆盖;视觉验证仍是人工/截图。
|
|
342
|
+
8. **Node 引擎为 `>=22.13.0`。** `node:sqlite` 从 22.13.0/23.4.0 起不再需要实验开关;CI 与本次验证使用 24.21.0。
|
|
343
|
+
9. **profile 里可能仍记录着旧的包 spec。** 请核对实际安装的版本(`dsh --profile <name> --dump-config` 或 profile 的 `package.json`);安装 0.10.0 tarball 或链接已构建的 checkout,不要假定上一次安装已被升级。
|
|
344
|
+
|
|
345
|
+
## 仓库历史与许可
|
|
346
|
+
|
|
347
|
+
`0.1.x` 是另一个项目 **dsh-os**,已不在分支顶端;`0.2.0` 变成 DSH 插件 bundle;`0.3`–`0.6` 的 UI 从 Angular 经 MDUI、纯 React 迁到 Material UI;`0.7`–`0.9` 加入五个下拉框、i18n、职能锁定、接管、压缩、P0 解析器、领域模型和共享数据结构;`0.10.0` 加入设置目录、四个文件工具、第二轮数据结构采用和可验证 tarball。旧状态仍在 git 历史和 tag 中。
|
|
348
|
+
|
|
349
|
+
MIT © 2026 Eloy Kim。见 [LICENSE](LICENSE)。源码:[github.com/eloyhere/deepseek-coordination](https://github.com/eloyhere/deepseek-coordination)。
|