dsh-perm-gate 0.2.0 → 0.2.1-beta.3

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.ja.md CHANGED
@@ -7,23 +7,132 @@
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1-beta.3] - 2026-09-10
11
+
10
12
  ### 追加
11
13
 
12
14
  - リスク判定 `llmAssist`:設定されたカスタム LLM(OpenAI 互換エンドポイント)が各 `ask` を
13
15
  `safe` / `risky:<カテゴリ>` で判定。ハードカテゴリ(deletion / credential / remote / system /
14
- bulk)は常に人手へ、タイムアウトは 1 回リトライ、失敗は常に fail-closed。
16
+ bulk)は自動拒否、タイムアウトは 1 回リトライ、失敗は常に fail-closed。
15
17
  - 裁決学習(`riskLearning`、既定オフ、`riskThreshold` 1–10、既定 3):人手で承認され実際に実行された
16
18
  neutral リスクをカウントし、指紋一致する同一操作のみ自動許可。`$DSH_HOME/perm-gate/learning.json`
17
19
  に永続化され、ユーザーの YAML ルールには書き込みません。
18
20
  - 決定イベントフィード:各決定を `$DSH_HOME/perm-gate/events.jsonl` に追記し、
19
21
  `GET /api/dsh-perm-gate/events?sessionId=&since=` で提供。ブラウザ側は入力欄上の通知バーで表示。
20
22
  - 権限ピッカーの盾アイコンが折りたたみトリガーにも表示されます。
23
+ - 承認記録ページ:会話ビューに「承認記録」タブを追加し、セッション内のゲート判定を新しい順に
24
+ タイムライン表示(種類タグ・リスクカテゴリ・時刻付き)。設定カードの許可リストは行ごとに削除できる
25
+ 編集可能なリスト+一括編集トグルになりました。
26
+ - dsh-approval-gate から継承したプリセット拒否キーワード(`DEFAULT_DENY_KEYWORDS`):キーワード一致
27
+ (大小文字を区別しない部分一致)で許可リスト / 許可 / LLM より先に拒否。設定カードでリスト編集でき
28
+ プリセットタグとワンクリック復元に対応、未設定・空ならプリセットを適用(黑名単は常に有効)。
29
+ - 学習沈殿(`riskSediment`、既定オン):しきい値に達したキーの確認サンプルが決定論的な自動許可ルールに——
30
+ 指紋の正確一致は LLM 呼び出しをスキップし、llmAssist オフでも継続。設定カードで一覧表示し、キー終止・
31
+ サンプル削除が可能(`GET/POST /api/dsh-perm-gate/learning`)。
32
+ - 選択可能な llmAssist 受信先:**カスタム API**(OpenAI 互換、Xiaomi MiMo
33
+ `https://api.xiaomimimo.com/v1` を含むプリセット付き)または **DSH ホストモデルグループ**
34
+ (`llm` サービス + `agentDefaultModel.currentSelection`、provider/model 上書き可)に加え、
35
+ **健全性テスト**ボタン(`POST /api/dsh-perm-gate/health`)で最小 completion の応答とレイテンシを確認。設定カードは
36
+ `GET /api/dsh-perm-gate/receiver` 経由でライブのプロバイダー/モデルグループ一覧(ホスト
37
+ `llm.listProviders`/`listModels`、カスタムグループ含む)を取得し、実効的な選択を表示します。
38
+ - **承認記録のレビュー面**:各決定が触れたファイルは変更前にスナップショットされ(≤5 ファイル、
39
+ 各 ≤256 KB、`$DSH_HOME/perm-gate/snapshots/`)、**承認記録**タブでファイルチップから行 diff
40
+ (`GET /api/dsh-perm-gate/diff`)を開き、**取り消し**(`POST /api/dsh-perm-gate/revert`)で会話に
41
+ 復元指示を送信できます。スナップショット管理バー(`GET /api/dsh-perm-gate/snapshots-stats` /
42
+ `POST /api/dsh-perm-gate/snapshots-clear`、セッション単位または全件)も追加。イベント行は
43
+ `files` / `justification` / `verdict` / `category` を持つようになりました。
44
+ - **人手承認の終態記録**:人手に回した `ask` を追跡し、受動的な `approval/request` オブザーバで
45
+ 実際の回答を記録します — `allowed-once` → 承認、`rejected` → 拒否、`cancelled` → キャンセル、
46
+ `unavailable` → 拒否(承認チャネルなし)。オブザーバが関連付けできない場合(`callId` 欠落、
47
+ approval サービスなし、上流リスナーの短絡)は `tools/result` がフォールバックとして同じ ask を
48
+ 解決します(「解決時に削除」が唯一の重複排除ルール)。承認時には承認後の学習進捗(`n`/しきい値)を
49
+ 表示し、通知バーは 3 つの終態をラベル表示します。
50
+
51
+ ### 削除
52
+
53
+ - 廃止された **Auto** 権限ティアを `cordis.patch.yml` に再記載しなくなりました。DSH の bundle
54
+ patch は `permission.config.presets` マップ全体を**置換**するため、このファイルは残すべき
55
+ ティアをすべて記載する必要があり、`dsh-auto-mode` が提供していた `auto` も含まれていました。
56
+ 同プラグインはアンインストール済みで、ノブは `workspace-write` と同一、説明文の
57
+ 「自動レビュー / ワンショット承認」は実装ごと失われ、さらにこのゲートはそのティアでは
58
+ 動作しません(`gatePresets` の既定は `['permissive']`)。ピッカーは組み込み 3 ティア
59
+ (`read-only` / `workspace-write` / `danger-full-access`、`@deepseek-ai/dsh-base/cordis.patch.yml`
60
+ から再記載)と本プラグインの `permissive` のみを提示します。
61
+ - `test/patch-presets.spec.ts` がこのキー集合を固定し、組み込みティアの脱落や廃止ティアの
62
+ 復活があればテストが失敗します。
21
63
 
22
64
  ### 変更
23
65
 
66
+ - ゲートは **`gatePresets` に列挙したティアでのみ動作**します(既定 `['permissive']`、
67
+ このプラグインが追加するティア)。それ以外のティア(Read Only / Workspace Write / Auto /
68
+ Full access / `custom`)では判定フローは一切実行されません——許可・ask・拒否・P0 ハード拒否・
69
+ 拒否キーワード遮断・監査イベントのいずれも行いません。`gatePresets: ['*']` で再び全体
70
+ (ハード拒否を含む)に適用できます。
24
71
  - `llmAssist` はリスクカテゴリプロトコルを使用(従来の allow/deny/ask 判定のスーパーセット)。
