dsh-perm-gate 2.1.0 → 3.0.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 (77) hide show
  1. package/CHANGELOG.ja.md +82 -0
  2. package/CHANGELOG.ko.md +80 -0
  3. package/CHANGELOG.md +321 -0
  4. package/INSTALL.ja.md +70 -8
  5. package/INSTALL.ko.md +68 -9
  6. package/INSTALL.md +68 -8
  7. package/INSTALL.zh.md +59 -7
  8. package/README.ja.md +103 -16
  9. package/README.ko.md +101 -17
  10. package/README.md +152 -25
  11. package/README.zh.md +99 -12
  12. package/cordis.patch.yml +34 -3
  13. package/docs/baseline-guard.md +56 -0
  14. package/docs/plan-rules-to-settings.md +217 -0
  15. package/docs/rules-format.en.md +294 -0
  16. package/docs/rules-format.md +279 -0
  17. package/docs/ui-gap-analysis-and-branch-permissions.md +338 -0
  18. package/dsh.plugin.json +22 -0
  19. package/examples/permissions.example.yaml +16 -1
  20. package/lib/agent-identity.d.ts +43 -0
  21. package/lib/agent-identity.js +95 -0
  22. package/lib/allowlist.d.ts +27 -0
  23. package/lib/allowlist.js +103 -1
  24. package/lib/cli.js +11 -14
  25. package/lib/client.js +598 -6
  26. package/lib/client.js.map +1 -1
  27. package/lib/command-dispatcher.d.ts +22 -0
  28. package/lib/command-dispatcher.js +97 -0
  29. package/lib/command-semantics.d.ts +62 -0
  30. package/lib/command-semantics.js +6 -0
  31. package/lib/compiler.d.ts +50 -0
  32. package/lib/compiler.js +122 -0
  33. package/lib/config.d.ts +161 -7
  34. package/lib/config.js +78 -11
  35. package/lib/dry-run.d.ts +87 -0
  36. package/lib/dry-run.js +110 -0
  37. package/lib/engine.js +61 -4
  38. package/lib/evaluate.d.ts +9 -0
  39. package/lib/evaluate.js +264 -1
  40. package/lib/events.d.ts +69 -2
  41. package/lib/events.js +123 -0
  42. package/lib/index.d.ts +1 -1
  43. package/lib/index.js +320 -11
  44. package/lib/network-lifecycle.d.ts +107 -0
  45. package/lib/network-lifecycle.js +180 -0
  46. package/lib/network.d.ts +109 -0
  47. package/lib/network.js +196 -0
  48. package/lib/parsers/git.d.ts +43 -0
  49. package/lib/parsers/git.js +404 -0
  50. package/lib/parsers/shell-cmds.d.ts +32 -0
  51. package/lib/parsers/shell-cmds.js +293 -0
  52. package/lib/preset.d.ts +13 -0
  53. package/lib/preset.js +17 -0
  54. package/lib/proxy.d.ts +160 -0
  55. package/lib/proxy.js +529 -0
  56. package/lib/risk.d.ts +17 -3
  57. package/lib/risk.js +19 -6
  58. package/lib/rule-chain.d.ts +39 -0
  59. package/lib/rule-chain.js +213 -0
  60. package/lib/rule-dims.d.ts +174 -0
  61. package/lib/rule-dims.js +230 -0
  62. package/lib/rule.d.ts +60 -0
  63. package/lib/rule.js +69 -7
  64. package/lib/rules-view.d.ts +52 -0
  65. package/lib/rules-view.js +126 -0
  66. package/lib/runtime.d.ts +208 -7
  67. package/lib/runtime.js +433 -27
  68. package/lib/session-sweep.d.ts +45 -0
  69. package/lib/session-sweep.js +153 -0
  70. package/lib/shadow.d.ts +34 -0
  71. package/lib/shadow.js +143 -0
  72. package/lib/shell.d.ts +14 -0
  73. package/lib/shell.js +17 -0
  74. package/lib/watch.d.ts +42 -0
  75. package/lib/watch.js +170 -0
  76. package/package.json +15 -2
  77. package/scripts/patch-permission-glyph.mjs +209 -0
package/INSTALL.ko.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- `dsh-perm-gate` 버전 **2.0.0**. 결정 체인, 규칙 파일 형식, 自动审查 티어는
15
+ `dsh-perm-gate` 버전 **2.6.0**. 결정 체인, 규칙 파일 형식, 自动审查 티어는
16
16
  [한국어 README](./README.ko.md)를 참고하세요.
17
17
 
18
18
  ## 요구 사항
@@ -23,20 +23,21 @@
23
23
  - 설치할 profile. 브라우저 절반은 `web` profile용으로만 빌드됩니다
24
24
  (`package.json`의 `dsh.client.platform = "web"`).
25
25
 
26
- ## DSH 버전에 맞는 tag 선택
26
+ ## DSH 버전에 맞는 라인 선택
27
27
 
28
- 단일 빌드는 두 DSH 라인을 모두 커버하므로, 어느 tag를 골라도 작동하는 코드가
29
- 설치됩니다 —— tag를 유지하는 이유는 당신이 고정하는 버전이 각 라인에서 의미 있게
30
- 유지되도록 하기 위함입니다.
28
+ 두 개의 장기 브랜치가 두 DSH 라인을 담당합니다: `main`(2.x)과
29
+ `sync/0.1.5-from-main`(3.x, DSH 0.1.5 패키징 변형 —— `compat/0.1.5`는 같은 코드
30
+ 트리를 가리킵니다).
31
31
 
32
32
  | DSH 버전 | 설치 방법 |
33
33
  |----------|-----------|
