dsh-perm-gate 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- > **호환성 안내:** v1.0.0은 `ja` / `ko` 사전을 포함하지만, 공식 DSH의 `LocaleRuntime`은
15
+ > **호환성 안내:** v2.0.0은 `ja` / `ko` 사전을 포함하지만, 공식 DSH의 `LocaleRuntime`은
16
16
  > `zh` / `en`만 노출합니다(`LOCALE_IDS = ["zh", "en"]`). 기본 DSH에서 `ja` / `ko`를
17
17
  > 선택하면 `locale "<id>" is not registered` 오류가 발생합니다. `LOCALE_IDS`
18
18
  > (locale-settings.ts)와 `LOCALES` 라벨(client/index.ts)을 갱신한 DSH fork를 사용해
@@ -27,7 +27,12 @@
27
27
  > | DSH 버전 | 브랜치 | 버전 | npm 태그 |
28
28
  > | --- | --- | --- | --- |
29
29
  > | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
30
- > | 0.1.2-alpha.1+ (0.1.5-rc.2 포함) | `main` | `0.2.x` | `@latest` |
30
+ > | 0.1.2-alpha.1+ (0.1.5-rc.2 포함) | `main` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x`는 범위) |
31
+ >
32
+ > 버전 계열은 **DSH 라인**을 따릅니다(`1.x` = DSH ≤ 0.1.1, `2.x` = DSH 0.1.2+). 메이저
33
+ > 버전이 서로를 차단하므로 `^1.x` 설치가 `2.x`를 해석하는 일은 없고 그 반대도
34
+ > 마찬가지입니다. `engines.dsh`도 같은 경계를 적어 두지만 DSH는 이를 읽지 않으므로,
35
+ > 구버전 DSH를 `1.x`에 묶어 두는 것은 버전 범위와 dist-tag입니다.
31
36
  >
32
37
  > `@deepseek-ai/dsh-client-runtime`은 `0.1.2-alpha.1`에서 **제거**되었습니다 —
33
38
  > 단순히 이동한 것이 아닙니다. `legacy` 라인은 여전히 이를 통해 `ctx.slots`에
@@ -38,7 +43,7 @@
38
43
  > 모두에서 user-approval 서비스의 **개인** 메소드이므로 `typeof` 프로브를 통해
39
44
  > 읽으며, 누락되거나 예외 발생 시 "정책_unknown"으로 후퇴합니다.
40
45
 
41
- 버전 **1.0.0** — 변경 내역은 [한국어 changelog](./CHANGELOG.ko.md)를 참고하세요.
46
+ 버전 **2.0.0** — 변경 내역은 [한국어 changelog](./CHANGELOG.ko.md)를 참고하세요.
42
47
 
43
48
  DeepSeek Harness용 단일·자족적·결정론 우선·fail-closed 권한 게이트입니다.
44
49
 
@@ -75,10 +80,11 @@ DSH 안전 생태계에서는 이 역할이 `dsh-permission-rules` / `dsh-auto-m
75
80
  소스 콘텐츠 해시 기반 컴파일 캐시.
76
81
  - **감사** — 모든 판정을 `callId`와 함께 `{ignorable:true}` 이벤트로 기록합니다.
77
82
  모델에 보이는 이유와 기록된 결과는 항상 일치합니다.
78
- - **Permissive 티어** — read-only / full-access / whitelist와 구별되는 **독립 승인
79
- 모드**. "자동 승인"도 아니고 포괄적 권한 부여도 아닙니다. 프론트는 **단일 스위치**
83
+ - **自动审查 티어**(`permissive`) — read-only / full-access / whitelist와 구별되는 **독립 승인
84
+ 모드**. 범용 "자동 승인"도 아니고 포괄적 권한 부여도 아닙니다. 프론트는 **단일 스위치**
80
85
  (`permissive`)만 노출하고, 백엔드의 네 가지 전략은 **조합 가능**하며 플러그인
81
86
  설정으로 제어됩니다. P0에 대해서는 여전히 fail-closed입니다.
87
+ 권한 선택기와 설정 행 모두 제품명 「自动审查」로 표시하며 아이콘은 그리지 않습니다.
82
88
  - **샌드박스 승격 자동 응답** (`trustEscalation`) — 샌드박스 승격은 shell / pwsh /
83
89
  edit 도구의 **내부**(`tools/pre-execute` 이후)에서 발생하므로 게이트가 이를 볼 수
84
90
  없고, 게이트가 자동 허용한 호출에도 확인 프롬프트가 나타납니다. 이 전략을 켜면 게이트가
@@ -138,13 +144,19 @@ permissions:
138
144
 
139
145
  전체 예시: [examples/permissions.example.yaml](./examples/permissions.example.yaml)
140
146
 
141
- ## Permissive 티어
147
+ ## 自动审查 티어(머신 값 `permissive`)
142
148
 