25
72
  `classifier.ts` はリスク判定器と OpenAI 互換トランスポート(`chatCompletion`)を共有します。
73
+ ハードリスクカテゴリ(`deletion` / `credential` / `remote` / `system` / `bulk`)は
74
+ **自動拒否**に変更(人手に渡すのではなく、明らかに危険な操作は確認不要);`risky:neutral`
75
+ (不確実)のみ人手に渡ります。
76
+ - `write` / `edit` に**パス感知のデフォルト**が追加され、ワークスペース内の書き込みは
77
+ `defaultAction`(通常 `allow`)に従い、ワークスペース外への書き込みはコンテンツ審査のため
78
+ `ask` に昇格。LLM 分類器が安全コンテンツを自動許可、有害コンテンツを自動拒否できるため、
79
+ 不確実な操作のみポップアップが発生します。
80
+
81
+ ### 修正
82
+
83
+ - **`llmAssist` の `safe` は記録されるだけで、実際には自動許可されていませんでした。**
84
+ `tools/pre-execute` リスナーは `ask` を即座に返してバックグラウンドで判定していましたが、ホストへ
85
+ 渡した ask はすでに承認 answerer へ向かっており、DSH にはそれを取り消す API がありません
86
+ (リクエストの `signal` ができるのは `cancelled` での決着だけです)。そのためパネルは表示され続け、
87
+ フィードには `safe` の自動許可が出ているのに人手でのクリックが必須でした — さらにハードリスクの
88
+ `auto-deny` はホストにまったく届いていませんでした。リスナーは決定を返す**前に** `refineAsk` を
89
+ await するようになり(`makePreExecuteListener`)、`safe` は `next()` で委譲されパネルは出ず、
90
+ ハードカテゴリは自動拒否、`risky:neutral` / `unresolved` / 判定失敗のみが人手の ask を維持します
91
+ (fail-closed)。待機時間は `riskTimeoutMs`(既定 20 秒)で上限が決まり、すでにキャンセルされた
92
+ 呼び出しは判定をスキップします。
93
+ - **ユーザーが選んだ権限ティアをゲートが上書きしていました。** ゲートは独立した承認ティアを
94
+ 持ちますが *すべての* プリセットで動作し、あらゆる越境が `ask` になって DSH 承認シームへ
95
+ 転送され、`danger-full-access`(`approval: never`)ではそのシームが **どの answerer よりも
96
+ 先に** `rejected` を返しました — パネルは一度も表示されず、read 専用以外のすべての呼び出しが
97
+ 誤解を招く `the user rejected tool "..."` で失敗しました。ハード拒否と拒否キーワード層も同様に
98
+ ティアを無視していたため、`danger-full-access`(「承認プロンプトなしのフルアクセス」)が
99
+ 黙って狭められていました。ゲート全体が `gatePresets` に限定され、範囲外では何も行わず記録も
100
+ しません。有効なティア内では、実効承認方針が `never` の場合 ask はパススルーに降格します。
101
+ - **読み取り専用の検索とセッション内ツールが ask になっていました。** `web_search` /
102
+ `modlens_read_image` は読み取り専用クエリ、`todo_write` / `render_ui` / `validate_dsh_ui` /
103
+ `ask_user_question` / `exit_plan_mode` / `ralph` / `workflow` はセッション内の状態または
104
+ 委譲でありながら自動許可の分類に含まれておらず、毎回 `ask`(`no rule matched; default action`
105
+ のポップアップ)になっていました。これらを自動許可に追加し、第三者製の読み取り専用ツール向けに
106
+ `autoAllowTools` 設定を新設しました。P0 ハード拒否と拒否キーワード層は先に実行されるため、
107
+ このリストが権限を広げることはありません。
108
+ - **`write`/`edit` がワークスペース外のパスに静かに書き込んでいました。** `~/.bashrc` や
109
+ 別ドライブへの書き込みに `defaultAction`(通常 `allow`)が適用され、審査なしで実行されて
110
+ いました。ルールが一致しない場合、`write`/`edit` ツールに対してパス感知のオーバーライドが
111
+ 適用され、ワークスペース内の書き込みは `defaultAction` に従い、ワークスペース外の書き込みは
112
+ コンテンツ審査のために `ask` に昇格します。有害なコンテンツは拒否され、`llmAssist` が有効
113
+ な場合は正常な追加が自動許可されます。`deny` ルールはパス範囲に関係なく勝ち、`allow` ルール
114
+ は内部パスを許可できますが、外部パスの審査を回避することはできません。
115
+ - **P0 の資格情報検出が文書本文まで走査していました。** トークン・秘密鍵・`credentials.yaml` を
116
+ 単に *言及* したファイルの書き込み/編集までハード拒否されていました。操作自体を表す引数
117
+ (`command` / `file_path` など)のみを走査するよう変更し、拒否キーワード層の「ファイルの
118
+ テキストは操作そのものではない」という規則に揃えました。
119
+ - **`$DSH_HOME/perm-gate/rules.yml` が読み込まれませんでした。** `rulesFile` に既定値がなく、
120
+ `config` を省略したプロファイルではルールセットが空+`defaultAction: ask` になり、ユーザーの
121
+ `defaultAction: allow` と `allow:` ルールは黙って無視されていました。`rulesFile` は
122
+ `<dataDir>/rules.yml` を既定値とし、設定カードの許可リストもそこへ書き込みます。
123
+ - **`format` が PowerShell の `Format-Table` を拒否していました。** ハイフンはコマンド識別子の
124
+ 単語境界ではないため、キーワードの端を識別子文字(`[A-Za-z0-9_-]`)で判定するようにし、
125
+ `format C: /q` は引き続き検出しつつ `Format-Table` / `mkfs.ext4` は正しく扱われます。
126
+ - 拒否キーワードのマッチングが、長い識別子の中に単に含まれるだけのキーワードを拒否しなくなり、
127
+ 文書本文の引数(`content` / `new_string` / `old_string` / `text` など)は走査対象外になりました
128
+ — ファイルのテキストは操作そのものではありません。マッチングは単語境界を意識するようになり、
129
+ 句読点で終わるキーワードや CJK キーワードも引き続き機能し、空白は圧縮されるため余分な空白を含む
130
+ コマンドも検出されます。
131
+
132
+ ### Fixed
26
133
 