34
- | `0.1.2-alpha.1` 이상 (`0.1.5-rc.2` 포함) | `dsh plugin --profile web add dsh-perm-gate` (tag `latest`) |
34
+ | `0.1.2-alpha.1` – `0.1.5-rc.0` | `dsh plugin --profile web add dsh-perm-gate` (tag `latest`, 2.x 라인) |
35
+ | `0.1.5-rc.1` 이상 | `dsh plugin --profile web add github:drscrewdriver/dsh-perm-gate#sync/0.1.5-from-main` (3.x 라인; npm dist-tag 미게시 —— `@latest`는 동일 코드의 2.x 패키징을 설치) |
35
36
  | `0.1.1-rc.2` 이하 | `dsh plugin --profile web add dsh-perm-gate@legacy` |
36
37
 
37
- DSH는 `engines.dsh`를 강제하지 않으므로, tag는 선택 메커니즘이지 호환성 게이트가
38
- 아닙니다. 단일 아티팩트가 두 라인을 커버하는 이유와 tag가 게시되는 방식에 대해서는
39
- [RELEASING.md](./RELEASING.md)를 참고하세요.
38
+ DSH는 `engines.dsh`를 강제하지 않으므로, tag과 ref은 선택 메커니즘이지 호환성
39
+ 게이트가 아닙니다. 라인과 tag 게시 레이아웃은 [RELEASING.md](./RELEASING.md)를
40
+ 참고하세요.
40
41
 
41
42
  ## 공식 CLI로 설치
42
43
 
@@ -73,6 +74,11 @@ profile의 `cordis.yml`에 플러그인을 추가합니다:
73
74
  defaultAction: ask # allow | ask | deny
74
75
  ```
75
76
 
77
+ 2.6.0부터 규칙 본체는 `dsh-perm-gate-rules` settings 네임스페이스에 저장되며, 변경 시
78
+ 마다 게이트가 그 자리에서 다시 컴파일됩니다. 위의 YAML 파일은 선택 사항이 되었습니다.
79
+ 네임스페이스가 설정되지 않은 동안에만 읽히며, 플러그인이 처음으로 규칙을 쓸 때(허용
80
+ 목록 승인 또는 설정 UI에서의 편집) 파일의 규칙이 자동으로 흡수됩니다.
81
+
76
82
  [examples/permissions.example.yaml](./examples/permissions.example.yaml)에서 시작한 뒤
77
83
  profile을 다시 불러오세요.
78
84
 
@@ -88,6 +94,53 @@ dsh plugin --profile web update dsh-perm-gate
88
94
  dsh profile reload --profile web
89
95
  ```
90
96
 
97
+ ## **DSH** 업그레이드 후: 입력창 아이콘 패치 재적용
98
+
99
+ 「自动审查(高权限)」가 「自动审查」와 같은 방패+눈 아이콘을 보여 주는 것은
100
+ `scripts/patch-permission-glyph.mjs`가 **DSH 호스트 패키지 안의 닫힌 Map**에 그 항목을
101
+ 추가했기 때문일 뿐입니다. DSH는 플러그인이 제공한 티어에 **설계상 아이콘을 주지
102
+ 않습니다** —— 그 Map 자체의 주석이 *"host-configured names outside the design set get
103
+ none"*이라고 말합니다. 플러그인이 영향을 줄 수 있는 option 객체는
104
+ `{value, name, description}`만 나르므로, 대신 쓸 수 있는 플러그인 측 이음새가 없습니다.
105
+
106
+ 이 패치는 **호스트** 파일을 고치므로 **DSH 업그레이드나 재설치에서 사라집니다**.
107
+ *이 플러그인*을 업그레이드할 때는 사라지지 않습니다. 아이콘은 애초에 플러그인 소유가
108
+ 아니었고, 플러그인 자신의 기여(`cordis.patch.yml`의 `name:` / `description:`)는
109
+ 패키지에 함께 실려 나갑니다.
110
+
111
+ ```sh
112
+ npx dsh-perm-gate-patch-glyph # 패치 적용
113
+ npx dsh-perm-gate-patch-glyph --check # 확인만. 글리프가 사라졌으면 종료 코드 1
114
+ dsh profile reload --profile web
115
+ ```
116
+
117
+ 스크립트는 **이 패키지에 함께 실려 나갑니다** —— `scripts/patch-permission-glyph.mjs`로도,
118
+ `dsh-perm-gate-patch-glyph` bin으로도 —— 소스 checkout이 필요 없습니다.
119
+ **의도적으로 `postinstall`에 연결하지 않았습니다**: 이 스크립트는 호스트 패키지를 고치며,
120
+ 플러그인이 허락 없이 자신이 설치된 harness를 다시 쓸 이유는 없습니다. 적용 여부와 무관하게
121
+ DSH 동작에는 영향이 없고 티어 자체는 그대로 기능합니다.
122
+
123
+ 이 스크립트는 멱등이며(두 번째 실행은 no-op), 백업은 한 번만 뜨고, 잘못 잘린 조각을
124
+ 쓰지 않습니다 —— 결과에 `node --check`를 돌려 실패하면 백업을 복원합니다 —— 따라서
125
+ DSH 업그레이드 때마다 무조건 다시 실행해도 안전합니다. 실행 중인 `node` 바이너리에서
126
+ 패키지를 역산하므로 nvm 버전 변경이나 설치 symlink 재지정에도 깨지지 않습니다.
127
+
128
+ **플러그인을 재설치해도 아이콘은 돌아오지 않습니다.**
129
+ `dsh plugin --profile web add …`는 profile 디렉터리 안의 pnpm으로 인자를 넘기고
130
+ profile 자신의 `node_modules`만 씁니다(`-w` = `--workspace-root`인데, 이 profile의
131
+ workspace는 `packages: ['.']` 그 자체이므로 달라지는 것이 없습니다). 아이콘은 DSH
132
+ 설치 쪽에 있습니다. 표준 설치에서 아래 세 경로는 사본 세 개가 아니라
133
+ **하나의 물리 파일**입니다:
134
+
135
+ | 경로 | 실체 |
136
+ |------|------|
137
+ | `dirname(node)/node_modules/@deepseek-ai/dsh` | DSH 설치 위치(symlink일 수 있음) |
138
+ | `<profile>/node_modules/@deepseek-ai/dsh-client-ui-conversation` | 그곳을 가리키는 **정션(junction)** |
139
+ | `<dsh>/node_modules/@deepseek-ai/dsh-client-ui-conversation/lib/client.js` | 패치 대상 파일 |
140
+
141
+ 관찰할 증상: 티어는 여전히 동작하고 게이트도 그대로지만, 입력창 드롭다운의 그 행에
142
+ 아이콘이 없고 접힌 트리거가 다른 티어의 아이콘+텍스트와 달리 텍스트만 표시됩니다.
143
+
91
144
  ## 분리된 플러그인에서 이전하기