143
- Permissive는 권한 선택기에서 Read Only / Workspace Write / Full access / Whitelist와
144
- 나란한 **독립 승인 티어**입니다. "자동 승인"이 아니며 포괄적 권한을 발행하지도 않습니다.
149
+ 自动审查는 권한 선택기에서 읽기 전용 / 워크스페이스 내 수정 / 완전 권한 / 허용 목록과
150
+ 나란한 **독립 승인 티어**입니다. 범용 "자동 승인"이 아니며 포괄적 권한을 발행하지도 않습니다.
145
151
  사람/LLM 심 **이전에** 판정을 좁히거나 넓힐 뿐이고, P0 하드 거부는 단조롭고 협상
146
152
  불가능하게 유지됩니다.
147
153
 
154
+ 선택기의 표시 이름은 **호스트가 공급하는 제품명**이며 언어별 사전 항목이 아닙니다. DSH 0.1.2는
155
+ 플러그인 티어의 `name:`을 두 권한 화면(일반 설정 기본 행과 입력창 선택기)에 그대로 렌더링하고
156
+ 자체 지역화 라벨은 세 가지 내장 값에만 부여하므로, `cordis.patch.yml`이 모든 세션에 중국어
157
+ 라벨을 배포합니다. 이 티어는 **아이콘을 그리지 않습니다** —— 선택기의 글리프는 내장 값에만
158
+ 연결됩니다.
159
+
148
160
  `cordis.yml`에서:
149
161
 
150
162
  ```yaml
@@ -187,7 +199,7 @@ LLM이 `safe`로 판정하고 게이트가 자동 허용한 호출조차 샌드
187
199
  `callId`로 키화, 승격 요청이 이를 반복)을 기억하고 승격을 여기서 `allowed-once`로
188
200
  자응답합니다. **모든 조건이 충족될 때만** 적용됩니다:
189
201
 
190
- - Permissive 티어가 켜져 있고 `trustEscalation`이 켜져 있음;
202
+ - 自动审查 티어가 켜져 있고 `trustEscalation`이 켜져 있음;
191
203
  - 호출에 게이트가 허용한 `callId`가 있고, 툴 이름이 일치;
192
204
  - 원인이 알려진 승격이며 `workspace-write` 또는 `danger-full-access`를 명시.
193
205
 
@@ -199,15 +211,15 @@ never` 패스스루 — 는 사람에 그대로 위임되므로 향후 DSH 변
199
211
 
200
212
  ### 선택 가능한 세션 티어
201
213
 
202
- `cordis.patch.yml`은 DSH의 `permission.config.presets`에 Workspace Write와
203
- Full access 사이의 `permissive` preset(`sandbox: workspace-write`, `approval: ask`,
204
- 이름 **Permissive**)을 추가합니다. DSH의 bundle patch는 이 map을 **전체 교체**하므로
214
+ `cordis.patch.yml`은 DSH의 `permission.config.presets`에 워크스페이스 내 수정과
215
+ 완전 권한 사이의 `permissive` preset(`sandbox: workspace-write`, `approval: ask`,
216
+ 이름 **自动审查**)을 추가합니다. DSH의 bundle patch는 이 map을 **전체 교체**하므로
205
217
  (키 단위 병합이 아닙니다) 내장 3개 티어
206
218
  (`read-only` / `workspace-write` / `danger-full-access`,
207
219
  `@deepseek-ai/dsh-base/cordis.patch.yml` 기준)도 다시 기재해야 하며,
208
220
  `test/patch-presets.spec.ts`가 그 키 집합을 고정합니다. 따라서 세션 권한
209
- 선택기에는 "auto-approval"이 아니라 **독립적으로 선택 가능한 승인 티어**인 Permissive가
210
- 놓입니다.
221
+ 선택기에는 "auto-approval"이 아니라 **독립적으로 선택 가능한 승인 티어**인
222
+ 「自动审查」가 놓입니다.
211
223
 