134
+ - **すべての読み取りクエリに手動承認が必要でした。** `read` / `read_image` / `grep` / `glob` / `ls` / `lsp`
135
+ はワークスペース内のみの読み取り操作で、変更を加えることはできません。これらは自動許可されました。
27
136
 
28
137
  ### Added
29
138
  - **Permissive モード(独立審批枠)** — read-only / workspace-write / full-access / whitelist と並ぶ独立モード。
package/CHANGELOG.ko.md CHANGED
@@ -7,23 +7,130 @@
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1-beta.3] - 2026-09-10
11
+
10
12
  ### 추가
11
13
 
12
14
  - 위험 등급 `llmAssist`: 설정된 사용자 지정 LLM(OpenAI 호환 엔드포인트)이 각 `ask`를
13
15
  `safe` / `risky:<카테고리>`로 판정합니다. 하드 카테고리(deletion / credential / remote / system /
14
- bulk)는 항상 사람에게 전달되고, 시간 초과는 1회 재시도하며, 모든 실패는 fail-closed를 유지합니다.
16
+ bulk)는 자동 거부되고, 시간 초과는 1회 재시도하며, 모든 실패는 fail-closed를 유지합니다.
15
17
  - 판정 학습(`riskLearning`, 기본 꺼짐, `riskThreshold` 1–10, 기본 3): 사람이 승인하고 실제 실행된
16
18
  neutral 위험은 카운트되어 지문이 일치하는 동일 작업만 자동 허용 — `$DSH_HOME/perm-gate/learning.json`에
17
19
  영속화되며 사용자 YAML 규칙에는 기록되지 않습니다.
18
20
  - 결정 이벤트 피드: 모든 결정이 `$DSH_HOME/perm-gate/events.jsonl`에 추가되고
19
21
  `GET /api/dsh-perm-gate/events?sessionId=&since=`로 제공되며, 브라우저 절반이 입력창 위 알림 바로 표시합니다.
20
22
  - 권한 선택기 방패 아이콘이 축소된 트리거에도 표시됩니다.
23
+ - 승인 기록 페이지: 대화 보기에「승인 기록」탭이 추가되어 세션의 게이트 판정을 최신 순 타임라인으로
24
+ 표시합니다(종류 태그·위험 카테고리·시간 포함). 설정 카드의 허용 목록은 행별 삭제가 가능한 편집형
25
+ 목록 + 일괄 편집 토글로 바뀌었습니다.
26
+ - dsh-approval-gate에서 계승한 프리셋 거부 키워드(`DEFAULT_DENY_KEYWORDS`): 키워드 일치(대소문자 구분
27
+ 없는 부분 일치) 시 허용 목록/권한/LLM보다 먼저 거부. 설정 카드에서 목록으로 편집 가능하며 프리셋 태그와
28
+ 원클릭 복원을 지원하고, 미설정·빈 목록 시 프리셋이 적용됩니다(블랙리스트는 항상 활성).
29
+ - 학습 침전(`riskSediment`, 기본 켜짐): 임계값에 도달한 키의 확인 샘플이 결정론적 자동 허용 규칙이 됩니다——
30
+ 지문 정확 일치 시 LLM 호출을 건너뛰고 llmAssist가 꺼져도 유지됩니다. 설정 카드에 목록으로 표시되며 키
31
+ 종료·샘플 삭제가 가능합니다(`GET/POST /api/dsh-perm-gate/learning`).
32
+ - 선택 가능한 llmAssist 수신처: **사용자 지정 API**(OpenAI 호환, Xiaomi MiMo
33
+ `https://api.xiaomimimo.com/v1` 포함 프리셋) 또는 **DSH 호스트 모델 그룹**(`llm` 서비스 +
34
+ `agentDefaultModel.currentSelection`, provider/model 재정의 가능) — 그리고 **상태 테스트**
35
+ 버튼(`POST /api/dsh-perm-gate/health`)으로 최소 completion 응답과 지연 시간을 확인. 설정 카드는 `GET /api/dsh-perm-gate/receiver`를 통해
36
+ 라이브 프로바이더/모델 그룹 목록(호스트 `llm.listProviders`/`listModels`, 사용자 지정 그룹 포함)을
37
+ 읽어 현재 적용 선택을 표시합니다.
38
+ - **승인 기록 리뷰 화면**: 각 결정이 건드린 파일은 변경 전에 스냅샷되며(파일 ≤5개, 각 ≤256KB,
39
+ `$DSH_HOME/perm-gate/snapshots/`), **승인 기록** 탭에서 파일 칩을 눌러 줄 단위 diff
40
+ (`GET /api/dsh-perm-gate/diff`)를 열고 **되돌리기**(`POST /api/dsh-perm-gate/revert`)로 대화에
41
+ 복원 지시를 보낼 수 있습니다. 스냅샷 관리 바(`GET /api/dsh-perm-gate/snapshots-stats` /
42
+ `POST /api/dsh-perm-gate/snapshots-clear`, 세션 단위 또는 전체)도 추가. 이벤트 행은 이제
43
+ `files` / `justification` / `verdict` / `category`를 가집니다.
44
+ - **사람 승인 종결 기록**: 사람에게 넘긴 `ask`를 추적하고 수동적 `approval/request` 옵서버로
45
+ 실제 응답을 기록합니다 — `allowed-once` → 승인, `rejected` → 거부, `cancelled` → 취소,
46
+ `unavailable` → 거부(승인 채널 없음). 옵서버가 연관시키지 못하는 경우(`callId` 누락,
47
+ approval 서비스 없음, 상류 리스너의 단락) `tools/result`가 폴백으로 같은 ask를 정산합니다
48
+ ("정산 시 삭제"가 유일한 중복 제거 규칙). 승인 시 승인 후 학습 진행률(`n`/임계값)을 표시하고,
49
+ 알림 바는 세 가지 종결 상태를 라벨로 보여줍니다.
50
+
51
+ ### 제거
52
+
53
+ - 폐기된 **Auto** 권한 티어를 더 이상 `cordis.patch.yml`에 다시 기재하지 않습니다. DSH의 bundle
54
+ patch는 `permission.config.presets` 맵 전체를 **교체**하므로 이 파일은 유지할 티어를 모두
55
+ 기재해야 했고, `dsh-auto-mode`가 제공한 `auto`도 포함되어 있었습니다. 해당 플러그인은
56
+ 제거되었고 노브는 `workspace-write`와 동일하며, 설명문의 "자동 검토 / 일회성 승인"은 구현과
57
+ 함께 사라졌고, 이 게이트는 그 티어에서 동작하지 않습니다(`gatePresets` 기본값은
58
+ `['permissive']`). 피커는 내장 3개 티어(`read-only` / `workspace-write` / `danger-full-access`,
59
+ `@deepseek-ai/dsh-base/cordis.patch.yml`에서 재기재)와 이 플러그인의 `permissive`만 제공합니다.
60
+ - `test/patch-presets.spec.ts`가 이 키 집합을 고정하므로 내장 티어가 빠지거나 폐기 티어가
61
+ 되살아나면 테스트가 실패합니다.
21
62
 
