dsh-perm-gate 5.2.0 → 5.3.1

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/lib/client.js CHANGED
@@ -29,6 +29,30 @@ window.__ModuleLoader__.load({
29
29
  "card.description": "独立审批档位:与只读 / 完全权限 / 白名单平行,在会话权限下拉中显示为「自动审查」。前端只暴露一个开关;后台四个审批策略可组合、由此处设置决定,仍对 P0 硬拒绝保持 fail-closed。",
30
30
  "card.permissive": "启用自动审查档位",
31
31
  "card.permissiveHint": "关闭时门禁行为与此前完全一致。",
32
+ "card.unattended": "无人值守档位(三套实现,每颗开关决定自己那一档是否存在)",
33
+ "card.unattendedHint": "三颗开关,每颗只管它旁边标着的那一档:关掉即实现不存在,那一档退回普通人工流,不会偷偷变成别的档。",
34
+ "card.mode.ask": "保留人工",
35
+ "card.mode.pass": "机械直通",
36
+ "card.askHint": "机械先筛并列出疑点,但确认仍交给人,且一次审查模型都不叫——适用于无审查 LLM 可用、或不值当花 token 的运行。",
37
+ "card.passHint": "只跑确定性机械审查器:命中不可逆 / 不可沉淀 / 读不懂的载荷就拦截并把理由回喂主模型,其余照常执行。零模型调用、永不问人——相当于 approval: never 的有审查版,也是 yolo 拿掉 LLM 后的劣化形态。",
38
+ "card.tierScopeHint": "只有落在门禁作用域(%scope)里的预设才会走到这一步;预设不在作用域内时整个门禁停用,任何开关都不会接管它的确认。",
39
+ "card.yolo": "YOLO — 永不人工确认:危险才拦,其余放行",
40
+ "card.yoloHint": "命中不可逆 / 不可沉淀 / 读不懂的载荷,或模型报硬风险时拦截,并把理由以「工具审核意见」回喂主模型;其余照常执行。没有可用模型时只靠确定性规则网拦截。",
41
+ "card.rulesFallback": "无模型时降级成规则闸门(cc-safety-net 级)",
42
+ "card.rulesFallbackHint": "只在审查模型未配置、或模型未能给出裁决时生效:按确定性规则拦危险、放行其余,不再把调用挂在没人应答的确认框上。模型正常回答时行为与之前完全一致。",
43
+ "card.scriptReview": "脚本正文随审查一并送审(默认开)",
44
+ "card.scriptReviewHint": "调用运行脚本文件时读取它的正文:审查模型与人看的是同一份代码,规则层也按正文判定不可逆(文件名看不出危险的删除正是这么漏掉的)。正文出境前脱敏并截断;关掉它就不会让文件内容离开本机,代价是工作区外的脚本回到「看不清」档位。",
45
+ "card.credentialPaths": "自定义凭据路径(长尾)",
46
+ "card.credentialPathsHint": "插件内置清单之外的机密文件。写法是路径 glob:双星跨目录、单星与问号只在一段内;不含通配符时按路径片段匹配(token 不会命中 ambient.txt);行首 ~/ 会展开为你的 home;相对写法也在任意深度匹配(secrets/*.env 覆盖 a/b/secrets/prod.env)。命中后与 .ssh 同档:工作区外读取被硬拒、被文件工具改写被硬拒(这一档不可申诉,也不会被审查模型放行),所以只写确实需要保护的路径。",
47
+ "card.credentialPathsPlaceholder": "新路径,如 ~/.myco/vault.token 或 secrets/*.env",
48
+ "card.denyKeywordsExtra": "追加黑名单关键词(不替换预置)",
49
+ "card.denyKeywordsExtraHint": "在你自己的命令词上加一道确定性拦截,例如公司内部 CLI 的吊销/清库子命令或自有令牌前缀。这里只做追加:无论上面「黑名单」是预置还是你替换过,这些词都会并进去。",
50
+ "card.listRemove": "移除该条目",
51
+ "card.extraContentArgKeys": "追加正文键(这些参数不被当操作读)",
52
+ "card.extraContentArgKeysHint": "内置清单(content/text/plan…)之外的文档字段名,例如某个自有工具的 steps/notes:加进来后,它的文字不再进入危险词与凭据形态的扁平扫描。只追加、不替换内置表——漏写一个内置键就会重现“把计划当操作拒掉”的静默失效。被执行的命令本身与被改写的路径走各自判据,这张表关不掉它们。",
53
+ "card.extraContentArgKeysPlaceholder": "新参数名,如 steps",
54
+ "card.extraOperationArgKeys": "追加操作键(把该字段当会执行的代码读)",
55
+ "card.extraOperationArgKeysHint": "给「正文其实会被执行」的字段开口子(如 workflowYaml):它的文本会进入黑名单与凭据预筛;与上一张表撞名时本表获胜。代价:把真正的文档字段写进来,就等于让「计划里提到 rm -rf」重新变成拒绝理由。",
32
56
  "card.scopeNote": "档位作用域:本门禁仅在会话权限档位为 %scope 时生效。其他档位下整个门禁停用(含 P0 硬拒绝),由所选档位自身的策略接管;停用时会写入一条 stand-down 事件,并在输入框上方常驻一条提示条。",
33
57
  "card.strategies": "后台审批策略(至少一个生效;可组合)",
34
58
  "card.strategy.trustAutoAllow": "trustAutoAllow — 作用域内安全操作自动放行,危险/未知转 ask(中间档基线)",
@@ -191,6 +215,30 @@ window.__ModuleLoader__.load({
191
215
  "card.description": "Independent approval tier, parallel to read-only / full-access / whitelist, shown as 自动审查 in the session permission picker. The front-end exposes a single switch; the four backend approval strategies are combinable via this panel and still fail-closed against P0 hard-deny.",
192
216
  "card.permissive": "Enable the 自动审查 tier",
193
217
  "card.permissiveHint": "When off, the gate behaves exactly as before.",
218
+ "card.unattended": "Unattended tiers (three implementations — each switch decides whether its own tier exists)",
219
+ "card.unattendedHint": "Three switches, each governing only the tier named beside it: switching an implementation off returns that tier to the ordinary human flow — it never quietly becomes a different tier.",
220
+ "card.mode.ask": "Keep human",
221
+ "card.mode.pass": "Mechanical pass-through",
222
+ "card.askHint": "The mechanical screen still filters and lists its doubts, but the ask stays with a person and no grader is called at all — the shape for runs with no reviewer LLM connected, or where the tokens are not worth spending.",
223
+ "card.passHint": "Runs the deterministic mechanical judge only: an irreversible, unlearnable or unreadable call is blocked and the reason fed back to the main model, everything else runs. No model call, no human prompt — the reviewed form of approval: never, and YOLO with the model taken away.",
224
+ "card.tierScopeHint": "Only presets inside the gate's scope (%scope) reach this step; outside it the whole gate stands down, so no switch takes over its confirmations.",
225
+ "card.yolo": "YOLO — never ask a human: block the dangerous, run the rest",
226
+ "card.yoloHint": "Blocks an irreversible / unlearnable / unreadable payload, or a hard-risk verdict from the model, and feeds the reason back to the main LLM as a 工具审核意见 message; everything else executes. With no reviewer available only the deterministic nets catch anything.",
227
+ "card.rulesFallback": "Degrade to a rule-only gate when there is no model (cc-safety-net class)",
228
+ "card.rulesFallbackHint": "Takes effect only when the risk reviewer is not configured or gave no verdict: the deterministic nets block what they can name and let the rest through, instead of parking the call on a popup nobody will answer. While a reviewer answers, behaviour is unchanged.",
229
+ "card.scriptReview": "Attach script bodies to the review (on by default)",
230
+ "card.scriptReviewHint": "When a call runs a script file, its body is read and given to the risk model and the human alike, and the deterministic nets grade the body too (a delete that only shows up inside the file is exactly what used to slip through). Bodies are redacted and length-capped before leaving the host; switching this off keeps file contents local at the cost of sending out-of-workspace scripts back to the unreadable tier.",
231
+ "card.credentialPaths": "Custom credential paths (the long tail)",
232
+ "card.credentialPathsHint": "Secret files the built-in list does not know. Entries are path globs: a double star crosses directories, a single star and a question mark stay inside one segment; without a wildcard it matches a whole path fragment (token will not hit ambient.txt); a leading ~/ expands to your home; a relative pattern also matches at any depth (secrets/*.env covers a/b/secrets/prod.env). A hit is held to the same standard as .ssh: reading it outside the workspace is hard-denied, mutating it with a file tool is hard-denied — that tier cannot be appealed or talked past by the reviewer, so declare only paths you truly need protected.",
233
+ "card.credentialPathsPlaceholder": "New path, e.g. ~/.myco/vault.token or secrets/*.env",
234
+ "card.denyKeywordsExtra": "Appended blacklist keywords (never replaces the preset)",
235
+ "card.denyKeywordsExtraHint": "A deterministic block on words of your own — a revoke/wipe subcommand of a company CLI, a token prefix you issue. This list only ever appends: whatever the blacklist above holds (preset or your own replacement), these words are merged in.",
236
+ "card.listRemove": "Remove this entry",
237
+ "card.extraContentArgKeys": "Appended document keys (never read as an operation)",
238
+ "card.extraContentArgKeysHint": "Document field names beyond the built-in list (content/text/plan…): once listed, their text stops entering the flattened scan that the destructive-word blacklist and the credential pre-screen read. Additive only — the built-in list cannot be replaced, because dropping one of its keys would silently re-create the bug where a plan gets refused for describing work. The executed command and the mutated path are judged on their own terms; this list cannot switch them off.",
239
+ "card.extraContentArgKeysPlaceholder": "New argument name, e.g. steps",
240
+ "card.extraOperationArgKeys": "Appended operation keys (read this field as code that runs)",
241
+ "card.extraOperationArgKeysHint": "Opens a route for fields whose body really is executed (workflowYaml and friends): their text enters the blacklist and the credential screen, and this list wins when both name the same key. Cost: naming a genuine document field here makes “the plan mentions rm -rf” a refusal reason again.",
194
242
  "card.scopeNote": "Preset scope: this gate only acts while the session permission preset is %scope. Under any other preset the whole gate stands down — P0 hard-deny included — and the selected preset's own policy takes over. A stand-down records one event and leaves a sticky strip above the input.",
195
243
  "card.strategies": "Backend approval strategies (at least one effective; combinable)",
196
244
  "card.strategy.trustAutoAllow": "trustAutoAllow — safe in-scope ops auto-allow; dangerous/unknown ask (baseline middle tier)",
@@ -353,6 +401,30 @@ window.__ModuleLoader__.load({
353
401
  "card.description": "read-only / full-access / whitelist と並ぶ独立の承認モード。セッション権限ピッカーでは「自动审查」と表示されます。フロントは単一スイッチのみ。バックエンドの 4 つの承認戦略はこのパネルで組み合わせ可能で、P0 ハード拒否に対して依然 fail-closed。",
354
402
  "card.permissive": "自动审查ティアを有効化",
355
403
  "card.permissiveHint": "オフのときは以前と完全に同じ動作です。",
404
+ "card.unattended": "無人確認ティア(3 つの実装 — スイッチは各自のティアの有無を決めます)",
405
+ "card.unattendedHint": "スイッチは三つ。それぞれ脇に書いたティアだけを制御します。実装をオフにするとそのティアは通常の人確認に戻り、別のティアに化けることはありません。",
406
+ "card.mode.ask": "人手確認を残す",
407
+ "card.mode.pass": "機械直通",
408
+ "card.askHint": "機械スクリーニングは引き続き危険を絞り疑点を列挙しますが、確認は人に残り審査モデルは一度も呼びません — 審査 LLM 未接続、またはトークンを費やさない運用の形です。",
409
+ "card.passHint": "決定論的な機械審査器のみを実行:不可逆 / 復元不能 / 読めない処理を検知したら遮断して理由を主モデルへ返し、それ以外は実行します。モデル呼び出しゼロ・人手確認なし。approval: never の審査付き版であり、LLM を外した YOLO の劣化形態でもあります。",
410
+ "card.tierScopeHint": "このステップまで到達するのはゲートの適用範囲(%scope)内のプリセットだけです。範囲外ではゲート全体が停止し、どのスイッチも確認を代行しません。",
411
+ "card.yolo": "YOLO — 人工確認は決して求めない:危険のみ遮断、他は実行",
412
+ "card.yoloHint": "復元不能 / 学習不能 / 読解不能なペイロード、またはモデルの重リスク判定を遮断し、理由を「工具審核意見」メッセージでメイン LLM に返します。それ以外は実行されます。レビューモデルが無い場合は決定論的ネットのみが機能します。",
413
+ "card.rulesFallback": "モデル不在時はルールゲートへ劣化(cc-safety-net 級)",
414
+ "card.rulesFallbackHint": "レビューモデルが未設定、または判定を返せなかったときだけ発効します。決定論的ルールが名指しできる危険を遮断し、残りは通します。誰も答えない確認画面で処理を止めないためです。モデルが答えている間は挙動は変わりません。",
415
+ "card.scriptReview": "スクリプト本文をレビューに添付(既定はオン)",
416
+ "card.scriptReviewHint": "呼び出しがスクリプトファイルを実行するとき、その本文を読み取り、レビューモデルと人が同じコードを見ます。決定論的ネットも本文を評価します(ファイル名からは見えない削除がまさに通過していた箇所)。送出前に秘匿化と長さ制限を実施。オフにするとファイル内容が外部へ出ませんが、ワークスペース外のスクリプトは「読解不能」ティアに戻ります。",
417
+ "card.credentialPaths": "カスタム認証情報パス(長尾)",
418
+ "card.credentialPathsHint": "内蔵リスト外の機密ファイル。書き方はパス glob:双アスタリスクはディレクトリを横断、単アスタリスクとはてなは 1 段内。ワイルドカードなしならパス断片として一致(token は ambient.txt に掛からない)。先頭の ~/ は home に展開、相対指定は任意の深さでも一致(secrets/*.env は a/b/secrets/prod.env を覆う)。該当すれば .ssh と同格:ワークスペース外からの読み取りもファイルツールでの書き換えも強制拒否——このティアは異議申立て、レビューモデルの通過も不可なので、本当に守るべきパスだけを宣言してください。",
419
+ "card.credentialPathsPlaceholder": "新しいパス、例:~/.myco/vault.token または secrets/*.env",
420
+ "card.denyKeywordsExtra": "ブラックリスト追加キーワード(プリセットを置き換えない)",
421
+ "card.denyKeywordsExtraHint": "自社 CLI の取消し/消去サブコマンドや自社トークン接頭語など、自分の語彙に決定論的な遮断を一段足します。ここは追加のみ:上のブラックリストがプリセットでも自分で置き換えたものでも、これらの語はマージされます。",
422
+ "card.listRemove": "この項目を削除",
423
+ "card.extraContentArgKeys": "追加のドキュメントキー(操作として読ませない)",
424
+ "card.extraContentArgKeysHint": "内蔵リスト(content/text/plan…)外のドキュメント項目名。追加すると、その文言は危険語と認証情報の平坦スキャンに入らなくなります。追加専用で内蔵表は置き換えません——内蔵キーを一つ落とすと「計画書を書いたという理由で拒否される」不具合が静かに復活します。実行されるコマンド自体と書き換えられるパスはそれぞれの判別経路なので、この表では無効化できません。",
425
+ "card.extraContentArgKeysPlaceholder": "新しい引数名、例:steps",
426
+ "card.extraOperationArgKeys": "追加の操作キー(その項目を実行されるコードとして読む)",
427
+ "card.extraOperationArgKeysHint": "本文が実際に実行される項目(workflowYaml など)用の出口です。その文言はブラックリストと認証情報スクリーンに入り、上の表と同名ならこちらが勝ちます。代償:本物のドキュメント項目を指定すると、「計画に rm -rf と書いた」ことが再び拒否理由になります。",
356
428
  "card.scopeNote": "プリセットスコープ: このゲートはセッションの権限プリセットが %scope のときだけ動作します。それ以外のプリセットでは P0 ハード拒否を含むゲート全体が停止し、選択されたプリセット自身のポリシーが引き継ぎます。停止時は stand-down イベントを 1 件記録し、入力欄の上に常駐表示します。",
357
429
  "card.strategies": "バックエンド承認戦略(少なくとも 1 つ有効、組み合わせ可)",
358
430
  "card.strategy.trustAutoAllow": "trustAutoAllow — スコープ内の安全操作は自動許可、危険/不明は ask(中間ティアのベースライン)",
@@ -515,6 +587,30 @@ window.__ModuleLoader__.load({
515
587
  "card.description": "read-only / full-access / whitelist와 나란한 독립 승인 모드. 세션 권한 선택기에서는 「自动审查」로 표시됩니다. 프론트는 단일 스위치만 노출. 백엔드의 네 가지 승인 전략은 이 패널에서 조합 가능하며 P0 하드 거부에 대해 여전히 fail-closed.",
516
588
  "card.permissive": "自动审查 티어 활성화",
517
589
  "card.permissiveHint": "꺼져 있으면 이전과 완전히 동일하게 동작합니다.",
590
+ "card.unattended": "무인 등급(구현 세 개 — 각 스위치가 자기 등급의 존재 여부를 정합니다)",
591
+ "card.unattendedHint": "스위치는 셋, 각자 옆에 적힌 등급만 제어합니다. 구현을 끄면 그 등급은 보통의 사람 확인으로 돌아가며, 다른 등급으로 몰래 바뀌지 않습니다.",
592
+ "card.mode.ask": "사람 확인 유지",
593
+ "card.mode.pass": "기계 직통과",
594
+ "card.askHint": "기계 선별로 위험을 걸러 내고 의문점을 적지만, 확인은 그대로 사람에게 남고 심사 모델은 한 번도 부르지 않습니다 — 심사 LLM 미연결이나 토큰을 아끼는 운용에 맞는 형태입니다.",
595
+ "card.passHint": "결정론적 기계 심사기만 실행:되돌릴 수 없음 / 복원 불가 / 읽히지 않는 처리를 감지하면 차단하고 이유를 주모델에 되돌려 보내며, 나머지는 실행합니다. 모델 호출 0회·사람 확인 없음. approval: never 의 심사판이며, LLM 을 뺀 YOLO 의 열화된 형태이기도 합니다.",
596
+ "card.tierScopeHint": "게이트 적용 범위(%scope) 안의 프리셋만 이 단계까지 옵니다. 범위 밖에서는 게이트 전체가 중지되므로 어떤 스위치도 확인을 대신하지 않습니다.",
597
+ "card.yolo": "YOLO — 사람 확인은 절대 요구하지 않음: 위험만 차단하고 나머지는 실행",
598
+ "card.yoloHint": "복구 불가 / 학습 불가 / 해석 불가 페이로드, 또는 모델이 위험 판정을 내리면 차단하고 이유를 「工具审核意见」 메시지로 메인 LLM에 알려합니다. 나머지는 그대로 실행됩니다. 검토 모델이 없으면 결정론적 규칙망만 작동합니다.",
599
+ "card.rulesFallback": "모델이 없을 때 규칙 게이트로 강등 (cc-safety-net 급)",
600
+ "card.rulesFallbackHint": "검토 모델이 설정되지 않았거나 판정을 내리지 못했을 때만 발동합니다. 결정론적 규칙이 식별한 위험은 차단하고 나머지는 통과시켜, 아무도 응답하지 않는 확인 창에 호출을 대기시키지 않습니다. 모델이 정상 응답하는 동안에는 동작이 변하지 않습니다.",
601
+ "card.scriptReview": "스크립트 본문 검토 함께 전송 (기본 켜짐)",
602
+ "card.scriptReviewHint": "호출이 스크립트 파일을 실행하면 본문을 읽어 검토 모델과 사람이 같은 코드를 보게 하고, 결정론적 규칙도 본문을 평가합니다 (파일명으로는 위험이 보이지 않는 삭제가 바로 그 통과 지점이었습니다). 전송 전 마스킹과 길이 제한을 적용합니다. 끄면 파일 내용이 외부로 나가지 않지만, 작업 공간 외부 스크립트는 다시 해석 불가 등급이 됩니다.",
603
+ "card.credentialPaths": "사용자 정의 자격증명 경로 (롱테일)",
604
+ "card.credentialPathsHint": "내장 목록이 모르는 비밀 파일입니다. 항목은 경로 glob: 이중 별은 디렉터리를 횡단하고 단별과 물음표는 한 단계 안에서만 동작합니다. 와일드카드가 없으면 경로 단편 전체로 일치 (token 은 ambient.txt 에 걸리지 않음). 선행 ~/ 는 home 으로 확장되고, 상대 지정은 어떤 깊이에서도 일치합니다 (secrets/*.env 는 a/b/secrets/prod.env 를 커버). 걸리면 .ssh 와 동일한 등급: 작업 공간 외부 읽기와 파일 도구 통한 변경 모두 경성 거부입니다—이 등급은 이의 제기나 검토 모델 우회가 불가능하니 정말 보호할 경로만 선언하세요.",
605
+ "card.credentialPathsPlaceholder": "새 경로, 예: ~/.myco/vault.token 또는 secrets/*.env",
606
+ "card.denyKeywordsExtra": "추가 블랙리스트 키워드 (프리셋을 대체하지 않음)",
607
+ "card.denyKeywordsExtraHint": "사내 CLI 의 폐기/삭제 서브커맨드나 자체 토큰 접두사처럼 자신만의 용어에 결정론적 차단을 한 겹 더합니다. 여기는 추가만 합니다: 위의 블랙리스트가 프리셋이든 대체한 것이든 이 단어들은 병합됩니다.",
608
+ "card.listRemove": "이 항목 삭제",
609
+ "card.extraContentArgKeys": "추가 문서 키 (작업으로 읽지 않음)",
610
+ "card.extraContentArgKeysHint": "내장 목록(content/text/plan…) 밖의 문서 필드명입니다. 추가하면 그 텍스트가 위험 단어 · 자격증물 평탄화 스캔에 들어가지 않습니다. 추가 전용이며 내장 표를 대체하지 않습니다 — 내장 키 하나를 빠뜨리면 『계획서를 작성했다는 이유로 거부되는』 결함이 조용히 부활합니다. 실제로 실행되는 명령과 변경되는 경로는 각자의 판정을 따르므로 이 표로는 끌 수 없습니다.",
611
+ "card.extraContentArgKeysPlaceholder": "새 인자 이름, 예: steps",
612
+ "card.extraOperationArgKeys": "추가 작업 키 (해당 필드를 실행되는 코드로 읽음)",
613
+ "card.extraOperationArgKeysHint": "본문이 실제로 실행되는 필드(workflowYaml 등)를 위한 통로입니다. 텍스트가 블랙리스트와 자격증물 스크린에 들어가며 위 표와 같은 키를 지정하면 이쪽이 이깁니다. 대가: 진짜 문서 필드를 지정하면 『계획에 rm -rf 가 적혀 있다』는 다시 거부 이유가 됩니다.",
518
614
  "card.scopeNote": "프리셋 범위: 이 게이트는 세션 권한 프리셋이 %scope일 때만 동작합니다. 다른 프리셋에서는 P0 하드 거부를 포함한 게이트 전체가 정지하고, 선택된 프리셋 자체의 정책이 대신합니다. 정지 시 stand-down 이벤트가 1건 기록되고 입력창 위에 상시 표시됩니다.",
519
615
  "card.strategies": "백엔드 승인 전략(하나 이상 유효, 조합 가능)",
520
616
  "card.strategy.trustAutoAllow": "trustAutoAllow — 범위 내 안전 작업은 자동 허용, 위험/미확인은 ask(중간 티어 기준)",
@@ -677,6 +773,30 @@ window.__ModuleLoader__.load({
677
773
  "card.description": "Niveau d'approbation indépendant : parallèle à lecture seule / accès complet / liste blanche, affiché comme « Examen automatique » dans le sélecteur de permissions de session. Le front-end n'expose qu'un seul interrupteur ; les quatre stratégies d'approbation backend sont combinables via ce panneau et restent fail-closed contre le refus dur P0.",
678
774
  "card.permissive": "Activer le niveau d'examen automatique",
679
775
  "card.permissiveHint": "Lorsqu'il est désactivé, la porte se comporte exactement comme avant.",
776
+ "card.unattended": "Modes sans supervision (trois implémentations ; chaque interrupteur décide si son palier existe)",
777
+ "card.unattendedHint": "Trois interrupteurs, chacun ne régit que le palier nommé à côté : éteindre une implémentation rend ce palier au flux humain ordinaire — il ne devient jamais un autre palier en secret.",
778
+ "card.mode.ask": "Garder l'humain",
779
+ "card.mode.pass": "Passage mécanique",
780
+ "card.askHint": "Le tamis mécanique filtre toujours et expose ses doutes, mais la demande reste soumise à une personne et aucun évaluateur n'est appelé — la forme qui convient quand aucun LLM relecteur n'est branché, ou quand les jetons ne valent pas la dépense.",
781
+ "card.passHint": "N'exécute que le juge mécanique déterministe : une opération irréversible, irrécupérable ou illisible est bloquée et justifiée en retour au modèle principal, le reste s'exécute. Aucun appel de modèle, aucune demande humaine — la version examinée de approval: never, et le YOLO privé de son modèle.",
782
+ "card.tierScopeHint": "Seuls les préréglages compris dans le périmètre du garde (%scope) atteignent cette étape ; en dehors, le garde s'efface complètement et aucun interrupteur ne prend ses confirmations en charge.",
783
+ "card.yolo": "YOLO — ne jamais demander un humain : bloquer le dangereux, exécuter le reste",
784
+ "card.yoloHint": "Bloque une charge irréversible / inapprenable / illisible, ou un verdict à risque dur du modèle, et renvoie la raison au LLM principal dans un message 工具审核意见 ; tout le reste s'exécute. Sans modèle disponible, seules les filets déterministes bloquent.",
785
+ "card.rulesFallback": "Dégrader vers une porte à règles quand il n'y a pas de modèle (classe cc-safety-net)",
786
+ "card.rulesFallbackHint": "Ne s'applique que lorsque le relecteur de risque n'est pas configuré ou n'a pas rendu de verdict : les filets déterministes bloquent ce qu'ils peuvent nommer et laissent passer le reste, au lieu de immobiliser l'appel sur une popup que personne ne répondra. Tant qu'un relecteur répond, le comportement est inchangé.",
787
+ "card.scriptReview": "Joindre le contenu des scripts à la revue (activé par défaut)",
788
+ "card.scriptReviewHint": "Quand un appel exécute un fichier script, son corps est lu et donné au modèle de risque comme à l'humain, et les filets déterministes évaluent aussi ce corps (une suppression invisible dans le nom de fichier est précisément ce qui passait). Le corps est masqué et borné avant sortie ; le désactiver garde le contenu des fichiers en local, au prix de renvoyer les scripts hors espace de travail dans le palier « illisible ».",
789
+ "card.credentialPaths": "Chemins de credentials personnalisés (la longue traîne)",
790
+ "card.credentialPathsHint": "Des fichiers secrets que la liste intégrée ne connaît pas. Les entrées sont des globs de chemin : une double étoile traverse les répertoires, une étoile simple et un point d'interrogation restent dans un segment ; sans joker, la correspondance se fait sur un fragment de chemin entier (token ne touchera pas ambient.txt) ; un ~/ initial se développe vers votre home ; un motif relatif correspond aussi à n'importe quelle profondeur (secrets/*.env couvre a/b/secrets/prod.env). Une correspondance est tenue au même standard que .ssh : la lecture hors espace de travail et la mutation par un outil de fichier sont refusées durablement — ce niveau n'est ni contestable ni franchissable par le relecteur, ne déclarez donc que les paths à protéger vraiment.",
791
+ "card.credentialPathsPlaceholder": "Nouveau chemin, ex. ~/.myco/vault.token ou secrets/*.env",
792
+ "card.denyKeywordsExtra": "Mots-clés blacklist ajoutés (ne remplace pas le prédéfini)",
793
+ "card.denyKeywordsExtraHint": "Un blocage déterministe sur vos propres termes — une sous-commande de révocation d'un CLI interne, un préfixe de jeton qui vous est propre. Cette liste n'ajoute que des termes : quel que soit le contenu de la blacklist au-dessus (prédéfinie ou remplacée par vous), ces mots sont fusionnés.",
794
+ "card.listRemove": "Retirer cette entrée",
795
+ "card.extraContentArgKeys": "Clés de document ajoutées (jamais lues comme une opération)",
796
+ "card.extraContentArgKeysHint": "Des noms de champs documentaires absents de la liste intégrée (content/text/plan…) : une fois déclarés, leur texte ne participe plus à la lecture à plat des mots dangereux et des formes d'identifiants. Purement additif : la liste intégrée ne peut pas être remplacée, car oublier une de ses clés ferait reparaître en silence le bug où un plan est refusé pour décrire une action. La commande réellement exécutée et le chemin modifié jugés par leurs propres voies, cette liste ne peut pas les éteindre.",
797
+ "card.extraContentArgKeysPlaceholder": "Nouveau nom d'argument, ex. steps",
798
+ "card.extraOperationArgKeys": "Clés d'opération ajoutées (lire ce champ comme du code exécuté)",
799
+ "card.extraOperationArgKeysHint": "Un passage pour les champs dont le corps est réellement exécuté (workflowYaml etc.) : leur texte entre dans la blacklist et l'écran d'identifiants, et cette liste gagne en cas de doublon. Coût : désigner un vrai champ documentaire rend de nouveau « le plan mentionne rm -rf » motif de refus.",
680
800
  "card.scopeNote": "Portée du préréglage : cette porte n'agit que lorsque le préréglage de permission de session est %scope. Sous tout autre préréglage, la porte entière se met en veille — y compris le refus dur P0 — et la politique du préréglage sélectionné prend le relais. La mise en veille enregistre un événement et affiche une bande persistante au-dessus de la saisie.",
681
801
  "card.strategies": "Stratégies d'approbation backend (au moins une active ; combinables)",
682
802
  "card.strategy.trustAutoAllow": "trustAutoAllow — les opérations sûres dans le périmètre sont auto-autorisées ; dangereuses/inconnues → demande (niveau intermédiaire de base)",
@@ -839,6 +959,30 @@ window.__ModuleLoader__.load({
839
959
  "card.description": "Unabhängige Genehmigungsstufe: parallel zu Nur-Lesen / Vollzugriff / Whitelist, angezeigt als „Automatische Überprüfung\" im Sitzungs-Berechtigungswähler. Das Frontend bietet einen einzigen Schalter; die vier Backend-Genehmigungsstrategien sind über dieses Panel kombinierbar und bleiben fail-closed gegen P0-Hartablehnung.",
840
960
  "card.permissive": "Stufe „Automatische Überprüfung\" aktivieren",
841
961
  "card.permissiveHint": "Wenn deaktiviert, verhält sich das Gate genau wie zuvor.",
962
+ "card.unattended": "Unbeaufsichtigte Stufen (drei Implementierungen; jeder Schalter entscheidet, ob seine Stufe existiert)",
963
+ "card.unattendedHint": "Drei Schalter, jeder steuert nur die Stufe daneben: wer die Implementierung abschaltet, lässt diese Stufe zum normalen Menschenablauf zurückkehren — sie wird nie stillschweigend zu einer anderen.",
964
+ "card.mode.ask": "Mensch behalten",
965
+ "card.mode.pass": "Mechanischer Durchlass",
966
+ "card.askHint": "Die mechanische Vorprüfung filtert und nennt ihre Zweifel, aber die Anfrage bleibt bei einem Menschen, und das Bewertungsmodell wird kein einziges Mal aufgerufen — die Form für Läufe ohne verbundenen Reviewer-LLM oder wenn sich die Tokens nicht lohnen.",
967
+ "card.passHint": "Lässt nur den deterministischen mechanischen Prüfer laufen: eine irreversible, nicht rücknehmbare oder unlesbare Anfrage wird blockt und dem Hauptmodell begründet zurückgemeldet, alles andere läuft. Kein Modellaufruf, keine Frage an einen Menschen — die geprüfte Form von approval: never und YOLO ohne Modell.",
968
+ "card.tierScopeHint": "Nur Presets innerhalb des Gate-Umfangs (%scope) erreichen diesen Schritt; außerhalb davon schaltet sich das Gate komplett ab, kein Schalter übernimmt dann dessen Bestätigungen.",
969
+ "card.yolo": "YOLO — niemals einen Menschen fragen: nur Gefährliches blockieren, Rest ausführen",
970
+ "card.yoloHint": "Blockiert eine unwiderrufliche / nicht lernbare / unlesbare Last oder ein Hartrisiko-Urteil des Modells und meldet den Grund dem Haupt-LLM als 工具审核意见-Nachricht; alles andere läuft. Ohne erreichbares Review-Modell fangen nur die deterministischen Netze.",
971
+ "card.rulesFallback": "Ohne Modell zu einem Regel-Gate degradieren (cc-safety-net-Klasse)",
972
+ "card.rulesFallbackHint": "Greift nur, wenn das Review-Modell nicht konfiguriert ist oder kein Urteil geliefert hat: die deterministischen Netze blockieren, was sie benennen können, und lassen den Rest durch, statt den Aufruf in einem Popup warten zu lassen, das niemand beantwortet. Solange ein Modell antwortet, bleibt das Verhalten unverändert.",
973
+ "card.scriptReview": "Skriptinhalte der Prüfung beifügen (standardmäßig an)",
974
+ "card.scriptReviewHint": "Führt ein Aufruf eine Skriptdatei aus, wird ihr Inhalt gelesen und dem Risikomodell wie dem Menschen gezeigt, und die deterministischen Netze bewerten ebenfalls den Inhalt (ein Löschen, das nur in der Datei sichtbar ist, war genau das, was durchrutschte). Inhalte werden vor dem Verlassen des Rechners maskiert und begrenzt; abschalten hält Dateiinhalte lokal, kostet aber, dass Skripte außerhalb des Arbeitsbereichs wieder als „unlesbar“ gelten.",
975
+ "card.credentialPaths": "Eigene Credential-Pfade (der lange Schwanz)",
976
+ "card.credentialPathsHint": "Geheime Dateien, die die eingebaute Liste nicht kennt. Einträge sind Pfad-Globs: ein Doppelstern überquert Verzeichnisse, ein Einzelstern und ein Fragezeichen bleiben in einem Segment; ohne Joker wird über ein ganzes Pfadfragment verglichen (token trifft nicht ambient.txt); ein führendes ~/ wird zu Ihrem Home aufgelöst; ein relativer Muster passt auch in jeder Tiefe (secrets/*.env deckt a/b/secrets/prod.env ab). Ein Treffer gilt nach demselben Maßstab wie .ssh: Lesen außerhalb des Arbeitsbereichs und Ändern durch ein Dateiwerkzeug werden hart abgelehnt — diese Stufe ist weder anfechtbar noch vom Prüfer umgehbar, erklären Sie also nur Pfade, die wirklich geschützt müssen werden.",
977
+ "card.credentialPathsPlaceholder": "Neuer Pfad, z. B. ~/.myco/vault.token oder secrets/*.env",
978
+ "card.denyKeywordsExtra": "Zusätzliche Blacklist-Schlüsselwörter (ersetzt den Voreinstellung nicht)",
979
+ "card.denyKeywordsExtraHint": "Eine deterministische Blockierung für Ihre eigenen Begriffe — ein Sperr-/Lösch-Unterbefehl einer firmeneigenen CLI, ein von Ihnen ausgegebener Token-Präfix. Diese Liste fügt nur hinzu: was die Blacklist oben enthält (Voreinstellung oder eigene Ersetzung), diese Wörter werden übernommen.",
980
+ "card.listRemove": "Diesen Eintrag entfernen",
981
+ "card.extraContentArgKeys": "Zusätzliche Dokument-Schlüssel (nicht als Operation gelesen)",
982
+ "card.extraContentArgKeysHint": "Feldnamen außerhalb der eingebauten Liste (content/text/plan…): gelistet, gelangt ihr Text nicht mehr in das flache Lesen von Gefahrwörtern und Credential-Formen. Nur ergänzend — die eingebaute Liste ist nicht ersetzbar, denn ein fehlender Schlüssel würde den Fehler leise zurückbringen, dass ein Plan für das bloße Beschreiben einer Arbeit abgelehnt wird. Das tatsächlich ausgeführte Kommando und der geänderte Pfad werden auf eigenen Wegen geprüft; diese Liste kann sie nicht abschalten.",
983
+ "card.extraContentArgKeysPlaceholder": "Neuer Argumentname, z. B. steps",
984
+ "card.extraOperationArgKeys": "Zusätzliche Operations-Schlüssel (Feld als ausgeführten Code lesen)",
985
+ "card.extraOperationArgKeysHint": "Ein Weg für Felder, deren Inhalt wirklich ausgeführt wird (workflowYaml u. ä.): ihr Text erreicht Blacklist und Credential-Prüfung, und bei Namensgleichheit gewinnt diese Liste. Preis: ein echtes Dokumentfeld hier einzutragen macht „der Plan erwähnt rm -rf\" wieder zum Ablehnungsgrund.",
842
986
  "card.scopeNote": "Voreinstellungsbereich: Dieses Gate wirkt nur, während die Sitzungsberechtigungsvoreinstellung %scope ist. Unter jeder anderen Voreinstellung tritt das gesamte Gate zurück — einschließlich P0-Hartablehnung — und die Richtlinie der gewählten Voreinstellung übernimmt. Der Rücktritt protokolliert ein Ereignis und zeigt einen dauerhaften Hinweis über der Eingabe.",
843
987
  "card.strategies": "Backend-Genehmigungsstrategien (mindestens eine aktiv; kombinierbar)",
844
988
  "card.strategy.trustAutoAllow": "trustAutoAllow — sichere Operationen im Bereich werden automatisch erlaubt; gefährliche/unbekannte → Nachfrage (mittlere Baseline-Stufe)",
@@ -1001,6 +1145,30 @@ window.__ModuleLoader__.load({
1001
1145
  "card.description": "Livello di approvazione indipendente: parallelo a sola lettura / accesso completo / whitelist, mostrato come «Revisione automatica» nel selettore delle autorizzazioni della sessione. Il front-end espone un unico interruttore; le quattro strategie di approvazione backend sono combinabili tramite questo pannello e restano fail-closed contro il rifiuto rigido P0.",
1002
1146
  "card.permissive": "Abilita il livello di revisione automatica",
1003
1147
  "card.permissiveHint": "Quando disattivato, il portale si comporta esattamente come prima.",
1148
+ "card.unattended": "Modalità senza supervisione (tre implementazioni; ogni interruttore decide se il proprio livello esiste)",
1149
+ "card.unattendedHint": "Tre interruttori, ciascuno governa solo il livello scritto accanto: spegnere un'implementazione fa tornare quel livello al flusso umano ordinario, senza che diventi di nascosto un altro.",
1150
+ "card.mode.ask": "Mantieni umano",
1151
+ "card.mode.pass": "Passaggio meccanico",
1152
+ "card.askHint": "La selezione meccanica filtra ed elenca i dubbi, ma la richiesta resta a una persona e il modello revisore non viene chiamato nemmeno una volta — la forma adatta quando nessun LLM revisore è collegato o quando i token non valgono la spesa.",
1153
+ "card.passHint": "Esegue solo il giudice meccanico deterministico: un'operazione irreversibile, irrecuperabile o illeggibile viene bloccata e motivata al modello principale, il resto esegue. Nessuna chiamata al modello, nessuna richiesta umana: la forma esaminata di approval: never, e YOLO privato del modello.",
1154
+ "card.tierScopeHint": "Solo i preset dentro l'ambito del gate (%scope) arrivano a questo passo; fuori dall'ambito il gate si disattiva del tutto e nessun interruttore prende in carico le sue conferme.",
1155
+ "card.yolo": "YOLO — non chiedere mai un umano: blocca il pericoloso, esegui il resto",
1156
+ "card.yoloHint": "Blocca un carico irreversibile / non apprendibile / illeggibile, o un verdetto a rischio duro del modello, e restituisce il motivo al LLM principale come messaggio 工具审核意见; tutto il resto viene eseguito. Senza un modello disponibile, solo le reti deterministiche intercettano.",
1157
+ "card.rulesFallback": "Degradare a una porta a regole quando non c’è modello (classe cc-safety-net)",
1158
+ "card.rulesFallbackHint": "Agisce solo quando il revisore di rischio non è configurato o non ha dato un verdetto: le reti deterministiche bloccano ciò che sanno nominare e lasciano passare il resto, invece di parcheggiare la chiamata su un popup che nessuno risponderà. Finché un modello risponde, il comportamento resta invariato.",
1159
+ "card.scriptReview": "Allega il contenuto degli script alla revisione (attivo per impostazione predefinita)",
1160
+ "card.scriptReviewHint": "Quando una chiamata esegue uno script, il suo corpo viene letto e mostrato al modello di rischio come all’umano, e le reti deterministiche valutano anch’esse il corpo (un’eliminazione visibile solo dentro il file è esattamente ciò che passava). I corpi sono oscurati e limitati prima di lasciare l’host; disattivarli tiene il contenuto dei file in locale, al prezzo di rimandare gli script fuori dal workspace nel livello «illeggibile».",
1161
+ "card.credentialPaths": "Percorsi di credenziali personalizzati (la coda lunga)",
1162
+ "card.credentialPathsHint": "File segreti che l’elenco integrato non conosce. Le voci sono glob di percorso: un doppio asterisco attraversa le directory, un asterisco semplice e un punto di domanda restano dentro un segmento; senza jolly si confronta un frammento di percorso intero (token non colpisce ambient.txt); un ~/ iniziale si espande nella tua home; un modello relativo corrisponde anche a qualsiasi profondità (secrets/*.env copre a/b/secrets/prod.env). Una corrispondenza è trattata allo stesso livello di .ssh: la lettura fuori dal workspace e la modifica tramite strumento di file sono rifiutate in modo duro — questo livello non è appellabile né aggirabile dal revisore, dichiara solo percorsi che devi davvero proteggere.",
1163
+ "card.credentialPathsPlaceholder": "Nuovo percorso, es. ~/.myco/vault.token o secrets/*.env",
1164
+ "card.denyKeywordsExtra": "Parole chiave blacklist aggiunte (non sostituiscono il preset)",
1165
+ "card.denyKeywordsExtraHint": "Un blocco deterministico per termini tuoi — un sottocomando di revoca/cancellazione di una CLI aziendale, un prefisso di token che emetti. Questa lista aggiunge soltanto: qualunque cosa contenga la blacklist sopra (preset o tua sostituzione), queste parole vengono unite.",
1166
+ "card.listRemove": "Rimuovi questa voce",
1167
+ "card.extraContentArgKeys": "Chiavi documento aggiunte (mai lette come operazione)",
1168
+ "card.extraContentArgKeysHint": "Nomi di campi documentali fuori dall’elenco integrato (content/text/plan…): una volta dichiarati, il loro testo non entra più nella scansione piatta di parole pericolose e forme di credenziali. Solo in aggiunta: l’elenco integrato non è sostituibile, perché dimenticare una chiave riporterebbe in silenzio il bug per cui un piano viene rifiutato per aver descritto un’azione. Il comando effettivamente eseguito e il percorso modificato sono giudicati sui propri binari; questo elenco non può disattivarli.",
1169
+ "card.extraContentArgKeysPlaceholder": "Nuovo nome argomento, es. steps",
1170
+ "card.extraOperationArgKeys": "Chiavi operazione aggiunte (leggi il campo come codice eseguito)",
1171
+ "card.extraOperationArgKeysHint": "Un passaggio per i campi il cui corpo è davvero eseguito (workflowYaml e simili): il testo raggiunge blacklist e controllo credenziali, e in caso di omonimia questo elenco vince. Costo: indicare un campo documentale reale rende di nuovo «il piano menziona rm -rf» un motivo di rifiuto.",
1004
1172
  "card.scopeNote": "Ambito del preset: questo portale agisce solo quando il preset di autorizzazione della sessione è %scope. Con qualsiasi altro preset l'intero portale si mette in pausa — incluso il rifiuto rigido P0 — e la politica del preset selezionato prende il controllo. La pausa registra un evento e lascia una striscia persistente sopra l'input.",
1005
1173
  "card.strategies": "Strategie di approvazione backend (almeno una attiva; combinabili)",
1006
1174
  "card.strategy.trustAutoAllow": "trustAutoAllow — le operazioni sicure nell'ambito sono auto-consentite; pericolose/sconosciute → richiesta (livello intermedio di base)",
@@ -1163,6 +1331,30 @@ window.__ModuleLoader__.load({
1163
1331
  "card.description": "Независимый уровень одобрения: параллельно с «только чтение» / «полный доступ» / «белый список», отображается как «Автоматическая проверка» в выборе разрешений сессии. Фронтенд предоставляет один переключатель; четыре серверные стратегии одобрения комбинируемы через эту панель и остаются fail-closed против жёсткого отказа P0.",
1164
1332
  "card.permissive": "Включить уровень автоматической проверки",
1165
1333
  "card.permissiveHint": "Когда выключено, шлюз ведёт себя точно так же, как раньше.",
1334
+ "card.unattended": "Режимы без подтверждения человеком (три реализации; каждый переключатель решает, существует ли его уровень)",
1335
+ "card.unattendedHint": "Переключателя три, каждый управляет только тем уровнем, что написан рядом: выключить реализацию — значит вернуть этот уровень обычному человеческому потоку; тайно стать другим он не может.",
1336
+ "card.mode.ask": "Оставить человека",
1337
+ "card.mode.pass": "Механический пропуск",
1338
+ "card.askHint": "Механический отбор фильтрует и перечисляет сомнения, но запрос остаётся человеку, а модель-ревизор не вызывается ни разу — это форма для запусков без подключённого LLM-ревизора или когда токены не стоят затрат.",
1339
+ "card.passHint": "Работает только детерминированный механический судья: необратимое, невосстановимое или нечитаемое действие блокируется с причиной обратно основной модели, остальное выполняется. Ни вызова модели, ни вопроса человеку — проверенная форма approval: never и YOLO без модели.",
1340
+ "card.tierScopeHint": "До этого шага доходят только пресеты внутри области гейта (%scope); вне области гейт отключается целиком, и ни один переключатель не берёт его подтверждения на себя.",
1341
+ "card.yolo": "YOLO — никогда не спрашивать человека: блокировать опасное, остальное выполнять",
1342
+ "card.yoloHint": "Блокирует необратимую / необучаемую / неразбираемую нагрузку или жёсткий вердикт модели и возвращает причину основной LLM сообщением 工具审核意见; остальное выполняется. Когда доступной модели нет, срабатывают только детерминированные сети.",
1343
+ "card.rulesFallback": "Деградировать в шлюз по правилам, когда модели нет (класс cc-safety-net)",
1344
+ "card.rulesFallbackHint": "Действует только когда проверяющая модель не настроена или не дала вердикта: детерминированные сети блокируют то, что могут назвать, и пропускают остальное, вместо того чтобы вешать вызов на окно, которое никто не ответит. Пока модель отвечает, поведение не меняется.",
1345
+ "card.scriptReview": "Прилагать содержимое скриптов к проверке (по умолчанию включено)",
1346
+ "card.scriptReviewHint": "Когда вызов запускает скрипт-файл, его тело читается и даётся модели риска и человеку, и детерминированные сети тоже оценивают тело (удаление, видимое только внутри файла, — ровно то, что пропускалось). Тела маскируются и ограничиваются по длине до отправки; отключение оставляет содержимое файлов локально, ценой возврата скриптов вне рабочей области в уровень «неразбираемо».",
1347
+ "card.credentialPaths": "Свои пути к учётным данным (длинный хвост)",
1348
+ "card.credentialPathsHint": "Секретные файлы, которых нет во встроенном списке. Записи — это глобы путей: двойная звёздочка переходит между каталогами, одиночная звёздочка и вопросительный знак остаются внутри одного сегмента; без маска-символов совпадение идёт по целому фрагменту пути (token не попадёт в ambient.txt); ведущий ~/ раскрывается в ваш home; относительный шаблон совпадает и на любой глубине (secrets/*.env покрывает a/b/secrets/prod.env). Совпадение держится по тому же стандарту, что и .ssh: чтение вне рабочей области и изменение файловым инструментом жёстко отклоняются — этот уровень нельзя оспорить и не обходят проверяющей моделью, поэтому объявляйте только те пути, которые действительно нужно защитить.",
1349
+ "card.credentialPathsPlaceholder": "Новый путь, напр. ~/.myco/vault.token или secrets/*.env",
1350
+ "card.denyKeywordsExtra": "Добавляемые ключевые слова чёрного списка (не заменяют пресет)",
1351
+ "card.denyKeywordsExtraHint": "Детерминированная блокировка для ваших собственных терминов — подкоманда отзыва/стирания внутреннего CLI, префикс выдаваемого вами токена. Этот список только добавляет: что бы ни содержал чёрный список выше (пресет или ваша замена), эти слова сливаются с ним.",
1352
+ "card.listRemove": "Удалить эту запись",
1353
+ "card.extraContentArgKeys": "Дополнительные ключи документов (не читаются как операция)",
1354
+ "card.extraContentArgKeysHint": "Имена полей вне встроенного списка (content/text/plan…): после добавления их текст не попадает в плоское чтение опасных слов и форм учётных данных. Только дополняет — встроенный список не заменяется, потому что пропуск одного из его ключей молча вернёт баг, когда план отклоняют за само описание работы. Реально выполняемая команда и изменяемый путь проверяются своими путями; этот список их не отключает.",
1355
+ "card.extraContentArgKeysPlaceholder": "Новое имя аргумента, напр. steps",
1356
+ "card.extraOperationArgKeys": "Дополнительные ключи операций (читать поле как исполняемый код)",
1357
+ "card.extraOperationArgKeysHint": "Канал для полей, чьё тело действительно исполняется (workflowYaml и т. п.): их текст попадает в чёрный список и проверку учётных данных, и при совпадении ключа выигрывает этот список. Цена: вписав сюда настоящий документ, вы снова делаете «в плане упоминается rm -rf» причиной отказа.",
1166
1358
  "card.scopeNote": "Область пресета: этот шлюз действует только когда пресет разрешений сессии — %scope. При любом другом пресете весь шлюз отключается — включая жёсткий отказ P0 — и политика выбранного пресета берёт управление на себя. Отключение записывает событие и оставляет постоянную полоску над полем ввода.",
1167
1359
  "card.strategies": "Серверные стратегии одобрения (хотя бы одна активна; комбинируемы)",
1168
1360
  "card.strategy.trustAutoAllow": "trustAutoAllow — безопасные операции в области автоматически разрешаются; опасные/неизвестные → запрос (базовый средний уровень)",
@@ -1325,6 +1517,30 @@ window.__ModuleLoader__.load({
1325
1517
  "card.description": "Nivel de aprobación independiente: paralelo a solo lectura / acceso completo / lista blanca, mostrado como «Revisión automática» en el selector de permisos de sesión. El frontend expone un único interruptor; las cuatro estrategias de aprobación del backend son combinables mediante este panel y permanecen fail-closed contra la denegación dura P0.",
1326
1518
  "card.permissive": "Habilitar el nivel de revisión automática",
1327
1519
  "card.permissiveHint": "Cuando está desactivado, la compuerta se comporta exactamente igual que antes.",
1520
+ "card.unattended": "Modos sin supervisión (tres implementaciones; cada interruptor decide si su nivel existe)",
1521
+ "card.unattendedHint": "Tres interruptores, cada uno gobierna solo el nivel escrito a su lado: apagar una implementación devuelve ese nivel al flujo humano ordinario, sin convertirse a escondidas en otro.",
1522
+ "card.mode.ask": "Mantener humano",
1523
+ "card.mode.pass": "Paso mecánico",
1524
+ "card.askHint": "El tamiz mecánico filtra y enumera sus dudas, pero la solicitud sigue quedando con una persona y no se llama al modelo revisor ni una vez — la forma para ejecuciones sin LLM revisor conectado, o cuando los tokens no valen la pena.",
1525
+ "card.passHint": "Ejecuta solo el juez mecánico determinista: una operación irreversible, irrecuperable o ilegible se bloquea y se devuelve justificada al modelo principal; lo demás corre. Ninguna llamada al modelo, ninguna pregunta humana: la forma revisada de approval: never, y un YOLO sin modelo.",
1526
+ "card.tierScopeHint": "Solo los presets dentro del alcance del gate (%scope) llegan a este paso; fuera de él el gate se aparta por completo y ningún interruptor toma sus confirmaciones.",
1527
+ "card.yolo": "YOLO — nunca pedir un humano: bloquear lo peligroso, ejecutar el resto",
1528
+ "card.yoloHint": "Bloquea una carga irreversible / no aprendible / ilegible, o un veredicto de riesgo duro del modelo, y devuelve la razón al LLM principal como mensaje 工具审核意见; todo lo demás se ejecuta. Sin un modelo disponible, solo las redes deterministas interceptan.",
1529
+ "card.rulesFallback": "Degradar a una compuerta de reglas cuando no hay modelo (clase cc-safety-net)",
1530
+ "card.rulesFallbackHint": "Solo actúa cuando el revisor de riesgo no está configurado o no dio veredicto: las redes deterministas bloquean lo que saben nombrar y dejan pasar el resto, en lugar de dejar la llamada colgada en un emergente que nadie responderá. Mientras un modelo responde, el comportamiento no cambia.",
1531
+ "card.scriptReview": "Adjuntar el contenido de los scripts a la revisión (activado por defecto)",
1532
+ "card.scriptReviewHint": "Cuando una llamada ejecuta un archivo script, su cuerpo se lee y se da al modelo de riesgo y al humano por igual, y las redes deterministas también evalúan el cuerpo (un borrado que solo se ve dentro del archivo es justo lo que se escapaba). Los cuerpos se redactan y limitan antes de salir del host; desactivarlo mantiene el contenido de archivos local, al costo de devolver los scripts fuera del espacio de trabajo al nivel «ilegible».",
1533
+ "card.credentialPaths": "Rutas de credenciales personalizadas (la cola larga)",
1534
+ "card.credentialPathsHint": "Archivos secretos que la lista integrada no conoce. Las entradas son globos de ruta: un doble asterisco cruza directorios, un asterisco simple y un signo de interrogación se quedan dentro de un segmento; sin comodines se compara un fragmento de ruta completo (token no toca ambient.txt); un ~/ inicial se expande a tu home; un patrón relativo también coincide a cualquier profundidad (secrets/*.env cubre a/b/secrets/prod.env). Una coincidencia se trata al mismo estándar que .ssh: leerla fuera del espacio de trabajo y mutarla con una herramienta de archivo se deniegan de forma dura — ese nivel no se puede apelar ni sortear por el revisor, así que declara solo las rutas que de verdad necesitas proteger.",
1535
+ "card.credentialPathsPlaceholder": "Nueva ruta, p. ej. ~/.myco/vault.token o secrets/*.env",
1536
+ "card.denyKeywordsExtra": "Palabras clave añadidas a la blacklist (no reemplaza el preajuste)",
1537
+ "card.denyKeywordsExtraHint": "Un bloqueo determinista sobre términos propios — una subcomando de revocación/borrado de una CLI interna, un prefijo de token que emites. Esta lista solo añade: lo que contenga la blacklist de arriba (preajuste o tu propio reemplazo), estas palabras se fusionan.",
1538
+ "card.listRemove": "Quitar esta entrada",
1539
+ "card.extraContentArgKeys": "Claves de documento añadidas (nunca leídas como operación)",
1540
+ "card.extraContentArgKeysHint": "Nombres de campos documentales fuera de la lista integrada (content/text/plan…): una vez declarados, su texto deja de entrar en la lectura plana de palabras peligrosas y formas de credenciales. Solo se añade: la lista integrada no se reemplaza, porque olvidar una de sus claves traería de vuelta en silencio el fallo por el que un plan se rechaza por describir una acción. El comando realmente ejecutado y la ruta modificada se juzgan por sus propios caminos; esta lista no puede apagarlos.",
1541
+ "card.extraContentArgKeysPlaceholder": "Nuevo nombre de argumento, p. ej. steps",
1542
+ "card.extraOperationArgKeys": "Claves de operación añadidas (leer el campo como código que se ejecuta)",
1543
+ "card.extraOperationArgKeysHint": "Una vía para campos cuyo cuerpo sí se ejecuta (workflowYaml etc.): su texto entra en la blacklist y en el filtro de credenciales, y esta lista gana si ambas nombran la misma clave. Costo: declarar aquí un campo documental real vuelve a hacer que «el plan menciona rm -rf» sea motivo de rechazo.",
1328
1544
  "card.scopeNote": "Ámbito del preajuste: esta compuerta solo actúa mientras el preajuste de permisos de la sesión sea %scope. Bajo cualquier otro preajuste la compuerta completa se retira — incluida la denegación dura P0 — y la política del preajuste seleccionado toma el control. La retirada registra un evento y deja una franja persistente sobre la entrada.",
1329
1545
  "card.strategies": "Estrategias de aprobación del backend (al menos una activa; combinables)",
1330
1546
  "card.strategy.trustAutoAllow": "trustAutoAllow — las operaciones seguras dentro del ámbito se auto-permiten; peligrosas/desconocidas → consulta (nivel intermedio base)",
@@ -1483,6 +1699,42 @@ window.__ModuleLoader__.load({
1483
1699
  }
1484
1700
  };
1485
1701
  //#endregion
1702
+ //#region src/client/switch-state.ts
1703
+ /**
1704
+ * The optimistic state machine behind the settings card's pill switch and its tier
1705
+ * picker, kept out of the components so its three rules are testable without a DOM
1706
+ * renderer (the client half ships no react-dom).
1707
+ *
1708
+ * The posture: the control flips the instant it is clicked and holds that
1709
+ * position while the write round-trips through the host settings service — a
1710
+ * checkbox that waits for the snapshot reads as a dead control. The store stays
1711
+ * the truth, so the local override is dropped the moment the snapshot agrees
1712
+ * with it, on a rejected write, and on a watchdog in case a host line neither
1713
+ * applies nor reports the change.
1714
+ */
1715
+ /** How long an unconfirmed click is allowed to hold the control before it snaps back. */
1716
+ const SWITCH_SETTLE_MS = 4e3;
1717
+ /**
1718
+ * What the control displays: the pending click wins over the store until the store
1719
+ * catches up (or the watchdog gives up on it). Generic because the same posture covers a
1720
+ * two-state pill and a multi-state tier picker alike.
1721
+ */
1722
+ function shownValue(pending, checked) {
1723
+ return pending ?? checked;
1724
+ }
1725
+ /**
1726
+ * Store update: once the snapshot reports the requested value the override is redundant,
1727
+ * and handing the visual back to the snapshot is what keeps the control honest if another
1728
+ * writer (a patch file, the app's own plugin manager) moved the same field meanwhile.
1729
+ */
1730
+ function pendingAfterSync(pending, checked) {
1731
+ return pending !== null && pending === checked ? null : pending;
1732
+ }
1733
+ /** A write that came back rejected: fall back to whatever the store holds. */
1734
+ function pendingAfterFailure(_pending) {
1735
+ return null;
1736
+ }
1737
+ //#endregion
1486
1738
  //#region src/deny-defaults.ts
1487
1739
  /**
1488
1740
  * Preset deny keywords inherited from dsh-approval-gate's `DEFAULT_DENY_KEYWORDS`
@@ -1551,16 +1803,25 @@ window.__ModuleLoader__.load({
1551
1803
  //#endregion
1552
1804
  //#region src/preset.ts
1553
1805
  /**
1554
- * Presets in which the gate is active. It owns both 自动审查 tiers — the plain
1555
- * one (file sandbox kept) and the full-access one (sandbox restriction lifted,
1556
- * same approval behaviour) — so a user can have the gate without inheriting a
1557
- * sandbox that breaks `git` / Cygwin tools. `'*'` makes it global (every
1558
- * preset, including the hard-deny layer).
1806
+ * Presets in which the gate is active. The plugin ships five 自动审查 tiers of its own
1807
+ * (declared in its `cordis.patch.yml`, which is how they reach the composer picker at all):
1808
+ * the plain one (file sandbox kept), the full-access one (sandbox lifted, same approval
1809
+ * behaviour), and the three human-free / model-free implementations — mechanical takeover of
1810
+ * asks (`permissive-mech`), mechanical screen that still asks (`permissive-snet`), and the
1811
+ * model-backed YOLO. Each has exactly one preset and one switch, so they never cross: a
1812
+ * switch changes the behaviour of its own tier and of nothing else. `'*'` makes the gate
1813
+ * global (every preset, including the hard-deny layer).
1559
1814
  *
1560
1815
  * Lives in this dependency-free module (not `config.ts`) so the browser half can
1561
1816
  * render the scope without pulling schemastery into the client bundle.
1562
1817
  */
1563
- const DEFAULT_GATE_PRESETS = ["permissive", "permissive-full"];
1818
+ const DEFAULT_GATE_PRESETS = [
1819
+ "permissive",
1820
+ "permissive-full",
1821
+ "permissive-mech",
1822
+ "permissive-snet",
1823
+ "permissive-yolo"
1824
+ ];
1564
1825
  //#endregion
1565
1826
  //#region src/client/sediment.tsx
1566
1827
  /**
@@ -1785,6 +2046,243 @@ window.__ModuleLoader__.load({
1785
2046
  lineHeight: "18px",
1786
2047
  color: "var(--dsw-alias-label-tertiary)"
1787
2048
  };
2049
+ /** Focus ring for the pill switch. `:focus-visible` cannot be expressed inline, so
2050
+ * the one pseudo-class the card needs rides on a single injected rule. */
2051
+ const switchCss = ".pg-switch:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#3b82f6));outline-offset:2px}.pg-seg:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#3b82f6));outline-offset:1px}";
2052
+ /**
2053
+ * Optimistic control state, shared by the pill switch and the tier picker: show the
2054
+ * requested value the instant it is clicked and hold it while the write round-trips
2055
+ * through the host settings service, then hand the visual back to the store — on the
2056
+ * snapshot catching up, on a rejected write, and on a watchdog for a host line that
2057
+ * neither applies nor reports the change. A control may be fast, but it may not lie.
2058
+ */
2059
+ function useOptimistic(stored) {
2060
+ const [pending, setPending] = (0, react.useState)(null);
2061
+ (0, react.useEffect)(() => {
2062
+ setPending((current) => pendingAfterSync(current, stored));
2063
+ }, [stored]);
2064
+ (0, react.useEffect)(() => {
2065
+ if (pending === null) return void 0;
2066
+ const timer = setTimeout(() => {
2067
+ setPending(pendingAfterFailure(pending));
2068
+ }, SWITCH_SETTLE_MS);
2069
+ return () => {
2070
+ clearTimeout(timer);
2071
+ };
2072
+ }, [pending]);
2073
+ const commit = (next, write) => {
2074
+ setPending(next);
2075
+ Promise.resolve(write()).catch(() => {
2076
+ setPending(null);
2077
+ });
2078
+ };
2079
+ return [shownValue(pending, stored), commit];
2080
+ }
2081
+ /**
2082
+ * One row of the unattended block: a switch, and the preset it owns.
2083
+ *
2084
+ * The preset id is rendered next to the label because the mapping is the whole design —
2085
+ * one switch, one implementation, one tier — and a person who sees 「机械直通」 in the
2086
+ * composer needs to know which row governs it. The id is a machine key, so it stays
2087
+ * untranslated on purpose.
2088
+ */
2089
+ function TierRow(props) {
2090
+ const { id, label, preset, checked, disabled, hint, onCommit } = props;
2091
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2092
+ style: rowStyle,
2093
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2094
+ htmlFor: id,
2095
+ style: { fontSize: "12px" },
2096
+ children: [label, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2097
+ style: {
2098
+ color: "var(--dsw-alias-label-tertiary)",
2099
+ fontSize: "11px"
2100
+ },
2101
+ children: [" · ", preset]
2102
+ })]
2103
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2104
+ id,
2105
+ checked,
2106
+ disabled,
2107
+ onCommit
2108
+ })]
2109
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2110
+ style: hintStyle,
2111
+ children: hint
2112
+ })] });
2113
+ }
2114
+ /**
2115
+ * Pill switch — the card's boolean control, cut to the shape of the host's own
2116
+ * `ui-primitives` Switch: 36×20 capsule, 16px thumb, and the on/off appearance
2117
+ * keyed off `aria-checked` so assistive tech can never read a state the eye does
2118
+ * not see.
2119
+ *
2120
+ * Optimistic by design: the capsule flips on click and holds that position while
2121
+ * `scope.set` round-trips through the host settings service. A checkbox that
2122
+ * waits for the snapshot to come back reads as a dead control on a slow write.
2123
+ * The snapshot stays the truth — the state machine lives in `switch-state.ts`
2124
+ * (and under test there) so the pill can never sit in a state the profile does
2125
+ * not have.
2126
+ *
2127
+ * Every colour is a theme token with a fallback: `--dsw-alias-brand-primary`
2128
+ * only exists on newer host lines, and an "on" state that resolves to nothing is
2129
+ * an invisible control in dark mode.
2130
+ */
2131
+ function PillSwitch(props) {
2132
+ const { id, checked, label, disabled = false, title, onCommit } = props;
2133
+ const [shown, commit] = useOptimistic(checked);
2134
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2135
+ id,
2136
+ type: "button",
2137
+ role: "switch",
2138
+ "aria-checked": shown,
2139
+ "aria-label": label,
2140
+ title,
2141
+ disabled,
2142
+ className: "pg-switch",
2143
+ style: {
2144
+ boxSizing: "border-box",
2145
+ position: "relative",
2146
+ flex: "0 0 auto",
2147
+ width: "36px",
2148
+ height: "20px",
2149
+ padding: "2px",
2150
+ border: "0",
2151
+ borderRadius: "999px",
2152
+ background: shown ? "var(--dsw-alias-brand-primary, var(--dsw-alias-state-business-primary, #3b82f6))" : "var(--dsw-alias-border-l3, var(--dsw-alias-border-l2, rgba(128,128,128,0.35)))",
2153
+ cursor: disabled ? "default" : "pointer",
2154
+ opacity: disabled ? .5 : 1,
2155
+ transition: "background 120ms ease"
2156
+ },
2157
+ onClick: () => {
2158
+ const next = !shown;
2159
+ commit(next, () => onCommit(next));
2160
+ },
2161
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2162
+ "aria-hidden": true,
2163
+ style: {
2164
+ display: "block",
2165
+ width: "16px",
2166
+ height: "16px",
2167
+ borderRadius: "50%",
2168
+ background: "var(--dsw-alias-label-primary-foreground, #fff)",
2169
+ transform: shown ? "translateX(16px)" : "translateX(0)",
2170
+ transition: "transform 120ms ease"
2171
+ }
2172
+ })
2173
+ });
2174
+ }
2175
+ /**
2176
+ * A minimal add/remove editor for one string-array setting.
2177
+ *
2178
+ * Deliberately generic and small: the long-tail protection lists (`credentialPaths`,
2179
+ * `denyKeywordsExtra`) are the same shape — lines a person types to protect something
2180
+ * the preset cannot know — and they should not each grow their own copy of the row
2181
+ * markup the deny blacklist already has.
2182
+ */
2183
+ function StringListField(props) {
2184
+ const [draft, setDraft] = (0, react.useState)("");
2185
+ const add = () => {
2186
+ const next = draft.trim();
2187
+ if (next === "") return;
2188
+ props.onAdd(next);
2189
+ setDraft("");
2190
+ };
2191
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2193
+ style: labelStyle,
2194
+ children: props.label
2195
+ }),
2196
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2197
+ style: hintStyle,
2198
+ children: props.hint
2199
+ }),
2200
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2201
+ style: {
2202
+ display: "flex",
2203
+ flexDirection: "column",
2204
+ gap: "4px",
2205
+ margin: "6px 0"
2206
+ },
2207
+ children: props.items.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2208
+ style: {
2209
+ display: "flex",
2210
+ alignItems: "center",
2211
+ gap: "8px",
2212
+ padding: "4px 8px",
2213
+ borderRadius: "6px",
2214
+ border: "1px solid var(--dsw-alias-border-l2)",
2215
+ background: "var(--dsw-alias-bg-surface, transparent)"
2216
+ },
2217
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
2218
+ style: {
2219
+ flex: "1 1 auto",
2220
+ minWidth: 0,
2221
+ overflowWrap: "anywhere",
2222
+ font: "500 12px/18px var(--ds-font-family-code, monospace)",
2223
+ color: "var(--dsw-alias-label-primary)"
2224
+ },
2225
+ children: item
2226
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2227
+ type: "button",
2228
+ "aria-label": props.removeLabel,
2229
+ title: props.removeLabel,
2230
+ disabled: props.locked,
2231
+ onClick: () => {
2232
+ props.onRemove(index);
2233
+ },
2234
+ style: {
2235
+ flex: "0 0 auto",
2236
+ width: "20px",
2237
+ height: "20px",
2238
+ border: "none",
2239
+ borderRadius: "999px",
2240
+ background: "transparent",
2241
+ color: "var(--dsw-alias-label-tertiary)",
2242
+ cursor: props.locked ? "default" : "pointer"
2243
+ },
2244
+ children: "✕"
2245
+ })]
2246
+ }, `${index}:${item}`))
2247
+ }),
2248
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2249
+ style: {
2250
+ display: "flex",
2251
+ gap: "6px",
2252
+ marginTop: "6px"
2253
+ },
2254
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2255
+ type: "text",
2256
+ value: draft,
2257
+ disabled: props.locked,
2258
+ placeholder: props.placeholder,
2259
+ spellCheck: false,
2260
+ style: {
2261
+ ...controlStyle,
2262
+ flex: "1 1 auto",
2263
+ minWidth: 0
2264
+ },
2265
+ onChange: (event) => {
2266
+ setDraft(event.currentTarget.value);
2267
+ },
2268
+ onKeyDown: (event) => {
2269
+ if (event.key === "Enter") add();
2270
+ }
2271
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2272
+ type: "button",
2273
+ disabled: props.locked || draft.trim() === "",
2274
+ onClick: add,
2275
+ style: {
2276
+ ...controlStyle,
2277
+ flex: "0 0 auto",
2278
+ cursor: props.locked || draft.trim() === "" ? "default" : "pointer",
2279
+ opacity: props.locked || draft.trim() === "" ? .5 : 1
2280
+ },
2281
+ children: props.addLabel
2282
+ })]
2283
+ })
2284
+ ] });
2285
+ }
1788
2286
  /**
1789
2287
  * The card body, wrapped in a disclosure shell like every peer settings card:
1790
2288
  * a header (name + description + chevron) toggling the body, expanded by
@@ -1810,11 +2308,16 @@ window.__ModuleLoader__.load({
1810
2308
  const value = {
1811
2309
  ...raw,
1812
2310
  allowlist: listOf(raw.allowlist),
1813
- denyKeywords: listOf(raw.denyKeywords)
2311
+ denyKeywords: listOf(raw.denyKeywords),
2312
+ denyKeywordsExtra: listOf(raw.denyKeywordsExtra),
2313
+ credentialPaths: listOf(raw.credentialPaths),
2314
+ extraContentArgKeys: listOf(raw.extraContentArgKeys),
2315
+ extraOperationArgKeys: listOf(raw.extraOperationArgKeys)
1814
2316
  };
1815
2317
  const strategies = value.permissiveStrategies ?? {};
1816
2318
  const hasApiKey = typeof value.classifierApiKey === "string" && value.classifierApiKey !== "";
1817
2319
  const effective = value.permissive === true;
2320
+ const gateScope = (value.gatePresets ?? []).length > 0 ? value.gatePresets : [...DEFAULT_GATE_PRESETS];
1818
2321
  const commitApiKey = () => {
1819
2322
  const next = apiKeyDraft.trim();
1820
2323
  if (next === "") return;
@@ -2013,20 +2516,18 @@ window.__ModuleLoader__.load({
2013
2516
  },
2014
2517
  children: t("card.unavailable")
2015
2518
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2519
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: switchCss }),
2016
2520
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2017
2521
  style: rowStyle,
2018
2522
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2019
2523
  htmlFor: "plugin-config-perm-gate-permissive",
2020
2524
  style: labelStyle,
2021
2525
  children: t("card.permissive")
2022
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2526
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2023
2527
  id: "plugin-config-perm-gate-permissive",
2024
- type: "checkbox",
2025
2528
  checked: effective,
2026
2529
  disabled: readonly,
2027
- onChange: (event) => {
2028
- scope.set("permissive", event.currentTarget.checked);
2029
- }
2530
+ onCommit: (next) => scope.set("permissive", next)
2030
2531
  })]
2031
2532
  }),
2032
2533
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -2035,7 +2536,85 @@ window.__ModuleLoader__.load({
2035
2536
  }),
2036
2537
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2037
2538
  style: hintStyle,
2038
- children: t("card.scopeNote").replace("%scope", ((value.gatePresets ?? []).length > 0 ? value.gatePresets : [...DEFAULT_GATE_PRESETS]).join(" / "))
2539
+ children: t("card.scopeNote").replace("%scope", gateScope.join(" / "))
2540
+ }),
2541
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2542
+ style: sectionStyle,
2543
+ children: [
2544
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2545
+ style: labelStyle,
2546
+ children: t("card.unattended")
2547
+ }),
2548
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2549
+ style: hintStyle,
2550
+ children: t("card.unattendedHint")
2551
+ }),
2552
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TierRow, {
2553
+ id: "plugin-config-perm-gate-yolo",
2554
+ label: t("card.yolo"),
2555
+ preset: "permissive-yolo",
2556
+ checked: value.yolo === true,
2557
+ disabled: readonly,
2558
+ hint: t("card.yoloHint"),
2559
+ onCommit: (next) => scope.set("yolo", next)
2560
+ }),
2561
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TierRow, {
2562
+ id: "plugin-config-perm-gate-safenet-pass",
2563
+ label: t("card.mode.pass"),
2564
+ preset: "permissive-mech",
2565
+ checked: value.safenetPass !== false,
2566
+ disabled: readonly,
2567
+ hint: t("card.passHint"),
2568
+ onCommit: (next) => scope.set("safenetPass", next)
2569
+ }),
2570
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TierRow, {
2571
+ id: "plugin-config-perm-gate-safenet-ask",
2572
+ label: t("card.mode.ask"),
2573
+ preset: "permissive-snet",
2574
+ checked: value.safenetAsk === true,
2575
+ disabled: readonly,
2576
+ hint: t("card.askHint"),
2577
+ onCommit: (next) => scope.set("safenetAsk", next)
2578
+ }),
2579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2580
+ style: hintStyle,
2581
+ children: t("card.tierScopeHint").replace("%scope", gateScope.join(" / "))
2582
+ }),
2583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2584
+ style: rowStyle,
2585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2586
+ htmlFor: "plugin-config-perm-gate-rules-fallback",
2587
+ style: { fontSize: "12px" },
2588
+ children: t("card.rulesFallback")
2589
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2590
+ id: "plugin-config-perm-gate-rules-fallback",
2591
+ checked: value.rulesFallback === true,
2592
+ disabled: readonly,
2593
+ onCommit: (next) => scope.set("rulesFallback", next)
2594
+ })]
2595
+ }),
2596
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2597
+ style: hintStyle,
2598
+ children: t("card.rulesFallbackHint")
2599
+ }),
2600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2601
+ style: rowStyle,
2602
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2603
+ htmlFor: "plugin-config-perm-gate-script-review",
2604
+ style: { fontSize: "12px" },
2605
+ children: t("card.scriptReview")
2606
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2607
+ id: "plugin-config-perm-gate-script-review",
2608
+ checked: value.scriptReview !== false,
2609
+ disabled: readonly,
2610
+ onCommit: (next) => scope.set("scriptReview", next)
2611
+ })]
2612
+ }),
2613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2614
+ style: hintStyle,
2615
+ children: t("card.scriptReviewHint")
2616
+ })
2617
+ ]
2039
2618
  }),
2040
2619
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2041
2620
  style: sectionStyle,
@@ -2050,17 +2629,14 @@ window.__ModuleLoader__.load({
2050
2629
  htmlFor: "plugin-config-perm-gate-trust",
2051
2630
  style: { fontSize: "12px" },
2052
2631
  children: t("card.strategy.trustAutoAllow")
2053
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2632
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2054
2633
  id: "plugin-config-perm-gate-trust",
2055
- type: "checkbox",
2056
2634
  checked: strategies.trustAutoAllow ?? true,
2057
2635
  disabled: readonly || !effective,
2058
- onChange: (event) => {
2059
- scope.set("permissiveStrategies", {
2060
- ...strategies,
2061
- trustAutoAllow: event.currentTarget.checked
2062
- });
2063
- }
2636
+ onCommit: (next) => scope.set("permissiveStrategies", {
2637
+ ...strategies,
2638
+ trustAutoAllow: next
2639
+ })
2064
2640
  })]
2065
2641
  }),
2066
2642
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2069,17 +2645,14 @@ window.__ModuleLoader__.load({
2069
2645
  htmlFor: "plugin-config-perm-gate-confirm",
2070
2646
  style: { fontSize: "12px" },
2071
2647
  children: t("card.strategy.alwaysConfirm")
2072
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2648
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2073
2649
  id: "plugin-config-perm-gate-confirm",
2074
- type: "checkbox",
2075
2650
  checked: strategies.alwaysConfirm ?? false,
2076
2651
  disabled: readonly || !effective,
2077
- onChange: (event) => {
2078
- scope.set("permissiveStrategies", {
2079
- ...strategies,
2080
- alwaysConfirm: event.currentTarget.checked
2081
- });
2082
- }
2652
+ onCommit: (next) => scope.set("permissiveStrategies", {
2653
+ ...strategies,
2654
+ alwaysConfirm: next
2655
+ })
2083
2656
  })]
2084
2657
  }),
2085
2658
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2088,17 +2661,14 @@ window.__ModuleLoader__.load({
2088
2661
  htmlFor: "plugin-config-perm-gate-llm",
2089
2662
  style: { fontSize: "12px" },
2090
2663
  children: t("card.strategy.llmAssist")
2091
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2664
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2092
2665
  id: "plugin-config-perm-gate-llm",
2093
- type: "checkbox",
2094
2666
  checked: strategies.llmAssist ?? false,
2095
2667
  disabled: readonly || !effective,
2096
- onChange: (event) => {
2097
- scope.set("permissiveStrategies", {
2098
- ...strategies,
2099
- llmAssist: event.currentTarget.checked
2100
- });
2101
- }
2668
+ onCommit: (next) => scope.set("permissiveStrategies", {
2669
+ ...strategies,
2670
+ llmAssist: next
2671
+ })
2102
2672
  })]
2103
2673
  }),
2104
2674
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2107,17 +2677,14 @@ window.__ModuleLoader__.load({
2107
2677
  htmlFor: "plugin-config-perm-gate-escalation",
2108
2678
  style: { fontSize: "12px" },
2109
2679
  children: t("card.strategy.trustEscalation")
2110
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2680
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2111
2681
  id: "plugin-config-perm-gate-escalation",
2112
- type: "checkbox",
2113
2682
  checked: strategies.trustEscalation ?? true,
2114
2683
  disabled: readonly || !effective,
2115
- onChange: (event) => {
2116
- scope.set("permissiveStrategies", {
2117
- ...strategies,
2118
- trustEscalation: event.currentTarget.checked
2119
- });
2120
- }
2684
+ onCommit: (next) => scope.set("permissiveStrategies", {
2685
+ ...strategies,
2686
+ trustEscalation: next
2687
+ })
2121
2688
  })]
2122
2689
  }),
2123
2690
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -2380,14 +2947,11 @@ window.__ModuleLoader__.load({
2380
2947
  htmlFor: "plugin-config-perm-gate-risk-learning",
2381
2948
  style: { fontSize: "12px" },
2382
2949
  children: t("card.riskLearning")
2383
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2950
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2384
2951
  id: "plugin-config-perm-gate-risk-learning",
2385
- type: "checkbox",
2386
2952
  checked: value.riskLearning ?? false,
2387
2953
  disabled: readonly || !effective,
2388
- onChange: (event) => {
2389
- scope.set("riskLearning", event.currentTarget.checked);
2390
- }
2954
+ onCommit: (next) => scope.set("riskLearning", next)
2391
2955
  })]
2392
2956
  }),
2393
2957
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -2423,14 +2987,11 @@ window.__ModuleLoader__.load({
2423
2987
  htmlFor: "plugin-config-perm-gate-risk-sediment",
2424
2988
  style: { fontSize: "12px" },
2425
2989
  children: t("card.riskSediment")
2426
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2990
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
2427
2991
  id: "plugin-config-perm-gate-risk-sediment",
2428
- type: "checkbox",
2429
2992
  checked: value.riskSediment ?? true,
2430
2993
  disabled: readonly || !effective,
2431
- onChange: (event) => {
2432
- scope.set("riskSediment", event.currentTarget.checked);
2433
- }
2994
+ onCommit: (next) => scope.set("riskSediment", next)
2434
2995
  })]
2435
2996
  }),
2436
2997
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -2943,6 +3504,74 @@ window.__ModuleLoader__.load({
2943
3504
  })] }) : null
2944
3505
  ]
2945
3506
  }),
3507
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3508
+ style: sectionStyle,
3509
+ children: [
3510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StringListField, {
3511
+ label: t("card.credentialPaths"),
3512
+ hint: t("card.credentialPathsHint"),
3513
+ placeholder: t("card.credentialPathsPlaceholder"),
3514
+ addLabel: t("card.denylistAdd"),
3515
+ removeLabel: t("card.listRemove"),
3516
+ items: value.credentialPaths ?? [],
3517
+ locked: readonly,
3518
+ onAdd: (entry) => {
3519
+ scope.set("credentialPaths", [...value.credentialPaths ?? [], entry]);
3520
+ },
3521
+ onRemove: (index) => {
3522
+ scope.set("credentialPaths", (value.credentialPaths ?? []).filter((_, i) => i !== index));
3523
+ }
3524
+ }),
3525
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { height: "10px" } }),
3526
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StringListField, {
3527
+ label: t("card.denyKeywordsExtra"),
3528
+ hint: t("card.denyKeywordsExtraHint"),
3529
+ placeholder: t("card.denylistPlaceholder"),
3530
+ addLabel: t("card.denylistAdd"),
3531
+ removeLabel: t("card.listRemove"),
3532
+ items: value.denyKeywordsExtra ?? [],
3533
+ locked: readonly,
3534
+ onAdd: (entry) => {
3535
+ scope.set("denyKeywordsExtra", [...value.denyKeywordsExtra ?? [], entry]);
3536
+ },
3537
+ onRemove: (index) => {
3538
+ scope.set("denyKeywordsExtra", (value.denyKeywordsExtra ?? []).filter((_, i) => i !== index));
3539
+ }
3540
+ }),
3541
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { height: "10px" } }),
3542
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StringListField, {
3543
+ label: t("card.extraContentArgKeys"),
3544
+ hint: t("card.extraContentArgKeysHint"),
3545
+ placeholder: t("card.extraContentArgKeysPlaceholder"),
3546
+ addLabel: t("card.denylistAdd"),
3547
+ removeLabel: t("card.listRemove"),
3548
+ items: value.extraContentArgKeys ?? [],
3549
+ locked: readonly,
3550
+ onAdd: (entry) => {
3551
+ scope.set("extraContentArgKeys", [...value.extraContentArgKeys ?? [], entry]);
3552
+ },
3553
+ onRemove: (index) => {
3554
+ scope.set("extraContentArgKeys", (value.extraContentArgKeys ?? []).filter((_, i) => i !== index));
3555
+ }
3556
+ }),
3557
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { height: "10px" } }),
3558
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StringListField, {
3559
+ label: t("card.extraOperationArgKeys"),
3560
+ hint: t("card.extraOperationArgKeysHint"),
3561
+ placeholder: t("card.extraContentArgKeysPlaceholder"),
3562
+ addLabel: t("card.denylistAdd"),
3563
+ removeLabel: t("card.listRemove"),
3564
+ items: value.extraOperationArgKeys ?? [],
3565
+ locked: readonly,
3566
+ onAdd: (entry) => {
3567
+ scope.set("extraOperationArgKeys", [...value.extraOperationArgKeys ?? [], entry]);
3568
+ },
3569
+ onRemove: (index) => {
3570
+ scope.set("extraOperationArgKeys", (value.extraOperationArgKeys ?? []).filter((_, i) => i !== index));
3571
+ }
3572
+ })
3573
+ ]
3574
+ }),
2946
3575
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2947
3576
  style: sectionStyle,
2948
3577
  children: [
@@ -3089,24 +3718,20 @@ window.__ModuleLoader__.load({
3089
3718
  style: hintStyle,
3090
3719
  children: t("card.networkEnabledHint")
3091
3720
  }),
3092
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3721
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3093
3722
  style: {
3094
3723
  display: "flex",
3095
3724
  alignItems: "center",
3096
3725
  gap: "8px",
3097
3726
  marginTop: "6px"
3098
3727
  },
3099
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3100
- type: "checkbox",
3728
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
3729
+ id: "plugin-config-perm-gate-network-enabled",
3730
+ label: t("card.networkEnabled"),
3101
3731
  checked: value.networkEnabled === true,
3102
3732
  disabled: readonly,
3103
- onChange: (event) => {
3104
- scope.set("networkEnabled", event.currentTarget.checked);
3105
- }
3106
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3107
- style: { fontSize: "13px" },
3108
- children: value.networkEnabled === true ? "ON" : "OFF"
3109
- })]
3733
+ onCommit: (next) => scope.set("networkEnabled", next)
3734
+ })
3110
3735
  }),
3111
3736
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3112
3737
  style: hintStyle,
@@ -3125,24 +3750,20 @@ window.__ModuleLoader__.load({
3125
3750
  style: hintStyle,
3126
3751
  children: t("card.networkInjectEnvHint")
3127
3752
  }),
3128
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3753
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3129
3754
  style: {
3130
3755
  display: "flex",
3131
3756
  alignItems: "center",
3132
3757
  gap: "8px",
3133
3758
  marginTop: "6px"
3134
3759
  },
3135
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3136
- type: "checkbox",
3760
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
3761
+ id: "plugin-config-perm-gate-network-inject-env",
3762
+ label: t("card.networkInjectEnv"),
3137
3763
  checked: value.networkInjectEnv !== false,
3138
3764
  disabled: readonly,
3139
- onChange: (event) => {
3140
- scope.set("networkInjectEnv", event.currentTarget.checked);
3141
- }
3142
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3143
- style: { fontSize: "13px" },
3144
- children: value.networkInjectEnv !== false ? "ON" : "OFF"
3145
- })]
3765
+ onCommit: (next) => scope.set("networkInjectEnv", next)
3766
+ })
3146
3767
  })
3147
3768
  ]
3148
3769
  }),
@@ -3157,24 +3778,20 @@ window.__ModuleLoader__.load({
3157
3778
  style: hintStyle,
3158
3779
  children: t("card.watchHint")
3159
3780
  }),
3160
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3781
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3161
3782
  style: {
3162
3783
  display: "flex",
3163
3784
  alignItems: "center",
3164
3785
  gap: "8px",
3165
3786
  marginTop: "6px"
3166
3787
  },
3167
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3168
- type: "checkbox",
3788
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PillSwitch, {
3789
+ id: "plugin-config-perm-gate-watch",
3790
+ label: t("card.watch"),
3169
3791
  checked: value.watch !== false,
3170
3792
  disabled: readonly,
3171
- onChange: (event) => {
3172
- scope.set("watch", event.currentTarget.checked);
3173
- }
3174
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3175
- style: { fontSize: "13px" },
3176
- children: value.watch !== false ? "ON" : "OFF"
3177
- })]
3793
+ onCommit: (next) => scope.set("watch", next)
3794
+ })
3178
3795
  })
3179
3796
  ]
3180
3797
  }),
@@ -3380,7 +3997,18 @@ window.__ModuleLoader__.load({
3380
3997
  "human-cancelled": "人工取消",
3381
3998
  "no-approval-channel": "无审批通道",
3382
3999
  "preset-passthrough": "审批策略 never · 已放行",
3383
- "stand-down": "门禁停用"
4000
+ "stand-down": "门禁停用",
4001
+ "yolo-allow": "YOLO 放行",
4002
+ "yolo-allow-no-reviewer": "YOLO 放行(无审查模型·机械处置)",
4003
+ "yolo-block": "YOLO 拦截",
4004
+ "rules-fallback-allow": "无模型规则闸门放行",
4005
+ "rules-fallback-block": "无模型规则闸门拦截",
4006
+ "safety-net-passthrough-allow": "机械审查放行(零模型)",
4007
+ "safety-net-passthrough-block": "机械审查拦截(零模型)",
4008
+ "cleanup-safe": "清理类安全放行",
4009
+ "escalation-auto": "沙箱升级自动放行",
4010
+ "learned-granted": "学习授权放行",
4011
+ "network-ask": "网络审批"
3384
4012
  };
3385
4013
  /** Compact wall-clock rendering of an ISO timestamp. */
3386
4014
  function clockTime(iso) {