92
145
 
93
146
  `dsh-perm-gate`는 `dsh-permission-rules`, `dsh-auto-mode`, `dsh-auto-review`,
@@ -143,6 +196,12 @@ DSH 번들 패치는 `permission.config.presets`를 key 단위로 병합하지
143
196
  합니다. profile을 다시 불러와 `cordis.patch.yml`을 재적용하고, 더 늦게 로드되는
144
197
  플러그인이 `presets`를 덮어쓰지 않는지 확인하세요.
145
198
 
199
+ **「自动审查(高权限)」의 입력창 아이콘이 사라졌습니다.**
200
+ DSH 업그레이드나 재설치가 패치해 둔 호스트 번들을 교체했습니다. 플러그인을
201
+ 재설치해도 되돌아오지 않습니다. `npx dsh-perm-gate-patch-glyph`를 실행하고
202
+ 다시 불러오세요. 티어 자체는 영향받지 않습니다 —— 패치가 없어도 라벨과 게이트는
203
+ 정상 동작합니다.
204
+
146
205
  **규칙 파일이 있는데 `--list`가 `ruleCount: 0`을 표시합니다.**
147
206
  `rulesFile`은 플러그인 디렉터리가 아니라 Harness 프로세스의 CWD에서 해석됩니다.
148
207
  절대 경로를 쓰거나 셸의 CWD를 확인하세요. 잘못된 문서는 로드 시 loud-fail하며
package/INSTALL.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- `dsh-perm-gate` version **2.0.0**. Continue with the [README](./README.md) for the
15
+ `dsh-perm-gate` version **2.6.0**. Continue with the [README](./README.md) for the
16
16
  decision chain, the rules file format and the 自动审查 tier.
17
17
 
18
18
  ## Requirements
@@ -23,19 +23,21 @@ decision chain, the rules file format and the 自动审查 tier.
23
23
  - A profile to install into — the browser half only ships for the `web` profile
24
24
  (`dsh.client.platform = "web"` in `package.json`).
25
25
 
26
- ## Pick the tag for your DSH version
26
+ ## Pick the line for your DSH version
27
27
 
28
- One build serves both DSH lines, so either tag installs working code — the tag
29
- exists so a version you pin stays meaningful per line.
28
+ Two long-lived branches serve two DSH lines: `main` (2.x) and
29
+ `sync/0.1.5-from-main` (3.x, the DSH 0.1.5 packaging variant — `compat/0.1.5`
30
+ points at the same tree).
30
31
 
31
32
  | Your DSH | Install |
32
33
  |----------|---------|
33
- | `0.1.2-alpha.1` or newer (incl. `0.1.5-rc.2`) | `dsh plugin --profile web add dsh-perm-gate` (tag `latest`) |
34
+ | `0.1.2-alpha.1` – `0.1.5-rc.0` | `dsh plugin --profile web add dsh-perm-gate` (tag `latest`, the 2.x line) |
35
+ | `0.1.5-rc.1` or newer | `dsh plugin --profile web add github:drscrewdriver/dsh-perm-gate#sync/0.1.5-from-main` (the 3.x line; no npm dist-tag yet — `@latest` installs the same code with 2.x packaging) |
34
36
  | up to `0.1.1-rc.2` | `dsh plugin --profile web add dsh-perm-gate@legacy` |
35
37
 
36
- DSH does not enforce `engines.dsh`, so the tags are the selection mechanism rather
37
- than a compatibility gate. See [RELEASING.md](./RELEASING.md) for why one artifact
38
- covers both lines and how the tags are published.
38
+ DSH does not enforce `engines.dsh`, so tags and refs are the selection mechanism
39
+ rather than a compatibility gate. See [RELEASING.md](./RELEASING.md) for how the
40
+ lines and tags are laid out.
39
41
 
40
42
  ## Install with the official CLI
41
43
 
@@ -73,6 +75,12 @@ Add the plugin to your profile's `cordis.yml`:
73
75
  gatePresets: [permissive] # tiers where the gate is active at all (default)
74
76
  ```
75
77
 
78
+ Since 2.6.0 the rules themselves are stored in the `dsh-perm-gate-rules` settings
79
+ namespace, and every change recompiles the gate in place. The YAML file above is
80
+ optional: it is only read while the namespace is untouched, and the first rules
81
+ write from the plugin (an allowlist approval, or an edit in the settings UI)
82
+ absorbs the file's rules automatically.
83
+
76
84
  Start from [examples/permissions.example.yaml](./examples/permissions.example.yaml),
77
85
  then reload the profile.
78
86
 
@@ -88,6 +96,52 @@ Then re-apply the profile so the patch file is re-read:
88
96
  dsh profile reload --profile web
89
97
  ```
90
98
 