22
63
  ### 변경
23
64
 
65
+ - 게이트는 **`gatePresets`에 나열한 티어에서만 동작**합니다(기본 `['permissive']`,
66
+ 이 플러그인이 추가하는 티어). 그 밖의 티어(Read Only / Workspace Write / Auto / Full access /
67
+ `custom`)에서는 판정 흐름이 전혀 실행되지 않습니다 — 허용·ask·거부·P0 하드 거부·거부 키워드
68
+ 차단·감사 이벤트 모두 해당하지 않습니다. `gatePresets: ['*']`는 게이트를 다시 전역(하드 거부
69
+ 포함)으로 적용합니다.
24
70
  - `llmAssist`는 위험 카테고리 프로토콜을 사용합니다(기존 allow/deny/ask 판정의 상위 집합).
25
71
  `classifier.ts`는 위험 판정기와 OpenAI 호환 전송 계층(`chatCompletion`)을 공유합니다.
72
+ 하드 위험 카테고리(`deletion` / `credential` / `remote` / `system` / `bulk`)는 이제
73
+ **자동 거부**됩니다(사람에게 전달하지 않음 — 명백히 위험한 작업에는 확인 불필요);
74
+ `risky:neutral`(불확실)만 사람에게 전달됩니다.
75
+ - `write` / `edit` 도구에 **경로 인식 기본값**이 추가되어, 워크스페이스 내 쓰기는 `defaultAction`
76
+ (보통 `allow`)을 따르고, 워크스페이스 밖 쓰기는 콘텐츠 검토를 위해 `ask`로 승격됩니다.
77
+ LLM 분류기가 안전한 콘텐츠를 자동 허용하고 유해한 콘텐츠를 자동 거부할 수 있으므로, 불확실한
78
+ 작업만 팝업이 발생합니다.
79
+
80
+ ### 수정
81
+
82
+ - **`llmAssist`의 `safe`는 기록만 될 뿐 실제로 자동 허용되지 않았습니다.** `tools/pre-execute`
83
+ 리스너가 `ask`를 즉시 반환하고 백그라운드에서 판정했지만, 호스트로 넘어간 ask는 이미 승인
84
+ answerer로 향하고 있고 DSH에는 이를 되돌릴 API가 없습니다(요청의 `signal`이 할 수 있는 일은
85
+ `cancelled`로 종결시키는 것뿐입니다). 그래서 패널은 계속 표시되었고, 피드에는 `safe` 자동 허용이
86
+ 찍혔는데도 사람이 직접 클릭해야 했습니다 — 하드 위험의 `auto-deny`는 호스트에 아예 전달되지
87
+ 않았습니다. 이제 리스너는 결정을 반환하기 **전에** `refineAsk`를 await하며
88
+ (`makePreExecuteListener`), `safe`는 `next()`로 위임되어 패널이 뜨지 않고, 하드 카테고리는 자동
89
+ 거부되며, `risky:neutral` / `unresolved` / 판정 실패만 사람에게 ask로 남습니다(fail-closed).
90
+ 대기 시간은 `riskTimeoutMs`(기본 20초)로 제한되며, 이미 취소된 호출은 판정을 건너뜁니다.
91
+ - **사용자가 선택한 권한 티어를 게이트가 덮어썼습니다.** 게이트는 독립 승인 티어를 가지지만
92
+ *모든* 프리셋에서 동작했고, 모든 경계가 `ask`가 되어 DSH 승인 시임으로 전달되었으며,
93
+ `danger-full-access`(`approval: never`)에서는 그 시임이 **어떤 answerer보다도 먼저**
94
+ `rejected`를 반환했습니다 — 패널은 한 번도 표시되지 않았고 read 전용이 아닌 모든 호출이
95
+ 오해를 부르는 `the user rejected tool "..."`로 실패했습니다. 하드 거부와 거부 키워드 계층도
96
+ 티어를 무시했기에 `danger-full-access`("승인 프롬프트 없는 전체 접근")가 조용히 좁혀졌습니다.
97
+ 이제 게이트 전체가 `gatePresets`로 한정되며 범위 밖에서는 아무것도 하지 않고 기록도 남기지
98
+ 않습니다. 유효한 티어 안에서는 유효 승인 정책이 `never`이면 ask가 패스스루로 강등됩니다.
99
+ - **읽기 전용 검색과 세션 내 도구가 ask로 처리되었습니다.** `web_search` /
100
+ `modlens_read_image`는 읽기 전용 질의이고 `todo_write` / `render_ui` / `validate_dsh_ui` /
101
+ `ask_user_question` / `exit_plan_mode` / `ralph` / `workflow`는 세션 내 상태 또는 위임인데도
102
+ 자동 허용 분류에 없어 매번 `ask`(`no rule matched; default action` 팝업)가 발생했습니다.
103
+ 이들을 자동 허용에 추가하고, 서드파티 읽기 전용 도구를 위한 `autoAllowTools` 설정을
104
+ 신설했습니다. P0 하드 거부와 거부 키워드 계층이 먼저 실행되므로 이 목록이 권한을 넓히지
105
+ 않습니다.
106
+ - **`write`/`edit`이 워크스페이스 밖 경로로 조용히 쓰기 실행했습니다.** `~/.bashrc`나 다른
107
+ 드라이브의 파일에 `defaultAction`(보통 `allow`)이 적용되어 검토 없이 실행되었습니다. 규칙이
108
+ 일치하지 않을 때 `write`/`edit` 도구에 경로 인식 오버라이드가 적용되어, 워크스페이스 내 쓰기는
109
+ `defaultAction`을 따르고 바깥 쓰기는 콘텐츠 검토를 위해 `ask`로 승격됩니다. 유해한 콘텐츠는
110
+ 거부되고, `llmAssist`가 켜져 있으면 정상적인 추가는 자동 허용됩니다. `deny` 규칙은 경로 범위와
111
+ 관계없이 우선하고, `allow` 규칙은 내부 경로를 허용할 수 있지만 외부 경로의 검토를 우회할 수
112
+ 없습니다.
113
+ - **P0 자격 증명 검출이 문서 본문까지 스캔했습니다.** 토큰·개인 키·`credentials.yaml`을 단지
114
+ *언급*한 파일의 쓰기/편집까지 하드 거부되었습니다. 이제 동작 자체를 나타내는 인수
115
+ (`command` / `file_path` 등)만 스캔하며, 거부 키워드 계층의 "파일 텍스트는 동작이 아니다"라는
116
+ 규칙과 일치합니다.
117
+ - **`$DSH_HOME/perm-gate/rules.yml`이 로드되지 않았습니다.** `rulesFile`에 기본값이 없어
118
+ `config`를 생략한 프로필에서는 규칙 집합이 비고 `defaultAction: ask`가 되었으며, 사용자의
119
+ `defaultAction: allow`와 `allow:` 규칙은 조용히 무시되었습니다. `rulesFile`은 이제
120
+ `<dataDir>/rules.yml`을 기본값으로 하며 설정 카드의 허용 목록도 그곳에 기록됩니다.
121
+ - **`format`이 PowerShell의 `Format-Table`을 거부했습니다.** 하이픈은 명령 식별자의 단어
122
+ 경계가 아니므로 키워드 끝을 식별자 문자(`[A-Za-z0-9_-]`)로 판정하도록 바꿨습니다 —
123
+ `format C: /q`는 계속 탐지되고 `Format-Table` / `mkfs.ext4`는 올바르게 처리됩니다.
124
+ - 거부 키워드 매칭이 긴 식별자 안에 단순히 포함된 키워드를 더 이상 거부하지 않으며,
125
+ 문서 본문 인수(`content` / `new_string` / `old_string` / `text` 등)는 아예 검사 대상에서
126
+ 제외됩니다 — 파일의 텍스트는 작업 자체가 아닙니다. 매칭은 이제 단어 경계를 인식하므로
127
+ 구두점으로 끝나는 키워드와 CJK 키워드도 계속 동작하고, 공백이 압축되어 여분의 공백이 있는
128
+ 명령도 탐지됩니다.
129
+
130
+ ### Fixed
26
131
 
