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

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