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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- > **Compatibility note:** v0.1.0 ships `ja` / `ko` dictionaries, but official DSH exposes
15
+ > **Compatibility note:** v0.2.1-beta.3 ships `ja` / `ko` dictionaries, but official DSH exposes
16
16
  > only `zh` / `en` through `LocaleRuntime` (`LOCALE_IDS = ["zh", "en"]`). On stock DSH,
17
17
  > selecting `ja` / `ko` fails with `locale "<id>" is not registered`. Use a DSH fork that
18
18
  > updates `LOCALE_IDS` (locale-settings.ts) and `LOCALES` labels (client/index.ts), then
@@ -20,21 +20,24 @@
20
20
 
21
21
  > **▼ DSH version compatibility**
22
22
  >
23
- > | DSH version | Load | Settings registration | Host gate | Client half |
24
- > | --- | --- | --- | --- | --- |
25
- > | 0.1.0-rc.7 ~ 0.1.1-rc.x | ✅ | `ctx.settings.register(ns, schema, { base })` | ✅ `tools/pre-execute` identical | ✅ type-only imports |
26
- > | 0.1.2-alpha.2+ / 0.1.2-rc.1 | ✅ | `register` still present (`installSection` added) | ✅ `tools/pre-execute` identical | ✅ type-only imports |
23
+ > Two DSH lines are served from two long-lived branches, each with its own version
24
+ > series, `engines.dsh`, and npm dist-tag ([release layout](./RELEASING.md)):
27
25
  >
28
- > One artifact covers both. Two version-sensitive seams are handled by capability
29
- > probes rather than version checks: (1) settings registration uses `register`,
30
- > which exists in every supported version (`installSection` is a 0.1.2 addition,
31
- > not a replacement); (2) `effectivePolicy` is a **private** method of the
32
- > user-approval service in **both** versions, so it is read behind a `typeof`
33
- > probe and degrades to “policy unknown” when absent or throwing. The client
34
- > bundle value-imports nothing from `@deepseek-ai/*`, so the 0.1.2
35
- > `dsh-client-runtime` → `dsh-client-store` rename cannot break it.
26
+ > | DSH version | Branch | Version | npm tag |
27
+ > | --- | --- | --- | --- |
28
+ > | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
29
+ > | 0.1.2-alpha.1+ (incl. 0.1.5-rc.2) | `main` | `0.2.x` | `@latest` |
30
+ >
31
+ > `@deepseek-ai/dsh-client-runtime` was **removed** at `0.1.2-alpha.1` — it did not
32
+ > merely move. The `legacy` line still reaches `ctx.slots` through it; `main` gets
33
+ > the same declaration from `@deepseek-ai/dsh-client-ui-renderer/client`. Two
34
+ > version-sensitive seams are handled by capability probes rather than version
35
+ > checks: (1) settings registration uses `register`, which exists on both lines
36
+ > (`installSection` is an addition, not a replacement); (2) `effectivePolicy` is a
37
+ > **private** method of the user-approval service on both, so it is read behind a
38
+ > `typeof` probe and degrades to “policy unknown” when absent or throwing.
36
39
 
37
- Version **0.1.0** — see the [Changelog](./CHANGELOG.md).
40
+ Version **0.2.1-beta.3** — see the [Changelog](./CHANGELOG.md).
38
41
 
39
42
  A single, self-sufficient, deterministic-first, fail-closed permission gate for DeepSeek Harness.
40
43
 
@@ -71,8 +74,12 @@ cross-plugin version coupling.
71
74
  model-visible reason matches the recorded outcome.
72
75
  - **Permissive tier** — an **independent approval mode** (separate from read-only, full-access
73
76
  and whitelist tiers) that is neither "auto-approve" nor blanket trust. Front-end exposes a
74
- **single switch** (`permissive`); the three backend strategies are **combinable** and driven
77
+ **single switch** (`permissive`); the four backend strategies are **combinable** and driven
75
78
  by plugin settings — still fail-closed against P0.
79
+ - **Sandbox-escalation auto-answer** (`trustEscalation`) — a sandbox escalation is asked from
80
+ *inside* the shell / pwsh / edit tool body, after `tools/pre-execute`, so the gate never saw
81
+ it and a call it auto-allowed still prompted you to approve the widening. With this strategy
82
+ on, the exact call the gate cleared (matched by `callId`) is answered here instead.
76
83
  - **Risk-graded `llmAssist`** — a custom OpenAI-compatible LLM grades each `ask` as