132
+ - **모든 읽기 쿼리에 수동 승인이 필요했습니다.** `read` / `read_image` / `grep` / `glob` / `ls` / `lsp`
133
+ 은 워크스페이스 내의 읽기 전용 작업으로, 변경을 가할 수 없습니다. 이들은 자동 허용되었습니다.
27
134
 
28
135
  ### Added
29
136
  - **Permissive 모드(독립 승인 티어)** — read-only / workspace-write / full-access / whitelist와 나란한 독립 모드.
package/CHANGELOG.md CHANGED
@@ -7,11 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1-beta.3] - 2026-09-10
11
+
10
12
  ### Added
11
13
 
12
14
  - Risk-graded `llmAssist`: the configured custom LLM (any OpenAI-compatible endpoint) grades each
13
15
  `ask` as `safe` / `risky:<category>`; hard categories (deletion / credential / remote / system /
14
- bulk) always ask, timeouts retry once, and every failure stays fail-closed.
16
+ bulk) auto-deny, timeouts retry once, and every failure stays fail-closed.
15
17
  - Verdict learning (`riskLearning`, off by default; `riskThreshold` 1–10, default 3): neutral-risk
16
18
  asks confirmed by a human and actually executed count toward auto-allowing the exact same
17
19
  operation (fingerprint-matched) — persisted to `$DSH_HOME/perm-gate/learning.json`, never into
@@ -20,11 +22,167 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
20
22
  at `GET /api/dsh-perm-gate/events?sessionId=&since=`; the browser half shows the latest decision
21
23
  as a notice strip above the conversation input.
22
24
  - Permission-picker icon now also decorates the collapsed picker trigger.
