@joekytc/dsh-swarm 0.2.0 → 0.3.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.
Files changed (103) hide show
  1. package/README.md +176 -139
  2. package/README.zh-CN.md +172 -125
  3. package/client/ConfigSection.tsx +96 -5
  4. package/client/ConfigSelect.tsx +3 -1
  5. package/client/TaskDrawer.tsx +2 -0
  6. package/client/config-store.ts +48 -7
  7. package/client/kanban.css +69 -0
  8. package/client/timeline-model.ts +2 -0
  9. package/lib/client.js +172 -14
  10. package/lib/config.d.ts +18 -1
  11. package/lib/config.js +14 -0
  12. package/lib/dispatcher/agent-runner.d.ts +13 -5
  13. package/lib/dispatcher/agent-runner.js +78 -38
  14. package/lib/dispatcher/chain-auditor.d.ts +4 -4
  15. package/lib/dispatcher/chain-auditor.js +10 -8
  16. package/lib/dispatcher/dispatcher.d.ts +18 -18
  17. package/lib/dispatcher/dispatcher.js +59 -43
  18. package/lib/dispatcher/event-waker.d.ts +1 -1
  19. package/lib/dispatcher/event-waker.js +2 -2
  20. package/lib/dispatcher/git-credentials.d.ts +2 -2
  21. package/lib/dispatcher/git-credentials.js +2 -2
  22. package/lib/dispatcher/model-candidates.d.ts +1 -1
  23. package/lib/dispatcher/model-candidates.js +1 -1
  24. package/lib/dispatcher/session-events.d.ts +1 -1
  25. package/lib/dispatcher/session-events.js +1 -1
  26. package/lib/dispatcher/target-repo.d.ts +2 -2
  27. package/lib/dispatcher/target-repo.js +3 -3
  28. package/lib/dispatcher/v-orchestrator.d.ts +8 -8
  29. package/lib/dispatcher/v-orchestrator.js +41 -41
  30. package/lib/dispatcher/watchdog.d.ts +1 -1
  31. package/lib/dispatcher/watchdog.js +2 -2
  32. package/lib/domain/config-override.d.ts +14 -0
  33. package/lib/domain/config-override.js +38 -1
  34. package/lib/domain/delivery-contract.d.ts +2 -2
  35. package/lib/domain/delivery-contract.js +5 -5
  36. package/lib/domain/delivery-evidence.d.ts +1 -1
  37. package/lib/domain/delivery-evidence.js +1 -1
  38. package/lib/domain/event-store.js +1 -1
  39. package/lib/domain/im-message.d.ts +15 -0
  40. package/lib/domain/im-message.js +149 -0
  41. package/lib/domain/kanban-service.d.ts +13 -10
  42. package/lib/domain/kanban-service.js +36 -28
  43. package/lib/domain/memory.js +1 -1
  44. package/lib/domain/ocr-review.d.ts +47 -0
  45. package/lib/domain/ocr-review.js +105 -0
  46. package/lib/domain/permissions.js +5 -5
  47. package/lib/domain/projection.js +6 -6
  48. package/lib/domain/state-machine.js +1 -1
  49. package/lib/domain/task-parents.js +1 -1
  50. package/lib/domain/types.d.ts +4 -4
  51. package/lib/index.js +14 -9
  52. package/lib/roles/clean-fs-tools.js +3 -4
  53. package/lib/roles/preset-installer.js +3 -3
  54. package/lib/roles/skill-installer.js +1 -1
  55. package/lib/roles/toolsets.d.ts +60 -16
  56. package/lib/roles/toolsets.js +265 -47
  57. package/lib/roles/wiki-worker.js +3 -3
  58. package/lib/routes/kanban-http.d.ts +25 -2
  59. package/lib/routes/kanban-http.js +171 -11
  60. package/lib/routes/kanban-sse.d.ts +1 -1
  61. package/lib/routes/kanban-sse.js +1 -1
  62. package/lib/routes/planning-driver.d.ts +4 -2
  63. package/lib/routes/planning-driver.js +7 -3
  64. package/lib/routes/prefix-router.d.ts +3 -3
  65. package/lib/routes/prefix-router.js +3 -1
  66. package/lib/services/config-provider.d.ts +0 -4
  67. package/lib/services/config-provider.js +7 -9
  68. package/lib/services/im-delivery.d.ts +94 -0
  69. package/lib/services/im-delivery.js +263 -0
  70. package/lib/services/kanban-provider.d.ts +1 -1
  71. package/lib/services/kanban-provider.js +2 -2
  72. package/lib/services/ocr-cli.d.ts +35 -0
  73. package/lib/services/ocr-cli.js +128 -0
  74. package/lib/tools/kanban-tools.d.ts +2 -2
  75. package/lib/tools/kanban-tools.js +1 -1
  76. package/lib/tools/main-session-tools.d.ts +6 -2
  77. package/lib/tools/main-session-tools.js +58 -20
  78. package/lib/tools/ocr-review-tools.d.ts +11 -0
  79. package/lib/tools/ocr-review-tools.js +63 -0
  80. package/lib/tools/planning-tools.d.ts +5 -2
  81. package/lib/tools/planning-tools.js +11 -9
  82. package/lib/tools/wiki-tools.js +2 -2
  83. package/lib/wiki/kb-linkage.d.ts +1 -1
  84. package/lib/wiki/local-kb-client.js +3 -3
  85. package/lib/wiki/local-kb.d.ts +1 -1
  86. package/lib/wiki/local-kb.js +2 -2
  87. package/lib/wiki/page-path.d.ts +1 -1
  88. package/lib/wiki/page-path.js +6 -3
  89. package/lib/wiki/wiki-vault-client.d.ts +1 -1
  90. package/lib/wiki/wiki-vault-client.js +1 -1
  91. package/package.json +1 -1
  92. package/personas/kanban-d/agent.cordis.yml +5 -5
  93. package/personas/kanban-dt/agent.cordis.yml +23 -5
  94. package/personas/kanban-p/agent.cordis.yml +3 -3
  95. package/personas/kanban-pt/agent.cordis.yml +1 -1
  96. package/personas/kanban-v/agent.cordis.yml +3 -3
  97. package/personas/kanban-w/agent.cordis.yml +5 -5
  98. package/personas/persona-d.md +1 -1
  99. package/personas/persona-dt.md +12 -4
  100. package/personas/persona-p.md +1 -1
  101. package/personas/persona-v.md +2 -2
  102. package/personas/swarm/agent.cordis.yml +43 -0
  103. package/personas/swarm/preset.yml +5 -0
package/README.zh-CN.md CHANGED
@@ -4,9 +4,13 @@
4
4
 
5
5
  ---
6
6
 
7
- **受管六角色 DSH agent 蜂群:把单个需求变成严格、证据可核验的流水线。**
7
+ **说一句需求,回复一声确认——六名专职 agent 从规划到交付全程接管,一个命令都不用记。**
8
8
 