212
224
  게이트가 동작하는 범위는 **`gatePresets`에 나열한 티어 안뿐**입니다(기본 `['permissive']`,
213
225
  이 플러그인이 추가하는 티어). 그 밖의 티어(Read Only / Workspace Write / Full access /
@@ -248,7 +260,9 @@ host는 네임스페이스를 live로 읽으므로 변경은 재시작 없이
248
260
  키워드를 포함하는 호출(대소문자 구분 없는 부분 일치)은 허용 목록/권한/LLM보다 먼저 거부됩니다. 설정
249
261
  카드에서 목록으로 편집할 수 있고(프리셋 항목에는 태그 표시, 원클릭 복원 지원) 미설정·빈 목록 시 프리셋이
250
262
  적용됩니다——블랙리스트가 조용히 꺼지지 않습니다.
251
- Permissive 티어는 권한 선택기에서 방패 아이콘을 유지합니다(메뉴 항목과 축소 트리거 모두).
263
+ 블랙리스트가 조용히 꺼지는 일은 없습니다.
264
+ 이 티어는 권한 선택기에 **아이콘을 표시하지 않습니다** —— 선택기의 글리프는 내장 세 값에만
265
+ 연결되고, 플러그인 티어는 텍스트만입니다.
252
266
 
253
267
 
254
268
  ## CLI(독립 실행 dry-run)
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- > **Compatibility note:** v1.0.0 ships `ja` / `ko` dictionaries, but official DSH exposes
15
+ > **Compatibility note:** v2.0.0 ships `ja` / `ko` dictionaries, but official DSH exposes
16
16
  > only `zh` / `en` through `LocaleRuntime` (`LOCALE_IDS = ["zh", "en"]`). On stock DSH,
17
17
  > selecting `ja` / `ko` fails with `locale "<id>" is not registered`. Use a DSH fork that
18
18
  > updates `LOCALE_IDS` (locale-settings.ts) and `LOCALES` labels (client/index.ts), then
@@ -26,7 +26,12 @@
26
26
  > | DSH version | Branch | Version | npm tag |
27
27
  > | --- | --- | --- | --- |
28
28
  > | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
29
- > | 0.1.2-alpha.1+ (incl. 0.1.5-rc.2) | `main` | `0.2.x` | `@latest` |
29
+ > | 0.1.2-alpha.1+ (incl. 0.1.5-rc.2) | `main` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x` is a range) |
30
+ >
31
+ > The series number tracks the **DSH line** (`1.x` = DSH ≤ 0.1.1, `2.x` = DSH 0.1.2+),
32
+ > and the majors fence each other: a `^1.x` install never resolves a `2.x` release and
33
+ > vice versa. `engines.dsh` states the same split but DSH never reads it — the ranges
34
+ > and dist-tags are what hold an old DSH on `1.x`.
30
35
  >
31
36
  > `@deepseek-ai/dsh-client-runtime` was **removed** at `0.1.2-alpha.1` — it did not
32
37
  > merely move. The `legacy` line still reaches `ctx.slots` through it; `main` gets
@@ -37,7 +42,7 @@
37
42
  > **private** method of the user-approval service on both, so it is read behind a
38
43
  > `typeof` probe and degrades to “policy unknown” when absent or throwing.
39
44
 
40
- Version **1.0.0** — see the [Changelog](./CHANGELOG.md).
45
+ Version **2.0.0** — see the [Changelog](./CHANGELOG.md).
41
46
 
42
47
  A single, self-sufficient, deterministic-first, fail-closed permission gate for DeepSeek Harness.
43
48
 
@@ -72,10 +77,11 @@ cross-plugin version coupling.
72
77
  malformed rules, and source-hash compile caching.
73
78
  - **Audit** — every decision is logged as an `{ignorable:true}` event with its `callId`; the
74
79
  model-visible reason matches the recorded outcome.
75
- - **Permissive tier** — an **independent approval mode** (separate from read-only, full-access
76
- and whitelist tiers) that is neither "auto-approve" nor blanket trust. Front-end exposes a
77
- **single switch** (`permissive`); the four backend strategies are **combinable** and driven
78
- by plugin settings — still fail-closed against P0.
80
+ - **自动审查 tier** (`permissive`) — an **independent approval mode** (separate from read-only,
81
+ full-access and whitelist tiers) that is neither "auto-approve" nor blanket trust. Front-end
82
+ exposes a **single switch** (`permissive`); the four backend strategies are **combinable** and
83
+ driven by plugin settings — still fail-closed against P0. The permission picker and the
84
+ settings row both show it under the product label 自动审查, with no icon.
79
85
  - **Sandbox-escalation auto-answer** (`trustEscalation`) — a sandbox escalation is asked from
80
86
  *inside* the shell / pwsh / edit tool body, after `tools/pre-execute`, so the gate never saw
81
87
  it and a call it auto-allowed still prompted you to approve the widening. With this strategy
@@ -148,12 +154,18 @@ permissions:
148
154
  A command entry `word#flag` matches the command word (`word`) with the modifier `recursive` or
149
155
  `force` — so `rm#recursive` matches `rm -rf`, `env rm -rf`, and `sh -c "rm -rf /"`.
150
156
 
151
- ## Permissive mode
157
+ ## The 自动审查 tier (machine value `permissive`)
158
+
159
+ 自动审查 is an **independent approval tier** in the DSH permission picker, parallel to
160
+ Read Only / Workspace Write / Full access / Whitelist. It is **not** generic "auto-approval" and
161
+ never mints blanket authority: it only narrows or widens the seam *before* the human/LLM step
162
+ while P0 hard-deny stays monotonic and non-negotiable.
152
163
 
153
- Permissive is an **independent approval tier** in the DSH permission picker, parallel to
154
- Read Only / Workspace Write / Full access / Whitelist. It is **not** "auto-approval" and never
155
- mints blanket authority: it only narrows or widens the seam *before* the human/LLM step while
156
- P0 hard-deny stays monotonic and non-negotiable.
164
+ The picker label is a **host-supplied product string**, not a per-locale dictionary entry: DSH
165
+ 0.1.2 renders a plugin tier's `name:` verbatim on both permission surfaces (the General-settings
166
+ default row and the composer picker) and only supplies its own localized labels for the three
167
+ built-in values, so `cordis.patch.yml` ships the Chinese label for every session. The tier draws
168
+ **no icon** — the composer renders glyphs only for the built-in values.
157
169
 