25
+ - Approval-records page: the conversation view gains an **Approvals** tab listing the session's
26
+ gate decisions newest-first (timeline with kind tags, risk category, and time); the settings
27
+ card's whitelist is now an editable rule list with per-row delete plus a bulk-edit toggle.
28
+ - Learning sedimentation (`riskSediment`, default on): a threshold-reached key's confirmed samples
29
+ become deterministic auto-allow rules — exact fingerprint hits skip the LLM call entirely and
30
+ survive the llmAssist switch; the settings card lists them with per-key terminate and per-sample
31
+ remove (backed by `GET/POST /api/dsh-perm-gate/learning`).
32
+ - Selectable llmAssist receiver: **custom API** (OpenAI-compatible, with endpoint presets
33
+ including Xiaomi MiMo `https://api.xiaomimimo.com/v1`) or the **DSH host model group**
34
+ (`llm` service + `agentDefaultModel.currentSelection`, provider/model overridable) — plus a
35
+ **health test** button (`POST /api/dsh-perm-gate/health`) that runs one minimal completion
36
+ and reports latency. The card reads the live provider/model-group catalog
37
+ (host `llm.listProviders`/`listModels` — user-configured custom groups included) via
38
+ `GET /api/dsh-perm-gate/receiver` and shows the effective provider/model selection.
39
+ - Preset deny-keyword blacklist inherited from dsh-approval-gate (`DEFAULT_DENY_KEYWORDS`): a
40
+ case-insensitive keyword hit vetoes the call before whitelist / grants / LLM; editable as a
41
+ list in the settings card with preset tags and one-click restore; unset or empty applies the
42
+ preset (the blacklist never silently turns off).
43
+ - **Approval-records review plane**: every decision's affected files are snapshotted before the
44
+ change lands (≤5 files, ≤256 KB each) under `$DSH_HOME/perm-gate/snapshots/`; the **Approvals**
45
+ tab turns those files into clickable chips that open a line diff (`GET /api/dsh-perm-gate/diff`)
46
+ with a **revert** action delivering a restore instruction into the conversation
47
+ (`POST /api/dsh-perm-gate/revert`), plus a snapshot inventory bar
48
+ (`GET /api/dsh-perm-gate/snapshots-stats`, `POST /api/dsh-perm-gate/snapshots-clear`,
49
+ session-scoped or all). Event rows now carry `files`, `justification`, `verdict`, and `category`.
50
+ - **Manual-approval terminal records**: an `ask` routed to a human is tracked and settled with the
51
+ human's actual answer by a passive `approval/request` observer — `allowed-once` → approved,
52
+ `rejected` → rejected, `cancelled` → cancelled, `unavailable` → a denial (no approval channel).
53
+ `tools/result` settles the same ask as a fallback when the observer cannot correlate it (missing
54
+ `callId`, no approval service, or an earlier listener short-circuiting the waterfall);
55
+ "delete on settle" is the whole de-duplication rule. Approvals report the post-approval learning
56
+ progress (`n`/threshold), and the notice strip labels all three terminal states.
57
+
58
+ ### Removed
59
+
60
+ - The retired **Auto** permission tier is no longer restated in `cordis.patch.yml`. The DSH bundle
61
+ patch replaces the whole `permission.config.presets` map, so this file had to carry every tier
62
+ that should survive — including `auto`, which `dsh-auto-mode` contributed. That plugin is
63
+ uninstalled: its knobs were identical to `workspace-write`, its "automatic review / one-shot
64
+ approval" description lost its implementation with it, and this gate is inactive in that tier
65
+ (`gatePresets` defaults to `['permissive']`). The picker now offers the three built-ins
66
+ (`read-only` / `workspace-write` / `danger-full-access`, restated from
67
+ `@deepseek-ai/dsh-base/cordis.patch.yml`) plus this plugin's `permissive`.
68
+ - `test/patch-presets.spec.ts` pins that exact key set, so a built-in tier can no longer be dropped
69
+ (or a retired one resurrected) without failing the suite.
23
70
 
24
71
  ### Changed
25
72
 
73
+ - **DSH dual-version support (0.1.0-rc.7 … 0.1.2-rc.1).** One artifact now covers both
74
+ `dsh-v0.1.1-rc.2` and `dsh-v0.1.2-rc.1` without a version check. `effectivePolicy` is a
75
+ **private** method of the user-approval service in both versions, so it is read behind a
76
+ `typeof` probe and a throwing/absent probe now degrades to “policy unknown” (the ask stands)
77
+ instead of failing the decision path. Settings registration keeps `ctx.settings.register`,
78
+ which exists in every supported version. `dsh.client.inject` no longer names
79
+ `@deepseek-ai/dsh-client-runtime` (removed in 0.1.2) or `@deepseek-ai/dsh-client-ui-slots`
80
+ (not a dynamic client row in either version); it now lists the real client rows it renders
81
+ into. Added `engines.dsh` and a version-compatibility table to the README.
82
+ - The gate is **active only in the permission tiers listed in `gatePresets`** (default
83
+ `['permissive']`, the tier this plugin adds). In every other tier — Read Only, Workspace Write,
84
+ Auto, Full access, `custom` — its decision flow does not run at all: no allow, no ask, no deny,
85
+ no P0 hard-deny, no deny-keyword veto, and no audit event. `gatePresets: ['*']` makes the gate
86
+ global again (hard-deny included); an embedding that passes no scope keeps the legacy behaviour.
26
87
  - `llmAssist` now uses the risk-category protocol (superset of the previous allow/deny/ask verdicts);
27
88
  `classifier.ts` shares one OpenAI-compatible transport (`chatCompletion`) with the risk grader.