99
+ ## After a **DSH** upgrade: re-apply the composer glyph patch
100
+
101
+ The 自动审查(高权限) tier shows the same shield+eye glyph as 自动审查 only because
102
+ `scripts/patch-permission-glyph.mjs` added it to a **closed map inside a DSH host
103
+ package**. DSH gives host-configured tiers no glyph by design — the map's own
104
+ comment reads *"host-configured names outside the design set get none"* — and the
105
+ option objects a plugin can influence carry only `{value, name, description}`, so
106
+ there is no plugin-side seam to use instead.
107
+
108
+ That patch edits a **host** file, so a DSH upgrade or reinstall erases it. Upgrading
109
+ *this plugin* does not: the plugin never owned the glyph, and its own contribution
110
+ (`name:` / `description:` in `cordis.patch.yml`) ships inside the package.
111
+
112
+ ```sh
113
+ npx dsh-perm-gate-patch-glyph # apply the patch
114
+ npx dsh-perm-gate-patch-glyph --check # report only; exits 1 if the glyph is gone
115
+ dsh profile reload --profile web
116
+ ```
117
+
118
+ The script **ships inside this package** — as `scripts/patch-permission-glyph.mjs` and as
119
+ the `dsh-perm-gate-patch-glyph` bin — so no source checkout is needed. It is deliberately
120
+ **not** wired to `postinstall`: it edits a host package, and a plugin must not rewrite its
121
+ harness uninvited. Running DSH is unaffected either way; the tier works with or without it.
122
+
123
+ It is idempotent (a second run is a no-op), backs the bundle up once, and refuses to
124
+ write a mis-sliced bundle — it runs `node --check` on the result and restores the
125
+ backup if that fails — so it is safe to run unconditionally after every DSH upgrade.
126
+ It locates the bundle from the running `node` binary, so an nvm version change or a
127
+ re-pointed install symlink does not break it.
128
+
129
+ **Reinstalling the plugin does not restore the glyph.** `dsh plugin --profile web
130
+ add …` forwards to pnpm inside the profile directory and writes the profile's own
131
+ `node_modules` only; `-w` (`--workspace-root`, and this profile's workspace is just
132
+ `packages: ['.']`) changes nothing about that. The glyph lives in the DSH install.
133
+ On a stock install these are not three copies but **one physical file**:
134
+
135
+ | Path | What it is |
136
+ |------|------------|
137
+ | `dirname(node)/node_modules/@deepseek-ai/dsh` | the DSH install (may be a symlink) |
138
+ | `<profile>/node_modules/@deepseek-ai/dsh-client-ui-conversation` | a **junction** into it |
139
+ | `<dsh>/node_modules/@deepseek-ai/dsh-client-ui-conversation/lib/client.js` | the patched file |
140
+
141
+ Symptom to look for: the tier still works and still gates correctly, but its row in
142
+ the composer dropdown has no icon and the collapsed trigger renders plain text
143
+ where the other tiers render glyph + text.
144
+
91
145
  ## Migrate from the split plugins
92
146
 
93
147
  `dsh-perm-gate` merges the gate, the approval seam and the (optional) classifier
@@ -145,6 +199,12 @@ The DSH bundle patch replaces the whole `permission.config.presets` map instead
145
199
  merging per key. Reload the profile so `cordis.patch.yml` is re-applied, and make
146
200
  sure no later plugin overwrites `presets`.
147
201
 
202
+ **自动审查(高权限) lost its icon in the composer.**
203
+ A DSH upgrade or reinstall replaced the host bundle the glyph was patched into;
204
+ re-running the plugin install will not bring it back. Run
205
+ `npx dsh-perm-gate-patch-glyph` and reload. The tier itself is
206
+ unaffected — its label and its gating keep working without the patch.
207
+
148
208
  **`--list` reports `ruleCount: 0` although my rules file exists.**
149
209
  `rulesFile` is resolved against the process CWD of the harness, not the plugin
150
210
  directory. Prefer an absolute path or confirm the shell CWD. A malformed document
package/INSTALL.zh.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- `dsh-perm-gate` 版本 **2.0.0**。裁决链、规则文件格式与自动审查档位请见
15
+ `dsh-perm-gate` 版本 **2.6.0**。裁决链、规则文件格式与自动审查档位请见
16
16
  [中文 README](./README.zh.md)。
17
17
 
18
18
  ## 前置条件
@@ -23,18 +23,19 @@
23
23
  - 需要一个目标 profile —— 浏览器端只为 `web` profile 构建(`package.json`
24
24
  里 `dsh.client.platform = "web"`)。
25
25
 
26
- ## 按你的 DSH 版本选择 tag
26
+ ## 按你的 DSH 版本选择安装线
27
27
 
28
- 单一构建同时覆盖两条 DSH 线,所以任一 tag 安装的代码都能工作 —— 保留 tag
29
- 是为了让你锁定的版本在每条线上都有意义。
28
+ 两条长期分支分别服务两条 DSH 线:`main`(2.x)与 `sync/0.1.5-from-main`
29
+ (3.x,DSH 0.1.5 打包变体 —— `compat/0.1.5` 指向同一棵代码树)。
30
30
 
31
31
  | 你的 DSH | 安装方式 |
32
32
  |----------|----------|
33
- | `0.1.2-alpha.1` 或更高(含 `0.1.5-rc.2`) | `dsh plugin --profile web add dsh-perm-gate`(tag `latest`) |
33
+ | `0.1.2-alpha.1` – `0.1.5-rc.0` | `dsh plugin --profile web add dsh-perm-gate`(tag `latest`,2.x 线) |
34
+ | `0.1.5-rc.1` 或更高 | `dsh plugin --profile web add github:drscrewdriver/dsh-perm-gate#sync/0.1.5-from-main`(3.x 线;npm 尚无对应 dist-tag —— `@latest` 安装的是相同代码的 2.x 打包) |
34
35
  | 不高于 `0.1.1-rc.2` | `dsh plugin --profile web add dsh-perm-gate@legacy` |
35
36
 