77
84
  `safe` / `risky:<category>`; hard categories (deletion, credential, remote, system, bulk)
78
85
  **always ask**, neutral enters verdict learning, and every failure stays fail-closed.
@@ -161,6 +168,7 @@ In `cordis.yml`:
161
168
  trustAutoAllow: true # in-scope safe ops auto-allow; dangerous/unknown ask
162
169
  alwaysConfirm: false # every crossing asks; allow-controls add repeat-allow / wl-migrate buttons
163
170
  llmAssist: false # LLM classify first, human fallback on ask/failure
171
+ trustEscalation: true # a cleared call's own sandbox escalation needs no prompt
164
172
  ```
165
173
 
166
174
  `trustAutoAllow` is the baseline middle tier (rule-allow auto-passes). `alwaysConfirm` surfaces
@@ -169,22 +177,50 @@ the approval panel for every crossing; its "allow controls" add two extended but
169
177
  (which persists the command word into the `permissions.yaml` allow whitelist via
170
178
  `approveAllowEverywhere`). `llmAssist` consults a real, configurable LLM (`classifierEndpoint` /
171
179
  `classifierModel`, any OpenAI-compatible API) to auto-decide an `ask`, and falls back to the
172
- human seam on `ask`/error — always fail-closed. When `permissive` is off, the gate behaves
173
- exactly as before.
180
+ human seam on `ask`/error — always fail-closed. `trustEscalation` (on by default while the tier
181
+ is on) answers a `sandbox_permissions` escalation raised from inside a call the gate already
182
+ allowed; see below. When `permissive` is off, the gate behaves exactly as before.
183
+
184
+ ### Sandbox escalation: why a `safe` verdict still prompted
185
+
186
+ A tool call can raise **two independent approvals**. The gate owns the first — its own `ask`, on
187
+ the `tools/pre-execute` waterfall. The second comes from `approveEscalation` **inside the tool
188
+ body**, at `tools/execute` time, whenever the model passed `sandbox_permissions` +
189
+ `justification`; by then `tools/pre-execute` has already settled, so the gate's allow never
190
+ reaches it. A call the LLM graded `safe` and the gate auto-allowed therefore still showed a
191
+ prompt asking you to approve the sandbox widening.
192
+
193
+ `trustEscalation` closes that gap. The gate remembers every call it positively allowed (keyed by
194
+ the host's `callId`, which the escalation request repeats) and answers the escalation
195
+ `allowed-once` itself. It applies only when *all* hold:
196
+
197
+ - the Permissive tier is on and `trustEscalation` is on;
198
+ - the request carries a `callId` the gate cleared, with a matching tool name;
199
+ - the reason is a recognized escalation naming `workspace-write` or `danger-full-access`.
200
+
201
+ Everything else — an unrecognized reason, a different call, a call the gate asked or denied, the
202
+ `approval: never` passthrough — delegates to the human unchanged, so a future DSH wording change
203
+ fails closed rather than open. The auto-answer is recorded on the event feed
204
+ (`verdict: "escalation-auto"`, `mode: <target>`). Turn the switch off to keep widening
205
+ human-gated while other allows stay automatic.
206
+
174
207
 
175
208
  ### Risk-graded llmAssist, verdict learning, and the event feed
176
209
 
177
210
  When `llmAssist` is on, the configured LLM grades one `ask` at a time with a structured
178
- protocol. Two receiver sources are selectable in the settings card: a **custom API** (any
211
+ protocol. Grading happens **inside the gate's `tools/pre-execute` waterfall, before the decision
212
+ is returned to the host**: a `safe` verdict delegates the call straight through, so the approval
213
+ panel never appears; only a genuinely uncertain verdict reaches you. Two receiver sources are
214
+ selectable in the settings card: a **custom API** (any
179
215
  OpenAI-compatible endpoint — `classifierEndpoint` / `classifierModel` / `classifierApiKey`,
180
216
  with presets including Xiaomi MiMo `https://api.xiaomimimo.com/v1`), or the **DSH host model
181
217
  group** (the session's configured `llm` service, via `agentDefaultModel.currentSelection`,
182
218
  optionally overridden with `classifierProvider` / `classifierModel`). A **health test** button
183
219
  (`POST /api/dsh-perm-gate/health`) runs one minimal completion and reports latency.
184
220
 