9
- 编排者(V)把已批准的规格拆成严格有序的相位链(`p (pt?) → w2 → d → dt → w3 → summary`);六个单一职责的角色(V / P / W / D / PT / DT)以隔离、受权限约束的工具面执行每个相位;每份交接都经过针对证据契约的机器校验;故障通过幂等重试与人工把关的评审恢复;实时 Workflow 看板标签页通过 SSE 把全部状态流式同步到浏览器。设计灵感源自 [Hermes Agent kanban](https://github.com/NousResearch/hermes-agent)。
9
+ dsh-swarm DSH 的一个插件:把一个需求变成一条严格、证据可核验的交付流水线。
10
+ 编排者(V)把已批准的规格拆成严格有序的相位链(`p → (pt?) → w2 → d → dt → w3 → summary`);
11
+ 六个单一职责的角色(V / P / W / D / PT / DT)以隔离、受权限约束的工具面各执行一个相位;
12
+ 每份交接都经机器校验;故障通过幂等重试与人工把关的评审恢复;实时 Workflow 看板标签页通过
13
+ SSE 把全部状态流式同步到浏览器。设计灵感源自 [Hermes Agent kanban](https://github.com/NousResearch/hermes-agent)。
10
14
 
11
15
  ![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue)
12
16
  ![License](https://img.shields.io/badge/license-MIT-green)
@@ -14,116 +18,118 @@
14
18
 
15
19
  ---
16
20
 
17
- ## 为什么
21
+ ## 蜂群模式(推荐)
18
22
 
19
- 在单一任务上协调多个 AI agent,通常以三种方式失败:
23
+ 蜂群模式让主会话成为一个「领队」:**你说需求,它负责澄清、规划、确认、派工、跟进**。全程自然语言,不用记任何命令。
20
24
 
21
- 1. **角色漂移** ——"规划者"开始写代码,"执行者"评审自己的工作,没有人对结果负责。
22
- 2. **不可验证的交接** ——agent 声称"完成",却没有可复现的证据,下游在流沙上继续建设。
23
- 3. **静默死锁** ——agent 中途停住不再推进,管线挂起;或坏代码在任何人评审之前就被合并。
25
+ - **免记命令** —— 直接说需求即可,不需要 `/plan:`、`/openspec:` 前缀。
26
+ - **意图自动识别** —— 开发需求 → 进入澄清规划并建链;沉淀经验/复盘 → 记入记忆库;发通知到群 → 投递企微;问答/闲聊 → 直接回答。
27
+ - **确认闸防误建** —— 规划清单落库后,必须等你明确回复「确认 / 开干 / 开跑 / 开始 / go」等肯定语义才会建链;模糊、岔开话题、只提修改意见 = 未确认。
28
+ - **领队只读** —— 主会话不能写/改仓库源码,也不能执行 git 变更(push/commit/checkout 等);写代码由流水线中的执行者(D)在隔离工作区完成,这是设计使然。
29
+ - **进度实查** —— 任何时刻问「进度怎么样」,领队都以看板实查结果播报,绝不虚构。
24
30
 
25
- dsh-swarm 针对以上三种问题编码了*契约*:每个角色只有一项机器强制的职责;每次交接必须携带结构化证据,否则相位无法关闭;每次停滞或评审失败都会落入可见、可恢复的状态,并以**人类作为信任锚**。它被构建为**正确性优先**——确定性状态机、只追加的事件溯源、幂等调度器,以及一套红队测试套件——重放事件日志并拒绝任何非法转换。
31
+ ### 为什么这样设计
26
32
 
27
- ---
33
+ 让多个 agent 一起干活,最常见的三种翻车:
28
34
 
29
- ## 角色与执行管线
35
+ - **角色漂移** —— 规划的人跑去写代码,执行的人给自己验收,最后没人对结果负责。
36
+ - **不可验证的交接** —— agent 说「做完了」却拿不出可复现的证据,下游在流沙上继续盖楼。
37
+ - **静默死锁** —— agent 停住不吭声,管线挂起;坏代码没经评审就被合入。
30
38
 
31
- 六个角色由调度器作为一次性 agent 会话派发(确定性会话 id `kbn-<taskId>`,重试/返工时经 `resumeSessionId` 恢复)。每个角色 agent 会话绑定到恰好一个任务(`boundTaskId`),并获得裁剪后的工具面。V 是例外:链级编排会话(`kbn-v-<chainId>`),无 `boundTaskId`。
39
+ dsh-swarm 用一份「契约」对症下药:每个角色只有一项机器强制的职责;每次交接必须携带
40
+ 结构化证据,缺了卡就过不去;每次停滞或评审失败都落入可见、可恢复的状态——而最终的
41
+ 信任锚是你(人类)。技术底座是正确性优先的:确定性状态机、只追加事件日志、幂等调度器,
42
+ 并有一套红队测试重放事件日志、拒绝任何非法流转(机制细节见[进阶](#进阶--开发者))。
32
43
 
33
- | 角色 | 别名 | 职责 | 工具面(要点) |
34
- |---|---|---|---|
35
- | **V** | 编排者 | 驱动相位机,逐相位建卡,停滞时发布 `[blocked-review]` 指引。绝不执行。 | `kanban_create` + 任务工具 + 规格查看 |
36
- | **P** | 规划者 | 读取规格 + 仓库事实(含只读自查),编写 OpenSpec 实施计划,用 `pt_decision.needed` 决定是否需要 PT。绝不执行。 | 任务工具 + 规格查看,只读(仅写 `openspec/changes/`) |
37
- | **PT** | 计划评审者 | 对 P 的计划做只读评审(需求对齐、完整性、逻辑)。输出裁决 + 问题清单。 | 任务工具 + 规格查看,**只读 ToolGuard** |
38
- | **W** | 知识官 | W2/W3 知识库同步(`w:kb`)。绝不碰代码/git。 | 任务工具 + 远程 `wiki_search/read/write` / 本地 `skill`→llm-wiki + 只读规格查看 |
39
- | **D** | 执行者 | *唯一*写代码的角色:worktree → 实现 → 验证 → `[AI-GEN]` 提交 → 推送特性分支(合入规格声明的目标分支由 system 在 DT 通过后执行)。 | 任务工具 + wiki 只读 + bash/fs/run_code(完整开发面)+ subagent(spawn/fork/list-agents)+ goal |
40
- | **DT** | 实现评审者 | 实证验证 D 的工作(test/build/typecheck/diff/git + open-code-review),把评审页写入知识库。对仓库只读。 | 任务工具 + wiki 读写(评审命名空间)+ bash/fs/run_code,**只读 ToolGuard** |
44
+ ### 两种模式
41
45
 
42
- 管线(链路内严格串行,链路间并行):
43
-
44
- ```text
45
- p ──> (pt?) ──> w2 ──> d ──> dt ──> w3 ──> summary
46
- | | | | | | |
47
- 计划 计划评审 计划同步 实现 实现评审 知识库同步 收尾
48
- (P) (仅当 P 自选) (W2) (D) (固定) (W3) (system)
49
- ```
50
-
51
- - `pt` 仅在 P 的交接交付 `pt_decision = { needed: true, reason }` 时创建——V 只负责建卡,system 从不覆盖该判定。`needed: false` 直接跳入 `w2`。
52
- - `d` 之后**总是**创建 `dt`。
53
- - 仓库事实由阶段 0 规划会话采集(`planning_prefetch`,只读),不再由 W 相位承担。
54
- - 链路由机械规则完成,而非 agent:最后完成的任务是 W3(`w/kb`),D(`execute`)任务已带交付证据完成,且无未完成任务。
46
+ | 模式 | 怎么用 | 说明 |
47
+ |---|---|---|
48
+ | **蜂群模式**(推荐) | 自然语言直接说需求 | 免记命令,意图自动识别,确认即跑 |
49
+ | **命令模式**(兼容) | `/plan: <需求>` 澄清 `/openspec: 确认执行` | 兼容保留,功能等价;未来可能移除,新用户请用蜂群模式 |
55
50
 
56
51
  ---
57
52
 
58
- ## 安装
59
-
60
- ### 前置条件
53
+ ## 快速上手
61
54
 
62
- - 可用的 [DSH](https://github.com/deepseek-ai) 安装(`@deepseek-ai/*` 运行时包:cordis、dsh-agent、dsh-tools、dsh-persona、dsh-session)。
63
- - Node.js ≥ 22.19 与 npm(对齐 DSH 运行时要求)。
64
- - DSH 随附的 peer 依赖:`@deepseek-ai/dsh-tool-bash`、`@deepseek-ai/dsh-tool-fs`、`@deepseek-ai/dsh-tool-fs-search`、`@deepseek-ai/schemastery`。
65
- - 可选:供 W/P/D 知识库读取及 W2/W3 同步的 wiki-vault HTTP 服务(见[配置](#配置))。
55
+ ### 1. 安装
66
56
 
67
- ### 构建
57
+ 前置:可用的 DSH 运行时(`@deepseek-ai/*`)、Node.js ≥ 22.19 与 npm。可选:wiki-vault HTTP 服务(知识库功能,见[配置](#配置))。
68
58
 
69
59
  ```bash
70
60
  npm install
71
- npm run build # tsc -p tsconfig.build.json (lib/*.js) + client bundle (lib/client.js)
61
+ npm run build # tsc -p tsconfig.build.json + client bundle (lib/client.js)
62
+ dsh plugin --profile web add @joekytc/dsh-swarm
72
63
  ```
73
64
 
74
- ### 安装为 DSH 插件
65
+ > GitHub 源码安装:`dsh plugin --profile web add github:joekytc/dsh-swarm`
75
66
 
76
- ```bash
77
- # 从 npm 安装——Web profile 同时附带 kanban 浏览器标签页
78
- dsh plugin --profile web add @joekytc/dsh-swarm
67
+ ### 2. 切换主会话预设
68
+
69
+ 把主会话的 agent preset 切换为 **蜂群模式(Swarm)**(插件安装后已就绪于 `$DSH_HOME/.agent-presets/swarm`)。
70
+
71
+ ### 3. 说需求 → 确认 → 看进度
72
+
73
+ 对话示例:
79
74
 
80
- # 从本地检出安装(开发)
81
- dsh plugin --profile <name> add ./dsh-swarm
82
75
  ```
76
+ 你:帮我在 admin 项目里加一个角色管理页面,支持增删改查和权限勾选
77
+
78
+ 领队:先确认几个问题——
79
+ · 角色字段需要哪些(名称/描述/状态/…)?
80
+ · 权限来源是现有菜单树还是自定义?
81
+ · 验收有什么要求,比如「删除角色后关联用户不受影响」?
83
82
 
84
- > 也可从 GitHub 源码安装:`dsh plugin --profile web add github:joekytc/dsh-swarm`。
85
- >
86
- > `storageDir` 必须使用**不加引号**的 `!!js dshHomePath("storages/kanban")` 写法。加引号会把路径退化成字面量字符串(已知陷阱)。
83
+ 你:字段就名称和描述,权限用现有菜单树,验收按 TDD
87
84
 
88
- ### 快速上手
85
+ 领队:规划清单已落库(规格六段 + 仓库事实)。回复「确认」即可启动,
86
+ 我会拉起 p → (pt) → w2 → d → dt → w3 流水线。
89
87
 
90
- 1. 启动 DSH 会话,输入:
88
+ 你:确认
91
89
 
92
- ```
93
- /plan: <需求> / <项目> / <API>
94
- ```
90
+ 领队:链已创建(ch_…),实时进度见看板标签页(对话 → 轨迹 → 看板)。
91
+ 首个相位:规划(P)…
92
+ ```
95
93
 
96
- 进入阶段 0 规划(零副作用——此时不建任何卡):`grill-me` 一次只问一个澄清问题,
97
- `planning_prefetch` 只读采集仓库事实,对话收敛为规划清单的规格六段
98
- (`problem / solution / user_stories / impl_decisions / testing / out_of_scope`)
99
- 外加仓库 manifest。`planning_checklist_save` 对清单做 schema 校验——非法或不完整会阻塞批准。
94
+ - **看板**:会话中心第三个标签页(对话 轨迹 → 看板),点卡片查看 概览 / 轨迹 / 交接 / 规格 / 评论。
95
+ - **完成**:链路完成时系统审计工作区,并(对 D 链)把特性分支自动合并到规格声明的目标分支;若触发审计警告,需先在 GUI 确认归属。
96
+ - **查进度**:直接问「进度怎么样」,领队实查看板播报;阻塞会如实转述原因。
100
97
 
101
- 2. 确认并启动:
98
+ ---
102
99
 
103
- ```
104
- /openspec: 确认执行
105
- ```
100
+ ## 它替你做了什么
106
101
 
107
- 从已保存的清单创建链路与规格卡;挂上 `file-prefetch`(仓库路径)与 `kb`(清单页)
108
- 附件,规格被批准,链路转入 `executing`,调度器唤醒 V 编排者,后者逐相位搭建管线。
102
+ 六个角色,各管一件事,机器强制边界、绝不越权:
109
103
 
110
- 3. 在**看板标签页**观察进度(会话中心的第三个标签:对话 轨迹 看板)。点击卡片查看
111
- 概览 / 轨迹 / 交接 / 规格 / 评论。
104
+ | 角色 | 一句话职责 | 绝不做什么 |
105
+ |---|---|---|
106
+ | **V** 编排者 | 逐相位建卡、驱动流水线、停滞时给指引 | 不执行任务 |
107
+ | **P** 规划者 | 读规格 + 仓库事实,写实施计划 | 不写代码 |
108
+ | **PT** 计划评审 | 只读评审 P 的计划(按需出现) | 不改任何东西 |
109
+ | **W** 知识官 | 规划/完成阶段同步知识库 | 不碰代码/git |
110
+ | **D** 执行者 | 唯一写代码的角色:实现 → 验证 → 提交 → 推特性分支 | 不直接合入目标分支 |
111
+ | **DT** 实现评审 | 实证验证 D 的交付(测试/构建/类型/diff) | 对仓库只读 |
112
112
 
113
- 4. 链路完成时,系统审计工作区中是否有链路之外的写入,并(对 D 链路)把 D 的特性分支
114
- 合并到规格声明的目标分支。若触发审计警告,需先在 GUI 中确认归属,才会展示最终汇报。
113
+ 流水线(链路内严格串行,链路间并行):
114
+
115
+ ```text
116
+ p ──> (pt?) ──> w2 ──> d ──> dt ──> w3 ──> summary
117
+ 计划 计划评审 计划同步 实现 实现评审 知识库同步 收尾
118
+ ```
119
+
120
+ - `pt` 仅当 P 判定需要计划评审时出现;`d` 之后**总是**创建实现评审(`dt`)。
121
+ - 链路完成由机械规则判定(W3 完成 + D 带交付证据完成 + 无未完成任务),不是 agent 自评。
115
122
 
116
123
  ---
117
124
 
118
125
  ## 配置
119
126
 
120
- 所有键均可选;默认值如下。schema 位于 `src/config.ts`。
127
+ 所有键均可选;schema `src/config.ts`。**多数使用者只需关心前三项**,其余保持默认即可。
121
128
 
122
129
  | 键 | 默认值 | 说明 |
123
130
  |---|---|---|
124
- | `storageDir` | `$DSH_HOME/storages/kanban` | 事件日志(`events.jsonl`)、编排状态、每任务工作区、`dispatcher.log` |
131
+ | `storageDir` | `$DSH_HOME/storages/kanban` | 事件日志(`events.jsonl`)、编排状态、每任务工作区、`dispatcher.log`。取值须用不加引号的 `!!js dshHomePath("storages/kanban")` 写法,加引号会退化成字面量字符串 |
125
132
  | `wikiVault.baseUrl` | `''`(空) | 知识库读写用的 wiki-vault HTTP 服务——知识库功能必需,填你自己的服务地址 |
126
- | `wikiVault.pagePrefix` | `projects/` | W 页面写入的白名单根前缀;页面实际路径为 projects/<repoSlug>/…(repoSlug 由链 workspaceDir 派生) |
127
133
  | `roles.models.<role>` | `{}` | 每角色模型:`{ provider, model, reasoningEffort?, fallbacks?[] }` |
128
134
  | `roles.models.<role>.reasoningEffort` | `high` | 所有角色默认推理强度 |
129
135
  | `roles.models.<role>.fallbacks` | `[]` | 静默回退候选(经 `[model-fallback]` 评论审计) |
@@ -132,8 +138,8 @@ dsh plugin --profile <name> add ./dsh-swarm
132
138
  | `dispatcher.heartbeatIntervalSeconds` | `300` | 看门狗心跳周期 |
133
139
  | `dispatcher.maxProtocolViolations` | `2` | 协议违规护栏:连续违规超过该次数后,下一次即终局(`gave_up`) |
134
140
  | `dispatcher.maxReworksPerRole` | `{ pt: 2, dt: 3 }` | 评审返工轮数上限,超出进入 `review/gave-up` + `[review-final]` |
135
- | `prefixRoutes.plan` | `/plan:` | 阶段 0 规划前缀 |
136
- | `prefixRoutes.openspec` | `/openspec:` | 批准并执行前缀 |
141
+ | `prefixRoutes.plan` | `/plan:` | 命令模式阶段 0 规划前缀 |
142
+ | `prefixRoutes.openspec` | `/openspec:` | 命令模式批准并执行前缀 |
137
143
  | `ui.enabled` | `true` | 启用看板 Web 标签页 |
138
144
  | `ui.contentMinWidth` | `715` | 看板内容最小宽度(px) |
139
145
  | `ui.contentMaxWidth` | `780` | 看板内容最大宽度(px) |
@@ -141,9 +147,68 @@ dsh plugin --profile <name> add ./dsh-swarm
141
147
 
142
148
  ---
143
149
 
144
- ## 护栏
150
+ ## 评审引擎(ocr)
151
+
152
+ 实现评审(链上 DT 相位与独立评审)由 [open-code-review](https://open-codereview.ai)(ocr)驱动,
153
+ 支持两种模式,在 Web 配置面板「Swarm 配置 → 评审引擎(ocr)」卡切换:
154
+
155
+ | 模式 | 工作方式 | 特点 |
156
+ |---|---|---|
157
+ | **委托**(默认) | ocr 只输出评审范围与规则,由 DT 自己的模型逐文件深入评审 | 零 API key,开箱即用 |
158
+ | **托管** | ocr 调用你选定的提供方/模型跑完整评审,一次返回归一化 findings | 适合大变更集;委托模式下超 50 文件时会提示可切换(仅提醒,不自动切换) |
159
+
160
+ ### 安装
161
+
162
+ - 未安装时配置面板出现红横幅,点「安装 ocr」一键全局安装(异步执行,可取消);
163
+ - 或在终端执行 `npm install -g @alibaba-group/open-code-review`,装后用 `ocr --version` 验证。
164
+
165
+ ### 独立评审(不建链也能评)
145
166
 
146
- ### 权限矩阵
167
+ 1. 在 dsh Web 顶部把会话切换为「交付评审官(DT)」,直接对话;
168
+ 2. 说清评审对象:本地目录 / 分支 range(from…to)/ 单个 commit / 工作区未提交 diff / 公开仓库 URL(自动 clone 到临时目录,评完即弃);
169
+ 3. 报告先完整输出到对话;
170
+ 4. 你确认后再写入知识库 `projects/<仓库>/reviews/<主题>-<日期>/`。全程只读,不改被评审代码。
171
+
172
+ ### 配置要点
173
+
174
+ - 模式、提供方与模型都在「评审引擎(ocr)」卡选择,提供方/模型下拉与「模型链」同一目录;
175
+ - 选好后点「应用到 ocr」,系统自动把接入点写入 ocr 自定义配置(`dsh-managed`);API key 由 dsh 模型配置解析后写入 ocr,面板不展示明文;解析失败会降级并指引你在终端手动执行 `ocr config provider`;
176
+ - 托管未就绪时评审静默按委托模式兜底,不阻断。
177
+
178
+ 官方文档:[安装指南](https://open-codereview.ai/docs/installation) · [模型配置](https://open-codereview.ai/docs/configuration) · [委托模式](https://open-codereview.ai/docs/delegate)
179
+
180
+ ---
181
+
182
+ ## 信任与护栏(使用者视角)
183
+
184
+ - **领队只读硬闸** —— 蜂群模式主会话写/改源码与 git 变更被系统硬闸拦截;被拦时向领队说明即可,执行由 D 角色完成。
185
+ - **确认闸** —— 未经你明确确认,不会建链。
186
+ - **TDD 硬闸** —— 实现必须带测试(或说明跳过原因);评审会机器核验「测试真的跑过、且先写」。
187
+ - **人工信任锚** —— 规格审批、解除阻塞、审计确认、整链删除仅人类可做;主会话与角色 agent 都不能建链或批准规格。
188
+ - 详细机制(权限矩阵、交付契约、评审链、返工、故障恢复)见[进阶 / 开发者](#进阶--开发者)。
189
+
190
+ ---
191
+
192
+ ## 进阶 / 开发者
193
+
194
+ > 以下为机制与实现细节,普通使用者可跳过。
195
+
196
+ ### 角色与执行管线(详表)
197
+
198
+ 六个角色由调度器作为一次性 agent 会话派发(确定性会话 id `kbn-<taskId>`,重试/返工时经 `resumeSessionId` 恢复)。每个角色 agent 会话绑定到恰好一个任务(`boundTaskId`),并获得裁剪后的工具面。V 是例外:链级编排会话(`kbn-v-<chainId>`),无 `boundTaskId`。
199
+
200
+ | 角色 | 别名 | 职责 | 工具面(要点) |
201
+ |---|---|---|---|
202
+ | **V** | 编排者 | 驱动相位机,逐相位建卡,停滞时发布 `[blocked-review]` 指引。绝不执行。 | `kanban_create` + 任务工具 + 规格查看 |
203
+ | **P** | 规划者 | 读取规格 + 仓库事实(含只读自查),编写 OpenSpec 实施计划,用 `pt_decision.needed` 决定是否需要 PT。绝不执行。 | 任务工具 + 规格查看,只读(仅写 `openspec/changes/`) |
204
+ | **PT** | 计划评审者 | 对 P 的计划做只读评审(需求对齐、完整性、逻辑)。输出裁决 + 问题清单。 | 任务工具 + 规格查看,**只读 ToolGuard** |
205
+ | **W** | 知识官 | W2/W3 知识库同步(`w:kb`)。绝不碰代码/git。 | 任务工具 + 远程 `wiki_search/read/write` / 本地 `skill`→llm-wiki + 只读规格查看 |
206
+ | **D** | 执行者 | *唯一*写代码的角色:worktree → 实现 → 验证 → `[AI-GEN]` 提交 → 推送特性分支(合入规格声明的目标分支由 system 在 DT 通过后执行)。 | 任务工具 + wiki 只读 + bash/fs/run_code(完整开发面)+ subagent(spawn/fork/list-agents)+ goal |
207
+ | **DT** | 实现评审者 | 实证验证 D 的工作(test/build/typecheck/diff/git + open-code-review),把评审页写入知识库。对仓库只读。 | 任务工具 + wiki 读写(评审命名空间)+ bash/fs/run_code,**只读 ToolGuard** |
208
+
209
+ ### 护栏详解
210
+
211
+ #### 权限矩阵
147
212
 
148
213
  `can(action, actor, task, { boundTaskId })` 定义于 `src/domain/permissions.ts`。
149
214
  "Bound" 表示该 actor 是*针对那个精确任务*派生的角色 agent 会话(`boundTaskId === task.id`,
@@ -174,14 +239,14 @@ dsh plugin --profile <name> add ./dsh-swarm
174
239
 
175
240
  - **主会话不能执行。** 它只拿到 `kanban_show`/`kanban_list`/`kanban_comment` +
176
241
  `spec_card_view` + `kanban_route` —— 绝无 `kanban_create`/`kanban_complete`/
177
- `kanban_block`。建链/建规格只经 `/plan:`+`/openspec:`;GUI 只观察与变更任务状态,
242
+ `kanban_block`。建链/建规格只经蜂群模式意图或 `/plan:`+`/openspec:`;GUI 只观察与变更任务状态,
178
243
  从不建链/建任务——"谁决定运行什么"保持显式、可审计。
179
244
  - **会话绑定阻止跨任务越权**(绑定到任务 A 的 W agent,即使任务 B 同为 W 任务,也
180
245
  不能 complete/block 任务 B);DT 的写入被矩阵之上的 ToolGuard 限定在
181
246
  `projects/<repoSlug>/<chain>/review/` 命名空间;且任何角色 agent 都不能批准规格、解除阻塞或
182
247
  确认审计——这些是人类信任锚;`system` 只做机械性记账。
183
248
 
184
- ### 交付契约(上游欠下游)
249
+ #### 交付契约(上游欠下游)
185
250
 
186
251
  每个相位的交接必须携带下游真正会读到的键(`src/domain/delivery-contract.ts`)。
187
252
  缺键会立即阻塞当前角色的卡(且编排者不会在阻塞的父任务上建下游卡):
@@ -193,7 +258,7 @@ dsh plugin --profile <name> add ./dsh-swarm
193
258
  | D(`d:execute`) | `changed_files` +(`commit_hash` 或 `push`)——`hasDeliveryEvidence`;`branch`(特性分支)是合并闸门的期望输入,非硬性完成阻塞项;`tdd`(`test_files` 或 `skipped.reason`,二选一) |
194
259
  | PT / DT | `review_evidence`(schema 合法)——`validateReviewEvidence` |
195
260
 
196
- ### TDD 硬闸(证据门槛)
261
+ #### TDD 硬闸(证据门槛)
197
262
 
198
263
  D 只有带 `tdd` 才能完成——`test_files`(含 `test_first`)或 `skipped.reason`
199
264
  (二选一,见 `delivery-evidence.ts`)。DT 的 `review_evidence` 必须携带 `tdd`;
@@ -201,27 +266,27 @@ D 只有带 `tdd` 才能完成——`test_files`(含 `test_first`)或 `skipp
201
266
  (见 `review-evidence.ts`)。这让"测试确实跑过、且先写测试"成为机器校验的属性,
202
267
  而非一句声明。
203
268
 
204
- ### 阶段 0 规划清单
269
+ #### 阶段 0 规划清单
205
270
 
206
- `/plan:` 跑只读规划会话(`grill-me` → `planning_prefetch` → `planning_checklist_save`,
271
+ 规划期跑只读规划会话(`grill-me` → `planning_prefetch` → `planning_checklist_save`,
207
272
  见 `planning-driver.ts`)。清单携带结构化 manifest(仓库事实 + 文件基线,见
208
- `prefetch-manifest.ts`);非法 manifest 阻塞保存,`/openspec:` 把清单以 `file-prefetch`
273
+ `prefetch-manifest.ts`);非法 manifest 阻塞保存,建链时把清单以 `file-prefetch`
209
274
  + `kb` 附件挂到规格卡(见 `prefix-router.ts`)。
210
275
 
211
- ### 评审质量链
276
+ #### 评审质量链
212
277
 
213
278
  - **P** 完成后,仅当 P 的交接交付 `pt_decision.needed = true` 时才创建 **PT** 卡;
214
279
  编排者从不覆盖该判定(V 只负责建卡)。
215
280
  - **D** 完成后**总是**创建 **DT** 卡。
216
281
  - **PT/DT 只读**:ToolGuard 机械性拒绝写仓库源码、git 变更,以及(对 DT)评审命名空间
217
282
  之外的 wiki 写入。
218
- - **DT 评审引擎**:`open-code-review`(ocr,委派模式,diff `--from <目标分支> --to <特性分支>`)
219
- 回退 `superpowers code-review` → 两者都不可用才 block `review-tool-unavailable`。
283
+ - **DT 评审引擎**:`open-code-review`(ocr,双模:委托/托管,见[评审引擎(ocr)](#评审引擎ocr));
284
+ DT 卡启动前自动探活,ocr 未安装即 block `review-tool-unavailable`(原因注明可在 GUI 安装),不消耗重试。
220
285
  - `review_evidence` 必须通过 `validateReviewEvidence`,否则评审卡无法完成:PT 需要
221
286
  verdict + issues + 计划引用;DT 额外需要 test(通过时退出码 0)、build/typecheck、
222
287
  lint、非空 diff、git、ocr/回退结论,以及 `tdd`。
223
288
 
224
- ### 返工(评审失败)
289
+ #### 返工(评审失败)
225
290
 
226
291
  评审失败**从不改写** `done` 卡。系统改为记录 `review/failed`,创建**返工任务**
227
292
  (`[返工] ...`),继承源会话(`resumeSessionId`)、`reviewAttempt + 1`,初始为
@@ -229,7 +294,7 @@ D 只有带 `tdd` 才能完成——`test_files`(含 `test_first`)或 `skipp
229
294
  达到 `maxReworksPerRole`(PT 2 / DT 3)时,系统记录 `review/gave-up` 并发布
230
295
  `[review-final]` 证据链评论;管线停在评审阶段等待人类介入。
231
296
 
232
- ### 故障恢复
297
+ #### 故障恢复
233
298
 
234
299
  两条正交的故障路径,都可人工恢复:
235
300
 
@@ -246,7 +311,7 @@ D 只有带 `tdd` 才能完成——`test_files`(含 `test_first`)或 `skipp
246
311
  则 block `model-unavailable` 等待人类。单个挂起的 V 唤醒不会卡死调度器——每次派发
247
312
  都被包在超时里。
248
313
 
249
- ### 链路完成:审计闸门 + 合并闸门
314
+ #### 链路完成:审计闸门 + 合并闸门
250
315
 
251
316
  机械性链路完成规则触发时,两个闸门在 `chain/completed` 钩子中运行:
252
317
 
@@ -261,9 +326,7 @@ D 只有带 `tdd` 才能完成——`test_files`(含 `test_first`)或 `skipp
261
326
  (checkout/merge/push 失败,例如冲突)。失败绝不抛错——坏合并*不执行*,这是安全方向;
262
327
  人类事后可修复。
263
328
 
264
- ---
265
-
266
- ## 事件溯源与领域模型
329
+ ### 事件溯源与领域模型
267
330
 
268
331
  每次状态变更都追加到 `<storageDir>/events.jsonl`,每行一个 JSON 事件。`seq` 由存储
269
332
  分配(每次追加时从文件尾部重读,并发实例永不冲突)。**轨迹即事件日志本身**;重启回放
@@ -289,9 +352,7 @@ unblocked, failed, archived, renamed)、`review/*`(passed, failed, gave-up
289
352
  服务通过串行队列发布事件(先落盘再发布),订阅方(SSE)按序收到每个事件且恰好一次。
290
353
  UI 与调度器消费的是同一份持久化事件——不存在第二个真相源。
291
354
 
292
- ---
293
-
294
- ## Web 客户端(Workflow 看板标签页)
355
+ ### Web 客户端(Workflow 看板标签页)
295
356
 
296
357
  注册为第三个 `conversation.view` 槽位的浏览器半 React 标签页(`id=kanban`、`order=20`,
297
358
  位于 对话 与 轨迹 之后)。它**不**注册 shell 级浮层、侧栏或详情抽屉。
@@ -310,9 +371,7 @@ UI 与调度器消费的是同一份持久化事件——不存在第二个真
310
371
  格式(与 `dsh-client-*` 相同的约定)。把 dsh-swarm 加入 web profile 会自动把它嵌入
311
372
  `__DSH_BOOT__`。
312
373
 
313
- ---
314
-
315
- ## 架构
374
+ ### 架构
316
375
 
317
376
  五层结构,领域层**不依赖任何 DSH**,因此可以被完全单测并独立回放。
318
377
 
@@ -335,7 +394,7 @@ flowchart TB
335
394
 
336
395
  subgraph Integration ["integration (cordis)"]
337
396
  TOOLS["tools: kanban_* / spec_card_* / wiki_* / prefetch_* / kanban_route"]
338
- ROUTES["prefix-router + planning-driver (/plan: /openspec:)"]
397
+ ROUTES["prefix-router + planning-driver (/plan: /openspec: + intent)"]
339
398
  HTTP["kanban-http + kanban-sse (/kanban/board, /kanban/events, /kanban/action)"]
340
399
  end
341
400
 
@@ -349,8 +408,8 @@ flowchart TB
349
408
  end
350
409
 
351
410
  subgraph Roles ["roles/ + personas/"]
352
- PRESETS["preset-installer (6 trimmed presets)"]
353
- TOOLSETS["toolsets (per-role tool faces + write guards)"]
411
+ PRESETS["preset-installer (6 role presets + swarm)"]
412
+ TOOLSETS["toolsets (per-role tool faces + write guards + swarm hard gate)"]
354
413
  WK["wiki-worker (W prefetch worker)"]
355
414
  end
356
415
 
@@ -375,7 +434,7 @@ flowchart TB
375
434
  EC --> KS
376
435
  ```
377
436
 
378
- ### 各层职责
437
+ #### 各层职责
379
438
 
380
439
  - **领域层**(`src/domain/`)—— 整个业务模型,纯 TypeScript:事件存储、状态机、投影、
381
440
  权限矩阵、交付/评审/manifest 校验器,以及把来自工具、CLI、UI 的每次写入统一路由到
@@ -385,18 +444,16 @@ flowchart TB
385
444
  - **调度层**(`src/dispatcher/`)—— 事件唤醒、相位编排、一次性 agent 运行器(persona
386
445
  preset 挂载、模型候选链、ToolGuard 安装)、看门狗、链路审计器、合并闸门。
387
446
  - **角色层**(`src/roles/`、`personas/`)—— 安装到 `$DSH_HOME/.agent-presets/` 的裁剪
388
- preset、每角色工具装配、写保护逻辑。
447
+ preset(含蜂群模式 `swarm`)、每角色工具装配、写保护逻辑与蜂群会话硬闸。
389
448
  - **知识库层**(`src/wiki/`)—— 面向 wiki-vault 的轻量 HTTP 客户端。
390
449
 
391
- ---
392
-
393
- ## 开发
450
+ ### 开发
394
451
 
395
452
  质量闸门(见 `AGENTS.md`):
396
453
 
397
454
  ```bash
398
455
  npm run typecheck # tsc -p tsconfig.json --noEmit (0 errors)
399
- npm test # npx vitest run (52 个文件 / 450 用例,全绿)
456
+ npm test # npx vitest run (全绿)
400
457
  npm run build # tsc -p tsconfig.build.json + build:client (lib/client.js)
401
458
  ```
402
459
 
@@ -408,12 +465,11 @@ python tests/e2e/gui-check.py --url http://127.0.0.1:3080/
408
465
 
409
466
  > 部署到运行中的 DSH 实例需要插件重载/重启;仅构建不会热重载正在运行的插件。
410
467
 
411
- ---
412
-
413
- ## 路线图与已知限制
468
+ ### 已实现与已知限制
414
469
 
415
- ### 已实现(v0.1.0)
470
+ #### 已实现(v0.1.0)
416
471
 
472
+ - [x] **蜂群模式**:自然语言意图识别(plan/openspec/learning/send)+ 确认闸 + 主会话只读硬闸
417
473
  - [x] 事件溯源领域 + 确定性状态机(红队回放)
418
474
  - [x] 6 角色相位管线 + 裁剪 preset + 会话绑定权限
419
475
  - [x] 交付契约 + 评审证据闸门 + 返工生命周期
@@ -426,25 +482,16 @@ python tests/e2e/gui-check.py --url http://127.0.0.1:3080/
426
482
  - [x] 模型候选链:静默回退 + High 推理强度
427
483
  - [x] 实时 SSE 看板标签页(对话 → 轨迹 → 看板)
428
484
 
429
- ### 规划中
430
-
431
- - [ ] 每任务预算护栏(最大 token / 工具调用 / 墙钟时间)与按故障分类的退避
432
- - [ ] 可复现的 DT 验证(回放命令 + stdout 证据)与硬标记上的双模型仲裁
433
- - [ ] 结构化指标 + 每链路审计轨迹聚合
434
- - [ ] V 上下文压缩 / 状态摘要注入 + 会话自愈
435
- - [ ] 多 agent 流程的端到端契约测试框架
436
- - [ ] 更多人工介入点(推送前 / 硬标记时)+ 系统辅助硬标记检测
437
-
438
- ### 已知限制
485
+ #### 已知限制
439
486
 
487
+ - **蜂群模式意图识别依赖模型自判**:误判有确认闸兜底(未确认不建链),非零误判风险。
440
488
  - **写保护是字符串启发式,不是硬隔离。** PT/DT ToolGuard 依赖路径/命令正则,评审者
441
489
  没有 git 凭据;这是软约束加审计轨迹,而非挂载级沙箱。
442
- - **验证环境中没有 `open-code-review` CLI**:回退路径(superpowers `code-review`)
443
- 已实现并测试,但 ocr 委派模式输出解析有待在装有 ocr 的机器上验证。
444
- - **评审证据是存在性检查,而非回放证明。** 字段必须存在且格式合法;证明测试确实运行
445
- 在路线图上。
490
+ - **`open-code-review`(ocr)按机器可选**:未安装时链上评审在 DT 启动前即 block
491
+ `review-tool-unavailable` 并给出安装指引(可在 GUI 一键安装),不消耗重试。
492
+ - **评审证据是存在性检查,而非回放证明。** 字段必须存在且格式合法;证明测试确实运行尚未实现。
446
493
  - **配置默认值里只有一个 wiki-vault 主机**——请把 `wikiVault.baseUrl` 指向你的部署。
447
- - **PT 建卡依赖 P 自报的 `pt_decision.needed`**——从仓库信号做系统辅助检测在路线图上。
494
+ - **PT 建卡依赖 P 自报的 `pt_decision.needed`**——从仓库信号做系统辅助检测尚未实现。
448
495
 
449
496
  ---
450
497
 
@@ -1,22 +1,44 @@
1
1
  import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
2
- import { createConfigStore, type EditableSnapshot } from './config-store.js';
2
+ import { createConfigStore, type EditableSnapshot, type OcrStatus } from './config-store.js';
3
3
  import { ConfigSelect } from './ConfigSelect.js';
4
4
 
5
5
  const ROLES = ['v', 'p', 'w', 'd', 'pt', 'dt'] as const;
6
6
  type Model = { provider: string; model: string; reasoningEffort: string };
7
7
 
8
- /** T9:settings.section 配置面板——本地 draft 编辑态、下拉选中即存、级联下拉、来源徽章、右下角重置。 */
8
+ /** ocr 卡横幅文案:null=已安装无横幅;安装中不显示横幅(spinner 行替代)。 */
9
+ function ocrBanner(ocr: OcrStatus | null, installing: boolean): string | null {
10
+ if (installing) return null;
11
+ if (ocr === null) return 'ocr 状态未知——委托/托管评审可能不可用';
12
+ if (!ocr.installed) return 'ocr 未安装——委托/托管评审均不可用';
13
+ return null;
14
+ }
15
+
16
+ /** settings.section 配置面板——本地 draft 编辑态、下拉选中即存、级联下拉、来源徽章。
17
+ * 第三卡「评审引擎(ocr)」:安装单飞(组件持轮询定时器 1.5s)、模式切换、托管提供方/模型级联、应用到 ocr。
18
+ * 无「重置」:各用户模型配置不同,无有意义的公共默认值可回退(历史教训:重置按钮已移除,勿再加回)。 */
9
19
  export function ConfigSection({ fetchImpl, close }: { fetchImpl?: typeof fetch; close: () => void }) {
10
20
  const storeRef = useRef<ReturnType<typeof createConfigStore> | null>(null);
11
21
  if (!storeRef.current) storeRef.current = createConfigStore(fetchImpl ?? ((...args: Parameters<typeof fetch>) => fetch(...args)));
12
22
  const store = storeRef.current;
13
23
  const state = useSyncExternalStore(store.subscribe, store.get);
14
24
  const [draft, setDraft] = useState<EditableSnapshot | null>(null);
25
+ const [wireResult, setWireResult] = useState<{ ok: boolean; log: string } | null>(null);
15
26
 
16
27
  useEffect(() => {
17
28
  void store.load().then(() => setDraft(store.get().effective));
29
+ void store.loadOcrStatus(); // 与 load 并行;失败静默置 null = 未知态
18
30
  }, [store]);
19
31
 
32
+ const installing = state.install.phase === 'running';
33
+ // 安装轮询由组件持有:进入 running 立即查一次,此后 1.5s 间隔;终态/卸载由 effect 清理定时器
34
+ useEffect(() => {
35
+ if (!installing) return;
36
+ const tick = () => { void store.loadInstallState(); };
37
+ tick();
38
+ const timer = setInterval(tick, 1500);
39
+ return () => clearInterval(timer);
40
+ }, [store, installing]);
41
+
20
42
  if (!draft) return <div className="dsh-kb-config">加载中…</div>;
21
43
 
22
44
  const commit = (next: EditableSnapshot) => { setDraft(next); void store.save(next); };
@@ -24,8 +46,18 @@ export function ConfigSection({ fetchImpl, close }: { fetchImpl?: typeof fetch;
24
46
  const prev = draft.roles.models[r] ?? { provider: '', model: '', reasoningEffort: '' };
25
47
  commit({ ...draft, roles: { models: { ...draft.roles.models, [r]: { ...prev, ...patch } } } });
26
48
  };
49
+ const commitReviewEngine = (mode: 'delegate' | 'managed', managed: { provider: string; model: string }) => {
50
+ commit({ ...draft, reviewEngine: { mode, managed } });
51
+ };
27
52
  const onBlur = () => { void store.save(draft); };
28
53
 
54
+ const ocr = state.ocrStatus;
55
+ const banner = ocrBanner(ocr, installing);
56
+ // ocr 不可用(未安装/未知/安装中)时,卡内其余字段整体置灰
57
+ const ocrBlocked = installing || ocr === null || !ocr.installed;
58
+ const re = draft.reviewEngine;
59
+ const managedFieldsEnabled = !ocrBlocked && re.mode === 'managed';
60
+
29
61
  return (
30
62
  <div className="dsh-kb-config">
31
63
  <section className="dsh-kb-config__card">
@@ -62,9 +94,68 @@ export function ConfigSection({ fetchImpl, close }: { fetchImpl?: typeof fetch;
62
94
  );
63
95
  })}
64
96
  </section>
65
- <div className="dsh-kb-config__footer">
66
- <button onClick={() => { void store.reset().then((r) => setDraft(r.effective)); }}>重置</button>
67
- </div>
97
+ <section className="dsh-kb-config__card">
98
+ <h3>评审引擎(ocr)</h3>
99
+ {installing && (
100
+ <div className="dsh-kb-config__installing">
101
+ <span className="dsh-kb-config__installing-spinner" aria-hidden="true" />
102
+ <span>正在安装 ocr…(npm 全局安装,约 1-2 分钟)</span>
103
+ <button type="button" onClick={() => { void store.cancelInstall(); }}>取消</button>
104
+ </div>
105
+ )}
106
+ {banner && <div className="dsh-kb-config__error" role="alert">{banner}</div>}
107
+ {state.install.phase === 'failed' && (
108
+ <div className="dsh-kb-config__error">安装失败:{state.install.log.slice(0, 200) || 'npm 全局安装未成功,可重试'}</div>
109
+ )}
110
+ {state.install.phase === 'cancelled' && (
111
+ <div className="dsh-kb-config__error">安装已取消,可重新发起</div>
112
+ )}
113
+ {ocr?.installed && <div className="dsh-kb-config__ocr-ok">✓ ocr {ocr.version} 已安装</div>}
114
+ {!installing && (!ocr || !ocr.installed) && (
115
+ <button type="button" onClick={() => { void store.startInstall(); }}>安装 ocr</button>
116
+ )}
117
+ <label className="dsh-kb-config__label">评审模式</label>
118
+ <ConfigSelect value={re.mode} disabled={ocrBlocked} placeholder="选择评审模式"
119
+ options={[
120
+ { value: 'delegate', label: '委托(DT 自己的模型评审,零 key)' },
121
+ { value: 'managed', label: '托管(ocr 用下选模型评审)' },
122
+ ]}
123
+ onChange={(v) => { if (v !== re.mode) commitReviewEngine(v as 'delegate' | 'managed', re.managed); }} />
124
+ <span className="dsh-kb-config__src">{state.sources['reviewEngine.mode'] === 'override' ? '已覆盖' : '继承'}</span>
125
+ <label className="dsh-kb-config__label">提供方</label>
126
+ <ConfigSelect value={re.managed.provider} disabled={!managedFieldsEnabled} placeholder="选择提供方"
127
+ options={state.catalog.providers.map((p) => ({ value: p.id, label: p.name }))}
128
+ onChange={(v) => { if (v !== re.managed.provider) commitReviewEngine(re.mode, { provider: v, model: '' }); }} />
129
+ <label className="dsh-kb-config__label">模型</label>
130
+ <ConfigSelect value={re.managed.model} disabled={!managedFieldsEnabled} placeholder="选择模型"
131
+ options={(state.catalog.models[re.managed.provider] ?? []).map((x) => ({ value: x.id, label: x.name }))}
132
+ onChange={(v) => { if (v !== re.managed.model) commitReviewEngine(re.mode, { ...re.managed, model: v }); }} />
133
+ {ocr?.managedReady === false && re.mode === 'managed' && (
134
+ <div className="dsh-kb-config__ocr-warn">托管未就绪:选好提供方与模型后点『应用到 ocr』写入 ocr 配置;也可在终端手动 ocr config provider(委托模式不受影响)</div>
135
+ )}
136
+ {re.mode === 'managed' && re.managed.provider && re.managed.model && (
137
+ <button type="button" disabled={ocrBlocked}
138
+ onClick={() => { void store.wireOcr(re.managed.provider, re.managed.model).then(setWireResult); }}>
139
+ 应用到 ocr
140
+ </button>
141
+ )}
142
+ {wireResult && (
143
+ <div className={wireResult.ok ? 'dsh-kb-config__ocr-ok' : 'dsh-kb-config__error'}>
144
+ {wireResult.ok ? '✓ 已写入 ocr(dsh-managed)' : '写入失败:' + wireResult.log.slice(0, 200)}
145
+ </div>
146
+ )}
147
+ <span className="dsh-kb-config__help">托管评审由 ocr 调用上方选定的提供方/模型执行;接入点由系统自动写入 ocr 自定义配置(dsh-managed),API key 从 dsh 模型配置解析,不在面板明文展示</span>
148
+ <span className="dsh-kb-config__help">
149
+ 官方文档:<a href="https://open-codereview.ai/docs/installation" target="_blank" rel="noreferrer">安装指南</a>
150
+ {' · '}
151
+ <a href="https://open-codereview.ai/docs/configuration" target="_blank" rel="noreferrer">模型配置</a>
152
+ {' · '}
153
+ <a href="https://open-codereview.ai/docs/delegate" target="_blank" rel="noreferrer">委托模式说明</a>
154
+ </span>
155
+ {ocr?.kbMode === 'local' && (
156
+ <span className="dsh-kb-config__help">本地知识库模式下独立评审报告暂不落 wiki,请直接留存对话</span>
157
+ )}
158
+ </section>
68
159
  {state.error && <div className="dsh-kb-config__error">{state.error}</div>}
69
160
  </div>
70
161
  );