36
- DSH 不强制 `engines.dsh`,因此 tag 是选择机制而非兼容性关卡。
37
- 详见 [RELEASING.md](./RELEASING.md) 了解为何单一制品覆盖两条线以及 tag 的发布方式。
37
+ DSH 不强制 `engines.dsh`,因此 tag 与 ref 是选择机制而非兼容性关卡。
38
+ 各线与 tag 的发布布局详见 [RELEASING.md](./RELEASING.md)。
38
39
 
39
40
  ## 用官方 CLI 安装
40
41
 
@@ -70,6 +71,10 @@ dsh plugin --profile web add .
70
71
  defaultAction: ask # allow | ask | deny
71
72
  ```
72
73
 
74
+ 自 2.6.0 起,规则本体存储在 `dsh-perm-gate-rules` settings 命名空间中,每次变更都会
75
+ 就地重编译闸门。上面的 YAML 文件变为可选:只有命名空间未被配置时才会读取它,插件
76
+ 发起的首次规则写入(白名单批准或设置界面编辑)会自动收编文件中的规则。
77
+
73
78
  可从 [examples/permissions.example.yaml](./examples/permissions.example.yaml) 起步,
74
79
  然后重载 profile。
75
80
 
@@ -85,6 +90,48 @@ dsh plugin --profile web update dsh-perm-gate
85
90
  dsh profile reload --profile web
86
91
  ```
87
92
 
93
+ ## **DSH** 升级后:重打输入区图标补丁
94
+
95
+ 「自动审查(高权限)」之所以和「自动审查」一样显示盾+眼图标,只是因为
96
+ `scripts/patch-permission-glyph.mjs` 把这一项加进了 **DSH 宿主包里的一张封闭 Map**。
97
+ DSH 对插件档位**设计上就不给图标** —— 那张表自己的注释写着 *"host-configured
98
+ names outside the design set get none"*;而插件能影响的 option 对象只携带
99
+ `{value, name, description}`,所以插件侧没有可用的接缝。
100
+
101
+ 该补丁改的是**宿主**文件,因此 **DSH 升级或重装会把它抹掉**。升级*本插件*不会:
102
+ 图标从来不属于插件,而插件自己的贡献(`cordis.patch.yml` 里的 `name:` /
103
+ `description:`)随包发布。
104
+
105
+ ```sh
106
+ npx dsh-perm-gate-patch-glyph # 应用补丁
107
+ npx dsh-perm-gate-patch-glyph --check # 只检查;图标已丢失时退出码为 1
108
+ dsh profile reload --profile web
109
+ ```
110
+
111
+ 脚本**随本包分发** —— 既是 `scripts/patch-permission-glyph.mjs`,也是一个
112
+ `dsh-perm-gate-patch-glyph` bin —— 所以不需要源码 checkout。它**故意没有**挂在
113
+ `postinstall` 上:它改的是宿主包,插件不该未经许可改写自己所在的 harness。有没有它
114
+ 都不影响 DSH 运行,档位本身照常工作。
115
+
116
+ 脚本是幂等的(重复执行是 no-op),只备份一次,且拒绝写坏切片 —— 它会对结果跑
117
+ `node --check`,失败即还原备份 —— 所以每次 DSH 升级后无条件重跑都是安全的。
118
+ 它从运行中的 `node` 二进制反推包路径,因此 nvm 换版本或安装软链重指都不会让它失效。
119
+
120
+ **重装插件并不会恢复图标。**`dsh plugin --profile web add …` 只是把参数转发给
121
+ profile 目录里的 pnpm,只写 profile 自己的 `node_modules`;`-w`
122
+ (`--workspace-root`,而本 profile 的 workspace 就是 `packages: ['.']`)对此没有
123
+ 任何改变。图标住在 DSH 安装里。在标准安装上,下面三条路径不是三份副本,而是
124
+ **同一个物理文件**:
125
+
126
+ | 路径 | 实际是什么 |
127
+ |------|-----------|
128
+ | `dirname(node)/node_modules/@deepseek-ai/dsh` | DSH 安装位(可能是软链) |
129
+ | `<profile>/node_modules/@deepseek-ai/dsh-client-ui-conversation` | 指向它的**目录联接(junction)** |
130
+ | `<dsh>/node_modules/@deepseek-ai/dsh-client-ui-conversation/lib/client.js` | 被补丁的那个文件 |
131
+
132
+ 要观察的现象:档位依然工作、门禁依然生效,但它在输入区下拉里那一行没有图标,
133
+ 收起后的触发器显示纯文字,而同排其他档位是图标+文字。
134
+
88
135
  ## 从分裂的插件迁移
89
136
 
90
137
  `dsh-perm-gate` 把原先散落在 `dsh-permission-rules`、`dsh-auto-mode`、
@@ -138,6 +185,11 @@ DSH 的 bundle patch 是整体替换 `permission.config.presets`,而非逐 key
138
185
  请重载 profile 让 `cordis.patch.yml` 重新生效,并确认没有更晚加载的插件覆盖了
139
186
  `presets`。
140
187
 
188
+ **「自动审查(高权限)」在输入区丢了图标。**
189
+ DSH 升级或重装替换了被打了补丁的宿主 bundle;重装插件不会把它带回来。执行
190
+ `npx dsh-perm-gate-patch-glyph` 后重载即可。档位本身不受影响 ——
191
+ 没有补丁,它的标签与门禁照常工作。
192
+
141
193
  **规则文件存在但 `--list` 显示 `ruleCount: 0`。**
142
194
  `rulesFile` 按 harness 进程的 CWD 解析,而不是插件目录。建议使用绝对路径,
143
195
  或确认 shell 的 CWD。格式错误的文档在加载时 loud fail,绝不会静默失效。
