dsh-perm-gate 0.2.1-beta.4 → 0.2.1-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.ja.md +28 -0
- package/CHANGELOG.ko.md +28 -0
- package/CHANGELOG.md +31 -0
- package/INSTALL.ja.md +14 -7
- package/INSTALL.ko.md +13 -7
- package/INSTALL.md +15 -8
- package/INSTALL.zh.md +13 -7
- package/README.ja.md +22 -13
- package/README.ko.md +23 -14
- package/README.md +26 -19
- package/README.zh.md +17 -12
- package/cordis.patch.yml +15 -3
- package/lib/client.js +7 -117
- package/lib/client.js.map +1 -1
- package/lib/runtime.d.ts +22 -5
- package/lib/runtime.js +57 -8
- package/package.json +1 -1
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.2.1-beta.
|
|
15
|
+
> **Compatibility note:** v0.2.1-beta.5 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
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
> **private** method of the user-approval service on both, so it is read behind a
|
|
38
38
|
> `typeof` probe and degrades to “policy unknown” when absent or throwing.
|
|
39
39
|
|
|
40
|
-
Version **0.2.1-beta.
|
|
40
|
+
Version **0.2.1-beta.5** — see the [Changelog](./CHANGELOG.md).
|
|
41
41
|
|
|
42
42
|
A single, self-sufficient, deterministic-first, fail-closed permission gate for DeepSeek Harness.
|
|
43
43
|
|
|
@@ -72,10 +72,11 @@ cross-plugin version coupling.
|
|
|
72
72
|
malformed rules, and source-hash compile caching.
|
|
73
73
|
- **Audit** — every decision is logged as an `{ignorable:true}` event with its `callId`; the
|
|
74
74
|
model-visible reason matches the recorded outcome.
|
|
75
|
-
-
|
|
76
|
-
and whitelist tiers) that is neither "auto-approve" nor blanket trust. Front-end
|
|
77
|
-
**single switch** (`permissive`); the four backend strategies are **combinable** and
|
|
78
|
-
by plugin settings — still fail-closed against P0.
|
|
75
|
+
- **自动审查 tier** (`permissive`) — an **independent approval mode** (separate from read-only,
|
|
76
|
+
full-access and whitelist tiers) that is neither "auto-approve" nor blanket trust. Front-end
|
|
77
|
+
exposes a **single switch** (`permissive`); the four backend strategies are **combinable** and
|
|
78
|
+
driven by plugin settings — still fail-closed against P0. The permission picker and the
|
|
79
|
+
settings row both show it under the product label 自动审查, with no icon.
|
|
79
80
|
- **Sandbox-escalation auto-answer** (`trustEscalation`) — a sandbox escalation is asked from
|
|
80
81
|
*inside* the shell / pwsh / edit tool body, after `tools/pre-execute`, so the gate never saw
|
|
81
82
|
it and a call it auto-allowed still prompted you to approve the widening. With this strategy
|
|
@@ -148,12 +149,18 @@ permissions:
|
|
|
148
149
|
A command entry `word#flag` matches the command word (`word`) with the modifier `recursive` or
|
|
149
150
|
`force` — so `rm#recursive` matches `rm -rf`, `env rm -rf`, and `sh -c "rm -rf /"`.
|
|
150
151
|
|
|
151
|
-
##
|
|
152
|
+
## The 自动审查 tier (machine value `permissive`)
|
|
152
153
|
|
|
153
|
-
|
|
154
|
-
Read Only / Workspace Write / Full access / Whitelist. It is **not** "auto-approval" and
|
|
155
|
-
mints blanket authority: it only narrows or widens the seam *before* the human/LLM step
|
|
156
|
-
P0 hard-deny stays monotonic and non-negotiable.
|
|
154
|
+
自动审查 is an **independent approval tier** in the DSH permission picker, parallel to
|
|
155
|
+
Read Only / Workspace Write / Full access / Whitelist. It is **not** generic "auto-approval" and
|
|
156
|
+
never mints blanket authority: it only narrows or widens the seam *before* the human/LLM step
|
|
157
|
+
while P0 hard-deny stays monotonic and non-negotiable.
|
|
158
|
+
|
|
159
|
+
The picker label is a **host-supplied product string**, not a per-locale dictionary entry: DSH
|
|
160
|
+
0.1.2 renders a plugin tier's `name:` verbatim on both permission surfaces (the General-settings
|
|
161
|
+
default row and the composer picker) and only supplies its own localized labels for the three
|
|
162
|
+
built-in values, so `cordis.patch.yml` ships the Chinese label for every session. The tier draws
|
|
163
|
+
**no icon** — the composer renders glyphs only for the built-in values.
|
|
157
164
|
|
|
158
165
|
In `cordis.yml`:
|
|
159
166
|
|
|
@@ -194,7 +201,7 @@ prompt asking you to approve the sandbox widening.
|
|
|
194
201
|
the host's `callId`, which the escalation request repeats) and answers the escalation
|
|
195
202
|
`allowed-once` itself. It applies only when *all* hold:
|
|
196
203
|
|
|
197
|
-
- the
|
|
204
|
+
- the 自动审查 tier is on and `trustEscalation` is on;
|
|
198
205
|
- the request carries a `callId` the gate cleared, with a matching tool name;
|
|
199
206
|
- the reason is a recognized escalation naming `workspace-write` or `danger-full-access`.
|
|
200
207
|
|
|
@@ -253,18 +260,18 @@ channel existed), with `tools/result` settling the same ask as a fallback when t
|
|
|
253
260
|
correlate it. Approvals report the post-approval learning progress (`n`/threshold), and the notice
|
|
254
261
|
strip labels all three terminal states.
|
|
255
262
|
|
|
256
|
-
The
|
|
257
|
-
|
|
263
|
+
The tier draws no icon in the permission picker: the composer's glyphs are keyed to the three
|
|
264
|
+
built-in values, so a plugin-contributed tier is text-only on every surface.
|
|
258
265
|
|
|
259
266
|
### A selectable session tier
|
|
260
267
|
|
|
261
268
|
`cordis.patch.yml` adds a `permissive` preset (`sandbox: workspace-write`, `approval: ask`, name
|
|
262
|
-
|
|
269
|
+
**自动审查**) between Workspace Write and Full access. The DSH bundle patch replaces the whole
|
|
263
270
|
`permission.config.presets` map rather than merging per key, so the file also restates the three
|
|
264
271
|
built-ins (`read-only` / `workspace-write` / `danger-full-access`, from
|
|
265
272
|
`@deepseek-ai/dsh-base/cordis.patch.yml`); `test/patch-presets.spec.ts` pins that key set. So the
|
|
266
|
-
session permission picker offers
|
|
267
|
-
"auto-approval" mode.
|
|
273
|
+
session permission picker offers 自动审查 as an independent selectable approval tier, not a
|
|
274
|
+
generic "auto-approval" mode.
|
|
268
275
|
|
|
269
276
|
The gate is active **only in the tiers listed in `gatePresets`** (default `['permissive']`, the
|
|
270
277
|
tier this plugin adds). In every other tier — Read Only, Workspace Write, Full access,
|
|
@@ -279,11 +286,11 @@ session's effective approval policy is `never`.
|
|
|
279
286
|
|
|
280
287
|
### Configurable in the UI
|
|
281
288
|
|
|
282
|
-
The tier is also adjustable at runtime from **Settings → Plugins →
|
|
289
|
+
The tier is also adjustable at runtime from **Settings → Plugins → 自动审查**
|
|
283
290
|
(a `settings.plugins.tab` page rendered by the plugin's browser client): one switch toggles
|
|
284
291
|
`permissive`, and four toggles edit the backend `permissiveStrategies`. The host reads the
|
|
285
292
|
namespace live, so a change applies to the next tool call without a restart. This is an
|
|
286
|
-
independent approval class, NOT
|
|
293
|
+
independent approval class, NOT a generic "auto-approval" mode.
|
|
287
294
|
|
|
288
295
|
## CLI
|
|
289
296
|
|
package/README.zh.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- [日本語 changelog](./CHANGELOG.ja.md)
|
|
13
13
|
- [한국어 changelog](./CHANGELOG.ko.md)
|
|
14
14
|
|
|
15
|
-
> **兼容性说明:** v0.2.1-beta.
|
|
15
|
+
> **兼容性说明:** v0.2.1-beta.5 自带 `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 并重新构建。
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
> 线上都是 user-approval 服务的**私有**方法,因此通过 `typeof` 探测读取,缺失
|
|
36
36
|
> 或抛错时降级为「策略未知」。
|
|
37
37
|
|
|
38
|
-
版本 **0.2.1-beta.
|
|
38
|
+
版本 **0.2.1-beta.5** —— 变更见 [Changelog](./CHANGELOG.md)。
|
|
39
39
|
|
|
40
40
|
一个**单一自足、确定性优先、fail-closed** 的 DeepSeek Harness 权限门插件。
|
|
41
41
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
- **会话放行** — 精确的 `(工具, 规范化 fingerprint)` grant,带 `TTL` + `maxUses`;换目标绝不复用。子代理继承但不可自授。
|
|
59
59
|
- **纯函数规则引擎** — glob/regex 编译 + ReDoS 上限、坏规则 loud fail、按源内容哈希缓存。
|
|
60
60
|
- **审计** — 每次决策写为 `{ignorable:true}` 事件并带 `callId`;模型可见理由与记录一致。
|
|
61
|
-
-
|
|
61
|
+
- **自动审查档位**(机器值 `permissive`)——一个**独立审批模式**(区别于只读、完全权限与白名单档),既不是"自动审批",也不授予泛化权限。前端只暴露**一个开关**(`permissive`),后台四个审批策略**可组合**、由插件设置决定——仍对 P0 保持 fail-closed。权限下拉框与设置行都按产品名「自动审查」显示,且不带图标。
|
|
62
62
|
- **沙箱提权自动答复**(`trustEscalation`)— 沙箱提权是从 shell / pwsh / edit 工具**体内部**(`tools/pre-execute` 之后)发出的,所以门禁从未见过它,一个它自动放行的调用仍会弹出确认。开启后,门禁以 `callId` 精确匹配已放行调用并直接答复。
|
|
63
63
|
|
|
64
64
|
## 安装
|
|
@@ -88,12 +88,17 @@ dsh plugin --profile web add dsh-perm-gate
|
|
|
88
88
|
|
|
89
89
|
规则示例:见 [examples/permissions.example.yaml](./examples/permissions.example.yaml)。
|
|
90
90
|
|
|
91
|
-
##
|
|
91
|
+
## 自动审查档位(机器值 `permissive`)
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
自动审查是权限下拉框里一个**独立审批档**,与只读 / 工作区内修改 / 完全权限 / 白名单平行。
|
|
94
|
+
它不是泛化的"自动审批"、也不授予泛化权限:只会在人类/LLM 接缝**之前**收窄或放宽决策,
|
|
95
95
|
P0 硬拒绝始终单调且不可协商。
|
|
96
96
|
|
|
97
|
+
下拉框里的名字是**宿主提供的产品名**,不是逐语言的字典项:DSH 0.1.2 对插件档位在**两个**权限界面上
|
|
98
|
+
(通用设置默认档行、输入栏权限选择器)都原样渲染补丁里的 `name:`,只给三个内置档提供自己的本地化
|
|
99
|
+
标签,因此 `cordis.patch.yml` 直接写中文名,对所有会话一致。该档位**不画图标**——选择器的图标只按
|
|
100
|
+
三个内置值取。
|
|
101
|
+
|
|
97
102
|
`cordis.yml`:
|
|
98
103
|
|
|
99
104
|
```yaml
|
|
@@ -129,7 +134,7 @@ LLM 评定为 `safe` 且门禁自动放行的调用因此仍会弹出确认。
|
|
|
129
134
|
`trustEscalation` 填补这个缺口。门禁记住每个它正面向上放行的调用(以宿主 `callId` 为键,提权
|
|
130
135
|
请求会重复该值),并在本处自行答复 `allowed-once`。它仅在**全部满足**时适用:
|
|
131
136
|
|
|
132
|
-
-
|
|
137
|
+
- 自动审查档位开启且 `trustEscalation` 开启;
|
|
133
138
|
- 请求携带门禁放行的 `callId`,且工具名匹配;
|
|
134
139
|
- 原因为已知的提权,指明 `workspace-write` 或 `danger-full-access`。
|
|
135
140
|
|
|
@@ -141,11 +146,11 @@ LLM 评定为 `safe` 且门禁自动放行的调用因此仍会弹出确认。
|
|
|
141
146
|
### 权限下拉里可选档位
|
|
142
147
|
|
|
143
148
|
`cordis.patch.yml` 在 DSH 的 `permission.config.presets` 里新增了 `permissive` preset
|
|
144
|
-
(`sandbox: workspace-write`、`approval: ask`、名称
|
|
145
|
-
|
|
149
|
+
(`sandbox: workspace-write`、`approval: ask`、名称 **自动审查**),位于工作区内修改与
|
|
150
|
+
完全权限之间。DSH 的 bundle patch 对这个 map 是**整表替换**而非逐键合并,所以该文件还必须重述三个内置档
|
|
146
151
|
(`read-only` / `workspace-write` / `danger-full-access`,取自
|
|
147
152
|
`@deepseek-ai/dsh-base/cordis.patch.yml`);`test/patch-presets.spec.ts` 固定了这份键集合。因此会话权限
|
|
148
|
-
|
|
153
|
+
下拉里会出现「自动审查」这个**独立可选审批档**,而不是"auto-approval"档。
|
|
149
154
|
|
|
150
155
|
门禁**只在 `gatePresets` 列出的档位里生效**(默认 `['permissive']`,即本插件新增的那一档)。在其余任何档位
|
|
151
156
|
(Read Only、Workspace Write、Full access、`custom`)里,门禁的判定流程**完全不运行**:不放行、不弹审批、
|
|
@@ -157,7 +162,7 @@ Full access 之间。DSH 的 bundle patch 对这个 map 是**整表替换**而
|
|
|
157
162
|
|
|
158
163
|
### 在 UI 里可配置
|
|
159
164
|
|
|
160
|
-
该档位也可在运行时从 **设置 → 插件 →
|
|
165
|
+
该档位也可在运行时从 **设置 → 插件 → 自动审查** 调整(插件浏览器端渲染的
|
|
161
166
|
`settings.plugins.tab` 页面):一个开关切换 `permissive`,四个开关编辑后台
|
|
162
167
|
`permissiveStrategies`。host 端 live 读取该命名空间,改动对下一条工具调用即时生效,无需重启。
|
|
163
168
|
这是一个独立审批类,**不是** DSH 的"auto-approval"档。
|
|
@@ -182,7 +187,7 @@ Full access 之间。DSH 的 bundle patch 对这个 map 是**整表替换**而
|
|
|
182
187
|
调用文本命中任一关键词(大小写不敏感子串)即直接拒绝,且先于白名单 / 授权 / LLM。黑名单在设置
|
|
183
188
|
卡片中按列表查看与增删(预置条目带标签,可一键恢复预置);未设置或为空时应用预置列表——黑名单
|
|
184
189
|
不会静默关闭。
|
|
185
|
-
|
|
190
|
+
该档位在权限选择器中**不显示图标**:选择器的图标只按三个内置值取,插件新增的档位是纯文字。
|
|
186
191
|
|
|
187
192
|
|
|
188
193
|
## CLI(独立 dry-run)
|
package/cordis.patch.yml
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
# dsh-perm-gate profile patch.
|
|
2
2
|
#
|
|
3
3
|
# Two jobs:
|
|
4
|
-
# 1) Add the plugin's own "
|
|
4
|
+
# 1) Add the plugin's own "自动审查" session approval tier to the DSH
|
|
5
5
|
# permission picker, and restate the built-in tiers it must not drop.
|
|
6
6
|
# 2) Insert the plugin itself (host half) into the profile.
|
|
7
7
|
#
|
|
8
|
+
# The tier's DISPLAY NAME is the Chinese product label, matching every official
|
|
9
|
+
# 0.1.2 permission label (仅可查看 / 工作区内修改 / 完全权限) in a zh session. Both
|
|
10
|
+
# permission surfaces — the General-settings default row and the composer
|
|
11
|
+
# picker — render a host-supplied name verbatim unless the value is one of the
|
|
12
|
+
# three built-ins, so a single `name:` string is what both show; neither surface
|
|
13
|
+
# is localized per-dictionary for plugin-contributed tiers. The machine value
|
|
14
|
+
# stays `permissive`: it is the key `gatePresets` matches and the value the
|
|
15
|
+
# permission projection reports, so only the label text is user-facing.
|
|
16
|
+
#
|
|
17
|
+
# The tier carries no icon. The built-in glyphs the composer draws are keyed by
|
|
18
|
+
# the three built-in values, and a plugin tier deliberately has none.
|
|
19
|
+
#
|
|
8
20
|
# The DSH bundle patch REPLACES the whole `permission.config.presets` map (it
|
|
9
21
|
# does not merge per key), so this file MUST restate every tier that should
|
|
10
22
|
# survive. The authoritative built-in set is
|
|
@@ -31,8 +43,8 @@
|
|
|
31
43
|
permissive:
|
|
32
44
|
sandbox: workspace-write
|
|
33
45
|
approval: ask
|
|
34
|
-
name:
|
|
35
|
-
description:
|
|
46
|
+
name: 自动审查
|
|
47
|
+
description: 独立审批档 —— 作用域内安全操作自动放行,危险/未知越界转人工确认(审批强度可在「设置 → 插件 → 自动审查」调整)。
|
|
36
48
|
danger-full-access:
|
|
37
49
|
sandbox: danger-full-access
|
|
38
50
|
approval: never
|
package/lib/client.js
CHANGED
|
@@ -12,9 +12,9 @@ window.__ModuleLoader__.load({
|
|
|
12
12
|
const NS = "dsh-perm-gate";
|
|
13
13
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
14
14
|
const zh = {
|
|
15
|
-
"card.title": "
|
|
16
|
-
"card.description": "
|
|
17
|
-
"card.permissive": "
|
|
15
|
+
"card.title": "自动审查",
|
|
16
|
+
"card.description": "独立审批档位:与只读 / 完全权限 / 白名单平行,在会话权限下拉中显示为「自动审查」。前端只暴露一个开关;后台四个审批策略可组合、由此处设置决定,仍对 P0 硬拒绝保持 fail-closed。",
|
|
17
|
+
"card.permissive": "启用自动审查档位",
|
|
18
18
|
"card.permissiveHint": "关闭时门禁行为与此前完全一致。",
|
|
19
19
|
"card.strategies": "后台审批策略(至少一个生效;可组合)",
|
|
20
20
|
"card.strategy.trustAutoAllow": "trustAutoAllow — 作用域内安全操作自动放行,危险/未知转 ask(中间档基线)",
|
|
@@ -121,9 +121,9 @@ window.__ModuleLoader__.load({
|
|
|
121
121
|
};
|
|
122
122
|
/** English dictionary (keys mirror zh). */
|
|
123
123
|
const en = {
|
|
124
|
-
"card.title": "
|
|
125
|
-
"card.description": "Independent approval
|
|
126
|
-
"card.permissive": "Enable
|
|
124
|
+
"card.title": "自动审查 (Auto review)",
|
|
125
|
+
"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.",
|
|
126
|
+
"card.permissive": "Enable the 自动审查 tier",
|
|
127
127
|
"card.permissiveHint": "When off, the gate behaves exactly as before.",
|
|
128
128
|
"card.strategies": "Backend approval strategies (at least one effective; combinable)",
|
|
129
129
|
"card.strategy.trustAutoAllow": "trustAutoAllow — safe in-scope ops auto-allow; dangerous/unknown ask (baseline middle tier)",
|
|
@@ -1417,115 +1417,6 @@ window.__ModuleLoader__.load({
|
|
|
1417
1417
|
});
|
|
1418
1418
|
}
|
|
1419
1419
|
//#endregion
|
|
1420
|
-
//#region src/client/permission-icon.ts
|
|
1421
|
-
/**
|
|
1422
|
-
* Permission-picker icon decoration for the Permissive tier.
|
|
1423
|
-
*
|
|
1424
|
-
* dsh-auto-mode injects a shield glyph onto its "Auto" permission menu item
|
|
1425
|
-
* via a `data-*` attribute + a CSS mask. This module does the same for the
|
|
1426
|
-
* "Permissive" preset so the two approval tiers look consistent in the
|
|
1427
|
-
* dropdown. It only annotates the item; the preset itself comes from
|
|
1428
|
-
* `cordis.patch.yml` (the `permission.config.presets` table).
|
|
1429
|
-
*
|
|
1430
|
-
* Pure DOM + CSS, no @deepseek-ai value imports (client bundle purity).
|
|
1431
|
-
*/
|
|
1432
|
-
const PLUGIN_ID = "dsh-perm-gate";
|
|
1433
|
-
const ICON_ATTRIBUTE = "data-dsh-perm-gate-icon";
|
|
1434
|
-
/** Labels of the Permissive preset item, current + future localized variants. */
|
|
1435
|
-
const PERMISSIVE_LABELS = /* @__PURE__ */ new Set([
|
|
1436
|
-
"Permissive",
|
|
1437
|
-
"Permissive 审批档",
|
|
1438
|
-
"Permissive 审批档位"
|
|
1439
|
-
]);
|
|
1440
|
-
/** Same shield glyph as dsh-auto-mode so the two approval tiers match visually. */
|
|
1441
|
-
const ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path d=\"M8.21.9l6.58 2.47v3.64c0 4.99-3.74 7.2-6.58 8.29C5.36 14.21 1.62 12 1.62 7.01V3.37L8.21.9Z\" fill=\"none\" stroke=\"black\" stroke-width=\"1.32\" stroke-linejoin=\"round\"/><path d=\"M8.75 3.65 5.95 8.2h2.08l-.78 4.15 2.82-4.9H8.12l.63-3.8Z\" fill=\"black\"/></svg>";
|
|
1442
|
-
function iconStyles() {
|
|
1443
|
-
const mask = `url("data:image/svg+xml,${encodeURIComponent(ICON_SVG)}")`;
|
|
1444
|
-
return `
|
|
1445
|
-
[${ICON_ATTRIBUTE}]::before {
|
|
1446
|
-
content: "";
|
|
1447
|
-
display: inline-block;
|
|
1448
|
-
flex: 0 0 auto;
|
|
1449
|
-
width: 16px;
|
|
1450
|
-
height: 16px;
|
|
1451
|
-
background-color: var(--dsw-alias-label-tertiary, currentColor);
|
|
1452
|
-
-webkit-mask-image: ${mask};
|
|
1453
|
-
mask-image: ${mask};
|
|
1454
|
-
-webkit-mask-position: center;
|
|
1455
|
-
mask-position: center;
|
|
1456
|
-
-webkit-mask-repeat: no-repeat;
|
|
1457
|
-
mask-repeat: no-repeat;
|
|
1458
|
-
-webkit-mask-size: contain;
|
|
1459
|
-
mask-size: contain;
|
|
1460
|
-
}
|
|
1461
|
-
[${ICON_ATTRIBUTE}='trigger']::before {
|
|
1462
|
-
width: 14px;
|
|
1463
|
-
height: 14px;
|
|
1464
|
-
margin-right: 6px;
|
|
1465
|
-
}
|
|
1466
|
-
`;
|
|
1467
|
-
}
|
|
1468
|
-
/** The permission dropdown menu (lists the permission presets as menuitems). */
|
|
1469
|
-
function isPermissionMenu(menu) {
|
|
1470
|
-
return menu.matches("[role=\"menu\"]") && Array.from(menu.querySelectorAll("button[role=\"menuitem\"]")).length >= 4;
|
|
1471
|
-
}
|
|
1472
|
-
/** Whether one menuitem is the Permissive preset. */
|
|
1473
|
-
function isPermissiveMenuItem(element) {
|
|
1474
|
-
if (!element.matches("button[role=\"menuitem\"]")) return false;
|
|
1475
|
-
const label = (element.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
1476
|
-
if (!PERMISSIVE_LABELS.has(label)) return false;
|
|
1477
|
-
return element.closest("[role=\"menu\"]") !== null;
|
|
1478
|
-
}
|
|
1479
|
-
/** The collapsed permission-picker trigger (opens the presets menu). */
|
|
1480
|
-
function isPermissionTrigger(element) {
|
|
1481
|
-
return element.matches("button[aria-haspopup=\"menu\"]") && PERMISSIVE_LABELS.has((element.textContent ?? "").replace(/\s+/g, " ").trim());
|
|
1482
|
-
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Annotate the Permissive permission menuitems with the icon attribute, plus
|
|
1485
|
-
* the collapsed picker trigger when its label names the Permissive preset.
|
|
1486
|
-
* Unknown DOM shapes simply never match, so decoration stays conservative.
|
|
1487
|
-
*/
|
|
1488
|
-
function decorate(document) {
|
|
1489
|
-
for (const marked of document.querySelectorAll(`[${ICON_ATTRIBUTE}]`)) if (!isPermissiveMenuItem(marked) && !isPermissionTrigger(marked)) marked.removeAttribute(ICON_ATTRIBUTE);
|
|
1490
|
-
for (const menu of document.querySelectorAll("[role=\"menu\"]")) {
|
|
1491
|
-
if (!isPermissionMenu(menu)) continue;
|
|
1492
|
-
for (const item of menu.querySelectorAll("button[role=\"menuitem\"]")) if (isPermissiveMenuItem(item)) item.setAttribute(ICON_ATTRIBUTE, "menu");
|
|
1493
|
-
}
|
|
1494
|
-
for (const trigger of document.querySelectorAll("button[aria-haspopup=\"menu\"]")) if (isPermissionTrigger(trigger)) trigger.setAttribute(ICON_ATTRIBUTE, "trigger");
|
|
1495
|
-
}
|
|
1496
|
-
/** Install the Permissive tile icon into the permission picker; returns the disposer. */
|
|
1497
|
-
function installPermissivePermissionIcon(document) {
|
|
1498
|
-
for (const existing of document.querySelectorAll("style[data-plugin]")) if (existing.getAttribute("data-plugin") === PLUGIN_ID) existing.remove();
|
|
1499
|
-
const style = document.createElement("style");
|
|
1500
|
-
style.dataset.plugin = PLUGIN_ID;
|
|
1501
|
-
style.dataset.pluginCss = `${PLUGIN_ID}/permission-icon`;
|
|
1502
|
-
style.textContent = iconStyles();
|
|
1503
|
-
document.head.appendChild(style);
|
|
1504
|
-
let active = true;
|
|
1505
|
-
let queued = false;
|
|
1506
|
-
const scan = () => {
|
|
1507
|
-
if (!active || queued) return;
|
|
1508
|
-
queued = true;
|
|
1509
|
-
queueMicrotask(() => {
|
|
1510
|
-
queued = false;
|
|
1511
|
-
if (active) decorate(document);
|
|
1512
|
-
});
|
|
1513
|
-
};
|
|
1514
|
-
decorate(document);
|
|
1515
|
-
const observer = new MutationObserver(scan);
|
|
1516
|
-
observer.observe(document.documentElement, {
|
|
1517
|
-
childList: true,
|
|
1518
|
-
characterData: true,
|
|
1519
|
-
subtree: true
|
|
1520
|
-
});
|
|
1521
|
-
return () => {
|
|
1522
|
-
active = false;
|
|
1523
|
-
observer.disconnect();
|
|
1524
|
-
for (const marked of document.querySelectorAll(`[${ICON_ATTRIBUTE}]`)) marked.removeAttribute(ICON_ATTRIBUTE);
|
|
1525
|
-
style.remove();
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
//#endregion
|
|
1529
1420
|
//#region src/client/feed.ts
|
|
1530
1421
|
/** Fetch decision events for one session; `since > 0` returns only newer ones. */
|
|
1531
1422
|
async function fetchEvents(sessionId, since) {
|
|
@@ -1609,7 +1500,7 @@ window.__ModuleLoader__.load({
|
|
|
1609
1500
|
"deny-keyword": "黑名单关键词",
|
|
1610
1501
|
default: "默认策略",
|
|
1611
1502
|
ask: "默认策略",
|
|
1612
|
-
permissive: "
|
|
1503
|
+
permissive: "自动审查放行",
|
|
1613
1504
|
classifier: "LLM 裁决",
|
|
1614
1505
|
"llm-safe": "LLM 判定安全",
|
|
1615
1506
|
"llm-learned": "LLM 学习放行",
|
|
@@ -2575,7 +2466,6 @@ window.__ModuleLoader__.load({
|
|
|
2575
2466
|
zh,
|
|
2576
2467
|
en
|
|
2577
2468
|
}), "dsh-perm-gate: dictionaries");
|
|
2578
|
-
ctx.effect(() => installPermissivePermissionIcon(globalThis.document), "dsh-perm-gate: permission icon");
|
|
2579
2469
|
ctx.slots.inject("conversation.input.dock", function* () {
|
|
2580
2470
|
yield ctx.slots.register({
|
|
2581
2471
|
name: "conversation.input.dock",
|