185
- - `safe` → the ask is auto-allowed (audited as the `classifier` source).
186
- - `risky` + a **hard category** (`deletion`, `credential`, `remote`, `system`, `bulk`) →
187
- the ask **always** reaches the human; hard risks are never auto-allowed and never learned.
221
+ - `safe` → the call is auto-allowed (audited as the `classifier` source); no panel is shown.
222
+ - `risky` + a **hard category** (`deletion`, `credential`, `remote`, `system`, `bulk`) → the call
223
+ is **auto-denied** without a panel; hard risks are never auto-allowed and never learned.
188
224
  - `risky:neutral` → with `riskLearning` enabled (Settings card, off by default), each human
189
225
  approval that actually executes (settled via the host's `tools/result` event) counts toward
190
226
  a `tool|category` key; once the count reaches `riskThreshold` (default 3) **and** the new
@@ -245,7 +281,7 @@ session's effective approval policy is `never`.
245
281
 
246
282
  The tier is also adjustable at runtime from **Settings → Plugins → Permissive approval tier**
247
283
  (a `settings.plugins.tab` page rendered by the plugin's browser client): one switch toggles
248
- `permissive`, and three toggles edit the backend `permissiveStrategies`. The host reads the
284
+ `permissive`, and four toggles edit the backend `permissiveStrategies`. The host reads the
249
285
  namespace live, so a change applies to the next tool call without a restart. This is an
250
286
  independent approval class, NOT the DSH "auto-approval" mode.
251
287
 
package/README.zh.md CHANGED
@@ -12,26 +12,30 @@
12
12
  - [日本語 changelog](./CHANGELOG.ja.md)
13
13
  - [한국어 changelog](./CHANGELOG.ko.md)
14
14
 
15
- > **兼容性说明:** v0.1.0 自带 `ja` / `ko` 字典,但官方 DSH 的 `LocaleRuntime`
15
+ > **兼容性说明:** v0.2.1-beta.3 自带 `ja` / `ko` 字典,但官方 DSH 的 `LocaleRuntime`
16
16
  > 只暴露 `zh` / `en`(`LOCALE_IDS = ["zh", "en"]`)。在原版 DSH 上选择 `ja` / `ko`
17
17
  > 会报 `locale "<id>" is not registered`。请使用更新了 `LOCALE_IDS`
18
18
  > (locale-settings.ts)与 `LOCALES` 标签(client/index.ts)的 DSH fork 并重新构建。
19
19
 
20
20
  > **▼ DSH 版本适配**
21
21
  >
22
- > | DSH 版本 | 加载 | 设置注册 | 宿主门 | 客户端半 |
23
- > | --- | --- | --- | --- | --- |
24
- > | 0.1.0-rc.7 ~ 0.1.1-rc.x | ✅ | `ctx.settings.register(ns, schema, { base })` | ✅ `tools/pre-execute` 一致 | ✅ 仅类型导入 |
25
- > | 0.1.2-alpha.2+ / 0.1.2-rc.1 | ✅ | `register` 仍保留(另加 `installSection`) | ✅ `tools/pre-execute` 一致 | ✅ 仅类型导入 |
22
+ > 两个 DSH 版本线从两个长期分支分别维护,各有一套版本号系列、`engines.dsh`
23
+ > 和 npm 分发标签([发布布局](./RELEASING.md)):
26
24
  >
27
- > 一份产物同时支持两版本。两处版本敏感点都用**能力探测**而非版本号判断:
28
- > ① 设置注册走 `register`,它在所有目标版本都存在(`installSection` 是 0.1.2 的
29
- > **新增**而非替代);② `effectivePolicy` 在**两版本**中都是 user-approval 服务的
30
- > **私有**方法,故只在 `typeof` 探测后调用,缺失或抛错时降级为「策略未知」。
31
- > 客户端 bundle 对 `@deepseek-ai/*` 无任何值导入,因此 0.1.2 的
32
- > `dsh-client-runtime` → `dsh-client-store` 改名不会影响它。
25
+ > | DSH 版本 | 分支 | 版本号 | npm 标签 |
26
+ > | --- | --- | --- | --- |
27
+ > | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
28
+ > | 0.1.2-alpha.1+(含 0.1.5-rc.2) | `main` | `0.2.x` | `@latest` |
29
+ >
30
+ > `@deepseek-ai/dsh-client-runtime` 在 `0.1.2-alpha.1` 中已被**移除**——不仅仅是更名。
31
+ > `legacy` 线仍通过它访问 `ctx.slots`;`main` 从
32
+ > `@deepseek-ai/dsh-client-ui-renderer/client` 获得相同的声明。两处版本敏感
33
+ > 接缝通过**能力探测**处理,而非版本号检查:(1)设置注册使用 `register`,两线
34
+ > 都存在(`installSection` 是新增项,不是替代);(2)`effectivePolicy` 在两
35
+ > 线上都是 user-approval 服务的**私有**方法,因此通过 `typeof` 探测读取,缺失
36
+ > 或抛错时降级为「策略未知」。
33
37
 
34
- 版本 **0.1.0** —— 变更见 [Changelog](./CHANGELOG.md)。
38
+ 版本 **0.2.1-beta.3** —— 变更见 [Changelog](./CHANGELOG.md)。
35
39
 
36
40
  一个**单一自足、确定性优先、fail-closed** 的 DeepSeek Harness 权限门插件。
37
41
 
@@ -54,7 +58,8 @@
54
58
  - **会话放行** — 精确的 `(工具, 规范化 fingerprint)` grant,带 `TTL` + `maxUses`;换目标绝不复用。子代理继承但不可自授。
55
59
  - **纯函数规则引擎** — glob/regex 编译 + ReDoS 上限、坏规则 loud fail、按源内容哈希缓存。
56
60
  - **审计** — 每次决策写为 `{ignorable:true}` 事件并带 `callId`;模型可见理由与记录一致。
57
- - **Permissive 档位** — 一个**独立审批模式**(区别于只读、完全权限与白名单档),既不是"自动审批",也不授予泛化权限。前端只暴露**一个开关**(`permissive`),后台三个审批策略**可组合**、由插件设置决定——仍对 P0 保持 fail-closed。
61
+ - **Permissive 档位** — 一个**独立审批模式**(区别于只读、完全权限与白名单档),既不是"自动审批",也不授予泛化权限。前端只暴露**一个开关**(`permissive`),后台四个审批策略**可组合**、由插件设置决定——仍对 P0 保持 fail-closed。
62
+ - **沙箱提权自动答复**(`trustEscalation`)— 沙箱提权是从 shell / pwsh / edit 工具**体内部**(`tools/pre-execute` 之后)发出的,所以门禁从未见过它,一个它自动放行的调用仍会弹出确认。开启后,门禁以 `callId` 精确匹配已放行调用并直接答复。
58
63
 
59
64
  ## 安装
60
65
 
@@ -101,6 +106,7 @@ P0 硬拒绝始终单调且不可协商。
101
106
  permissiveStrategies: # 后台策略,可组合
102
107
  trustAutoAllow: true # 作用域内安全操作自动放行;危险/未知转 ask
103
108
  alwaysConfirm: false # 一律逐次 ask;允许控件附带重复允许/迁白名单按钮
109
+ trustEscalation: true # 门禁已放行的调用,其自身的沙箱提权免确认
104
110
  llmAssist: false # 先由 LLM 分类裁决;ask/无分类器时回退到人工
105
111
  ```
106
112
  (llmAssist 的真实接收 LLM 在设置页填 `classifierEndpoint` / `classifierModel`,OpenAI 兼容的自定义 API
@@ -110,7 +116,27 @@ P0 硬拒绝始终单调且不可协商。
110
116
  「允许控件」含两个扩展按钮:**本会话重复允许该类**(会话限次 grant,`approveRepeat`)与
111
117
  **允许所有类型**(把命令词持久写进 `permissions.yaml` 的 allow 白名单,`approveAllowEverywhere`)。
112
118
  `llmAssist` 调用配置的真实 LLM(任意 OpenAI 兼容 API)自动裁决 `ask`,结果不确定/出错时回退人工
113
- 接缝——始终 fail-closed。`permissive` 关闭时,门禁行为与之前完全一致。
119
+ 接缝——始终 fail-closed。`trustEscalation`(档位开启时默认开)答复门禁已放行的调用在其工具体内
120
+ 提出的 `sandbox_permissions` 提权;见下文。`permissive` 关闭时,门禁行为与之前完全一致。
121
+
122
+ ### 沙箱提权:为何 `safe` 裁决仍会弹窗
123
+
124
+ 一个工具调用可能触发**两个独立的审批**。门禁负责第一个——它的 `ask`,在 `tools/pre-execute`
125
+ 瀑布上。第二个来自工具体内部的 `approveEscalation`,在 `tools/execute` 时刻,只要模型传了
126
+ `sandbox_permissions` + `justification`;此时 `tools/pre-execute` 已结算,门禁的放行从未到达它。
127
+ LLM 评定为 `safe` 且门禁自动放行的调用因此仍会弹出确认。
128
+
129
+ `trustEscalation` 填补这个缺口。门禁记住每个它正面向上放行的调用(以宿主 `callId` 为键,提权
130
+ 请求会重复该值),并在本处自行答复 `allowed-once`。它仅在**全部满足**时适用:
131
+
132
+ - Permissive 档位开启且 `trustEscalation` 开启;
133
+ - 请求携带门禁放行的 `callId`,且工具名匹配;
134
+ - 原因为已知的提权,指明 `workspace-write` 或 `danger-full-access`。
135
+
136
+ 其余所有情况——未知原因、不同的调用、门禁要求或拒绝的调用、`approval: never` 透传——都保持交给人
137
+ 工,因此未来 DSH 措辞变更时 fail-closed。自动答复记录在事件流中
138
+ (`verdict: "escalation-auto"`,`mode: <目标模式>`)。关闭开关可使沙箱放宽保持人工审批,其余
139
+ 自动放行不变。
114
140
 
115
141
  ### 权限下拉里可选档位
116
142
 
@@ -132,16 +158,16 @@ Full access 之间。DSH 的 bundle patch 对这个 map 是**整表替换**而
132
158
  ### 在 UI 里可配置
133
159
 
134
160
  该档位也可在运行时从 **设置 → 插件 → Permissive 审批档** 调整(插件浏览器端渲染的
135
- `settings.plugins.tab` 页面):一个开关切换 `permissive`,三个开关编辑后台
161
+ `settings.plugins.tab` 页面):一个开关切换 `permissive`,四个开关编辑后台
136
162
  `permissiveStrategies`。host 端 live 读取该命名空间,改动对下一条工具调用即时生效,无需重启。
137
163
  这是一个独立审批类,**不是** DSH 的"auto-approval"档。
138
164
 
139
165
  ### 风险分级 llmAssist、裁决学习与事件流
140
166
 
141
- 开启 `llmAssist` 后,接收 LLM(自定义 OpenAI 兼容端点,或 DSH 宿主模型组——见上文)按结构化协议逐条评估 `ask`:
167
+ 开启 `llmAssist` 后,接收 LLM(自定义 OpenAI 兼容端点,或 DSH 宿主模型组——见上文)按结构化协议逐条评估 `ask`。**判定发生在门禁的 `tools/pre-execute` 瀑布内部、决策返回宿主之前**:`safe` 直接放行,审批面板根本不会出现;只有真正无法确定的判定才会弹到你面前。
142
168
 
143
- - `safe` → 自动放行(审计来源为 `classifier`)。
144
- - `risky` + **硬风险类别**(`deletion`、`credential`、`remote`、`system`、`bulk`)→ 一律转人工;硬风险永不自动放行、也永不进入学习。
169
+ - `safe` → 自动放行(审计来源为 `classifier`),不弹面板。
170
+ - `risky` + **硬风险类别**(`deletion`、`credential`、`remote`、`system`、`bulk`)→ **自动拒绝**、不弹面板;硬风险永不自动放行、也永不进入学习。
145
171
  - `risky:neutral` → 若开启 `riskLearning`(设置卡片内,默认关闭),人工批准且真实执行的 neutral 风险会按 `tool|类别` 计数;计数达到 `riskThreshold`(默认 3)且新调用的操作指纹(命令词 + 目标基名)命中已确认样本时,**同一操作**自动放行。不同目标永不复用该放行。开启学习沉淀(`riskSediment`,默认开)后,满阈值 key 的确认样本会成为**确定性放行规则**:指纹精确命中即直接放行、无需再过 LLM——即使关闭 llmAssist 也继续生效;沉淀规则在设置卡片中可见、可管理(终止学习 / 删除样本)。
146
172
  - 超时(`riskTimeoutMs`,默认 20s,重试 1 次)、传输失败与协议外输出均维持原 `ask`——门禁绝不猜测。
147
173
 
package/lib/client.js CHANGED
@@ -20,6 +20,8 @@ window.__ModuleLoader__.load({
20
20
  "card.strategy.trustAutoAllow": "trustAutoAllow — 作用域内安全操作自动放行,危险/未知转 ask(中间档基线)",
21
21
  "card.strategy.alwaysConfirm": "alwaysConfirm — 一律逐次 ask;审批面板「允许控件」含两个扩展按钮:「本会话重复允许该类」(会话语限次 grant)与「允许所有类型」(命令词持久迁入 rulesFile 白名单)",
22
22
  "card.strategy.llmAssist": "llmAssist — 自定义 LLM 按风险分级裁决:safe 自动放行;deletion/credential/remote/system/bulk 硬风险自动拒绝;neutral 进入确认学习;失败/超时回退人工",
23
+ "card.strategy.trustEscalation": "trustEscalation — 沙箱升级免确认:本门禁已放行的调用,其工具内部发起的沙箱提权(shell / pwsh / edit 的 sandbox_permissions)由门禁直接批准,不再弹窗",
24
+ "card.strategy.trustEscalationHint": "沙箱提权审批发生在 tools/pre-execute 之后、工具体内部,门禁原本看不到它,因此「LLM 判安全已自动放行」的调用仍会弹窗要求你批准提权。开启后,仅对门禁确实放行过的那一次调用(按 callId 精确匹配)免确认,其余一律照旧走人工;目标模式非 workspace-write / danger-full-access 或理由无法识别时同样回退人工。关闭后提权保持人工把关。",
23
25
  "card.readonly": "只读",
24
26
  "card.unavailable": "设置命名空间不可用:请确认 dsh-perm-gate 已装配进 profile。",
25
27
  "card.llmReceiver": "llmAssist 接收 LLM(OpenAI 兼容,可用自定义 API)",
@@ -127,6 +129,8 @@ window.__ModuleLoader__.load({
127
129
  "card.strategy.trustAutoAllow": "trustAutoAllow — safe in-scope ops auto-allow; dangerous/unknown ask (baseline middle tier)",
128
130
  "card.strategy.alwaysConfirm": "alwaysConfirm — every crossing asks; the approval \"allow controls\" get two extended buttons: \"repeat-allow this type this session\" (bounded session grant) and \"allow every occurrence\" (persist the command word into the rulesFile whitelist)",
129
131
  "card.strategy.llmAssist": "llmAssist — a custom LLM grades risk: safe auto-allows; deletion/credential/remote/system/bulk hard risks auto-deny; neutral enters confirm-learning; failure/timeout falls back to the human",
132
+ "card.strategy.trustEscalation": "trustEscalation — sandbox escalation without a prompt: for a call this gate already allowed, the sandbox widening a shell / pwsh / edit tool raises from inside itself is approved here instead of asking you",
133
+ "card.strategy.trustEscalationHint": "A sandbox escalation is asked from inside the tool body, after tools/pre-execute, so the gate never saw it — a call the LLM graded safe and the gate auto-allowed still prompted you to approve the widening. With this on, only the exact call the gate allowed (matched by callId) skips the prompt; everything else goes to the human as before, as does any unrecognized reason or a target other than workspace-write / danger-full-access. Turn it off to keep widening human-gated.",
130
134
  "card.readonly": "Read-only",
131
135
  "card.unavailable": "Settings namespace unavailable: make sure dsh-perm-gate is assembled into this profile.",
132
136
  "card.llmReceiver": "llmAssist receiving LLM (OpenAI-compatible; any custom API)",
@@ -742,6 +746,29 @@ window.__ModuleLoader__.load({
742
746
  }
743
747
  })]
744
748
  }),
749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
750
+ style: rowStyle,
751
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
752
+ htmlFor: "plugin-config-perm-gate-escalation",
753
+ style: { fontSize: "12px" },
754
+ children: t("card.strategy.trustEscalation")
755
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
756
+ id: "plugin-config-perm-gate-escalation",
757
+ type: "checkbox",
758
+ checked: strategies.trustEscalation ?? true,
759
+ disabled: readonly || !effective,
760
+ onChange: (event) => {
761
+ scope.set("permissiveStrategies", {
762
+ ...strategies,
763
+ trustEscalation: event.currentTarget.checked
764
+ });
765
+ }
766
+ })]
767
+ }),
768
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
769
+ style: hintStyle,
770
+ children: t("card.strategy.trustEscalationHint")
771
+ }),
745
772
  strategies.llmAssist === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
746
773
  style: sectionStyle,
747
774
  children: [