package/README.ja.md CHANGED
@@ -27,7 +27,8 @@
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` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x` は範囲) |
30
+ > | 0.1.2-alpha.1 ~ 0.1.5-rc.0 | `main` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x` は範囲) |
31
+ > | 0.1.5-rc.1+ | `sync/0.1.5-from-main`(=`compat/0.1.5`) | `3.x` | GitHub ref インストール(dist-tag 未公開) |
31
32
  >
32
33
  > バージョン系列は **DSH ライン**を追います(`1.x` = DSH ≤ 0.1.1、`2.x` = DSH 0.1.2+)。
33
34
  > メジャー同士が互いを遮断するため、`^1.x` のインストールが `2.x` を解決することは
@@ -45,7 +46,7 @@
45
46
  > メソッドであるため、`typeof` プロブで読み取り、欠落時やエラー時には「ポリシー
46
47
  > 不明」にフォールバックします。
47
48
 
48
- バージョン **2.0.0** — 変更履歴は [日本語 changelog](./CHANGELOG.ja.md) を参照。
49
+ バージョン **2.6.0** — 変更履歴は [日本語 changelog](./CHANGELOG.ja.md) を参照。
49
50
 
50
51
  DeepSeek Harness 向けの、単一・自己完結・決定論優先・fail-closed な権限ゲートです。
51
52
 
@@ -86,7 +87,7 @@ DSH の安全まわりのエコシステムでは、この役割が `dsh-permiss
86
87
  モード**。汎用の「自動承認」でもなく、包括的な権限付与でもありません。フロントが露出する
87
88
  のは**単一スイッチ**(`permissive`)だけで、バックエンドの 4 つの戦略は**組み合わせ
88
89
  可能**でプラグイン設定から制御されます。P0 に対しては依然 fail-closed です。
89
- 権限ピッカーと設定行はいずれも製品名「自动审查」で表示し、アイコンは描きません。
90
+ 権限ピッカーと設定行はいずれも製品名「自动审查」で表示します。アイコンについては後述(組み込みティアは標準装備、プラグイン提供ティアはパッチが必要)。
90
91
  - **サンドボックス昇格の自動回答**(`trustEscalation`)— サンドボックス昇格は shell /
91
92
  pwsh / edit ツールの**内部**(`tools/pre-execute` 後)から発生するためゲートはそれを
92
93
  見ておらず、ゲートが自動許可した呼び出しでも確認プロンプトが表示されます。この戦略を
@@ -117,7 +118,7 @@ dsh plugin --profile web add dsh-perm-gate
117
118
  rulesFile: ./permissions.yaml # 任意。既定は $DSH_HOME/perm-gate/rules.yml
118
119
  dshHome: $DSH_HOME # 保護対象チェックのルート固定
119
120
  defaultAction: ask # allow | ask | deny