89
+ Hard-risk categories (`deletion` / `credential` / `remote` / `system` / `bulk`) now **auto-deny**
90
+ instead of routing to the human — the operation is clearly dangerous and no popup is needed;
91
+ only `risky:neutral` (uncertain) still reaches the human seam.
92
+ - `write` / `edit` tools now have a **path-aware default**: workspace-internal writes follow the
93
+ configured `defaultAction` (typically `allow`); writes to paths outside the session workspace
94
+ escalate to `ask` so the content can be reviewed. The LLM classifier can then auto-allow safe
95
+ content or auto-deny harmful content, so only truly uncertain operations produce a popup.
96
+
97
+ ### Fixed
98
+
99
+ - **`llmAssist` "safe" now actually auto-allows instead of only being recorded.** The
100
+ `tools/pre-execute` listener returned the `ask` immediately and graded the call in the
101
+ background, but an ask handed to the host is already on its way to the approval answerers and
102
+ DSH offers no API to retract it (the request's own `signal` can only settle it `cancelled`). The
103
+ panel therefore still appeared and the human still had to click, while the feed showed the
104
+ `safe` auto-allow — and a hard-risk `auto-deny` never reached the host at all. The listener now
105
+ awaits `refineAsk` **before** returning the decision (`makePreExecuteListener`): `safe` delegates
106
+ via `next()` with no panel, a hard-risk category auto-denies, and only `risky:neutral` /
107
+ `unresolved` / a grader failure keep the human ask (fail-closed). The wait is bounded by
108
+ `riskTimeoutMs` (default 20 s) and an already-cancelled call skips grading.
109
+ - **The shell write-pattern layer never ran for DSH's primary shell tool.** `SHELL_TOOLS` listed
110
+ `bash` / `pwsh` / `sh` / `cmd` / `powershell` but not **`shell`** (DSH's own tool name) or
111
+ `terminal`, so `git push`, `chmod`, `tee`, redirects and every other write pattern fell through
112
+ to `defaultAction` instead of escalating to `ask`. The roster is now
113
+ `shell` / `terminal` / `bash` / `pwsh` / `sh` / `cmd` / `powershell`, and the duplicate copy in
114
+ `learning.ts` was removed (single source in `evaluate.ts`, so the decision path and the learning
115
+ fingerprint cannot drift again).
116
+ - **Redirect and `tee` detection was too narrow.** Only `>>` matched, so `echo x > /tmp/out.txt`
117
+ and a bare `tee /tmp/log.txt` were treated as read-only. `> f` / `>> f` / `>f` now escalate while
118
+ the stderr idioms a read-only command uses (`2>&1`, `>&2`, `2>/dev/null`, `1>&2`) stay
119
+ read-only; `curl` / `wget` escalate only when they write a file (`-o` / `-O` / `>`).
120
+ - **`decideRules` ignored `args.command`.** It read only the derived `ctx.commandText`, so any
121
+ caller that built a context by hand (tests, embedders) silently lost command inspection. It now
122
+ falls back to `args.command`, matching `PermGateRuntime.ctxFor`.
123
+ - **The gate overruled the permission tier the user selected.** It owns an independent approval
124
+ tier, but it acted in *every* preset: any crossing became an `ask`, `dsh-tools` forwarded it to
125
+ the DSH approval seam, and under the `danger-full-access` preset (`approval: never`) that seam
126
+ returns `rejected` **before any answerer runs** — no panel was ever rendered and every
127
+ non-read-only call failed with the misleading `the user rejected tool "..."`. Its hard-deny and
128
+ deny-keyword layers likewise ignored the tier, so `danger-full-access` ("full access without
129
+ approval prompts") was silently narrowed. The whole gate is now scoped to `gatePresets`; outside
130
+ it the gate is inert and records nothing. Inside an active tier an `ask` is still degraded to
131
+ passthrough when the session's effective approval policy is `never`, so an unanswerable ask can
132
+ never become a denial.
133
+ - **Read-only search and session-local tools were asked about.** `web_search` and
134
+ `modlens_read_image` are read-only queries and `todo_write` / `render_ui` / `validate_dsh_ui` /
135
+ `ask_user_question` / `exit_plan_mode` / `ralph` / `workflow` are session-local state or
136
+ delegation, but none were in the auto-allow classification, so each one raised an `ask` (the
137
+ `no rule matched; default action` popup). They are auto-allowed now, and a new `autoAllowTools`
138
+ config list extends the classification for third-party read-only tools. P0 hard-deny and the
139
+ deny-keyword layer still run first, so the list can never widen authority.
140
+ - **`write` / `edit` to paths outside the workspace silently passed through.** A call that writes
141
+ to `~/.bashrc` or another drive's files received the configured `defaultAction` (typically `allow`)
142
+ and executed without review. A path-aware override now applies to write/edit tools when no rule
143
+ matches: workspace-internal writes follow the configured `defaultAction`, but any target path
144
+ outside the session's workspace escalates to `ask` so the content can be reviewed — harmful
145
+ content is denied; benign additions are auto-allowed when `llmAssist` is enabled. Explicit deny
146
+ rules still win regardless of path scope, and explicit allow rules can grant access to inside
147
+ paths but never bypass the review for outside ones.
148
+ - **P0 credential detection scanned document bodies**, so writing or editing any file that merely
149
+ *mentioned* a token, a private key, or `credentials.yaml` was hard-denied. It now scans only the
150
+ arguments that describe the operation (`command`, `file_path`, …), matching the deny-keyword
151
+ layer's existing rule that a file's text is not the operation.
152
+ - **A `$DSH_HOME/perm-gate/rules.yml` was never loaded.** `rulesFile` had no default, so a profile
153
+ entry that omitted `config` left the gate with an empty ruleset and `defaultAction: ask` — the
154
+ user's `defaultAction: allow` and `allow:` rules were silently ignored. `rulesFile` now defaults
155
+ to `<dataDir>/rules.yml` (like `eventsFile` / `learningFile`), and the settings card's whitelist
156
+ mirror actually writes there.
157
+ - **`format` vetoed the PowerShell cmdlet `Format-Table`.** A hyphen is not a word boundary for a
158
+ command identifier, so the blacklist matched `Format-Table` / `Format-List`. Keyword edges now
159
+ match against identifier characters (`[A-Za-z0-9_-]`), so `format C: /q` is still caught while
160
+ `Format-Table` and `mkfs.ext4` behave as before.
161
+ - **Every read query required manual approval.** `read` / `read_image` / `grep` / `glob` / `ls` / `lsp`
162
+ are workspace-only read operations that can't modify anything (P0 hard-deny still blocks reads
163
+ of sensitive paths outside the workspace root). They are now auto-allowed. The same applies to
164
+ all DSH internal coordination tools (`agent_teams_*`, `conversation_search`, `memory_*`,
165
+ `get_goal` / `update_goal` / `create_goal`, `taskboard_*`, `job_*`, `list_agents` /
166
+ `interrupt_agent` / `send_message`, `subagent` / `subagent_fork` / `terminal` / `skill`).
167
+
168
+ - **The Approvals tab resolved no session id**, so it listed nothing while the snapshot bar showed
169
+ the global inventory. A `conversation.view` occupant receives the standard props at the top level
170
+ (`sessionId` / `useSessions`) — the shape `ConversationRoot` consumes — not under `slotsProps`;
171
+ the view and the notice strip now probe the top level, the nested shape, and the session-list hook.
172
+ - **Events were recorded with an empty session id**, so the Approvals tab (which filters by the
173
+ current session) showed nothing even though `events.jsonl` was filling up. DSH's `ToolExecution`
174
+ carries the session on `agent.session.id` (workspace cwd on `agent.session.header.cwd`), not on
175
+ `exec.sessionId` / `exec.cwd`; both are now read from the agent when the direct fields are absent.
176
+ - **The review page was silently dead on a default install**: with a profile entry that omits
177
+ `config`, `dshHome` was empty, so the data directory was `undefined` and the event log was never
178
+ constructed — no `events.jsonl`, no snapshots, no persisted learning, and a 404 on
179
+ `GET /api/dsh-perm-gate/events`. `dshHome` now resolves to an explicit value, else `$DSH_HOME`,
180
+ else `~/.dsh`, so `<dshHome>/perm-gate/{events.jsonl,learning.json,snapshots/}` is always written.
181
+ - Deny-keyword matching no longer vetoes a keyword that merely appears inside a longer identifier,
182
+ and document-body arguments (`content`, `new_string`, `old_string`, `text`, …) are no longer
183
+ scanned at all — a file's text is not the operation. Matching is word-boundary aware now, so
184
+ punctuation-edged and CJK keywords keep working, and whitespace is collapsed so a command
185
+ written with extra spaces is still caught.
28
186
 
29
187
 
30
188
  ### Added
package/INSTALL.ja.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- `dsh-perm-gate` バージョン **0.1.0**。判定チェーン、ルールファイル形式、Permissive
15
+ `dsh-perm-gate` バージョン **0.2.1-beta.3**。判定チェーン、ルールファイル形式、Permissive
16
16
  ティアについては [日本語 README](./README.ja.md) を参照してください。
17
17
 
18
18
  ## 必要条件
@@ -52,7 +52,7 @@ profile の `cordis.yml` にプラグインを追加します:
52
52
  - id: dsh-perm-gate
53
53
  name: dsh-perm-gate
54
54
  config:
55
- rulesFile: ./permissions.yaml # 省略時は defaultAction にフォールバック
55
+ rulesFile: ./permissions.yaml # 任意。既定は $DSH_HOME/perm-gate/rules.yml
56
56
  dshHome: $DSH_HOME # 保護対象チェックのルート固定
57
57
  defaultAction: ask # allow | ask | deny
58
58
  ```
@@ -84,7 +84,7 @@ dsh profile reload --profile web
84
84
  追加します。
85
85
  3. それらが提供していた preset の上書きを削除します。`cordis.patch.yml` は
86
86
  `permission.config.presets` を**丸ごと置換**するため、他プラグインの古い
87
- key 単位パッチが Permissive / Auto ティアを黙って消すことがあります。
87
+ key 単位パッチが Permissive ティア(または組み込みティア)を黙って消すことがあります。
88
88
  4. profile を再読込し、後述の `--list` で検証します。
89
89
 
90
90
  ## 検証
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` 버전 **0.1.0**. 결정 체인, 규칙 파일 형식, Permissive 티어는
15
+ `dsh-perm-gate` 버전 **0.2.1-beta.3**. 결정 체인, 규칙 파일 형식, Permissive 티어는
16
16
  [한국어 README](./README.ko.md)를 참고하세요.
17
17
 
18
18
  ## 요구 사항
@@ -53,7 +53,7 @@ profile의 `cordis.yml`에 플러그인을 추가합니다:
53
53
  - id: dsh-perm-gate
54
54
  name: dsh-perm-gate
55
55
  config:
56
- rulesFile: ./permissions.yaml # 생략 시 defaultAction으로 폴백
56
+ rulesFile: ./permissions.yaml # 선택. 기본값은 $DSH_HOME/perm-gate/rules.yml
57
57
  dshHome: $DSH_HOME # 보호 대상 검사의 고정 루트
58
58
  defaultAction: ask # allow | ask | deny
59
59
  ```
@@ -83,7 +83,7 @@ dsh profile reload --profile web
83
83
  2. 위 네 플러그인을 `cordis.yml`에서 제거하고 `dsh-perm-gate` 항목만 추가합니다.
84
84
  3. 해당 플러그인들이 제공하던 preset 재정의를 삭제합니다. `cordis.patch.yml`은
85
85
  `permission.config.presets`를 **전체 교체**하므로, 다른 플러그인의 오래된
86
- key 단위 패치가 Permissive / Auto 티어를 조용히 없앨 수 있습니다.
86
+ key 단위 패치가 Permissive 티어(또는 내장 티어)를 조용히 없앨 수 있습니다.
87
87
  4. profile을 다시 불러오고 아래의 `--list`로 검증합니다.
88
88
 
89
89
  ## 검증
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 **0.1.0**. Continue with the [README](./README.md) for the
15
+ `dsh-perm-gate` version **0.2.1-beta.3**. Continue with the [README](./README.md) for the
16
16
  decision chain, the rules file format and the Permissive tier.
17
17
 
18
18
  ## Requirements
@@ -53,9 +53,10 @@ Add the plugin to your profile's `cordis.yml`:
53
53
  - id: dsh-perm-gate
54
54
  name: dsh-perm-gate
55
55
  config:
56
- rulesFile: ./permissions.yaml # optional; omit to fall back to defaultAction
56
+ rulesFile: ./permissions.yaml # optional; defaults to $DSH_HOME/perm-gate/rules.yml
57
57
  dshHome: $DSH_HOME # root pinned for protected-target checks
58
58
  defaultAction: ask # allow | ask | deny
59
+ gatePresets: [permissive] # tiers where the gate is active at all (default)
59
60
  ```
60
61
 
61
62
  Start from [examples/permissions.example.yaml](./examples/permissions.example.yaml),
@@ -85,7 +86,7 @@ and `dsh-movein-permissions`.
85
86
  entry above.
86
87
  3. Delete any preset overrides those plugins contributed — `cordis.patch.yml`
87
88
  **replaces** `permission.config.presets` wholesale, so stale per-key patches
88
- from other plugins can silently drop the Permissive or Auto tier.
89
+ from other plugins can silently drop the Permissive tier (or a built-in one).
89
90
  4. Reload the profile and verify with `--list` (below).
90
91
 
91
92
  ## Verify
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` 版本 **0.1.0**。裁决链、规则文件格式与 Permissive 档位请见
15
+ `dsh-perm-gate` 版本 **0.2.1-beta.3**。裁决链、规则文件格式与 Permissive 档位请见
16
16
  [中文 README](./README.zh.md)。
17
17
 
18
18
  ## 前置条件
@@ -52,7 +52,7 @@ dsh plugin --profile web add .
52
52
  - id: dsh-perm-gate
53
53
  name: dsh-perm-gate
54
54
  config:
55
- rulesFile: ./permissions.yaml # 可选;留空则按 defaultAction 兜底
55
+ rulesFile: ./permissions.yaml # 可选;默认 $DSH_HOME/perm-gate/rules.yml
56
56
  dshHome: $DSH_HOME # 受保护目标检查的钉死根目录
57
57
  defaultAction: ask # allow | ask | deny
58
58
  ```
@@ -82,7 +82,7 @@ dsh profile reload --profile web
82
82
  2. 从 `cordis.yml` 删除上述四个插件,只加入唯一的 `dsh-perm-gate` 条目。
83
83
  3. 删除这些插件贡献的 preset 覆盖 —— `cordis.patch.yml` 是**整体替换**
84
84
  `permission.config.presets`,其他插件遗留的逐 key 补丁可能静默抹掉
85
- Permissive 或 Auto 档。
85
+ Permissive 档(或某个内置档)。
86
86
  4. 重载 profile,并用下面的 `--list` 验证。
87
87
 
88
88
  ## 验证