158
170
  In `cordis.yml`:
159
171
 
@@ -194,7 +206,7 @@ prompt asking you to approve the sandbox widening.
194
206
  the host's `callId`, which the escalation request repeats) and answers the escalation
195
207
  `allowed-once` itself. It applies only when *all* hold:
196
208
 
197
- - the Permissive tier is on and `trustEscalation` is on;
209
+ - the 自动审查 tier is on and `trustEscalation` is on;
198
210
  - the request carries a `callId` the gate cleared, with a matching tool name;
199
211
  - the reason is a recognized escalation naming `workspace-write` or `danger-full-access`.
200
212
 
@@ -253,18 +265,18 @@ channel existed), with `tools/result` settling the same ask as a fallback when t
253
265
  correlate it. Approvals report the post-approval learning progress (`n`/threshold), and the notice
254
266
  strip labels all three terminal states.
255
267
 
256
- The Permissive tier keeps its shield icon in the permission picker — on both the
257
- menu item and the collapsed picker trigger.
268
+ The tier draws no icon in the permission picker: the composer's glyphs are keyed to the three
269
+ built-in values, so a plugin-contributed tier is text-only on every surface.
258
270
 
259
271
  ### A selectable session tier
260
272
 
261
273
  `cordis.patch.yml` adds a `permissive` preset (`sandbox: workspace-write`, `approval: ask`, name
262
- **Permissive**) between Workspace Write and Full access. The DSH bundle patch replaces the whole
274
+ **自动审查**) between Workspace Write and Full access. The DSH bundle patch replaces the whole
263
275
  `permission.config.presets` map rather than merging per key, so the file also restates the three
264
276
  built-ins (`read-only` / `workspace-write` / `danger-full-access`, from
265
277
  `@deepseek-ai/dsh-base/cordis.patch.yml`); `test/patch-presets.spec.ts` pins that key set. So the
266
- session permission picker offers Permissive as an independent selectable approval tier, not an
267
- "auto-approval" mode.
278
+ session permission picker offers 自动审查 as an independent selectable approval tier, not a
279
+ generic "auto-approval" mode.
268
280
 
269
281
  The gate is active **only in the tiers listed in `gatePresets`** (default `['permissive']`, the
270
282
  tier this plugin adds). In every other tier — Read Only, Workspace Write, Full access,
@@ -279,11 +291,11 @@ session's effective approval policy is `never`.
279
291
 
280
292
  ### Configurable in the UI
281
293
 