120
- gatePresets: [permissive] # ゲートが有効なティア(既定)
121
+ gatePresets: [permissive, permissive-full] # ゲートが有効なティア(既定)
121
122
  ```
122
123
 
123
124
  ### ルール ファイル
@@ -147,18 +148,99 @@ permissions:
147
148
 
148
149
  完全な例: [examples/permissions.example.yaml](./examples/permissions.example.yaml)
149
150
 
151
+ ### ネットワークポリシー(任意で有効化)
152
+
153
+ ローカル HTTP/CONNECT プロキシです。**同じルールファイル**で **shell サブプロセス**の
154
+ 外向き通信を審査し、どのルールにも該当しない宛先には承認経路を提供します。**既定は無効** ——
155
+ 有効にするとループバックポートを確保し、子プロセスのプロキシ環境変数を書き換えるため、
156
+ 暗黙に有効化されることはありません。
157
+
158
+ ```yaml
159
+ - id: dsh-perm-gate
160
+ config:
161
+ networkEnabled: false # マスタースイッチ(既定 false)
162
+ networkMode: whitelist # deny-all | whitelist | allow-all
163
+ networkUnlisted: ask # ask | deny —— 未登録の宛先の扱い
164
+ networkUnattributed: allow # allow | deny —— shell 帰属のない通信
165
+ networkInjectEnv: true # 子プロセスに HTTP(S)_PROXY / ALL_PROXY を注入
166
+ networkAskTimeoutMs: 120000 # 承認待ちの上限。超過時は拒否
167
+ networkGrantTtlMs: 1800000 # 1 回の承認が有効なセッション期間
168
+ ```
169
+
170
+ **段階的な動作**:許可ルールがなければ、どの宛先にも到達できません。未登録の宛先は、
171
+ その接続を開いた shell コマンドのセッションに対して対話的承認へエスカレートし、承認すると
172
+ その宛先が当該セッションで許可されます。**`deny` ルールが承認に回ることはありません** ——
173
+ 承認は「どのルールも禁止していない宛先」の到達性を広げられますが、禁止しているルールを
174
+ 覆すことはできません。
175
+
176
+ **境界 —— 依存する前に必ず読んでください**:このプロキシは**協調型**のポリシー層であり、
177
+ 強制境界ではありません。プロキシ環境変数を**読む**クライアントの通信しか見えません。
178
+
179
+ | クライアント | 遮断できるか |
180
+ |--------------|--------------|
181
+ | `curl`、`wget`、`git`、Go `net/http`、Python `requests` | ✅ |
182
+ | **Node.js `http` / `https` / `fetch`** | ❌ **直接接続。プロキシから見えない** |
183
+ | Java(`-D` プロキシ指定なし)、.NET `HttpClient` | ❌ |
184
+ | 生ソケット、独自 TCP | ❌ |
185
+ | DNS、QUIC/HTTP3、非 HTTP プロトコル | ❌ |
186
+ | リテラル IP への接続 | ❌ |
187
+
188
+ したがって `node -e "require('http').get('http://host/')"` のようなコマンドは**遮断されません**。
189
+ これは「誤操作を防ぐガードレールであり、意図を宣言する場所」と考えてください。密閉された
190
+ サンドボックスではありません。
191
+
192
+ DSH **自身**の通信 —— 組み込みネットワークツールと LLM トランスポート —— は意図的に対象外
193
+ です。これらの接続には shell 帰属がなく、`networkUnattributed: allow`(既定)がそのまま
194
+ 通します。審査するとホストが**自分自身を遮断**してしまい、取り逃すよりはるかに悪い障害に
195
+ なります。ホストのクライアントがプロキシ環境変数を読まないと確信できる場合のみ `deny` に
196
+ してください。
197
+
198
+ 稼働状態の照会:`GET /api/dsh-perm-gate/network`(モード / バインド / ポート / プロキシ稼働 /
199
+ 環境注入状態 / 遮断カウンタ / 直近の遮断)。
200
+
150
201
  ## 自动审查ティア(マシン値 `permissive`)
151
202
 
152
203
  自动审查は権限ピッカーの中で 読み取り専用 / ワークスペース内変更 / 完全権限 /
153
204
  許可リストと並ぶ**独立した承認ティア**です。汎用の「自動承認」ではなく、包括的な権限を
154
205
  発行することもありません。人間/LLM のシーム**の前**で判定を狭めたり広めたりする
155
- だけで、P0 ハード拒否は単調かつ交渉不能のままです。
156
-
157
- ピッカーの表示名は**ホストが供給する製品名**で、言語別辞書の項目ではありません。DSH 0.1.2 は
206
+ だけで、P0 ハード拒否は**ゲート自身のスコープ内で**単調かつ交渉不能のままです。
207
+
208
+ > **P0 はスコープ付きであり、グローバルではありません。** このゲートはセッションの権限プリセットが
209
+ > `gatePresets`(既定は `permissive` / `permissive-full`)のいずれかである間だけ動作します。それ以外の
210
+ > プリセット —— 読み取り専用 / ワークスペース内変更 / 完全権限 —— では **P0 ハード拒否を含むゲート
211
+ > 全体が停止**し、選択されたティア自身のポリシーがそのセッションを統治します。これは意図的な設計です
212
+ > (設定表の `gatePresets` を参照)が、「P0 は交渉不能」は**ゲートのティア内**で成立するものであり、
213
+ > すべてのティアに跨るものではありません。停止は**沈黙しません**: セッション/プリセットの遷移ごとに
214
+ > `stand-down` イベントを 1 件記録し、ブラウザは入力欄の上に **GATE OFF** の常駐ストリップを表示します。
215
+ > `gatePresets: ['*']` に設定すると P0 は再びグローバルになります。
216
+
217
+ **2 つのバリアントを同梱します。** プリセットの `sandbox` と `approval` は**独立した**ノブであり、
218
+ 両者を結び付けると悪いトレードオフを強いられるためです。
219
+
220
+ | ピッカーの表示名 | マシン値 | sandbox | approval |
221
+ |------------------|----------|---------|----------|
222
+ | 自动审查 | `permissive` | `workspace-write` | `ask` |
223
+ | 自动审查(高権限) | `permissive-full` | `danger-full-access` | `ask` |
224
+
225
+ 通常のティアは組み込みのファイルサンドボックスを維持します。そのサンドボックスは子プロセスの
226
+ 起動に必要な名前付きパイプも拒否するため、`git clone`、MSYS2/Cygwin の `sh.exe`、ConPTY は
227
+ `Win32 error 5` / `couldn't create signal pipe` で失敗します。しかもゲートは
228
+ **`gatePresets` に列挙したティアでしか動作しない**ため、ゲートを使いたい場合はこの制限を受け入れる
229
+ 必要がありました。「自动审查(高権限)」はこの結合を解きます —— **承認動作は同一のまま、ファイル
230
+ サンドボックスの制限だけを外します**。どちらも既定の `gatePresets` に含まれるため、どちらを選んでも
231
+ P0–P4 の全チェーンが動作します。ゲートはプリセットの**名前**だけを読み、sandbox モードは読みません。
232
+
233
+ ピッカーの表示名は**ホストが供給する製品名**で、言語別辞書の項目ではありません。DSH は
158
234
  プラグイン提供ティアの `name:` を 2 つの権限サーフェス(一般設定の既定行と入力欄のピッカー)で
159
235
  そのまま描画し、自前のローカライズ済みラベルは 3 つの組み込み値にしか与えないため、
160
- `cordis.patch.yml` は全セッション共通で中国語ラベルを配布します。このティアは**アイコンを
161
- 描きません** —— ピッカーのグリフは組み込み値にのみ紐づきます。
236
+ `cordis.patch.yml` は全セッション共通で中国語ラベルを配布します。
237
+
238
+ **アイコンは別の話です。** 入力欄のグリフ表は閉じており、その表自身のコメントが規則を述べています:
239
+ *host-configured names outside the design set get none*。`permissive` は組み込み値なので
240
+ 「自动审查」には元から盾+目のアイコンがあります。「自动审查(高权限)」が同じアイコンを得るのは、
241
+ `npx dsh-perm-gate-patch-glyph` がその表に 1 項目を追加したからです。このパッチは
242
+ **ホスト**パッケージを書き換えるため、DSH アップグレードのたびに失われます ——
243
+ [DSH アップグレード後:入力欄アイコンのパッチを再適用](./INSTALL.ja.md) を参照してください。
162
244
 
163
245
  `cordis.yml` では:
164
246
 
