dsh-yolo-mode 0.4.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/CHANGELOG.md +62 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/lib/bridge-entry.js +51 -0
- package/lib/client/index.js +1268 -0
- package/lib/index.js +306 -0
- package/lib/judge.js +240 -0
- package/lib/policy.js +363 -0
- package/lib/remote.js +489 -0
- package/lib/settings.js +102 -0
- package/lib/state.js +92 -0
- package/package.json +73 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本项目遵循 [Semantic Versioning](https://semver.org/)。
|
|
4
|
+
|
|
5
|
+
## [0.4.0] - 2026-08-14
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
|
|
9
|
+
- **模型下拉选择**:设置页的 judge `provider` / `model` 改为下拉框,选项来自 Harness 模型配置(`connection.api.llm`),切换 provider 联动清空 model。
|
|
10
|
+
- **每预设默认裁判提示词**:`defaultJudgePromptFor(preset)` 为六预设提供默认裁判提示词(strict 最保守、balanced 通用、permissive 宽松、custom 按层级表),`judge.systemPrompt` 留空时自动选用。
|
|
11
|
+
- **预设预填充**:切换预设时自动把该预设的默认提示词与层级表填入 `systemPrompt` / `levels`(选 `custom` 留空);`statusView` 返回 `presetDefaults`。
|
|
12
|
+
|
|
13
|
+
## [0.3.0] - 2026-08-14
|
|
14
|
+
|
|
15
|
+
### 变更(UI 重做,对齐 dsh-plugin-subagent-director 参考架构)
|
|
16
|
+
|
|
17
|
+
- **独立桥接条目**:新增 `./bridge` 入口(`lib/bridge-entry.js`,`inject: ['webServer','settings']`),自发布 `/yolo-mode` 前缀路由。修复 v0.2.0 根因——宿主 `webServer` 只能经 `inject` 取得,树外插件 `ctx.get('webServer')` 永远拿不到(参考项目已实测)。
|
|
18
|
+
- **RPC 信封桥**:`/yolo-mode` 上实现 `settingsView` / `settingsMutate` / `statusView` 三端点,语义镜像 connection RPC 通道(loopback 围栏、乐观 revision 冲突、redacted 视图、路径 op)。
|
|
19
|
+
- **settings 规范布线**:改用 `installSettingsSection`(`@deepseek-ai/dsh-settings`),行 config 作为 `base` 层;`effectiveConfig() = normalizeConfig(resolved)`;移除手写 mergeConfig。
|
|
20
|
+
- **客户端 rolldown 构建**:`src/client/*.js` → `scripts/build-client.mjs`(rolldown,external react/@deepseek-ai)→ `lib/client/index.js`;客户端 `inject: ['slots','locale','connection','remote']`,locale 双语字典、`bindSnapshotSelector` 快照 store、`connection.rpc.call` 走桥、revision 冲突机。
|
|
21
|
+
- **主条目 inject**:`export const inject = ['llm','settings']`(命名导出插件,非 default 函数)。
|
|
22
|
+
|
|
23
|
+
## [0.2.0] - 2026-08-14
|
|
24
|
+
|
|
25
|
+
### 新增
|
|
26
|
+
|
|
27
|
+
- **客户端 UI(双面包)**:`dsh-yolo-mode` 声明 `dsh.client` 并导出 `lib/client.js`(手写 ModuleLoader 工厂,零构建),提供三处界面:
|
|
28
|
+
- 输入栏左侧状态 chip(`conversation.input.left`,显示 `YOLO <preset>`,点击开关弹窗);
|
|
29
|
+
- 全局面板(`shell.overlay`):运行统计(总审批/放行/拒绝/转人工)+ 最近 20 条决策表 + 刷新;
|
|
30
|
+
- 设置页(`settings.section`,'YOLO 审批'):预设/生效模式/judge 参数/levels JSON 的在线编辑与保存。
|
|
31
|
+
- **HTTP API**:`GET /plugins/yolo-mode/status`(状态与统计)、`POST /plugins/yolo-mode/config`(配置校验后持久化),由宿主 webServer 路由提供。
|
|
32
|
+
- **settings 集成**:settings 命名空间 `yolo-mode`(自由 JSON 分区,落盘 settings.yaml);`effectiveConfig()` 每次裁决将插件行 config 与 settings 分区合并规范化,配置改动即时生效(无需重启)。
|
|
33
|
+
- **内存统计**:每次裁决累计 total/allowed/rejected/delegated 与最近 20 条决策环形缓冲。
|
|
34
|
+
- **judge 实例缓存**:按 judge 配置键缓存裁判实例,配置变化自动重建。
|
|
35
|
+
|
|
36
|
+
### 变更
|
|
37
|
+
|
|
38
|
+
- package.json:version 0.2.0;exports 增加 `./client`;peerDependencies 增加 `react`、`@deepseek-ai/dsh-client-runtime`、`@deepseek-ai/dsh-client-ui-slots`、`@deepseek-ai/schemastery`。
|
|
39
|
+
|
|
40
|
+
### 已知限制
|
|
41
|
+
|
|
42
|
+
- 已挂载插件行的**模块代码**更新需要重启 DSH 才能重新导入(patch 热重载只覆盖新行挂载与 config 变更);v0.1.0 → v0.2.0 升级后请重启 DSH 并刷新浏览器。
|
|
43
|
+
|
|
44
|
+
## [0.1.0] - 2026-08-14
|
|
45
|
+
|
|
46
|
+
### 新增
|
|
47
|
+
|
|
48
|
+
- **宿主侧审批应答插件**:作为 `approval/request` seam 的应答者,在会话处于可写沙箱模式且审批策略为 `ask` 时,用 LLM 自动裁决沙箱升权申请。
|
|
49
|
+
- **抢占注册**:以 `ctx.on('approval/request', handler, { prepend: true })` 抢占监听列表头,抢在人工应答者之前裁决。
|
|
50
|
+
- **前置门槛**:按 `ctx.sandboxPolicy.resolve({session})` 校验当前有效模式,仅处理位于 `modes` 列表(默认 `['workspace-write']`)内的会话。
|
|
51
|
+
- **内置预设**:`off` / `strict` / `balanced`(默认)/ `permissive` / `yolo` / `custom`,各含 `workspace-write` 与 `danger-full-access` 的处置策略与失败 / 不确定回退。
|
|
52
|
+
- **自定义层级**:`levels` 支持目标模式、`error` / `unsure` 回退、以及逐工具 `tools.<toolName>` 覆盖,`custom` 预设全字段开放。
|
|
53
|
+
- **LLM 裁判封装**:基于 `@deepseek-ai/dsh-llm`(`BlockAssembler` / `createUserMessage` / `llm.stream`)与 `@deepseek-ai/dsh-timeout`(`deadline`)实现,含并发信号量上限与超时。
|
|
54
|
+
- **裁决策略映射**:`allow → allowed-once`、`deny → rejected`、`delegate → next()`、`judge → 裁判`;失败 / 不确定按预设 fail-closed 回退。
|
|
55
|
+
- **JSONL 审计**:`ctx.logger` + 文件(默认 `%TEMP%/dsh-yolo/judge.log`)记录每次裁决的明细。
|
|
56
|
+
- **纯函数策略层** `lib/policy.js`(零依赖,可单测)与单元测试 `test/policy.test.mjs`、`test/judge.test.mjs`。
|
|
57
|
+
|
|
58
|
+
### 安全
|
|
59
|
+
|
|
60
|
+
- fail-closed:任何错误、超时、非法输出、工具块、并发溢出路径均不放行。
|
|
61
|
+
- 裁判 prompt 与 agent 上下文隔离,防审批回环自批准。
|
|
62
|
+
- 默认预设为 `balanced`(不确定 → 转人工),不默认启用 `permissive` / `yolo`。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-yolo-mode contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# dsh-yolo-mode
|
|
2
|
+
|
|
3
|
+
> DeepSeek Harness 插件:用大模型自动裁决沙箱**升权申请**(LLM-powered auto-approval)。
|
|
4
|
+
|
|
5
|
+
当会话处于可写沙箱模式、审批策略为 `ask` 时,`dsh-yolo-mode` 拦截 `escalate sandbox to ...` 升权申请,按你选择的**预设**或**自定义权限层级**由大模型裁决「放行 / 拒绝 / 转人工」。任何不确定或失败路径都不会放行(**fail-closed**)。
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/dsh-yolo-mode)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 功能特性
|
|
13
|
+
|
|
14
|
+
- **LLM 自动裁决**:在 `workspace-write` 会话中,对 `danger-full-access` / `workspace-write` 升权申请调用大模型分级(`allow / deny / unsure`),确定时免去人工弹窗。
|
|
15
|
+
- **内置预设**:`off` / `strict` / `balanced`(默认)/ `permissive` / `yolo` / `custom`,覆盖从「全部转人工」到「确定性全放行」的完整光谱。
|
|
16
|
+
- **自定义层级**:`levels` 支持逐目标模式、逐工具(`tools.<toolName>`)的 `allow / judge / delegate / deny` 策略,及 `error` / `unsure` 回退。
|
|
17
|
+
- **每预设默认提示词**:`judge.systemPrompt` 留空时按预设自动选用对应裁判提示词(strict 最保守、permissive 宽松、custom 按层级表)。
|
|
18
|
+
- **图形化配置**:设置面板「YOLO 审批」页在线修改预设 / 生效模式 / 裁判模型(下拉选择)/ 层级表,保存即生效;输入栏状态 chip + 决策统计面板。
|
|
19
|
+
- **完整审计**:每次裁决落 JSONL 日志,含工具、目标模式、当前模式、理由、决策与结果。
|
|
20
|
+
- **fail-closed**:超时、非法输出、模型不可用、并发溢出等一切异常路径都回退为「拒绝」或「转人工」。
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
### 1. 安装插件包
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 从 npm
|
|
28
|
+
dsh plugin --profile web add dsh-yolo-mode
|
|
29
|
+
|
|
30
|
+
# 或从本地路径
|
|
31
|
+
dsh plugin --profile web add <项目绝对路径>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. 追加两行到 profile patch
|
|
35
|
+
|
|
36
|
+
编辑 `$DSH_HOME/profiles/web/cordis.patch.yml`,追加两个 `insert` 元素(主条目 + 设置桥接条目):
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- insert:
|
|
40
|
+
- id: yolo-mode
|
|
41
|
+
name: dsh-yolo-mode
|
|
42
|
+
config:
|
|
43
|
+
preset: balanced
|
|
44
|
+
judge:
|
|
45
|
+
provider: <provider>
|
|
46
|
+
model: <model>
|
|
47
|
+
- insert:
|
|
48
|
+
- id: yolo-mode-bridge
|
|
49
|
+
name: dsh-yolo-mode/bridge
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
重启 DSH 并刷新浏览器后生效。
|
|
53
|
+
|
|
54
|
+
## 配置
|
|
55
|
+
|
|
56
|
+
插件行 `config` 全字段可选,未填按默认值:
|
|
57
|
+
|
|
58
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `preset` | `off\|strict\|balanced\|permissive\|yolo\|custom` | `balanced` | 使用的预设;`custom` 时以 `levels` 为准 |
|
|
61
|
+
| `modes` | `string[]` | `['workspace-write']` | 会话有效沙箱模式 ∈ 此列表时才介入(`read-only` / `workspace-write` / `danger-full-access`) |
|
|
62
|
+
| `levels` | `object` | `{}` | 权限层级表;`levels.tools.<toolName>` 对任意预设逐工具覆盖 |
|
|
63
|
+
| `judge.provider` | `string` | `''` | 裁判模型 provider;空 = 未配置(judge 决策按错误回退) |
|
|
64
|
+
| `judge.model` | `string` | `''` | 裁判模型;与 provider 同非空才启用裁判 |
|
|
65
|
+
| `judge.systemPrompt` | `string` | `''` | 裁判 system prompt;空 = 按预设取默认 |
|
|
66
|
+
| `judge.timeoutMs` | `number` | `20000` | 单次裁判超时(毫秒) |
|
|
67
|
+
| `judge.maxTokens` | `number` | `256` | 裁判输出最大 token 数 |
|
|
68
|
+
| `judge.concurrency` | `number` | `2` | 并发裁判上限,溢出按错误回退 |
|
|
69
|
+
| `includeSubagents` | `boolean` | `true` | 子代理会话是否同样裁决 |
|
|
70
|
+
| `auditFile` | `string` | `''` | 审计日志路径;空 = `%TEMP%/dsh-yolo/judge.log` |
|
|
71
|
+
|
|
72
|
+
### 权限层级(`levels`)
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
levels:
|
|
76
|
+
workspace-write: judge # 目标模式 → 策略
|
|
77
|
+
danger-full-access: judge
|
|
78
|
+
error: delegate # 裁判错误回退
|
|
79
|
+
unsure: delegate # 裁判不确定回退
|
|
80
|
+
tools:
|
|
81
|
+
pwsh: delegate # 逐工具覆盖,优先级最高
|
|
82
|
+
write: allow
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
策略取值 `allow | judge | delegate | deny`;优先级:`levels.tools[toolName]` → 基础行(`custom` 时为 `levels[targetMode]`,其余预设为内置表)。
|
|
86
|
+
|
|
87
|
+
## 预设
|
|
88
|
+
|
|
89
|
+
| 预设 | `workspace-write` | `danger-full-access` | 失败回退 | 不确定回退 | 说明 |
|
|
90
|
+
|---|---|---|---|---|---|
|
|
91
|
+
| `off` | delegate | delegate | delegate | delegate | 不介入,全部转人工 |
|
|
92
|
+
| `strict` 严格 | judge | delegate | rejected | delegate | 仅裁决 `workspace-write`;`danger-full-access` 恒转人工 |
|
|
93
|
+
| `balanced` 均衡(默认) | judge | judge | delegate | delegate | 裁决全部升级目标;失败 / 不确定转人工 |
|
|
94
|
+
| `permissive` 宽松 | judge | judge | delegate | **allowed-once** | 裁决全部目标;不确定视为允许(慎用) |
|
|
95
|
+
| `yolo` | allow | allow | — | — | 确定性全放行,零 LLM 调用 |
|
|
96
|
+
| `custom` | 依 `levels` | 依 `levels` | 依 `levels.error` | 依 `levels.unsure` | 全字段开放 |
|
|
97
|
+
|
|
98
|
+
每预设默认裁判提示词(`judge.systemPrompt` 留空时自动选用):
|
|
99
|
+
|
|
100
|
+
| 预设 | 默认裁判立场 |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `off` / `yolo` | 不调用裁判 |
|
|
103
|
+
| `strict` | 最保守:`danger-full-access` 一律拒绝;仅最小范围 `workspace-write` 且理由极充分才允许 |
|
|
104
|
+
| `balanced` | 通用审计:只依据事实、防回环、存疑即 deny/unsure |
|
|
105
|
+
| `permissive` | 宽松:理由合理且范围可接受即倾向允许,仅明显破坏性 / 供应链风险拒绝 |
|
|
106
|
+
| `custom` | 按 `levels` 层级表裁决,存疑按 `levels.error` / `levels.unsure` 回退 |
|
|
107
|
+
|
|
108
|
+
## 界面
|
|
109
|
+
|
|
110
|
+
- **输入栏 chip**:显示 `YOLO <preset>`,点击弹出统计面板(总审批 / 放行 / 拒绝 / 转人工 + 最近 20 条决策)。
|
|
111
|
+
- **设置页**:「YOLO 审批」页在线修改预设、生效沙箱模式、裁判模型(provider / model 下拉,取自 Harness 模型配置)、层级表(JSON);切换预设时自动预填充该预设的默认提示词与层级表。
|
|
112
|
+
|
|
113
|
+
## 安全
|
|
114
|
+
|
|
115
|
+
- **fail-closed**:只有明确得到 `allow` 才返回一次性 `allowed-once`;其余一切路径拒绝或转人工。
|
|
116
|
+
- **防回环**:裁判 prompt 与 agent 上下文隔离,防止模型借 Web 审批回环自批准 `danger-full-access`。
|
|
117
|
+
- **不改写策略**:仅在 `ask` 策略下作为应答者,不改变 DSH 的沙箱 / 审批词汇。
|
|
118
|
+
- **默认保守**:默认预设 `balanced`(不确定转人工),不默认启用 `permissive` / `yolo`。
|
|
119
|
+
- **审计**:每次裁决落一行 JSONL,含 `{time, sessionId, origin, toolName, callId?, targetMode, currentMode, justification, decision, outcome, reason?}`。
|
|
120
|
+
|
|
121
|
+
## 开发
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npm test # node --test 全量测试
|
|
125
|
+
npm run build # 构建客户端 bundle(rolldown)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 许可
|
|
129
|
+
|
|
130
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-yolo-mode —— 独立桥接条目(lib/bridge-entry.js)
|
|
3
|
+
*
|
|
4
|
+
* 与主插件条目(lib/index.js)分离:因为宿主 webServer 服务只能经 cordis
|
|
5
|
+
* `inject` 拿到,树外插件经 ctx.get('webServer') 永远拿不到(参考项目头注已
|
|
6
|
+
* 实测验证;dsh-client-connection 自身也以 inject=["webServer"] 挂载)。
|
|
7
|
+
*
|
|
8
|
+
* 本条目在 Web profile 中经 patch 行 `- insert: [{ id: yolo-mode-bridge,
|
|
9
|
+
* name: dsh-yolo-mode/bridge }]` 挂载,注入 webServer + settings,安装
|
|
10
|
+
* `/yolo-mode` 设置桥。headless profile 无 webServer,本条目永不激活,
|
|
11
|
+
* 主条目在该处不受影响。
|
|
12
|
+
*
|
|
13
|
+
* statusView 的 preset/judgeConfigured 无法读取主条目的 effectiveConfig,
|
|
14
|
+
* 故从 settings.describe 的 yolo-mode resolved view.value 推导
|
|
15
|
+
* (preset=value.preset、judgeConfigured=!!(value.judge?.provider&&value.judge?.model)),
|
|
16
|
+
* stats/recent 来自 lib/state.js 模块级单例(design.md §12.4)。
|
|
17
|
+
*
|
|
18
|
+
* @module lib/bridge-entry.js
|
|
19
|
+
*/
|
|
20
|
+
import { installYoloRemoteBridge, pickYoloNamespaceView } from './remote.js';
|
|
21
|
+
import { getStatusPayload } from './state.js';
|
|
22
|
+
|
|
23
|
+
/** Cordis 插件名(桥接条目)。 */
|
|
24
|
+
export const name = 'yolo-mode-bridge';
|
|
25
|
+
|
|
26
|
+
/** 所需服务:webServer(路由宿主)与 settings(数据 seam)。 */
|
|
27
|
+
export const inject = ['webServer', 'settings'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 组合桥接条目专用的 statusView 载荷装配:
|
|
31
|
+
* preset/judgeConfigured 从 settings.describe 的 resolved view 推导,
|
|
32
|
+
* stats/recent 读 lib/state.js 模块级单例。
|
|
33
|
+
* @param {object} settings settings 服务对象
|
|
34
|
+
* @returns {{preset:string, judgeConfigured:boolean, stats:object, recent:Array<object>}}
|
|
35
|
+
*/
|
|
36
|
+
function composeStatusPayload(settings) {
|
|
37
|
+
const view = pickYoloNamespaceView(settings.describe({ redactSecrets: true }));
|
|
38
|
+
const value = view && view.value && typeof view.value === 'object' ? view.value : {};
|
|
39
|
+
// state.getStatusPayload 已按要求以 value 的字形(preset + judge.provider/model)
|
|
40
|
+
// 装配 { preset, judgeConfigured, stats, recent } 并做 stats/recent 浅拷贝。
|
|
41
|
+
return getStatusPayload(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 注册 `/yolo-mode` 设置桥路由;卸载时清理。
|
|
46
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx
|
|
47
|
+
*/
|
|
48
|
+
export function apply(ctx) {
|
|
49
|
+
const settings = ctx.get('settings');
|
|
50
|
+
installYoloRemoteBridge(ctx, settings, () => composeStatusPayload(settings));
|
|
51
|
+
}
|