282
- The tier is also adjustable at runtime from **Settings → Plugins → Permissive approval tier**
294
+ The tier is also adjustable at runtime from **Settings → Plugins → 自动审查**
283
295
  (a `settings.plugins.tab` page rendered by the plugin's browser client): one switch toggles
284
296
  `permissive`, and four toggles edit the backend `permissiveStrategies`. The host reads the
285
297
  namespace live, so a change applies to the next tool call without a restart. This is an
286
- independent approval class, NOT the DSH "auto-approval" mode.
298
+ independent approval class, NOT a generic "auto-approval" mode.
287
299
 
288
300
  ## CLI
289
301
 
package/README.zh.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- > **兼容性说明:** v1.0.0 自带 `ja` / `ko` 字典,但官方 DSH 的 `LocaleRuntime`
15
+ > **兼容性说明:** v2.0.0 自带 `ja` / `ko` 字典,但官方 DSH 的 `LocaleRuntime`
16
16
  > 只暴露 `zh` / `en`(`LOCALE_IDS = ["zh", "en"]`)。在原版 DSH 上选择 `ja` / `ko`
17
17
  > 会报 `locale "<id>" is not registered`。请使用更新了 `LOCALE_IDS`
18
18
  > (locale-settings.ts)与 `LOCALES` 标签(client/index.ts)的 DSH fork 并重新构建。
@@ -25,7 +25,11 @@
25
25
  > | DSH 版本 | 分支 | 版本号 | npm 标签 |
26
26
  > | --- | --- | --- | --- |
27
27
  > | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
28
- > | 0.1.2-alpha.1+(含 0.1.5-rc.2) | `main` | `0.2.x` | `@latest` |
28
+ > | 0.1.2-alpha.1+(含 0.1.5-rc.2) | `main` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x` 是范围) |
29
+ >
30
+ > 版本序列号跟的是 **DSH 线**(`1.x` = DSH ≤ 0.1.1,`2.x` = DSH 0.1.2+),两条大版本互
31
+ > 相隔离:锁在 `^1.x` 的安装绝不会解析到 `2.x`,反之亦然。`engines.dsh` 表达同样的
32
+ > 分界,但 DSH 从不读取它——真正把旧 DSH 钉在 `1.x` 上的是版本范围与 dist-tag。
29
33
  >
30
34
  > `@deepseek-ai/dsh-client-runtime` 在 `0.1.2-alpha.1` 中已被**移除**——不仅仅是更名。
31
35
  > `legacy` 线仍通过它访问 `ctx.slots`;`main` 从
@@ -35,7 +39,7 @@
35
39
  > 线上都是 user-approval 服务的**私有**方法,因此通过 `typeof` 探测读取,缺失
36
40
  > 或抛错时降级为「策略未知」。
37
41
 
38
- 版本 **1.0.0** —— 变更见 [Changelog](./CHANGELOG.md)。
42
+ 版本 **2.0.0** —— 变更见 [Changelog](./CHANGELOG.md)。
39
43
 
40
44
  一个**单一自足、确定性优先、fail-closed** 的 DeepSeek Harness 权限门插件。
41
45
 
@@ -58,7 +62,7 @@
58
62
  - **会话放行** — 精确的 `(工具, 规范化 fingerprint)` grant,带 `TTL` + `maxUses`;换目标绝不复用。子代理继承但不可自授。
59
63
  - **纯函数规则引擎** — glob/regex 编译 + ReDoS 上限、坏规则 loud fail、按源内容哈希缓存。
60
64
  - **审计** — 每次决策写为 `{ignorable:true}` 事件并带 `callId`;模型可见理由与记录一致。
61
- - **Permissive 档位** — 一个**独立审批模式**(区别于只读、完全权限与白名单档),既不是"自动审批",也不授予泛化权限。前端只暴露**一个开关**(`permissive`),后台四个审批策略**可组合**、由插件设置决定——仍对 P0 保持 fail-closed。
65
+ - **自动审查档位**(机器值 `permissive`)——一个**独立审批模式**(区别于只读、完全权限与白名单档),既不是"自动审批",也不授予泛化权限。前端只暴露**一个开关**(`permissive`),后台四个审批策略**可组合**、由插件设置决定——仍对 P0 保持 fail-closed。权限下拉框与设置行都按产品名「自动审查」显示,且不带图标。
62
66
  - **沙箱提权自动答复**(`trustEscalation`)— 沙箱提权是从 shell / pwsh / edit 工具**体内部**(`tools/pre-execute` 之后)发出的,所以门禁从未见过它,一个它自动放行的调用仍会弹出确认。开启后,门禁以 `callId` 精确匹配已放行调用并直接答复。
63
67
 
64
68
  ## 安装
@@ -88,12 +92,17 @@ dsh plugin --profile web add dsh-perm-gate
88
92
 
89
93
  规则示例:见 [examples/permissions.example.yaml](./examples/permissions.example.yaml)。
90
94
 
91
- ## Permissive 档位
95
+ ## 自动审查档位(机器值 `permissive`)
92
96
 
93
- Permissive 是权限下拉框里一个**独立审批档**,与 Read Only / Workspace Write / Full access /
94
- Whitelist 平行。它**不叫"自动审批"**、也不授予泛化权限:只会在人类/LLM 接缝**之前**收窄或放宽决策,
97
+ 自动审查是权限下拉框里一个**独立审批档**,与只读 / 工作区内修改 / 完全权限 / 白名单平行。
98
+ 它不是泛化的"自动审批"、也不授予泛化权限:只会在人类/LLM 接缝**之前**收窄或放宽决策,
95
99
  P0 硬拒绝始终单调且不可协商。
96
100
 
101
+ 下拉框里的名字是**宿主提供的产品名**,不是逐语言的字典项:DSH 0.1.2 对插件档位在**两个**权限界面上
102
+ (通用设置默认档行、输入栏权限选择器)都原样渲染补丁里的 `name:`,只给三个内置档提供自己的本地化
103
+ 标签,因此 `cordis.patch.yml` 直接写中文名,对所有会话一致。该档位**不画图标**——选择器的图标只按
104
+ 三个内置值取。
105
+
97
106
  `cordis.yml`:
98
107
 
99
108
  ```yaml
@@ -129,7 +138,7 @@ LLM 评定为 `safe` 且门禁自动放行的调用因此仍会弹出确认。
129
138
  `trustEscalation` 填补这个缺口。门禁记住每个它正面向上放行的调用(以宿主 `callId` 为键,提权
130
139
  请求会重复该值),并在本处自行答复 `allowed-once`。它仅在**全部满足**时适用:
131
140
 
132
- - Permissive 档位开启且 `trustEscalation` 开启;
141
+ - 自动审查档位开启且 `trustEscalation` 开启;
133
142
  - 请求携带门禁放行的 `callId`,且工具名匹配;
134
143
  - 原因为已知的提权,指明 `workspace-write` 或 `danger-full-access`。
135
144
 
@@ -141,11 +150,11 @@ LLM 评定为 `safe` 且门禁自动放行的调用因此仍会弹出确认。
141
150
  ### 权限下拉里可选档位
142
151
 
143
152
  `cordis.patch.yml` 在 DSH 的 `permission.config.presets` 里新增了 `permissive` preset
144
- (`sandbox: workspace-write`、`approval: ask`、名称 **Permissive**),位于 Workspace Write 与
145
- Full access 之间。DSH 的 bundle patch 对这个 map 是**整表替换**而非逐键合并,所以该文件还必须重述三个内置档
153
+ (`sandbox: workspace-write`、`approval: ask`、名称 **自动审查**),位于工作区内修改与
154
+ 完全权限之间。DSH 的 bundle patch 对这个 map 是**整表替换**而非逐键合并,所以该文件还必须重述三个内置档
146
155
  (`read-only` / `workspace-write` / `danger-full-access`,取自
147
156
  `@deepseek-ai/dsh-base/cordis.patch.yml`);`test/patch-presets.spec.ts` 固定了这份键集合。因此会话权限
148
- 下拉里会出现 Permissive 这个**独立可选审批档**,而不是"auto-approval"档。
157
+ 下拉里会出现「自动审查」这个**独立可选审批档**,而不是"auto-approval"档。
149
158
 
150
159
  门禁**只在 `gatePresets` 列出的档位里生效**(默认 `['permissive']`,即本插件新增的那一档)。在其余任何档位
151
160
  (Read Only、Workspace Write、Full access、`custom`)里,门禁的判定流程**完全不运行**:不放行、不弹审批、
@@ -157,7 +166,7 @@ Full access 之间。DSH 的 bundle patch 对这个 map 是**整表替换**而
157
166
 
158
167
  ### 在 UI 里可配置
159
168
 
160
- 该档位也可在运行时从 **设置 → 插件 → Permissive 审批档** 调整(插件浏览器端渲染的
169
+ 该档位也可在运行时从 **设置 → 插件 → 自动审查** 调整(插件浏览器端渲染的
161
170
  `settings.plugins.tab` 页面):一个开关切换 `permissive`,四个开关编辑后台
162
171
  `permissiveStrategies`。host 端 live 读取该命名空间,改动对下一条工具调用即时生效,无需重启。
163
172
  这是一个独立审批类,**不是** DSH 的"auto-approval"档。
@@ -182,7 +191,7 @@ Full access 之间。DSH 的 bundle patch 对这个 map 是**整表替换**而
182
191
  调用文本命中任一关键词(大小写不敏感子串)即直接拒绝,且先于白名单 / 授权 / LLM。黑名单在设置
183
192
  卡片中按列表查看与增删(预置条目带标签,可一键恢复预置);未设置或为空时应用预置列表——黑名单
184
193
  不会静默关闭。
185
- Permissive 档在权限选择器中保留盾形图标——菜单项与折叠触发按钮均有图标。
194
+ 该档位在权限选择器中**不显示图标**:选择器的图标只按三个内置值取,插件新增的档位是纯文字。
186
195
 
187
196
 
188
197
  ## CLI(独立 dry-run)
package/cordis.patch.yml CHANGED
@@ -1,10 +1,23 @@
1
1
  # dsh-perm-gate profile patch.
2
2
  #
3
3
  # Two jobs:
4
- # 1) Add the plugin's own "Permissive" session approval tier to the DSH
4
+ # 1) Add the plugin's own "自动审查" session approval tier to the DSH
5
5
  # permission picker, and restate the built-in tiers it must not drop.
6
6
  # 2) Insert the plugin itself (host half) into the profile.
7
7
  #
8
+ # The tier's DISPLAY NAME is the Chinese product label, matching every official
9
+ # 0.1.2 permission label (仅可查看 / 工作区内修改 / 完全权限) in a zh session. Both
10
+ # permission surfaces — the General-settings default row and the composer
11
+ # picker — render a host-supplied name verbatim unless the value is one of the
12
+ # three built-ins, so a single `name:` string is what both show; neither surface
13
+ # is localized per-dictionary for plugin-contributed tiers. The machine value
14
+ # stays `permissive`: it is the key `gatePresets` matches and the value the
15
+ # permission projection reports, so only the label text is user-facing.
16
+ #
17
+ # The tier now carries a shield+eye icon in the composer glyph map (patched
18
+ # into dsh-client-ui-conversation). The permissive glyph sits alongside the
19
+ # three built-in ones so both the trigger button and the dropdown rows show it.
20
+ #
8
21
  # The DSH bundle patch REPLACES the whole `permission.config.presets` map (it
9
22
  # does not merge per key), so this file MUST restate every tier that should
10
23
  # survive. The authoritative built-in set is
@@ -31,8 +44,8 @@
31
44
  permissive:
32
45
  sandbox: workspace-write
33
46
  approval: ask
34
- name: Permissive
35
- description: Independent approval tier — safe in-scope ops auto-allow, dangerous crossings ask (approval strengths tunable under Settings → Plugins → Permissive approval tier).
47
+ name: 自动审查
48
+ description: 独立审批档 —— 作用域内安全操作自动放行,危险/未知越界转人工确认(审批强度可在「设置 → 插件 → 自动审查」调整)。
36
49
  danger-full-access:
37
50
  sandbox: danger-full-access
38
51
  approval: never
package/lib/client.js CHANGED
@@ -12,9 +12,9 @@ window.__ModuleLoader__.load({
12
12
  const NS = "dsh-perm-gate";
13
13
  /** Simplified Chinese dictionary (the key-set source of truth). */
14
14
  const zh = {
15
- "card.title": "Permissive 审批档",
16
- "card.description": "独立审批模式档位:与只读 / 完全权限 / 白名单平行。前端只暴露一个开关;后台三个审批策略可组合、由此处设置决定,仍对 P0 硬拒绝保持 fail-closed。",
17
- "card.permissive": "启用 Permissive 档位",
15
+ "card.title": "自动审查",
16
+ "card.description": "独立审批档位:与只读 / 完全权限 / 白名单平行,在会话权限下拉中显示为「自动审查」。前端只暴露一个开关;后台四个审批策略可组合、由此处设置决定,仍对 P0 硬拒绝保持 fail-closed。",
17
+ "card.permissive": "启用自动审查档位",
18
18
  "card.permissiveHint": "关闭时门禁行为与此前完全一致。",
19
19
  "card.strategies": "后台审批策略(至少一个生效;可组合)",
20
20
  "card.strategy.trustAutoAllow": "trustAutoAllow — 作用域内安全操作自动放行,危险/未知转 ask(中间档基线)",
@@ -121,9 +121,9 @@ window.__ModuleLoader__.load({
121
121
  };
122
122
  /** English dictionary (keys mirror zh). */
123
123
  const en = {
124
- "card.title": "Permissive approval tier",
125
- "card.description": "Independent approval mode tier, parallel to read-only / full-access / whitelist. The front-end exposes a single switch; the three backend approval strategies are combinable via this panel and still fail-closed against P0 hard-deny.",
126
- "card.permissive": "Enable Permissive tier",
124
+ "card.title": "自动审查 (Auto review)",
125
+ "card.description": "Independent approval tier, parallel to read-only / full-access / whitelist, shown as 自动审查 in the session permission picker. The front-end exposes a single switch; the four backend approval strategies are combinable via this panel and still fail-closed against P0 hard-deny.",
126
+ "card.permissive": "Enable the 自动审查 tier",
127
127
  "card.permissiveHint": "When off, the gate behaves exactly as before.",
128
128
  "card.strategies": "Backend approval strategies (at least one effective; combinable)",
129
129
  "card.strategy.trustAutoAllow": "trustAutoAllow — safe in-scope ops auto-allow; dangerous/unknown ask (baseline middle tier)",
@@ -1417,115 +1417,6 @@ window.__ModuleLoader__.load({
1417
1417
  });
1418
1418
  }
1419
1419
  //#endregion
1420
- //#region src/client/permission-icon.ts
1421
- /**
1422
- * Permission-picker icon decoration for the Permissive tier.
1423
- *
1424
- * dsh-auto-mode injects a shield glyph onto its "Auto" permission menu item
1425
- * via a `data-*` attribute + a CSS mask. This module does the same for the
1426
- * "Permissive" preset so the two approval tiers look consistent in the
1427
- * dropdown. It only annotates the item; the preset itself comes from
1428
- * `cordis.patch.yml` (the `permission.config.presets` table).
1429
- *
1430
- * Pure DOM + CSS, no @deepseek-ai value imports (client bundle purity).
1431
- */
1432
- const PLUGIN_ID = "dsh-perm-gate";
1433
- const ICON_ATTRIBUTE = "data-dsh-perm-gate-icon";
1434
- /** Labels of the Permissive preset item, current + future localized variants. */
1435
- const PERMISSIVE_LABELS = /* @__PURE__ */ new Set([
1436
- "Permissive",
1437
- "Permissive 审批档",
1438
- "Permissive 审批档位"
1439
- ]);
1440
- /** Same shield glyph as dsh-auto-mode so the two approval tiers match visually. */
1441
- const ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path d=\"M8.21.9l6.58 2.47v3.64c0 4.99-3.74 7.2-6.58 8.29C5.36 14.21 1.62 12 1.62 7.01V3.37L8.21.9Z\" fill=\"none\" stroke=\"black\" stroke-width=\"1.32\" stroke-linejoin=\"round\"/><path d=\"M8.75 3.65 5.95 8.2h2.08l-.78 4.15 2.82-4.9H8.12l.63-3.8Z\" fill=\"black\"/></svg>";
1442
- function iconStyles() {
1443
- const mask = `url("data:image/svg+xml,${encodeURIComponent(ICON_SVG)}")`;
1444
- return `
1445
- [${ICON_ATTRIBUTE}]::before {
1446
- content: "";
1447
- display: inline-block;
1448
- flex: 0 0 auto;
1449
- width: 16px;
1450
- height: 16px;
1451
- background-color: var(--dsw-alias-label-tertiary, currentColor);
1452
- -webkit-mask-image: ${mask};
1453
- mask-image: ${mask};
1454
- -webkit-mask-position: center;
1455
- mask-position: center;
1456
- -webkit-mask-repeat: no-repeat;
1457
- mask-repeat: no-repeat;
1458
- -webkit-mask-size: contain;
1459
- mask-size: contain;
1460
- }
1461
- [${ICON_ATTRIBUTE}='trigger']::before {
1462
- width: 14px;
1463
- height: 14px;
1464
- margin-right: 6px;
1465
- }
1466
- `;
1467
- }
1468
- /** The permission dropdown menu (lists the permission presets as menuitems). */
1469
- function isPermissionMenu(menu) {
1470
- return menu.matches("[role=\"menu\"]") && Array.from(menu.querySelectorAll("button[role=\"menuitem\"]")).length >= 4;
1471
- }
1472
- /** Whether one menuitem is the Permissive preset. */
1473
- function isPermissiveMenuItem(element) {
1474
- if (!element.matches("button[role=\"menuitem\"]")) return false;
1475
- const label = (element.textContent ?? "").replace(/\s+/g, " ").trim();
1476
- if (!PERMISSIVE_LABELS.has(label)) return false;
1477
- return element.closest("[role=\"menu\"]") !== null;
1478
- }
1479
- /** The collapsed permission-picker trigger (opens the presets menu). */
1480
- function isPermissionTrigger(element) {
1481
- return element.matches("button[aria-haspopup=\"menu\"]") && PERMISSIVE_LABELS.has((element.textContent ?? "").replace(/\s+/g, " ").trim());
1482
- }
1483
- /**
1484
- * Annotate the Permissive permission menuitems with the icon attribute, plus
1485
- * the collapsed picker trigger when its label names the Permissive preset.
1486
- * Unknown DOM shapes simply never match, so decoration stays conservative.
1487
- */
1488
- function decorate(document) {
1489
- for (const marked of document.querySelectorAll(`[${ICON_ATTRIBUTE}]`)) if (!isPermissiveMenuItem(marked) && !isPermissionTrigger(marked)) marked.removeAttribute(ICON_ATTRIBUTE);
1490
- for (const menu of document.querySelectorAll("[role=\"menu\"]")) {
1491
- if (!isPermissionMenu(menu)) continue;
1492
- for (const item of menu.querySelectorAll("button[role=\"menuitem\"]")) if (isPermissiveMenuItem(item)) item.setAttribute(ICON_ATTRIBUTE, "menu");
1493
- }
1494
- for (const trigger of document.querySelectorAll("button[aria-haspopup=\"menu\"]")) if (isPermissionTrigger(trigger)) trigger.setAttribute(ICON_ATTRIBUTE, "trigger");
1495
- }
1496
- /** Install the Permissive tile icon into the permission picker; returns the disposer. */
1497
- function installPermissivePermissionIcon(document) {
1498
- for (const existing of document.querySelectorAll("style[data-plugin]")) if (existing.getAttribute("data-plugin") === PLUGIN_ID) existing.remove();
1499
- const style = document.createElement("style");
1500
- style.dataset.plugin = PLUGIN_ID;
1501
- style.dataset.pluginCss = `${PLUGIN_ID}/permission-icon`;
1502
- style.textContent = iconStyles();
1503
- document.head.appendChild(style);
1504
- let active = true;
1505
- let queued = false;
1506
- const scan = () => {
1507
- if (!active || queued) return;
1508
- queued = true;
1509
- queueMicrotask(() => {
1510
- queued = false;
1511
- if (active) decorate(document);
1512
- });
1513
- };
1514
- decorate(document);
1515
- const observer = new MutationObserver(scan);
1516
- observer.observe(document.documentElement, {
1517
- childList: true,
1518
- characterData: true,
1519
- subtree: true
1520
- });
1521
- return () => {
1522
- active = false;
1523
- observer.disconnect();
1524
- for (const marked of document.querySelectorAll(`[${ICON_ATTRIBUTE}]`)) marked.removeAttribute(ICON_ATTRIBUTE);
1525
- style.remove();
1526
- };
1527
- }
1528
- //#endregion
1529
1420
  //#region src/client/feed.ts
1530
1421
  /** Fetch decision events for one session; `since > 0` returns only newer ones. */
1531
1422
  async function fetchEvents(sessionId, since) {
@@ -1609,7 +1500,7 @@ window.__ModuleLoader__.load({
1609
1500
  "deny-keyword": "黑名单关键词",
1610
1501
  default: "默认策略",
1611
1502
  ask: "默认策略",
1612
- permissive: "Permissive 放行",
1503
+ permissive: "自动审查放行",
1613
1504
  classifier: "LLM 裁决",
1614
1505
  "llm-safe": "LLM 判定安全",
1615
1506
  "llm-learned": "LLM 学习放行",
@@ -2575,7 +2466,6 @@ window.__ModuleLoader__.load({
2575
2466
  zh,
2576
2467
  en
2577
2468
  }), "dsh-perm-gate: dictionaries");
2578
- ctx.effect(() => installPermissivePermissionIcon(globalThis.document), "dsh-perm-gate: permission icon");
2579
2469
  ctx.slots.inject("conversation.input.dock", function* () {
2580
2470
  yield ctx.slots.register({
2581
2471
  name: "conversation.input.dock",