@@ -221,11 +303,13 @@ never` パススルー — は人手に変更なく委譲されるため、将
221
303
  セッションの権限ピッカーには「auto-approval」ではなく、**独立して選択できる承認
222
304
  ティア**として「自动审查」が並びます。
223
305
 
224
- ゲートが動作するのは **`gatePresets` に列挙したティアの中だけ**です(既定 `['permissive']`、
225
- このプラグインが追加するティア)。それ以外のティア(Read Only / Workspace Write /
306
+ ゲートが動作するのは **`gatePresets` に列挙したティアの中だけ**です(既定
307
+ `['permissive', 'permissive-full']`、このプラグインが追加する 2 つのティア)。それ以外のティア(Read Only / Workspace Write /
226
308
  Full access / `custom`)では、ゲートの判定フローは**一切実行されません**——許可も、ask も、
227
- 拒否も、P0 ハード拒否も、拒否キーワードの遮断も、監査イベントの記録も行いません。選択された
228
- ティア自身の方針が呼び出しを決めます。`danger-full-access` の定義は「承認プロンプトなしの
309
+ 拒否も、P0 ハード拒否も、拒否キーワードの遮断も行いません。選択された
310
+ ティア自身の方針が呼び出しを決めます。ただし**沈黙はしません**: セッション/プリセットの遷移ごとに
311
+ `stand-down` イベントを 1 件だけ記録し(判定ではなく通知です)、ブラウザは入力欄の上に
312
+ **GATE OFF** の常駐ストリップを表示します。`danger-full-access` の定義は「承認プロンプトなしの
229
313
  フルアクセス」であり、そこを ask で上書きしても意味がありません(その方針では DSH の承認シームが
230
314
  **どの answerer よりも先に** `rejected` を返し、転送された ask はパネルを一度も表示せず
231
315
  `the user rejected tool "..."` にしかなりません)。ハード拒否で上書きするのも、ユーザーが選んだ
@@ -247,7 +331,8 @@ Full access / `custom`)では、ゲートの判定フローは**一切実行
247
331
  `llmAssist` 有効時、設定された LLM(OpenAI 互換エンドポイントなら任意——`classifierEndpoint` / `classifierModel` / `classifierApiKey` を自分の API に向けてください)が構造化プロトコルで `ask` を 1 件ずつ判定します。**判定はゲートの `tools/pre-execute` ウォーターフォール内、決定がホストへ返る前に行われます**:`safe` はそのまま委譲されるため承認パネルは一切表示されず、本当に判断できない判定だけが人手に回ります。
248
332
 
249
333
  - `safe` → 自動許可(監査ソースは `classifier`)。パネルは表示されません。
250
- - `risky` + **ハードリスクカテゴリ**(`deletion`、`credential`、`remote`、`system`、`bulk`)→ **自動拒否**、パネルなし。ハードリスクは自動許可も学習もされません。
334
+ - `risky` + **ハードリスクカテゴリ**(`deletion`、`credential`、`remote`、`system`、`bulk`)→ **人間への確認(ask)を維持**します。分類器は**決して拒否しません**。拒否は決定論的レイヤー(P0 ハード拒否、拒否キーワードのブラックリスト、明示的な `deny:` ルール)だけに属するため、誤判定されたカテゴリは常に交渉可能で、異議を唱えられないブロックにはなりません。ハードカテゴリが `neutral` と異なる重要な点は一つだけです: **決して学習されない**ため、承認を繰り返しても自動許可に沈殿しません。
335
+ (モデルの判断を拒否の根拠にした結果は実測済みです: 無害な `git commit -F …` が `remote` と判定され、パネルも再試行用の grant もない自動拒否が発生しました。)
251
336
  - `risky:neutral` → `riskLearning` 有効時(設定カード内、既定オフ)、人手で承認され実際に実行された neutral リスクは `tool|カテゴリ` ごとにカウントされ、`riskThreshold`(既定 3)到達かつ新呼び出しの操作指紋(コマンド語+対象の基底名)が確認済みサンプルに一致した場合、**同一操作のみ**自動許可されます。学習沈殿(`riskSediment`、既定オン)を有効にすると、しきい値に達したキーの確認サンプルは**決定論的な許可ルール**になります:指紋の正確一致は LLM を経由せず自動許可——llmAssist オフでも継続し、沈殿ルールは設定カードで確認・管理(終止 / サンプル削除)できます。
252
337
  - タイムアウト(`riskTimeoutMs`、既定 20 秒、1 回リトライ)、通信失敗、プロトコル外出力は元の `ask` を維持します。
253
338
 
@@ -258,8 +343,10 @@ Full access / `custom`)では、ゲートの判定フローは**一切実行
258
343
  人手に回した `ask` は回答が返るまで追跡されます。受動的な `approval/request` オブザーバが閉じた結果を記録し(`allowed-once` → **承認**、`rejected` → **拒否**、`cancelled` → **キャンセル**、`unavailable` → 拒否=承認チャネルなし)、オブザーバが関連付けできない場合(`callId` 欠落・approval サービスなし・上流リスナーの短絡)は `tools/result` が同じ ask をフォールバックとして解決します。承認時には承認後の学習進捗(`n`/しきい値)を表示し、通知バーも 3 つの終態にラベルを付けます。
259
344
 
260
345
  さらにプリセットの**拒否キーワード黑名単**(dsh-approval-gate の `DEFAULT_DENY_KEYWORDS` を継承:`rm -rf`、`push --force`、`drop table`、`mkfs`、`git reset --hard`、`docker system prune` など)を備え、テキストがキーワードを含む呼び出し(大小文字を区別しない部分一致)は許可リスト / 許可 / LLM より先に拒否します。設定カードでリストとして編集でき(プリセット項目にはタグ付き、ワンクリック復元対応)、未設定・空ならプリセットを適用します——黑名単が静かに無効化されることはありません。
261
- このティアは権限ピッカーに**アイコンを表示しません** —— ピッカーのグリフは組み込み 3 値にのみ
262
- 紐づき、プラグイン提供ティアはテキストのみです。
346
+ 「自动审查」と「自动审查(高权限)」はどちらもピッカーに盾+目のアイコンを描きます —— 前者は
347
+ DSH の組み込み表から、後者はインストールガイドに書かれたホストパッチからです。そのパッチが
348
+ ない場合、2 つ目のティアはどのサーフェスでもテキストのみになります。ラベルとゲートは影響を
349
+ 受けません。
263
350
 
264
351
 
265
352
  ## CLI(スタンドアロン dry-run)