dsh-perm-gate 5.0.3 → 5.1.0

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.
Files changed (50) hide show
  1. package/CHANGELOG.ja.md +0 -2
  2. package/CHANGELOG.ko.md +0 -2
  3. package/CHANGELOG.md +4 -2
  4. package/INSTALL.ja.md +0 -15
  5. package/INSTALL.ko.md +0 -15
  6. package/INSTALL.md +0 -15
  7. package/INSTALL.zh.md +0 -15
  8. package/README.ja.md +6 -18
  9. package/README.ko.md +6 -18
  10. package/README.md +40 -19
  11. package/README.zh.md +34 -18
  12. package/assets/appcontainer-launcher.cs +703 -0
  13. package/dsh.plugin.json +2 -2
  14. package/lib/appcontainer/compile.d.ts +17 -0
  15. package/lib/appcontainer/compile.js +74 -0
  16. package/lib/appcontainer/exempt.d.ts +18 -0
  17. package/lib/appcontainer/exempt.js +71 -0
  18. package/lib/appcontainer/index.d.ts +85 -0
  19. package/lib/appcontainer/index.js +286 -0
  20. package/lib/appcontainer/probe.d.ts +44 -0
  21. package/lib/appcontainer/probe.js +66 -0
  22. package/lib/appcontainer/sid.d.ts +23 -0
  23. package/lib/appcontainer/sid.js +33 -0
  24. package/lib/appcontainer/wrap.d.ts +22 -0
  25. package/lib/appcontainer/wrap.js +40 -0
  26. package/lib/cli.js +0 -0
  27. package/lib/client.js +1425 -1108
  28. package/lib/client.js.map +1 -1
  29. package/lib/config.d.ts +41 -1
  30. package/lib/config.js +6 -0
  31. package/lib/index.d.ts +1 -1
  32. package/lib/index.js +53 -0
  33. package/lib/runtime.d.ts +22 -0
  34. package/lib/runtime.js +24 -0
  35. package/locale/de.json +1 -0
  36. package/locale/en.json +1 -0
  37. package/locale/es.json +1 -0
  38. package/locale/fr.json +1 -0
  39. package/locale/it.json +1 -0
  40. package/locale/ja.json +1 -0
  41. package/locale/ko.json +1 -0
  42. package/locale/ru.json +1 -0
  43. package/locale/zh.json +1 -0
  44. package/package.json +28 -28
  45. package/scripts/patch-permission-glyph.mjs +0 -0
  46. package/README.de.md +0 -487
  47. package/README.es.md +0 -489
  48. package/README.fr.md +0 -498
  49. package/README.it.md +0 -490
  50. package/README.ru.md +0 -478
package/lib/client.js CHANGED
@@ -10,1076 +10,1387 @@ window.__ModuleLoader__.load({
10
10
  /** `dsh-perm-gate` client dictionaries (zh / en / ja / ko / fr / de / it / ru / es). */
11
11
  /** Dictionary namespace owned by this plugin. */
12
12
  const NS = "dsh-perm-gate";
13
- /** Simplified Chinese dictionary (the key-set source of truth). */
14
- const zh = {
15
- "card.title": "自动审查",
16
- "section.title": "自动审查门",
17
- "card.description": "独立审批档位:与只读 / 完全权限 / 白名单平行,在会话权限下拉中显示为「自动审查」。前端只暴露一个开关;后台四个审批策略可组合、由此处设置决定,仍对 P0 硬拒绝保持 fail-closed。",
18
- "card.permissive": "启用自动审查档位",
19
- "card.permissiveHint": "关闭时门禁行为与此前完全一致。",
20
- "card.scopeNote": "档位作用域:本门禁仅在会话权限档位为 %scope 时生效。其他档位下整个门禁停用(含 P0 硬拒绝),由所选档位自身的策略接管;停用时会写入一条 stand-down 事件,并在输入框上方常驻一条提示条。",
21
- "card.strategies": "后台审批策略(至少一个生效;可组合)",
22
- "card.strategy.trustAutoAllow": "trustAutoAllow — 作用域内安全操作自动放行,危险/未知转 ask(中间档基线)",
23
- "card.strategy.alwaysConfirm": "alwaysConfirm — 一律逐次 ask;审批面板「允许控件」含两个扩展按钮:「本会话重复允许该类」(会话语限次 grant)与「允许所有类型」(命令词持久迁入 rulesFile 白名单)",
24
- "card.strategy.llmAssist": "llmAssist — 自定义 LLM 按风险分级裁决:safe 自动放行;deletion/credential/remote/system/bulk 硬风险维持人工确认(分类器永不拒绝,只负责升级判断);neutral 进入确认学习;失败/超时回退人工",
25
- "card.strategy.trustEscalation": "trustEscalation — 沙箱升级免确认:本门禁已放行的调用,其工具内部发起的沙箱提权(shell / pwsh / edit 的 sandbox_permissions)由门禁直接批准,不再弹窗",
26
- "card.strategy.trustEscalationHint": "沙箱提权审批发生在 tools/pre-execute 之后、工具体内部,门禁原本看不到它,因此「LLM 判安全已自动放行」的调用仍会弹窗要求你批准提权。开启后,仅对门禁确实放行过的那一次调用(按 callId 精确匹配)免确认,其余一律照旧走人工;目标模式非 workspace-write / danger-full-access 或理由无法识别时同样回退人工。关闭后提权保持人工把关。",
27
- "card.readonly": "只读",
28
- "card.unavailable": "设置命名空间不可用:请确认 dsh-perm-gate 已装配进 profile。",
29
- "card.llmReceiver": "llmAssist 接收 LLM(OpenAI 兼容,可用自定义 API)",
30
- "card.llmEndpoint": "Endpoint(BaseURL,如 https://api.openai.com/v1)",
31
- "card.llmModel": "Model(模型 id,如 deepseek-chat)",
32
- "card.llmKey": "API Key(保密)",
33
- "card.llmKeyHidden": "未设置 · 首次输入后以 * 显示",
34
- "card.llmKeyMasked": "已设置 · 输入新值覆盖",
35
- "card.llmKeyOverwrite": "已设置保密值,输入新值并失焦即可覆盖。",
36
- "card.llmKeyClear": "清除",
37
- "card.allowlist": "白名单(allow,rulesFile 同步)",
38
- "card.allowlistHint": "命中 allow 段的命令模式直接放行;逐条添加或删除,实时写入 rulesFile 并重载。",
39
- "card.allowlistPresetNote": "预置白名单的等价物是 trustAutoAllow 策略(工作区内安全操作自动放行),在上方策略区开启;本列表镜像你的 rulesFile allow 段,初始为空属正常。",
40
- "card.allowlistEmpty": "无白名单规则",
41
- "card.allowlistPlaceholder": "新命令模式,如 npm test*",
42
- "card.allowlistAdd": "添加",
43
- "card.allowlistRemove": "删除该模式",
44
- "card.allowlistBulk": "批量编辑",
45
- "card.allowlistBulkHide": "收起批量编辑",
46
- "card.allowlistSave": "保存白名单",
47
- "card.riskLearning": "riskLearning — 裁决学习:neutral 风险操作经人工确认并执行后计数,满阈值且操作指纹一致时自动放行",
48
- "card.riskLearningHint": "学习状态为插件自有数据($DSH_HOME/perm-gate/learning.json),不写入你的 YAML 规则文件;不同目标永不复用放行。默认关闭。",
49
- "card.riskThreshold": "自动放行所需人工确认次数(1–10)",
50
- "notice.label": "权限门禁动态",
51
- "history.label": "审批记录",
52
- "history.title": "权限审批记录",
53
- "history.subtitle": "本会话中门禁的每次判定轨迹(最新在上)",
54
- "history.loading": "加载中…",
55
- "history.empty": "本会话暂无审批记录",
56
- "history.error": "加载失败:",
57
- "history.tag.auto": "自动放行",
58
- "history.tag.ask": "转人工",
59
- "history.tag.deny": "拒绝",
60
- "card.denylistRestore": "恢复预置黑名单",
61
- "card.denylist": "黑名单(deny 关键词,插件预置)",
62
- "card.denylistHint": "命中关键词(大小写不敏感子串)的调用直接拒绝,先于白名单/授权/LLM。预置列表继承自 dsh-approval-gate,安装即生效;可增删,清空或删除后可用「恢复预置黑名单」一键还原(空列表等同预置,黑名单常开)。",
63
- "card.denylistEmpty": "黑名单为空(所有调用都不会被关键词层拒绝)",
64
- "card.denylistPlaceholder": "新关键词,如 drop database",
65
- "card.denylistAdd": "添加",
66
- "card.denylistRemove": "删除该关键词",
67
- "card.denylistTagPreset": "预置",
68
- "card.denylistTagCustom": "自定义",
69
- "card.riskSediment": "riskSediment — 学习沉淀:满阈值的确认样本成为确定性放行规则",
70
- "card.riskSedimentHint": "开启后,同一「工具|类别」确认满阈值,其样本指纹即沉淀为放行规则——同工具同目标精确命中时直接放行,不再过 LLM(关闭 llmAssist 也继续生效)。",
71
- "card.sediment": "学习沉淀(确定性放行规则)",
72
- "card.sedimentHint": "来自人工确认的沉淀规则:样本指纹命中才放行,不同目标永不复用。可终止单个学习或删除单条样本。",
73
- "card.sedimentEmpty": "暂无沉淀规则(确认满阈值的样本会出现在这里)",
74
- "card.sedimentCount": "已确认 %n/%t",
75
- "card.sedimentStop": "终止",
76
- "card.sedimentStopTitle": "终止该学习(删除计数与样本)",
77
- "card.sedimentStopConfirm": "终止「%k」的学习?将删除其确认计数与全部样本。",
78
- "card.sedimentSampleRemove": "删除该沉淀样本",
79
- "card.llmSource": "接收 LLM 来源",
80
- "card.llmSourceCustom": "自定义 API(OpenAI 兼容)",
81
- "card.llmSourceHost": "使用会话当前默认模型(DSH llm 服务)",
82
- "card.llmSourceHostHint": "默认使用 DSH 会话当前选中的模型组执行审批判定(下方显示当前生效值);Provider / Model 留空即跟随会话,也可从下拉选择或手动输入固定下来——你在 DSH 里配置的自定义组(如 local-35b)会自动出现在下拉中。",
83
- "card.llmProvider": "Provider(留空 = 跟随会话当前选择)",
84
- "card.llmProviderPlaceholder": "跟随会话当前选择",
85
- "card.llmModelHostPlaceholder": "留空 = 跟随会话当前选择",
86
- "card.llmResolved": "当前生效:%p / %m",
87
- "card.llmPreset": "预置端点(选择后自动填充,可再编辑)",
88
- "card.llmPresetChoose": "选择预置…",
89
- "card.llmPresetPublic": "公共 API 预置",
90
- "card.llmPresetHost": "DSH 模型组(选择后自动切换为会话模式)",
91
- "card.networkEnabled": "网络拦截(networkEnabled)",
92
- "card.networkEnabledHint": "开启后启动本地代理拦截子进程出站连接,按规则放行/阻断。关闭则零行为变更。",
93
- "card.watch": "规则热重载(watch)",
94
- "card.watchHint": "开启后自动监听规则文件变更并即时生效,无需手动 reload。",
95
- "card.networkRebindNote": "切换后立即生效(自动重绑,无需重载插件)。",
96
- "card.networkInjectEnv": "注入代理环境变量(networkInjectEnv)",
97
- "card.networkInjectEnvHint": "开启后为子进程写入 HTTP(S)_PROXY / ALL_PROXY(并清空 NO_PROXY)。关闭则只启动监听、不改变任何进程环境。",
98
- "card.healthStale": "宿主路由不可用(404)——node 半区仍是旧版本,请完全重启 dsh(而非仅刷新页面)后重试。",
99
- "card.healthTest": "健康测试",
100
- "card.healthRunning": "测试中…",
101
- "card.healthOk": "正常(%ms ms)",
102
- "card.healthFail": "失败:",
103
- "history.tag.learned": "学习沉淀",
104
- "history.tag.manualApproved": "人工通过",
105
- "history.tag.manualRejected": "人工拒绝",
106
- "history.tag.manualCancelled": "人工取消",
107
- "history.tag.standDown": "门禁停用",
108
- "history.learnedProgress": "学习 %n/%t",
109
- "history.snapshots": "diff 快照",
110
- "history.clearSession": "仅清本会话",
111
- "history.clearAll": "清空全部",
112
- "history.clearSessionTitle": "仅清除本会话的 diff 快照(不影响其他会话)",
113
- "history.clearAllTitle": "清空全部会话(含其他会话未查看过的)diff 快照,需二次确认",
114
- "history.clearSessionConfirm": "确定清除【本会话】的 diff 快照?仅删除本会话审批产生的改动对比数据,不影响审批记录本身。",
115
- "history.clearAllConfirm": "确定清除【全部会话】的 diff 快照?这可能删除其他会话还没看过的改动对比记录,且不可恢复。",
116
- "history.diffTitle": "文件改动对比",
117
- "history.diffClose": "关闭",
118
- "history.diffLoading": "加载中…",
119
- "history.diffEmpty": "该文件无内容变化(或文件不可读)",
120
- "history.diffLoadFail": "加载 diff 失败:",
121
- "history.diffRevert": "撤销此改动",
122
- "history.diffReverting": "发送中…",
123
- "history.diffRevertDone": "已发送撤销指令",
124
- "history.diffRevertSent": "撤销指令已发送到对话框,AI 将按指令恢复文件",
125
- "history.diffRevertFail": "发送失败:",
126
- "history.diffNoSnapshot": "该事件没有此文件的快照",
127
- "history.diffChipTitle": "查看该文件改动对比",
128
- "history.diffStats": "+%a / -%r 行变更 · %c 行未变",
129
- "history.diffGone": "(文件当前已不存在)",
130
- "history.diffHidden": "%n 行未修改",
131
- "card.dryRun": "规则测试",
132
- "card.dryRunHint": "用当前生效的规则判定一次调用,不执行任何工具、不写入任何规则。改规则前先在这里验一遍。",
133
- "card.dryRunTool": "工具名",
134
- "card.dryRunCommand": "命令 / 参数",
135
- "card.dryRunCommandPlaceholder": "git push --force origin main",
136
- "card.dryRunTest": "测试",
137
- "card.dryRunRunning": "判定中…",
138
- "card.dryRunEmpty": "尚未测试。填入工具名与命令,点「测试」查看会怎么裁决。",
139
- "card.dryRunVerdict": "裁决",
140
- "card.dryRunAllow": "放行",
141
- "card.dryRunAsk": "人工确认",
142
- "card.dryRunDeny": "拒绝",
143
- "card.dryRunRule": "命中规则",
144
- "card.dryRunRuleNone": "无规则命中,走 defaultAction:%d",
145
- "card.dryRunDimensions": "约束维度",
146
- "card.dryRunReason": "原因",
147
- "card.dryRunNote": "「命中规则」来自规则层。若裁决由更早的硬拒绝或关键词黑名单产生,则不会有规则序号——那种情况下列出的维度仅供参考。",
148
- "card.dryRunNoRules": "(当前加载了 0 条规则:检查 rulesFile 路径)",
149
- "card.dryRunFail": "测试失败:",
150
- "card.dryRunStale": "路由不存在(插件未重启?)",
151
- "card.rules": "Settings 内置规则",
152
- "card.rulesHint": "门禁此刻实际加载的规则(来自本插件入口的 rules 配置字段;未配置时回退到规则文件)。只读——这里改不了任何东西。要改规则,用下面的白名单,或在设置页中编辑。",
153
- "card.rulesRefresh": "刷新",
154
- "card.rulesLoading": "读取中…",
155
- "card.rulesPath": "来源",
156
- "card.rulesNone": "未配置规则",
157
- "card.rulesCounts": "规则",
158
- "card.rulesDefault": "defaultAction",
159
- "card.rulesStats": "%b 字节 · %l 行",
160
- "card.rulesMissing": "规则不存在。门禁会退到内置默认。",
161
- "card.rulesTruncated": "规则过大,仅显示前 512 KB。",
162
- "card.rulesError": "加载失败:",
163
- "card.rulesStale": "路由不存在(插件未重启?)",
164
- "card.rulesNote": "此处只读:面板无法修改规则;白名单段与“始终允许”会写回本插件的入口配置。"
165
- };
166
- /** English dictionary (keys mirror zh). */
167
- const en = {
168
- "card.title": "自动审查 (Auto review)",
169
- "section.title": "Auto-Review Gate",
170
- "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.",
171
- "card.permissive": "Enable the 自动审查 tier",
172
- "card.permissiveHint": "When off, the gate behaves exactly as before.",
173
- "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.",
174
- "card.strategies": "Backend approval strategies (at least one effective; combinable)",
175
- "card.strategy.trustAutoAllow": "trustAutoAllow — safe in-scope ops auto-allow; dangerous/unknown ask (baseline middle tier)",
176
- "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)",
177
- "card.strategy.llmAssist": "llmAssist — a custom LLM grades risk: safe auto-allows; deletion/credential/remote/system/bulk hard risks keep the human ask (the classifier never denies — it only escalates); neutral enters confirm-learning; failure/timeout falls back to the human",
178
- "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",
179
- "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.",
180
- "card.readonly": "Read-only",
181
- "card.unavailable": "Settings namespace unavailable: make sure dsh-perm-gate is assembled into this profile.",
182
- "card.llmReceiver": "llmAssist receiving LLM (OpenAI-compatible; any custom API)",
183
- "card.llmEndpoint": "Endpoint (BaseURL, e.g. https://api.openai.com/v1)",
184
- "card.llmModel": "Model (model id, e.g. deepseek-chat)",
185
- "card.llmKey": "API Key (secret)",
186
- "card.llmKeyHidden": "Unset · shown as * after first entry",
187
- "card.llmKeyMasked": "Set · type a new value to overwrite",
188
- "card.llmKeyOverwrite": "A secret is stored; type a new value and blur to overwrite it.",
189
- "card.llmKeyClear": "Clear",
190
- "card.allowlist": "Whitelist (allow, synced to rulesFile)",
191
- "card.allowlistHint": "Patterns in the allow section allow directly; add or remove entries one by one, written to the rulesFile and reloaded live.",
192
- "card.allowlistPresetNote": "The preset-whitelist equivalent is the trustAutoAllow strategy (safe in-scope ops auto-allow), enabled in the strategies section above; this list mirrors your rulesFile allow section and legitimately starts empty.",
193
- "card.allowlistEmpty": "No whitelist rules",
194
- "card.allowlistPlaceholder": "New command pattern, e.g. npm test*",
195
- "card.allowlistAdd": "Add",
196
- "card.allowlistRemove": "Remove this pattern",
197
- "card.allowlistBulk": "Bulk edit",
198
- "card.allowlistBulkHide": "Hide bulk edit",
199
- "card.allowlistSave": "Save whitelist",
200
- "card.riskLearning": "riskLearning — verdict learning: neutral-risk asks that the human approves and that execute count up; the same operation auto-allows at threshold with a matching fingerprint",
201
- "card.riskLearningHint": "Learning state is plugin-owned ($DSH_HOME/perm-gate/learning.json) and never written into your YAML rules; a different target never reuses authority. Off by default.",
202
- "card.riskThreshold": "Human confirmations required before auto-allow (1–10)",
203
- "notice.label": "Permission gate activity",
204
- "history.label": "Approvals",
205
- "history.title": "Permission approval records",
206
- "history.subtitle": "Every gate decision in this conversation, newest first",
207
- "history.loading": "Loading…",
208
- "history.empty": "No approval records in this session yet",
209
- "history.error": "Failed to load: ",
210
- "history.tag.auto": "Auto-allowed",
211
- "history.tag.ask": "Asked",
212
- "history.tag.deny": "Denied",
213
- "card.denylistRestore": "Restore preset blacklist",
214
- "card.denylist": "Blacklist (deny keywords, plugin preset)",
215
- "card.denylistHint": "A call whose text contains a keyword (case-insensitive substring) is denied outright, before whitelist / grants / LLM. The preset list is inherited from dsh-approval-gate and active from install; edit freely — empty or cleared falls back to the preset (one-click restore available), so the blacklist never silently turns off.",
216
- "card.denylistEmpty": "Blacklist empty (no call is vetoed by the keyword layer)",
217
- "card.denylistPlaceholder": "New keyword, e.g. drop database",
218
- "card.denylistAdd": "Add",
219
- "card.denylistRemove": "Remove this keyword",
220
- "card.denylistTagPreset": "Preset",
221
- "card.denylistTagCustom": "Custom",
222
- "card.riskSediment": "riskSediment — learning sedimentation: threshold-reached samples become deterministic auto-allows",
223
- "card.riskSedimentHint": "When on, once a tool|category key reaches the threshold its sample fingerprints are sedimented into allow rules — an exact same-tool same-target hit allows outright without another LLM call (keeps working with llmAssist off).",
224
- "card.sediment": "Learning sediment (deterministic allow rules)",
225
- "card.sedimentHint": "Rules sedimented from human confirmations: only an exact fingerprint hit allows, never a different target. Terminate one learning or remove one sample.",
226
- "card.sedimentEmpty": "No sedimented rules yet (samples reaching the threshold appear here)",
227
- "card.sedimentCount": "confirmed %n/%t",
228
- "card.sedimentStop": "Terminate",
229
- "card.sedimentStopTitle": "Terminate this learning (drops the count and samples)",
230
- "card.sedimentStopConfirm": "Terminate learning for '%k'? Its count and samples are dropped.",
231
- "card.sedimentSampleRemove": "Remove this sedimented sample",
232
- "card.llmSource": "Receiver source",
233
- "card.llmSourceCustom": "Custom API (OpenAI-compatible)",
234
- "card.llmSourceHost": "Use the session's current default model (DSH llm service)",
235
- "card.llmSourceHostHint": "By default approval grading runs on the model group the DSH session currently uses (the effective value is shown below); leave Provider / Model empty to follow the session, or pick from the dropdown or type to pin one — custom groups you configured in DSH (e.g. local-35b) appear automatically.",
236
- "card.llmProvider": "Provider (empty = follow the session selection)",
237
- "card.llmProviderPlaceholder": "Follow the session selection",
238
- "card.llmModelHostPlaceholder": "Empty = follow the session selection",
239
- "card.llmResolved": "Effective now: %p / %m",
240
- "card.llmPreset": "Endpoint presets (fills the fields; still editable)",
241
- "card.llmPresetChoose": "Pick a preset…",
242
- "card.llmPresetPublic": "Public API presets",
243
- "card.llmPresetHost": "DSH model groups (selecting one switches to the session receiver)",
244
- "card.networkEnabled": "Network interception (networkEnabled)",
245
- "card.networkEnabledHint": "When on, starts a local proxy that intercepts outbound subprocess connections and applies allow/deny rules. Off = zero behavior change.",
246
- "card.watch": "Rule hot-reload (watch)",
247
- "card.watchHint": "When on, automatically watches rule files for changes and applies them instantly without manual reload.",
248
- "card.networkRebindNote": "Takes effect immediately — the proxy rebinds automatically, no plugin reload needed.",
249
- "card.networkInjectEnv": "Inject proxy environment (networkInjectEnv)",
250
- "card.networkInjectEnvHint": "Writes HTTP(S)_PROXY / ALL_PROXY for subprocesses (and clears NO_PROXY). Off = the listener runs but no process environment is changed.",
251
- "card.healthStale": "Host route unavailable (404) — the node half is still an old build; fully restart dsh (not just the browser page) and retry.",
252
- "card.healthTest": "Health test",
253
- "card.healthRunning": "Testing…",
254
- "card.healthOk": "OK (%ms ms)",
255
- "card.healthFail": "Failed: ",
256
- "history.tag.learned": "Learned",
257
- "history.tag.manualApproved": "Approved",
258
- "history.tag.manualRejected": "Rejected",
259
- "history.tag.manualCancelled": "Cancelled",
260
- "history.tag.standDown": "Gate off",
261
- "history.learnedProgress": "learned %n/%t",
262
- "history.snapshots": "diff snapshots",
263
- "history.clearSession": "Clear this session",
264
- "history.clearAll": "Clear all",
265
- "history.clearSessionTitle": "Clear only this session’s diff snapshots (other sessions untouched)",
266
- "history.clearAllTitle": "Clear diff snapshots for every session, including ones never reviewed — asks again",
267
- "history.clearSessionConfirm": "Clear this session’s diff snapshots? Only the change-comparison data produced by this session’s approvals is removed; the approval records stay.",
268
- "history.clearAllConfirm": "Clear diff snapshots for ALL sessions? This may delete change comparisons other sessions have not reviewed yet, and cannot be undone.",
269
- "history.diffTitle": "File change comparison",
270
- "history.diffClose": "Close",
271
- "history.diffLoading": "Loading…",
272
- "history.diffEmpty": "No content change (or the file is unreadable)",
273
- "history.diffLoadFail": "Failed to load diff: ",
274
- "history.diffRevert": "Revert this change",
275
- "history.diffReverting": "Sending…",
276
- "history.diffRevertDone": "Revert instruction sent",
277
- "history.diffRevertSent": "Revert instruction sent to the conversation; the AI will restore the file accordingly",
278
- "history.diffRevertFail": "Send failed: ",
279
- "history.diffNoSnapshot": "This event has no snapshot for that file",
280
- "history.diffChipTitle": "View this file’s change comparison",
281
- "history.diffStats": "+%a / -%r lines changed · %c unchanged",
282
- "history.diffGone": " (file no longer exists)",
283
- "history.diffHidden": "%n unmodified lines",
284
- "card.dryRun": "Rule test",
285
- "card.dryRunHint": "Judge one call against the rules in force. Runs no tool and writes no rule — check a rule here before you change it.",
286
- "card.dryRunTool": "Tool",
287
- "card.dryRunCommand": "Command / arguments",
288
- "card.dryRunCommandPlaceholder": "git push --force origin main",
289
- "card.dryRunTest": "Test",
290
- "card.dryRunRunning": "Judging…",
291
- "card.dryRunEmpty": "Nothing tested yet. Fill in a tool and command, then press Test to see how it would be judged.",
292
- "card.dryRunVerdict": "Verdict",
293
- "card.dryRunAllow": "allow",
294
- "card.dryRunAsk": "ask",
295
- "card.dryRunDeny": "deny",
296
- "card.dryRunRule": "Matched rule",
297
- "card.dryRunRuleNone": "no rule matched; defaultAction: %d",
298
- "card.dryRunDimensions": "Dimensions",
299
- "card.dryRunReason": "Reason",
300
- "card.dryRunNote": "\"Matched rule\" comes from the rule layer. When the verdict was produced by an earlier hard-deny or by the keyword blacklist there is no rule index — the dimensions listed are then informational only.",
301
- "card.dryRunNoRules": "(0 rules loaded — check the rulesFile path)",
302
- "card.dryRunFail": "Test failed: ",
303
- "card.dryRunStale": "route missing (plugin not restarted?)",
304
- "card.rules": "Settings Built-in Rules",
305
- "card.rulesHint": "The rules the gate is loading right now (from this plugin entry's `rules` config field; falls back to the rules file when unset). Read-only — nothing here can change them. To change rules, use the allowlist below or edit them on the settings page.",
306
- "card.rulesRefresh": "Refresh",
307
- "card.rulesLoading": "Reading…",
308
- "card.rulesPath": "Source",
309
- "card.rulesNone": "no rules configured",
310
- "card.rulesCounts": "Rules",
311
- "card.rulesDefault": "defaultAction",
312
- "card.rulesStats": "%b bytes · %l lines",
313
- "card.rulesMissing": "Rules not found. The gate falls back to its built-in defaults.",
314
- "card.rulesTruncated": "Rules are large; showing the first 512 KB only.",
315
- "card.rulesError": "Load failed: ",
316
- "card.rulesStale": "route missing (plugin not restarted?)",
317
- "card.rulesNote": "Read-only: this panel cannot modify the rules. The allowlist section and \"allow always\" write back to this plugin's entry config."
318
- };
319
- /** French dictionary (keys mirror zh). */
320
- const fr = {
321
- "card.title": "自动审查 (Revue automatique)",
322
- "section.title": "Porte de revue automatique",
323
- "card.description": "Niveau d’approbation indépendant, parallèle à lecture seule / accès complet / liste blanche, affiché comme 自动审查 dans le sélecteur de permissions de session. Le front n’expose qu’un seul interrupteur ; les quatre stratégies d’approbation backend sont combinables depuis ce panneau et restent fail-closed face au refus dur P0.",
324
- "card.permissive": "Activer le niveau 自动审查",
325
- "card.permissiveHint": "Désactivée, la porte se comporte exactement comme avant.",
326
- "card.scopeNote": "Portée prédéfinie : cette porte n’agit que lorsque le préréglage de permissions de session est %scope. Sous tout autre préréglage, la porte entière se met en retrait — refus dur P0 compris — et la politique du préréglage choisi prend le relais. Une mise en retrait enregistre un événement et laisse une bande permanente au-dessus de la zone de saisie.",
327
- "card.strategies": "Stratégies d’approbation backend (au moins une effective ; combinables)",
328
- "card.strategy.trustAutoAllow": "trustAutoAllow — les opérations sûres du périmètre sont autorisées d’office ; les dangereuses/inconnues demandent confirmation (référence du niveau intermédiaire)",
329
- "card.strategy.alwaysConfirm": "alwaysConfirm — chaque passage demande confirmation ; les « contrôles d’autorisation » du panneau gagnent deux boutons étendus : « réautoriser ce type pour cette session » (autorisation limitée à la session) et « autoriser toutes les occurrences » (le mot de commande est persisté dans la liste blanche du rulesFile)",
330
- "card.strategy.llmAssist": "llmAssist — un LLM personnalisé évalue le risque : safe est autorisé d’office ; les risques durs deletion/credential/remote/system/bulk restent soumis à l’humain (le classifieur ne refuse jamais — il ne fait qu’escalader) ; neutral entre en apprentissage par confirmation ; échec/timeout retombe sur l’humain",
331
- "card.strategy.trustEscalation": "trustEscalation — élargissement de sandbox sans invite : pour un appel déjà autorisé par cette porte, l’extension de sandbox qu’un outil shell / pwsh / edit demande de l’intérieur est approuvée ici au lieu de vous demander",
332
- "card.strategy.trustEscalationHint": "Une demande d’élargissement de sandbox est émise depuis le corps de l’outil, après tools/pre-execute, donc la porte ne l’a jamais vue — un appel jugé safe par le LLM et autorisé d’office vous demandait quand même d’approuver l’élargissement. Activée, seule la demande que la porte a réellement autorisée (correspondance exacte par callId) saute l’invite ; tout le reste passe par l’humain comme avant, de même qu’une raison non reconnue ou une cible autre que workspace-write / danger-full-access. Désactivez-la pour garder l’élargissement sous contrôle humain.",
333
- "card.readonly": "Lecture seule",
334
- "card.unavailable": "Espace de noms Settings indisponible : assurez-vous que dsh-perm-gate est assemblé dans ce profil.",
335
- "card.llmReceiver": "LLM récepteur de llmAssist (compatible OpenAI ; toute API personnalisée)",
336
- "card.llmEndpoint": "Endpoint (BaseURL, ex. https://api.openai.com/v1)",
337
- "card.llmModel": "Model (id de modèle, ex. deepseek-chat)",
338
- "card.llmKey": "Clé API (secrète)",
339
- "card.llmKeyHidden": "Non définie · affichée sous forme de * après la première saisie",
340
- "card.llmKeyMasked": "Définie · saisissez une nouvelle valeur pour la remplacer",
341
- "card.llmKeyOverwrite": "Un secret est enregistré ; saisissez une nouvelle valeur puis quittez le champ pour la remplacer.",
342
- "card.llmKeyClear": "Effacer",
343
- "card.allowlist": "Liste blanche (allow, synchronisée au rulesFile)",
344
- "card.allowlistHint": "Les motifs de la section allow autorisent directement ; ajoutez ou supprimez des entrées une à une, écrites au rulesFile et rechargées en direct.",
345
- "card.allowlistPresetNote": "L’équivalent de liste blanche prédéfinie est la stratégie trustAutoAllow (opérations sûres du périmètre autorisées d’office), à activer dans la section des stratégies ci-dessus ; cette liste reflète la section allow de votre rulesFile et légitimement commence vide.",
346
- "card.allowlistEmpty": "Aucune règle de liste blanche",
347
- "card.allowlistPlaceholder": "Nouveau motif de commande, ex. npm test*",
348
- "card.allowlistAdd": "Ajouter",
349
- "card.allowlistRemove": "Supprimer ce motif",
350
- "card.allowlistBulk": "Édition en masse",
351
- "card.allowlistBulkHide": "Masquer l’édition en masse",
352
- "card.allowlistSave": "Enregistrer la liste blanche",
353
- "card.riskLearning": "riskLearning — apprentissage des verdicts : les demandes à risque neutral approuvées puis exécutées par l’humain comptent ; la même opération s’autorise d’office au seuil avec une empreinte correspondante",
354
- "card.riskLearningHint": "L’état d’apprentissage appartient au plugin ($DSH_HOME/perm-gate/learning.json) et n’est jamais écrit dans vos règles YAML ; une cible différente ne réutilise jamais une autorité. Désactivé par défaut.",
355
- "card.riskThreshold": "Confirmations humaines requises avant autorisation automatique (1–10)",
356
- "notice.label": "Activité de la porte de permissions",
357
- "history.label": "Approbations",
358
- "history.title": "Registres d’approbation de permissions",
359
- "history.subtitle": "Chaque décision de la porte dans cette conversation, de la plus récente à la plus ancienne",
360
- "history.loading": "Chargement…",
361
- "history.empty": "Aucun registre d’approbation dans cette session",
362
- "history.error": "Échec du chargement : ",
363
- "history.tag.auto": "Autorisé d’office",
364
- "history.tag.ask": "Demandé",
365
- "history.tag.deny": "Refusé",
366
- "card.denylistRestore": "Restaurer la liste noire prédéfinie",
367
- "card.denylist": "Liste noire (mots-clés deny, préréglage du plugin)",
368
- "card.denylistHint": "Un appel dont le texte contient un mot-clé (sous-chaîne insensible à la casse) est refusé d’emblée, avant liste blanche / autorisations / LLM. La liste prédéfinie est héritée de dsh-approval-gate et active dès l’installation ; modifiez-la librement — vide ou effacée, elle retombe sur le préréglage (restauration en un clic disponible), la liste noire ne se désactive donc jamais en silence.",
369
- "card.denylistEmpty": "Liste noire vide (aucun appel n’est bloqué par la couche de mots-clés)",
370
- "card.denylistPlaceholder": "Nouveau mot-clé, ex. drop database",
371
- "card.denylistAdd": "Ajouter",
372
- "card.denylistRemove": "Supprimer ce mot-clé",
373
- "card.denylistTagPreset": "Prédéfini",
374
- "card.denylistTagCustom": "Personnalisé",
375
- "card.riskSediment": "riskSediment — sédimentation d’apprentissage : les échantillons au seuil deviennent des autorisations déterministes",
376
- "card.riskSedimentHint": "Une fois activée, quand une clé tool|category atteint le seuil, ses empreintes d’échantillons se sédimentent en règles d’autorisation — une correspondance exacte même outil/même cible autorise d’office sans nouvel appel LLM (reste actif même llmAssist désactivé).",
377
- "card.sediment": "Sédiment d’apprentissage (règles d’autorisation déterministes)",
378
- "card.sedimentHint": "Règles sédimentées des confirmations humaines : seule une correspondance d’empreinte exacte autorise, jamais une cible différente. Terminez un apprentissage ou supprimez un échantillon.",
379
- "card.sedimentEmpty": "Aucune règle sédimentée pour l’instant (les échantillons au seuil apparaissent ici)",
380
- "card.sedimentCount": "confirmés %n/%t",
381
- "card.sedimentStop": "Terminer",
382
- "card.sedimentStopTitle": "Terminer cet apprentissage (supprime le compteur et les échantillons)",
383
- "card.sedimentStopConfirm": "Terminer l’apprentissage de « %k » ? Son compteur et tous ses échantillons seront supprimés.",
384
- "card.sedimentSampleRemove": "Supprimer cet échantillon sédimenté",
385
- "card.llmSource": "Source du récepteur",
386
- "card.llmSourceCustom": "API personnalisée (compatible OpenAI)",
387
- "card.llmSourceHost": "Utiliser le modèle par défaut actuel de la session (service llm DSH)",
388
- "card.llmSourceHostHint": "Par défaut, l’évaluation d’approbation s’exécute sur le groupe de modèles que la session DSH utilise actuellement (la valeur effective s’affiche ci-dessous) ; laissez Provider / Model vides pour suivre la session, ou choisissez dans la liste déroulante ou saisissez pour épingler — vos groupes personnalisés configurés dans DSH (ex. local-35b) apparaissent automatiquement.",
389
- "card.llmProvider": "Provider (vide = suivre la sélection de session)",
390
- "card.llmProviderPlaceholder": "Suivre la sélection de session",
391
- "card.llmModelHostPlaceholder": "Vide = suivre la sélection de session",
392
- "card.llmResolved": "Effectif maintenant : %p / %m",
393
- "card.llmPreset": "Préréglages d’endpoint (remplit les champs ; reste modifiable)",
394
- "card.llmPresetChoose": "Choisir un préréglage…",
395
- "card.llmPresetPublic": "Préréglages d’API publiques",
396
- "card.llmPresetHost": "Groupes de modèles DSH (la sélection bascule vers le récepteur de session)",
397
- "card.networkEnabled": "Interception réseau (networkEnabled)",
398
- "card.networkEnabledHint": "Une fois activée, démarre un proxy local qui intercepte les connexions sortantes des sous-processus et applique les règles allow/deny. Désactivée : aucun changement de comportement.",
399
- "card.watch": "Rechargement à chaud des règles (watch)",
400
- "card.watchHint": "Une fois activé, surveille automatiquement les fichiers de règles et applique les changements instantanément, sans rechargement manuel.",
401
- "card.networkRebindNote": "Effet immédiat — le proxy se relie automatiquement, sans rechargement du plugin.",
402
- "card.networkInjectEnv": "Injection des variables de proxy (networkInjectEnv)",
403
- "card.networkInjectEnvHint": "Écrit HTTP(S)_PROXY / ALL_PROXY pour les sous-processus (et efface NO_PROXY). Désactivée : l’écouteur tourne mais aucun environnement de processus ne change.",
404
- "card.healthStale": "Route de l’hôte indisponible (404) — la moitié node est encore un ancien build ; redémarrez complètement dsh (pas seulement la page du navigateur) puis réessayez.",
405
- "card.healthTest": "Test de santé",
406
- "card.healthRunning": "Test en cours…",
407
- "card.healthOk": "OK (%ms ms)",
408
- "card.healthFail": "Échec : ",
409
- "history.tag.learned": "Appris",
410
- "history.tag.manualApproved": "Approuvé",
411
- "history.tag.manualRejected": "Rejeté",
412
- "history.tag.manualCancelled": "Annulé",
413
- "history.tag.standDown": "Porte désactivée",
414
- "history.learnedProgress": "appris %n/%t",
415
- "history.snapshots": "instantanés diff",
416
- "history.clearSession": "Effacer cette session",
417
- "history.clearAll": "Tout effacer",
418
- "history.clearSessionTitle": "Effacer uniquement les instantanés diff de cette session (les autres sessions restent intactes)",
419
- "history.clearAllTitle": "Effacer les instantanés diff de toutes les sessions, y compris celles jamais consultées — demande une seconde confirmation",
420
- "history.clearSessionConfirm": "Effacer les instantanés diff de cette session ? Seules les données de comparaison produites par les approbations de cette session sont supprimées ; les registres d’approbation restent.",
421
- "history.clearAllConfirm": "Effacer les instantanés diff de TOUTES les sessions ? Cela peut supprimer des comparaisons que d’autres sessions n’ont pas encore consultées, sans retour arrière.",
422
- "history.diffTitle": "Comparaison des modifications de fichiers",
423
- "history.diffClose": "Fermer",
424
- "history.diffLoading": "Chargement…",
425
- "history.diffEmpty": "Aucun changement de contenu (ou fichier illisible)",
426
- "history.diffLoadFail": "Échec du chargement du diff : ",
427
- "history.diffRevert": "Annuler cette modification",
428
- "history.diffReverting": "Envoi…",
429
- "history.diffRevertDone": "Instruction d’annulation envoyée",
430
- "history.diffRevertSent": "Instruction d’annulation envoyée à la conversation ; l’IA restaurera le fichier en conséquence",
431
- "history.diffRevertFail": "Échec de l’envoi : ",
432
- "history.diffNoSnapshot": "Cet événement n’a pas d’instantané pour ce fichier",
433
- "history.diffChipTitle": "Voir la comparaison des modifications de ce fichier",
434
- "history.diffStats": "+%a / -%r lignes modifiées · %c inchangées",
435
- "history.diffGone": " (le fichier n’existe plus)",
436
- "history.diffHidden": "%n lignes non modifiées",
437
- "card.dryRun": "Test de règles",
438
- "card.dryRunHint": "Juge un appel selon les règles en vigueur. N’exécute aucun outil et n’écrit aucune règle — vérifiez une règle ici avant de la modifier.",
439
- "card.dryRunTool": "Outil",
440
- "card.dryRunCommand": "Commande / arguments",
441
- "card.dryRunCommandPlaceholder": "git push --force origin main",
442
- "card.dryRunTest": "Tester",
443
- "card.dryRunRunning": "Évaluation…",
444
- "card.dryRunEmpty": "Rien de testé pour l’instant. Saisissez un outil et une commande, puis appuyez sur Tester pour voir le verdict.",
445
- "card.dryRunVerdict": "Verdict",
446
- "card.dryRunAllow": "autoriser",
447
- "card.dryRunAsk": "demander",
448
- "card.dryRunDeny": "refuser",
449
- "card.dryRunRule": "Règle correspondante",
450
- "card.dryRunRuleNone": "aucune règle correspondante ; defaultAction : %d",
451
- "card.dryRunDimensions": "Dimensions",
452
- "card.dryRunReason": "Motif",
453
- "card.dryRunNote": "« Règle correspondante » vient de la couche de règles. Quand le verdict provient d’un refus dur antérieur ou de la liste noire de mots-clés, il n’y a pas d’index de règle — les dimensions listées sont alors purement indicatives.",
454
- "card.dryRunNoRules": "(0 règle chargée — vérifiez le chemin du rulesFile)",
455
- "card.dryRunFail": "Échec du test : ",
456
- "card.dryRunStale": "route absente (plugin non redémarré ?)",
457
- "card.rules": "Règles intégrées de Settings",
458
- "card.rulesHint": "Les règles que la porte charge en ce moment (du champ de config `rules` de l’entrée de ce plugin ; retombe sur le fichier de règles si non défini). Lecture seule — rien ici ne peut les changer. Pour changer les règles, utilisez la liste blanche ci-dessous ou la page des réglages.",
459
- "card.rulesRefresh": "Actualiser",
460
- "card.rulesLoading": "Lecture…",
461
- "card.rulesPath": "Source",
462
- "card.rulesNone": "aucune règle configurée",
463
- "card.rulesCounts": "Règles",
464
- "card.rulesDefault": "defaultAction",
465
- "card.rulesStats": "%b octets · %l lignes",
466
- "card.rulesMissing": "Règles introuvables. La porte retombe sur ses valeurs par défaut intégrées.",
467
- "card.rulesTruncated": "Règles volumineuses ; seuls les premiers 512 Ko sont affichés.",
468
- "card.rulesError": "Échec du chargement : ",
469
- "card.rulesStale": "route absente (plugin non redémarré ?)",
470
- "card.rulesNote": "Lecture seule : ce panneau ne peut pas modifier les règles. La section liste blanche et « toujours autoriser » sont réécrites dans la config d’entrée de ce plugin."
471
- };
472
- /** German dictionary (keys mirror zh). */
473
- const de = {
474
- "card.title": "自动审查 (Automatische Prüfung)",
475
- "section.title": "Auto-Prüf-Gate",
476
- "card.description": "Unabhängige Genehmigungsstufe neben read-only / full-access / whitelist, angezeigt als 自动审查 in der Sitzungsrechte-Auswahl. Das Frontend bietet nur einen einzigen Schalter; die vier Backend-Genehmigungsstrategien sind über dieses Panel kombinierbar und bleiben gegenüber P0-Hartablehnung fail-closed.",
477
- "card.permissive": "Stufe 自动审查 aktivieren",
478
- "card.permissiveHint": "Ausgeschaltet verhält sich das Gate exakt wie bisher.",
479
- "card.scopeNote": "Voreingestellter Geltungsbereich: Dieses Gate wirkt nur, während der Sitzungsrechte-Preset %scope ist. Unter jedem anderen Preset steht das gesamte Gate still — P0-Hartablehnung eingeschlossen — und die eigene Politik des gewählten Presets übernimmt. Ein Stillstand schreibt ein Ereignis und hinterlässt einen dauerhaften Hinweisstreifen über der Eingabe.",
480
- "card.strategies": "Backend-Genehmigungsstrategien (mindestens eine wirksam; kombinierbar)",
481
- "card.strategy.trustAutoAllow": "trustAutoAllow — sichere Operationen im Umfang werden automatisch erlaubt; gefährliche/unbekannte fragen nach (Baseline der mittleren Stufe)",
482
- "card.strategy.alwaysConfirm": "alwaysConfirm — jeder Durchgang fragt; die „Erlaubnis-Steuerungen“ des Genehmigungspanels erhalten zwei erweiterte Schaltflächen: „diesen Typ in dieser Sitzung wiederholt erlauben“ (sitzungsbegrenzte Genehmigung) und „jedes Vorkommen erlauben“ (das Befehlswort wird dauerhaft in die Whitelist der rulesFile aufgenommen)",
483
- "card.strategy.llmAssist": "llmAssist — ein benutzerdefiniertes LLM stuft das Risiko ein: safe wird automatisch erlaubt; harte Risiken (deletion/credential/remote/system/bulk) behalten die menschliche Nachfrage (der Klassifikator lehnt nie ab — er eskaliert nur); neutral geht ins Bestätigungslernen; Fehler/Timeout fällt auf den Menschen zurück",
484
- "card.strategy.trustEscalation": "trustEscalation — Sandbox-Ausweitung ohne Rückfrage: Bei einem von diesem Gate bereits erlaubten Aufruf wird die Ausweitung, die ein shell-/pwsh-/edit-Werkzeug von innen anfordert, hier genehmigt, statt Sie zu fragen",
485
- "card.strategy.trustEscalationHint": "Eine Sandbox-Ausweitungsanfrage stellt das Werkzeug selbst, nach tools/pre-execute, daher sah das Gate sie nie — ein vom LLM als safe eingestuftes, automatisch erlaubtes Aufrufen fragte Sie dennoch, die Ausweitung zu genehmigen. Eingeschaltet überspringt nur der exakte Aufruf die Rückfrage, den das Gate erlaubt hat (exakt per callId gematcht); alles andere geht wie bisher an den Menschen, ebenso bei unbekanntem Grund oder einem Ziel außer workspace-write / danger-full-access. Ausgeschaltet bleibt jede Ausweitung manuell genehmigungspflichtig.",
486
- "card.readonly": "Nur Lesen",
487
- "card.unavailable": "Settings-Namespace nicht verfügbar: Stellen Sie sicher, dass dsh-perm-gate in dieses Profil eingebaut ist.",
488
- "card.llmReceiver": "llmAssist-Empfangs-LLM (OpenAI-kompatibel; jede eigene API)",
489
- "card.llmEndpoint": "Endpoint (BaseURL, z. B. https://api.openai.com/v1)",
490
- "card.llmModel": "Model (Modell-ID, z. B. deepseek-chat)",
491
- "card.llmKey": "API-Key (geheim)",
492
- "card.llmKeyHidden": "Nicht gesetzt · nach der ersten Eingabe als * angezeigt",
493
- "card.llmKeyMasked": "Gesetzt · neuen Wert eingeben zum Überschreiben",
494
- "card.llmKeyOverwrite": "Ein Geheimwert ist gespeichert; geben Sie einen neuen Wert ein und verlassen Sie das Feld, um ihn zu überschreiben.",
495
- "card.llmKeyClear": "Löschen",
496
- "card.allowlist": "Whitelist (allow, mit rulesFile synchronisiert)",
497
- "card.allowlistHint": "Muster im allow-Abschnitt erlauben direkt; Einträge einzeln hinzufügen oder entfernen — wird in die rulesFile geschrieben und live neu geladen.",
498
- "card.allowlistPresetNote": "Das Pendant zur voreingestellten Whitelist ist die Strategie trustAutoAllow (sichere Operationen im Umfang automatisch erlauben), aktivierbar im Strategiebereich oben; diese Liste spiegelt den allow-Abschnitt Ihrer rulesFile und darf leer beginnen.",
499
- "card.allowlistEmpty": "Keine Whitelist-Regeln",
500
- "card.allowlistPlaceholder": "Neues Befehlsmuster, z. B. npm test*",
501
- "card.allowlistAdd": "Hinzufügen",
502
- "card.allowlistRemove": "Dieses Muster entfernen",
503
- "card.allowlistBulk": "Massenbearbeitung",
504
- "card.allowlistBulkHide": "Massenbearbeitung einklappen",
505
- "card.allowlistSave": "Whitelist speichern",
506
- "card.riskLearning": "riskLearning — Urteilslernen: neutral-Risiko-Anfragen, die der Mensch genehmigt und die ausgeführt werden, zählen hoch; dieselbe Operation wird bei Erreichen des Schwellwerts mit passendem Fingerabdruck automatisch erlaubt",
507
- "card.riskLearningHint": "Der Lernzustand gehört zum Plugin ($DSH_HOME/perm-gate/learning.json) und wird nie in Ihre YAML-Regeln geschrieben; ein anderes Ziel nutzt eine Erlaubnis nie wieder. Standardmäßig aus.",
508
- "card.riskThreshold": "Menschliche Bestätigungen vor automatischer Erlaubnis (1–10)",
509
- "notice.label": "Aktivität des Rechte-Gates",
510
- "history.label": "Genehmigungen",
511
- "history.title": "Genehmigungsprotokoll der Rechte",
512
- "history.subtitle": "Jede Gate-Entscheidung dieser Konversation, neueste zuerst",
513
- "history.loading": "Wird geladen…",
514
- "history.empty": "Noch keine Genehmigungen in dieser Sitzung",
515
- "history.error": "Laden fehlgeschlagen: ",
516
- "history.tag.auto": "Automatisch erlaubt",
517
- "history.tag.ask": "Nachgefragt",
518
- "history.tag.deny": "Abgelehnt",
519
- "card.denylistRestore": "Voreingestellte Blacklist wiederherstellen",
520
- "card.denylist": "Blacklist (deny-Schlüsselwörter, Plugin-Voreinstellung)",
521
- "card.denylistHint": "Ein Aufruf, dessen Text ein Schlüsselwort enthält (Teilstring, Groß-/Kleinschreibung egal), wird rundum abgelehnt — vor Whitelist / Genehmigungen / LLM. Die Voreinstellung stammt von dsh-approval-gate und ist ab Installation aktiv; frei editierbar — leer oder geleert fällt auf die Voreinstellung zurück (Ein-Klick-Wiederherstellung), die Blacklist schaltet sich also nie still ab.",
522
- "card.denylistEmpty": "Blacklist leer (kein Aufruf wird von der Schlüsselwortschicht abgelehnt)",
523
- "card.denylistPlaceholder": "Neues Schlüsselwort, z. B. drop database",
524
- "card.denylistAdd": "Hinzufügen",
525
- "card.denylistRemove": "Dieses Schlüsselwort entfernen",
526
- "card.denylistTagPreset": "Voreingestellt",
527
- "card.denylistTagCustom": "Benutzerdefiniert",
528
- "card.riskSediment": "riskSediment — Lernsedimentierung: Stichproben am Schwellwert werden deterministische Auto-Erlaubnisse",
529
- "card.riskSedimentHint": "Eingeschaltet sedimentieren die Stichproben-Fingerabdrücke eines tool|category-Schlüssels bei Erreichen des Schwellwerts zu Erlaubnisregeln — ein exakter Treffer gleiches Werkzeug/gleiches Ziel erlaubt ohne weiteren LLM-Aufruf (wirkt auch bei ausgeschaltetem llmAssist).",
530
- "card.sediment": "Lernsediment (deterministische Erlaubnisregeln)",
531
- "card.sedimentHint": "Aus menschlichen Bestätigungen sedimentierte Regeln: Nur ein exakter Fingerabdruck-Treffer erlaubt, nie ein anderes Ziel. Ein Lernen beenden oder eine Stichprobe entfernen.",
532
- "card.sedimentEmpty": "Noch keine sedimentierten Regeln (Stichproben am Schwellwert erscheinen hier)",
533
- "card.sedimentCount": "bestätigt %n/%t",
534
- "card.sedimentStop": "Beenden",
535
- "card.sedimentStopTitle": "Dieses Lernen beenden (Zähler und Stichproben werden gelöscht)",
536
- "card.sedimentStopConfirm": "Lernen für „%k“ beenden? Zähler und alle Stichproben werden gelöscht.",
537
- "card.sedimentSampleRemove": "Diese sedimentierte Stichprobe entfernen",
538
- "card.llmSource": "Empfangsquelle",
539
- "card.llmSourceCustom": "Eigene API (OpenAI-kompatibel)",
540
- "card.llmSourceHost": "Aktuelles Sitzungsstandardmodell verwenden (DSH-llm-Dienst)",
541
- "card.llmSourceHostHint": "Standardmäßig läuft die Genehmigungsbewertung auf der Modellgruppe, die die DSH-Sitzung aktuell nutzt (der effektive Wert steht unten); lassen Sie Provider / Model leer, um der Sitzung zu folgen, oder wählen Sie aus der Liste bzw. tippen Sie einen festen Wert — eigene Gruppen aus Ihrer DSH-Konfiguration (z. B. local-35b) erscheinen automatisch.",
542
- "card.llmProvider": "Provider (leer = der Sitzungsauswahl folgen)",
543
- "card.llmProviderPlaceholder": "Der Sitzungsauswahl folgen",
544
- "card.llmModelHostPlaceholder": "Leer = der Sitzungsauswahl folgen",
545
- "card.llmResolved": "Derzeit wirksam: %p / %m",
546
- "card.llmPreset": "Endpoint-Voreinstellungen (füllt die Felder; weiter editierbar)",
547
- "card.llmPresetChoose": "Voreinstellung wählen…",
548
- "card.llmPresetPublic": "Voreinstellungen für öffentliche APIs",
549
- "card.llmPresetHost": "DSH-Modellgruppen (Auswahl wechselt zum Sitzungsempfänger)",
550
- "card.networkEnabled": "Netzwerkabfangen (networkEnabled)",
551
- "card.networkEnabledHint": "Eingeschaltet startet ein lokaler Proxy, der ausgehende Verbindungen von Unterprozessen abfängt und allow/deny-Regeln anwendet. Aus = kein Verhaltensunterschied.",
552
- "card.watch": "Heißes Nachladen der Regeln (watch)",
553
- "card.watchHint": "Eingeschaltet werden Regeldateien automatisch überwacht und Änderungen sofort angewendet — ohne manuelles Nachladen.",
554
- "card.networkRebindNote": "Wirkt sofort — der Proxy bindet automatisch neu, ein Plugin-Neuladen ist nicht nötig.",
555
- "card.networkInjectEnv": "Proxy-Umgebungsvariablen injizieren (networkInjectEnv)",
556
- "card.networkInjectEnvHint": "Schreibt HTTP(S)_PROXY / ALL_PROXY für Unterprozesse (und leert NO_PROXY). Aus = der Lauscher läuft, aber keine Prozessumgebung ändert sich.",
557
- "card.healthStale": "Host-Route nicht verfügbar (404) — die node-Hälfte ist noch ein alter Build; starten Sie dsh vollständig neu (nicht nur die Browserseite) und versuchen Sie es erneut.",
558
- "card.healthTest": "Gesundheitstest",
559
- "card.healthRunning": "Teste…",
560
- "card.healthOk": "OK (%ms ms)",
561
- "card.healthFail": "Fehlgeschlagen: ",
562
- "history.tag.learned": "Gelernt",
563
- "history.tag.manualApproved": "Genehmigt",
564
- "history.tag.manualRejected": "Abgelehnt",
565
- "history.tag.manualCancelled": "Abgebrochen",
566
- "history.tag.standDown": "Gate aus",
567
- "history.learnedProgress": "gelernt %n/%t",
568
- "history.snapshots": "diff-Snapshots",
569
- "history.clearSession": "Nur diese Sitzung leeren",
570
- "history.clearAll": "Alles leeren",
571
- "history.clearSessionTitle": "Nur die diff-Snapshots dieser Sitzung löschen (andere Sitzungen unberührt)",
572
- "history.clearAllTitle": "diff-Snapshots aller Sitzungen löschen, auch nie angesehener — fragt nochmals nach",
573
- "history.clearSessionConfirm": "diff-Snapshots dieser Sitzung löschen? Nur die von den Genehmigungen dieser Sitzung erzeugten Vergleichsdaten werden entfernt; die Genehmigungsprotokolle bleiben.",
574
- "history.clearAllConfirm": "diff-Snapshots ALLER Sitzungen löschen? Das kann Vergleiche löschen, die andere Sitzungen noch nicht angesehen haben, und ist nicht rückgängig zu machen.",
575
- "history.diffTitle": "Dateiänderungsvergleich",
576
- "history.diffClose": "Schließen",
577
- "history.diffLoading": "Wird geladen…",
578
- "history.diffEmpty": "Keine Inhaltsänderung (oder Datei nicht lesbar)",
579
- "history.diffLoadFail": "diff konnte nicht geladen werden: ",
580
- "history.diffRevert": "Diese Änderung zurücknehmen",
581
- "history.diffReverting": "Wird gesendet…",
582
- "history.diffRevertDone": "Rücknahmeanweisung gesendet",
583
- "history.diffRevertSent": "Rücknahmeanweisung an die Konversation gesendet; die KI stellt die Datei entsprechend wieder her",
584
- "history.diffRevertFail": "Senden fehlgeschlagen: ",
585
- "history.diffNoSnapshot": "Dieses Ereignis hat keinen Snapshot für diese Datei",
586
- "history.diffChipTitle": "Änderungsvergleich dieser Datei ansehen",
587
- "history.diffStats": "+%a / -%r Zeilen geändert · %c unverändert",
588
- "history.diffGone": " (Datei existiert nicht mehr)",
589
- "history.diffHidden": "%n unveränderte Zeilen",
590
- "card.dryRun": "Regeltest",
591
- "card.dryRunHint": "Beurteilt einen Aufruf gegen die geltenden Regeln. Führt kein Werkzeug aus und schreibt keine Regel — prüfen Sie eine Regel hier, bevor Sie sie ändern.",
592
- "card.dryRunTool": "Werkzeug",
593
- "card.dryRunCommand": "Befehl / Argumente",
594
- "card.dryRunCommandPlaceholder": "git push --force origin main",
595
- "card.dryRunTest": "Testen",
596
- "card.dryRunRunning": "Wird beurteilt…",
597
- "card.dryRunEmpty": "Noch nichts getestet. Werkzeug und Befehl eingeben, dann auf Testen drücken, um das Urteil zu sehen.",
598
- "card.dryRunVerdict": "Urteil",
599
- "card.dryRunAllow": "erlauben",
600
- "card.dryRunAsk": "nachfragen",
601
- "card.dryRunDeny": "ablehnen",
602
- "card.dryRunRule": "Passende Regel",
603
- "card.dryRunRuleNone": "keine Regel passt; defaultAction: %d",
604
- "card.dryRunDimensions": "Dimensionen",
605
- "card.dryRunReason": "Grund",
606
- "card.dryRunNote": "„Passende Regel“ stammt aus der Regelebene. Stammt das Urteil aus einer früheren Hartablehnung oder der Schlüsselwort-Blacklist, gibt es keine Regelnummer — die gelisteten Dimensionen sind dann rein informativ.",
607
- "card.dryRunNoRules": "(0 Regeln geladen — rulesFile-Pfad prüfen)",
608
- "card.dryRunFail": "Test fehlgeschlagen: ",
609
- "card.dryRunStale": "Route fehlt (Plugin nicht neu gestartet?)",
610
- "card.rules": "Eingebaute Regeln aus Settings",
611
- "card.rulesHint": "Die Regeln, die das Gate gerade lädt (aus dem `rules`-Konfigurationsfeld dieses Plugin-Eintrags; ohne Angabe fällt es auf die Regeldatei zurück). Nur Lesen — hier lässt sich nichts ändern. Regeln ändern Sie über die Whitelist unten oder auf der Einstellungsseite.",
612
- "card.rulesRefresh": "Aktualisieren",
613
- "card.rulesLoading": "Wird gelesen…",
614
- "card.rulesPath": "Quelle",
615
- "card.rulesNone": "keine Regeln konfiguriert",
616
- "card.rulesCounts": "Regeln",
617
- "card.rulesDefault": "defaultAction",
618
- "card.rulesStats": "%b Bytes · %l Zeilen",
619
- "card.rulesMissing": "Regeln nicht gefunden. Das Gate fällt auf seine eingebauten Standardwerte zurück.",
620
- "card.rulesTruncated": "Regeln sind groß; nur die ersten 512 KB werden angezeigt.",
621
- "card.rulesError": "Laden fehlgeschlagen: ",
622
- "card.rulesStale": "Route fehlt (Plugin nicht neu gestartet?)",
623
- "card.rulesNote": "Nur Lesen: Dieses Panel kann die Regeln nicht ändern. Der Whitelist-Abschnitt und „immer erlauben“ schreiben in die Eintragskonfiguration dieses Plugins zurück."
624
- };
625
- /** Italian dictionary (keys mirror zh). */
626
- const it = {
627
- "card.title": "自动审查 (Revisione automatica)",
628
- "section.title": "Gate di revisione automatica",
629
- "card.description": "Livello di approvazione indipendente, parallelo a read-only / full-access / whitelist, mostrato come 自动审查 nel selettore dei permessi di sessione. Il front-end espone un solo interruttore; le quattro strategie di approvazione del backend sono combinabili da questo pannello e restano fail-closed contro il diniego rigido P0.",
630
- "card.permissive": "Attiva il livello 自动审查",
631
- "card.permissiveHint": "Spento, il gate si comporta esattamente come prima.",
632
- "card.scopeNote": "Ambito preimpostato: questo gate agisce solo quando il preset dei permessi di sessione è %scope. Con qualsiasi altro preset l’intero gate si disattiva — diniego rigido P0 compreso — e prende il comando la politica del preset scelto. Una disattivazione registra un evento e lascia una striscia persistente sopra la casella di input.",
633
- "card.strategies": "Strategie di approvazione del backend (almeno una efficace; combinabili)",
634
- "card.strategy.trustAutoAllow": "trustAutoAllow — le operazioni sicure nell’ambito sono consentite automaticamente; quelle pericolose/sconosciute chiedono conferma (baseline del livello intermedio)",
635
- "card.strategy.alwaysConfirm": "alwaysConfirm — ogni passaggio chiede conferma; i «controlli di consenso» del pannello di approvazione ricevono due pulsanti estesi: «ri-consenti questo tipo per questa sessione» (concessione limitata alla sessione) e «consenti ogni occorrenza» (la parola di comando viene resa persistente nella whitelist del rulesFile)",
636
- "card.strategy.llmAssist": "llmAssist — un LLM personalizzato gradua il rischio: safe consente automaticamente; i rischi rigidi deletion/credential/remote/system/bulk mantengono la conferma umana (il classificatore non nega mai — si limita a escalare); neutral entra nell’apprendimento per conferma; fallimento/timeout ricade sull’umano",
637
- "card.strategy.trustEscalation": "trustEscalation — ampliamento sandbox senza prompt: per una chiamata già consentita da questo gate, l’ampliamento di sandbox che uno strumento shell / pwsh / edit richiede dal proprio interno viene approvato qui invece di chiedere a te",
638
- "card.strategy.trustEscalationHint": "Una richiesta di ampliamento sandbox parte dall’interno del corpo dello strumento, dopo tools/pre-execute, quindi il gate non l’ha mai vista — una chiamata valutata safe dal LLM e consentita automaticamente chiedeva comunque l’approvazione dell’ampliamento. Attiva, solo la chiamata esatta che il gate ha consentito (corrispondenza esatta per callId) salta il prompt; tutto il resto va all’umano come prima, così come una ragione non riconosciuta o una destinazione diversa da workspace-write / danger-full-access. Disattiva per tenere l’ampliamento sotto controllo umano.",
639
- "card.readonly": "Sola lettura",
640
- "card.unavailable": "Namespace delle impostazioni non disponibile: verifica che dsh-perm-gate sia assemblato in questo profilo.",
641
- "card.llmReceiver": "LLM ricevente di llmAssist (compatibile OpenAI; qualsiasi API personalizzata)",
642
- "card.llmEndpoint": "Endpoint (BaseURL, es. https://api.openai.com/v1)",
643
- "card.llmModel": "Model (id del modello, es. deepseek-chat)",
644
- "card.llmKey": "API Key (segreta)",
645
- "card.llmKeyHidden": "Non impostata · mostrata come * dopo il primo inserimento",
646
- "card.llmKeyMasked": "Impostata · digita un nuovo valore per sostituirla",
647
- "card.llmKeyOverwrite": "Un segreto è memorizzato; digita un nuovo valore e togli il focus per sostituirlo.",
648
- "card.llmKeyClear": "Cancella",
649
- "card.allowlist": "Whitelist (allow, sincronizzata con il rulesFile)",
650
- "card.allowlistHint": "I pattern nella sezione allow consentono direttamente; aggiungi o rimuovi voci una a una, scritte nel rulesFile e ricaricate in diretta.",
651
- "card.allowlistPresetNote": "L’equivalente della whitelist preimpostata è la strategia trustAutoAllow (operazioni sicure nell’ambito consentite automaticamente), da attivare nella sezione strategie sopra; questo elenco rispecchia la sezione allow del tuo rulesFile ed è normale che parta vuoto.",
652
- "card.allowlistEmpty": "Nessuna regola di whitelist",
653
- "card.allowlistPlaceholder": "Nuovo pattern di comando, es. npm test*",
654
- "card.allowlistAdd": "Aggiungi",
655
- "card.allowlistRemove": "Rimuovi questo pattern",
656
- "card.allowlistBulk": "Modifica in blocco",
657
- "card.allowlistBulkHide": "Nascondi la modifica in blocco",
658
- "card.allowlistSave": "Salva la whitelist",
659
- "card.riskLearning": "riskLearning — apprendimento dei verdetti: le richieste a rischio neutral approvate ed eseguite dall’umano contano; la stessa operazione si auto-consente alla soglia con impronta corrispondente",
660
- "card.riskLearningHint": "Lo stato di apprendimento è dati del plugin ($DSH_HOME/perm-gate/learning.json) e non viene mai scritto nelle tue regole YAML; una destinazione diversa non riusa mai un’autorità. Disattivato per impostazione predefinita.",
661
- "card.riskThreshold": "Conferme umane richieste prima del consenso automatico (1–10)",
662
- "notice.label": "Attività del gate dei permessi",
663
- "history.label": "Approvazioni",
664
- "history.title": "Registri di approvazione dei permessi",
665
- "history.subtitle": "Ogni decisione del gate in questa conversazione, dalla più recente",
666
- "history.loading": "Caricamento…",
667
- "history.empty": "Nessun registro di approvazione in questa sessione",
668
- "history.error": "Caricamento non riuscito: ",
669
- "history.tag.auto": "Auto-consentito",
670
- "history.tag.ask": "Chiesto",
671
- "history.tag.deny": "Negato",
672
- "card.denylistRestore": "Ripristina la blacklist preimpostata",
673
- "card.denylist": "Blacklist (parole chiave deny, preimpostazione del plugin)",
674
- "card.denylistHint": "Una chiamata il cui testo contiene una parola chiave (sottostringa senza distinzione tra maiuscole e minuscole) viene negata outright — prima di whitelist / concessioni / LLM. L’elenco preimpostato è ereditato da dsh-approval-gate e attivo dall’installazione; modifica liberamente — vuoto o svuotato ricade sulla preimpostazione (ripristino con un clic disponibile), così la blacklist non si spegne mai in silenzio.",
675
- "card.denylistEmpty": "Blacklist vuota (nessuna chiamata è bloccata dal livello delle parole chiave)",
676
- "card.denylistPlaceholder": "Nuova parola chiave, es. drop database",
677
- "card.denylistAdd": "Aggiungi",
678
- "card.denylistRemove": "Rimuovi questa parola chiave",
679
- "card.denylistTagPreset": "Preimpostato",
680
- "card.denylistTagCustom": "Personalizzato",
681
- "card.riskSediment": "riskSediment — sedimentazione dell’apprendimento: i campioni arrivati alla soglia diventano consensi deterministici",
682
- "card.riskSedimentHint": "Attiva, quando una chiave tool|category raggiunge la soglia le impronte dei suoi campioni si sedimentano in regole di consenso — una corrispondenza esatta stesso strumento/stessa destinazione consente senza un’altra chiamata LLM (resta attivo anche con llmAssist spento).",
683
- "card.sediment": "Sedimento di apprendimento (regole di consenso deterministiche)",
684
- "card.sedimentHint": "Regole sedimentate dalle conferme umane: solo una corrispondenza esatta di impronta consente, mai una destinazione diversa. Termina un apprendimento o rimuovi un campione.",
685
- "card.sedimentEmpty": "Ancora nessuna regola sedimentata (i campioni arrivati alla soglia compaiono qui)",
686
- "card.sedimentCount": "confermati %n/%t",
687
- "card.sedimentStop": "Termina",
688
- "card.sedimentStopTitle": "Termina questo apprendimento (elimina il conteggio e i campioni)",
689
- "card.sedimentStopConfirm": "Terminare l’apprendimento di «%k»? Il conteggio e tutti i campioni saranno eliminati.",
690
- "card.sedimentSampleRemove": "Rimuovi questo campione sedimentato",
691
- "card.llmSource": "Sorgente del ricevente",
692
- "card.llmSourceCustom": "API personalizzata (compatibile OpenAI)",
693
- "card.llmSourceHost": "Usa il modello predefinito corrente della sessione (servizio llm DSH)",
694
- "card.llmSourceHostHint": "Per impostazione predefinita la graduazione dell’approvazione gira sul gruppo di modelli che la sessione DSH usa attualmente (il valore effettivo è mostrato sotto); lascia Provider / Model vuoti per seguire la sessione, oppure scegli dal menu a tendina o digita per fissarne uno — i gruppi personalizzati configurati in DSH (es. local-35b) compaiono automaticamente.",
695
- "card.llmProvider": "Provider (vuoto = segue la selezione della sessione)",
696
- "card.llmProviderPlaceholder": "Segue la selezione della sessione",
697
- "card.llmModelHostPlaceholder": "Vuoto = segue la selezione della sessione",
698
- "card.llmResolved": "Effettivo ora: %p / %m",
699
- "card.llmPreset": "Preimpostazioni di endpoint (compila i campi; resta modificabile)",
700
- "card.llmPresetChoose": "Scegli una preimpostazione…",
701
- "card.llmPresetPublic": "Preimpostazioni di API pubbliche",
702
- "card.llmPresetHost": "Gruppi di modelli DSH (la selezione passa al ricevente di sessione)",
703
- "card.networkEnabled": "Intercettazione di rete (networkEnabled)",
704
- "card.networkEnabledHint": "Attiva, avvia un proxy locale che intercetta le connessioni in uscita dei sottoprocessi e applica regole allow/deny. Spenta = nessun cambiamento di comportamento.",
705
- "card.watch": "Ricarica a caldo delle regole (watch)",
706
- "card.watchHint": "Attivo, osserva automaticamente i file di regole e applica i cambiamenti all’istante, senza ricarica manuale.",
707
- "card.networkRebindNote": "Effetto immediato — il proxy si ricollega da solo, nessun ricaricamento del plugin.",
708
- "card.networkInjectEnv": "Iniezione delle variabili d’ambiente del proxy (networkInjectEnv)",
709
- "card.networkInjectEnvHint": "Scrive HTTP(S)_PROXY / ALL_PROXY per i sottoprocessi (e cancella NO_PROXY). Spenta = il listener gira ma nessun ambiente di processo cambia.",
710
- "card.healthStale": "Rotta dell’host non disponibile (404) — la metà node è ancora una build vecchia; riavvia completamente dsh (non solo la pagina del browser) e riprova.",
711
- "card.healthTest": "Test di salute",
712
- "card.healthRunning": "Test in corso…",
713
- "card.healthOk": "OK (%ms ms)",
714
- "card.healthFail": "Non riuscito: ",
715
- "history.tag.learned": "Appreso",
716
- "history.tag.manualApproved": "Approvato",
717
- "history.tag.manualRejected": "Rifiutato",
718
- "history.tag.manualCancelled": "Annullato",
719
- "history.tag.standDown": "Gate disattivato",
720
- "history.learnedProgress": "appreso %n/%t",
721
- "history.snapshots": "snapshot diff",
722
- "history.clearSession": "Svuota questa sessione",
723
- "history.clearAll": "Svuota tutto",
724
- "history.clearSessionTitle": "Svuota solo gli snapshot diff di questa sessione (le altre sessioni restano intatte)",
725
- "history.clearAllTitle": "Svuota gli snapshot diff di ogni sessione, comprese quelle mai riviste — chiede una seconda conferma",
726
- "history.clearSessionConfirm": "Svuotare gli snapshot diff di questa sessione? Vengono rimossi solo i dati di confronto prodotti dalle approvazioni di questa sessione; i registri di approvazione restano.",
727
- "history.clearAllConfirm": "Svuotare gli snapshot diff di TUTTE le sessioni? Questo può eliminare confronti che altre sessioni non hanno ancora visto, e non si può annullare.",
728
- "history.diffTitle": "Confronto delle modifiche ai file",
729
- "history.diffClose": "Chiudi",
730
- "history.diffLoading": "Caricamento…",
731
- "history.diffEmpty": "Nessun cambiamento di contenuto (o file non leggibile)",
732
- "history.diffLoadFail": "Caricamento del diff non riuscito: ",
733
- "history.diffRevert": "Annulla questa modifica",
734
- "history.diffReverting": "Invio…",
735
- "history.diffRevertDone": "Istruzione di annullamento inviata",
736
- "history.diffRevertSent": "Istruzione di annullamento inviata alla conversazione; l’AI ripristinerà il file di conseguenza",
737
- "history.diffRevertFail": "Invio non riuscito: ",
738
- "history.diffNoSnapshot": "Questo evento non ha uno snapshot per quel file",
739
- "history.diffChipTitle": "Vedi il confronto delle modifiche di questo file",
740
- "history.diffStats": "+%a / -%r righe modificate · %c invariate",
741
- "history.diffGone": " (il file non esiste più)",
742
- "history.diffHidden": "%n righe non modificate",
743
- "card.dryRun": "Test delle regole",
744
- "card.dryRunHint": "Giudica una chiamata secondo le regole in vigore. Non esegue alcuno strumento e non scrive alcuna regola — verifica una regola qui prima di modificarla.",
745
- "card.dryRunTool": "Strumento",
746
- "card.dryRunCommand": "Comando / argomenti",
747
- "card.dryRunCommandPlaceholder": "git push --force origin main",
748
- "card.dryRunTest": "Testa",
749
- "card.dryRunRunning": "Giudizio in corso…",
750
- "card.dryRunEmpty": "Niente ancora testato. Inserisci strumento e comando, poi premi Testa per vedere come sarebbe giudicato.",
751
- "card.dryRunVerdict": "Verdetto",
752
- "card.dryRunAllow": "consenti",
753
- "card.dryRunAsk": "chiedi",
754
- "card.dryRunDeny": "nega",
755
- "card.dryRunRule": "Regola corrisposta",
756
- "card.dryRunRuleNone": "nessuna regola corrisposta; defaultAction: %d",
757
- "card.dryRunDimensions": "Dimensioni",
758
- "card.dryRunReason": "Motivo",
759
- "card.dryRunNote": "«Regola corrisposta» viene dal livello delle regole. Quando il verdetto proviene da un diniego rigido precedente o dalla blacklist di parole chiave non c’è un indice di regola — le dimensioni elencate sono allora solo indicative.",
760
- "card.dryRunNoRules": "(0 regole caricate — controlla il percorso del rulesFile)",
761
- "card.dryRunFail": "Test non riuscito: ",
762
- "card.dryRunStale": "rotta assente (plugin non riavviato?)",
763
- "card.rules": "Regole incorporate di Settings",
764
- "card.rulesHint": "Le regole che il gate sta caricando adesso (dal campo di configurazione `rules` della voce di questo plugin; senza configurazione ricade sul file di regole). Sola lettura — qui non si può cambiare nulla. Per cambiare le regole usa la whitelist sotto o la pagina delle impostazioni.",
765
- "card.rulesRefresh": "Aggiorna",
766
- "card.rulesLoading": "Lettura…",
767
- "card.rulesPath": "Sorgente",
768
- "card.rulesNone": "nessuna regola configurata",
769
- "card.rulesCounts": "Regole",
770
- "card.rulesDefault": "defaultAction",
771
- "card.rulesStats": "%b byte · %l righe",
772
- "card.rulesMissing": "Regole non trovate. Il gate ricade sui suoi valori predefiniti incorporati.",
773
- "card.rulesTruncated": "Regole grandi; mostro solo i primi 512 KB.",
774
- "card.rulesError": "Caricamento non riuscito: ",
775
- "card.rulesStale": "rotta assente (plugin non riavviato?)",
776
- "card.rulesNote": "Sola lettura: questo pannello non può modificare le regole. La sezione whitelist e «consenti sempre» riscrivono nella configurazione della voce di questo plugin."
777
- };
778
- /** Russian dictionary (keys mirror zh). */
779
- const ru = {
780
- "card.title": "自动审查 (Автопроверка)",
781
- "section.title": "Шлюз автопроверки",
782
- "card.description": "Отдельный уровень одобрения наряду с read-only / full-access / whitelist, в выборе прав сессии показывается как 自动审查. Фронтенд показывает один переключатель; четыре серверные стратегии одобрения комбинируются на этой панели и по-прежнему fail-closed против жёсткого отказа P0.",
783
- "card.permissive": "Включить уровень 自动审查",
784
- "card.permissiveHint": "При выключении шлюз ведёт себя ровно как раньше.",
785
- "card.scopeNote": "Область пресета: шлюз действует, только когда пресет прав сессии — %scope. При любом другом пресете весь шлюз стоит (включая жёсткий отказ P0), и действует политика выбранного пресета. Остановка записывает одно событие и оставляет постоянную полосу над полем ввода.",
786
- "card.strategies": "Серверные стратегии одобрения (хотя бы одна действует; сочетаются)",
787
- "card.strategy.trustAutoAllow": "trustAutoAllow — безопасные операции в области пропускаются автоматически; опасные/неизвестные спрашивают (базовый средний уровень)",
788
- "card.strategy.alwaysConfirm": "alwaysConfirm — каждый проход спрашивает; у «элементов разрешения» панели одобрения появляются две расширенные кнопки: «повторно разрешать этот тип в этой сессии» (сессионная ограниченная выдача) и «разрешать каждое вхождение» (слово команды переносится на постоянной основе в белый список rulesFile)",
789
- "card.strategy.llmAssist": "llmAssist — пользовательская LLM оценивает риск: safe пропускается автоматически; жёсткие риски deletion/credential/remote/system/bulk остаются на подтверждении человеком (классификатор никогда не отказывает — только эскалирует); neutral уходит в обучение по подтверждениям; сбой/тайм-аут возвращается человеку",
790
- "card.strategy.trustEscalation": "trustEscalation — расширение песочницы без запроса: для вызова, который шлюз уже пропустил, расширение песочницы, запрашиваемое инструментом shell / pwsh / edit изнутри, одобряется здесь, а не вопросом вам",
791
- "card.strategy.trustEscalationHint": "Запрос расширения песочницы возникает внутри тела инструмента, после tools/pre-execute, поэтому шлюз его не видел — вызов, оценённый LLM как safe и пропущенный автоматически, всё равно спрашивал у вас одобрения расширения. При включении запрос пропускает только тот точный вызов, который шлюз пропустил (точное совпадение по callId); всё остальное идёт человеку как раньше — как и нераспознанная причина или цель, отличная от workspace-write / danger-full-access. Выключите, чтобы оставить расширение под контролем человека.",
792
- "card.readonly": "Только чтение",
793
- "card.unavailable": "Пространство имён настроек недоступно: убедитесь, что dsh-perm-gate собран в этот профиль.",
794
- "card.llmReceiver": "Принимающая LLM для llmAssist (совместимая с OpenAI; любой свой API)",
795
- "card.llmEndpoint": "Endpoint (BaseURL, напр. https://api.openai.com/v1)",
796
- "card.llmModel": "Model (id модели, напр. deepseek-chat)",
797
- "card.llmKey": "API-ключ (секрет)",
798
- "card.llmKeyHidden": "Не задан · после первого ввода отображается как *",
799
- "card.llmKeyMasked": "Задан · введите новое значение, чтобы перезаписать",
800
- "card.llmKeyOverwrite": "Секрет сохранён; введите новое значение и уберите фокус, чтобы перезаписать.",
801
- "card.llmKeyClear": "Очистить",
802
- "card.allowlist": "Белый список (allow, синхронизируется с rulesFile)",
803
- "card.allowlistHint": "Шаблоны из секции allow пропускают напрямую; добавляйте и удаляйте записи по одной — запись идёт в rulesFile и перезагружается на лету.",
804
- "card.allowlistPresetNote": "Эквивалент предустановленного белого списка — стратегия trustAutoAllow (безопасные операции в области пропускаются автоматически), включается в блоке стратегий выше; этот список отражает секцию allow вашего rulesFile и поначалу пуст — это нормально.",
805
- "card.allowlistEmpty": "Нет правил белого списка",
806
- "card.allowlistPlaceholder": "Новый шаблон команды, напр. npm test*",
807
- "card.allowlistAdd": "Добавить",
808
- "card.allowlistRemove": "Удалить этот шаблон",
809
- "card.allowlistBulk": "Массовое редактирование",
810
- "card.allowlistBulkHide": "Свернуть массовое редактирование",
811
- "card.allowlistSave": "Сохранить белый список",
812
- "card.riskLearning": "riskLearning — обучение вердиктам: запросы риска neutral, подтверждённые и исполненные человеком, считаются; та же операция при достижении порога с совпадающим отпечатком пропускается автоматически",
813
- "card.riskLearningHint": "Состояние обучения принадлежит плагину ($DSH_HOME/perm-gate/learning.json) и никогда не пишется в ваши YAML-правила; другая цель никогда не переиспользует разрешение. По умолчанию выключено.",
814
- "card.riskThreshold": "Подтверждений человека до автопропуска (1–10)",
815
- "notice.label": "Активность шлюза прав",
816
- "history.label": "Одобрения",
817
- "history.title": "Журнал одобрений прав",
818
- "history.subtitle": "Каждое решение шлюза в этом разговоре, новые сверху",
819
- "history.loading": "Загрузка…",
820
- "history.empty": "В этой сессии пока нет одобрений",
821
- "history.error": "Не удалось загрузить: ",
822
- "history.tag.auto": "Автопропуск",
823
- "history.tag.ask": "Спрошено",
824
- "history.tag.deny": "Отказано",
825
- "card.denylistRestore": "Вернуть предустановленный чёрный список",
826
- "card.denylist": "Чёрный список (ключевые слова deny, предустановка плагина)",
827
- "card.denylistHint": "Вызов, чей текст содержит ключевое слово (подстрока без учёта регистра), отклоняется сразу — до белого списка / выдач / LLM. Предустановка унаследована от dsh-approval-gate и активна с установки; правьте свободно — пустой или очищенный список возвращается к предустановке (есть восстановление в один клик), поэтому чёрный список никогда не выключается молча.",
828
- "card.denylistEmpty": "Чёрный список пуст (ни один вызов не отклоняется слоем ключевых слов)",
829
- "card.denylistPlaceholder": "Новое ключевое слово, напр. drop database",
830
- "card.denylistAdd": "Добавить",
831
- "card.denylistRemove": "Удалить это ключевое слово",
832
- "card.denylistTagPreset": "Предустановка",
833
- "card.denylistTagCustom": "Своё",
834
- "card.riskSediment": "riskSediment — осаждение обучения: образцы, достигшие порога, становятся детерминированными автопропусками",
835
- "card.riskSedimentHint": "При включении, когда ключ tool|category достигает порога, отпечатки его образцов оседают в правила пропуска — точное совпадение тот же инструмент/та же цель пропускает без нового вызова LLM (работает и при выключенном llmAssist).",
836
- "card.sediment": "Осадок обучения (детерминированные правила пропуска)",
837
- "card.sedimentHint": "Правила, осевшие из человеческих подтверждений: пропускает только точное совпадение отпечатка, другая цель — никогда. Можно завершить одно обучение или удалить один образец.",
838
- "card.sedimentEmpty": "Осевших правил пока нет (образцы, достигшие порога, появятся здесь)",
839
- "card.sedimentCount": "подтверждено %n/%t",
840
- "card.sedimentStop": "Завершить",
841
- "card.sedimentStopTitle": "Завершить это обучение (счётчик и образцы будут удалены)",
842
- "card.sedimentStopConfirm": "Завершить обучение для «%k»? Счётчик и все образцы будут удалены.",
843
- "card.sedimentSampleRemove": "Удалить этот осевший образец",
844
- "card.llmSource": "Источник принимающей LLM",
845
- "card.llmSourceCustom": "Свой API (совместимый с OpenAI)",
846
- "card.llmSourceHost": "Использовать текущую модель по умолчанию сессии (сервис llm DSH)",
847
- "card.llmSourceHostHint": "По умолчанию оценка одобрения идёт на группе моделей, которую сессия DSH использует сейчас (действующее значение показано ниже); оставьте Provider / Model пустыми, чтобы следовать за сессией, или выберите из списка либо введите своё — настроенные в DSH свои группы (напр. local-35b) появляются автоматически.",
848
- "card.llmProvider": "Provider (пусто = следовать выбору сессии)",
849
- "card.llmProviderPlaceholder": "Следовать выбору сессии",
850
- "card.llmModelHostPlaceholder": "Пусто = следовать выбору сессии",
851
- "card.llmResolved": "Действует сейчас: %p / %m",
852
- "card.llmPreset": "Предустановки endpoint (заполняют поля; можно править)",
853
- "card.llmPresetChoose": "Выбрать предустановку…",
854
- "card.llmPresetPublic": "Предустановки публичных API",
855
- "card.llmPresetHost": "Группы моделей DSH (выбор переключает на сессионный приёмник)",
856
- "card.networkEnabled": "Перехват сети (networkEnabled)",
857
- "card.networkEnabledHint": "При включении запускается локальный прокси, перехватывающий исходящие соединения подпроцессов и применяющий правила allow/deny. Выключено — поведение не меняется.",
858
- "card.watch": "Горячая перезагрузка правил (watch)",
859
- "card.watchHint": "При включении файлы правил отслеживаются автоматически, изменения применяются мгновенно, без ручной перезагрузки.",
860
- "card.networkRebindNote": "Вступает в силу сразу — прокси перепривязывается сам, перезагрузка плагина не нужна.",
861
- "card.networkInjectEnv": "Ввод переменных окружения прокси (networkInjectEnv)",
862
- "card.networkInjectEnvHint": "Записывает подпроцессам HTTP(S)_PROXY / ALL_PROXY (и очищает NO_PROXY). Выключено — слушатель работает, но окружение процессов не меняется.",
863
- "card.healthStale": "Маршрут хоста недоступен (404) — node-половина всё ещё старой сборки; полностью перезапустите dsh (а не только страницу браузера) и повторите.",
864
- "card.healthTest": "Проверка здоровья",
865
- "card.healthRunning": "Проверка…",
866
- "card.healthOk": "OK (%ms ms)",
867
- "card.healthFail": "Не удалось: ",
868
- "history.tag.learned": "Выучено",
869
- "history.tag.manualApproved": "Одобрено",
870
- "history.tag.manualRejected": "Отклонено",
871
- "history.tag.manualCancelled": "Отменено",
872
- "history.tag.standDown": "Шлюз отключён",
873
- "history.learnedProgress": "выучено %n/%t",
874
- "history.snapshots": "diff-снимки",
875
- "history.clearSession": "Очистить эту сессию",
876
- "history.clearAll": "Очистить всё",
877
- "history.clearSessionTitle": "Очистить diff-снимки только этой сессии (другие сессии не затронуты)",
878
- "history.clearAllTitle": "Очистить diff-снимки всех сессий, включая непросмотренные — потребуется повторное подтверждение",
879
- "history.clearSessionConfirm": "Очистить diff-снимки этой сессии? Удаляются только данные сравнения, порождённые одобрениями этой сессии; сами записи одобрений остаются.",
880
- "history.clearAllConfirm": "Очистить diff-снимки ВСЕХ сессий? Это может удалить сравнения, которые другие сессии ещё не просмотрели, и отменить это нельзя.",
881
- "history.diffTitle": "Сравнение изменений файлов",
882
- "history.diffClose": "Закрыть",
883
- "history.diffLoading": "Загрузка…",
884
- "history.diffEmpty": "Изменений содержимого нет (или файл не читается)",
885
- "history.diffLoadFail": "Не удалось загрузить diff: ",
886
- "history.diffRevert": "Отменить это изменение",
887
- "history.diffReverting": "Отправка…",
888
- "history.diffRevertDone": "Инструкция отмены отправлена",
889
- "history.diffRevertSent": "Инструкция отмены отправлена в разговор; ИИ восстановит файл по инструкции",
890
- "history.diffRevertFail": "Не удалось отправить: ",
891
- "history.diffNoSnapshot": "У этого события нет снимка для этого файла",
892
- "history.diffChipTitle": "Посмотреть сравнение изменений этого файла",
893
- "history.diffStats": "+%a / -%r строк изменено · %c без изменений",
894
- "history.diffGone": " (файла больше нет)",
895
- "history.diffHidden": "%n неизменённых строк",
896
- "card.dryRun": "Тест правил",
897
- "card.dryRunHint": "Оценивает один вызов по действующим правилам. Инструмент не запускает и правил не пишет — проверьте правило здесь, прежде чем менять.",
898
- "card.dryRunTool": "Инструмент",
899
- "card.dryRunCommand": "Команда / аргументы",
900
- "card.dryRunCommandPlaceholder": "git push --force origin main",
901
- "card.dryRunTest": "Тест",
902
- "card.dryRunRunning": "Оценка…",
903
- "card.dryRunEmpty": "Пока ничего не тестировалось. Введите инструмент и команду и нажмите «Тест», чтобы увидеть вердикт.",
904
- "card.dryRunVerdict": "Вердикт",
905
- "card.dryRunAllow": "пропустить",
906
- "card.dryRunAsk": "спросить",
907
- "card.dryRunDeny": "отказать",
908
- "card.dryRunRule": "Сработавшее правило",
909
- "card.dryRunRuleNone": "правило не сработало; defaultAction: %d",
910
- "card.dryRunDimensions": "Измерения",
911
- "card.dryRunReason": "Причина",
912
- "card.dryRunNote": "«Сработавшее правило» приходит из слоя правил. Если вердикт вынесен более ранним жёстким отказом или чёрным списком ключевых слов, номера правила нет — перечисленные измерения тогда носят справочный характер.",
913
- "card.dryRunNoRules": "(загружено 0 правил — проверьте путь rulesFile)",
914
- "card.dryRunFail": "Тест не удался: ",
915
- "card.dryRunStale": "маршрута нет (плагин не перезапущен?)",
916
- "card.rules": "Встроенные правила Settings",
917
- "card.rulesHint": "Правила, которые шлюз загружает прямо сейчас (из поля конфигурации `rules` этого плагина; без настройки берётся файл правил). Только чтение — здесь ничего не изменить. Меняйте правила через белый список ниже или на странице настроек.",
918
- "card.rulesRefresh": "Обновить",
919
- "card.rulesLoading": "Чтение…",
920
- "card.rulesPath": "Источник",
921
- "card.rulesNone": "правила не настроены",
922
- "card.rulesCounts": "Правил",
923
- "card.rulesDefault": "defaultAction",
924
- "card.rulesStats": "%b байт · %l строк",
925
- "card.rulesMissing": "Правила не найдены. Шлюз вернётся к встроенным значениям по умолчанию.",
926
- "card.rulesTruncated": "Правила велики; показаны первые 512 КБ.",
927
- "card.rulesError": "Не удалось загрузить: ",
928
- "card.rulesStale": "маршрута нет (плагин не перезапущен?)",
929
- "card.rulesNote": "Только чтение: эта панель не может менять правила. Секция белого списка и «всегда разрешать» записываются обратно в конфигурацию входа этого плагина."
930
- };
931
- /** Spanish dictionary (keys mirror zh). */
932
- const es = {
933
- "card.title": "自动审查 (Revisión automática)",
934
- "section.title": "Compuerta de revisión automática",
935
- "card.description": "Nivel de aprobación independiente, paralelo a read-only / full-access / whitelist, mostrado como 自动审查 en el selector de permisos de sesión. El frontal expone un único interruptor; las cuatro estrategias de aprobación del backend son combinables desde este panel y siguen siendo fail-closed frente al rechazo duro P0.",
936
- "card.permissive": "Activar el nivel 自动审查",
937
- "card.permissiveHint": "Apagada, la compuerta se comporta exactamente igual que antes.",
938
- "card.scopeNote": "Ámbito del preajuste: esta compuerta solo actúa cuando el preajuste de permisos de sesión es %scope. Con cualquier otro preajuste toda la compuerta se retira — rechazo duro P0 incluido — y toma el mando la política del preajuste elegido. Un retiro registra un evento y deja una franja permanente sobre el campo de entrada.",
939
- "card.strategies": "Estrategias de aprobación del backend (al menos una vigente; combinables)",
940
- "card.strategy.trustAutoAllow": "trustAutoAllow — las operaciones seguras dentro del ámbito se permiten automáticamente; las peligrosas/desconocidas preguntan (línea base del nivel intermedio)",
941
- "card.strategy.alwaysConfirm": "alwaysConfirm — cada paso pregunta; los «controles de permiso» del panel de aprobación ganan dos botones ampliados: «volver a permitir este tipo en esta sesión» (concesión limitada a la sesión) y «permitir cada aparición» (la palabra del comando se persiste en la whitelist del rulesFile)",
942
- "card.strategy.llmAssist": "llmAssist — un LLM personalizado gradúa el riesgo: safe se permite automáticamente; los riesgos duros deletion/credential/remote/system/bulk mantienen la confirmación humana (el clasificador nunca niega — solo escala); neutral entra en aprendizaje por confirmación; fallo/timeout vuelve al humano",
943
- "card.strategy.trustEscalation": "trustEscalation — ampliación de sandbox sin prompt: para una llamada que esta compuerta ya permitió, la ampliación de sandbox que una herramienta shell / pwsh / edit pide desde su interior se aprueba aquí en lugar de preguntarte",
944
- "card.strategy.trustEscalationHint": "Una petición de ampliación de sandbox se emite desde dentro del cuerpo de la herramienta, tras tools/pre-execute, así que la compuerta nunca la vio — una llamada que el LLM gradúo como safe y se permitió automáticamente igual te pedía aprobar la ampliación. Activada, solo la llamada exacta que la compuerta permitió (coincidencia exacta por callId) se salta el prompt; todo lo demás pasa por el humano como antes, igual que una razón no reconocida o un destino distinto de workspace-write / danger-full-access. Desactívala para dejar la ampliación bajo control humano.",
945
- "card.readonly": "Solo lectura",
946
- "card.unavailable": "Namespace de ajustes no disponible: asegúrate de que dsh-perm-gate está ensamblado en este perfil.",
947
- "card.llmReceiver": "LLM receptor de llmAssist (compatible con OpenAI; cualquier API propia)",
948
- "card.llmEndpoint": "Endpoint (BaseURL, p. ej. https://api.openai.com/v1)",
949
- "card.llmModel": "Model (id del modelo, p. ej. deepseek-chat)",
950
- "card.llmKey": "API Key (secreta)",
951
- "card.llmKeyHidden": "Sin definir · se muestra como * tras la primera entrada",
952
- "card.llmKeyMasked": "Definida · escribe un valor nuevo para sobrescribirla",
953
- "card.llmKeyOverwrite": "Hay un secreto guardado; escribe un valor nuevo y pierde el foco para sobrescribirlo.",
954
- "card.llmKeyClear": "Borrar",
955
- "card.allowlist": "Whitelist (allow, sincronizada con rulesFile)",
956
- "card.allowlistHint": "Los patrones de la sección allow permiten directamente; añade o elimina entradas una a una, se escriben en el rulesFile y se recargan en vivo.",
957
- "card.allowlistPresetNote": "El equivalente de la whitelist preajustada es la estrategia trustAutoAllow (operaciones seguras del ámbito se permiten automáticamente), activable en la sección de estrategias de arriba; esta lista refleja la sección allow de tu rulesFile y es normal que empiece vacía.",
958
- "card.allowlistEmpty": "Sin reglas de whitelist",
959
- "card.allowlistPlaceholder": "Nuevo patrón de comando, p. ej. npm test*",
960
- "card.allowlistAdd": "Añadir",
961
- "card.allowlistRemove": "Eliminar este patrón",
962
- "card.allowlistBulk": "Edición en bloque",
963
- "card.allowlistBulkHide": "Ocultar la edición en bloque",
964
- "card.allowlistSave": "Guardar la whitelist",
965
- "card.riskLearning": "riskLearning — aprendizaje de veredictos: las peticiones de riesgo neutral aprobadas y ejecutadas por el humano cuentan; la misma operación se auto-permite al umbral con huella coincidente",
966
- "card.riskLearningHint": "El estado de aprendizaje son datos del plugin ($DSH_HOME/perm-gate/learning.json) y nunca se escribe en tus reglas YAML; un destino distinto nunca reutiliza una autoridad. Desactivado por defecto.",
967
- "card.riskThreshold": "Confirmaciones humanas necesarias antes del permiso automático (1–10)",
968
- "notice.label": "Actividad de la compuerta de permisos",
969
- "history.label": "Aprobaciones",
970
- "history.title": "Registros de aprobación de permisos",
971
- "history.subtitle": "Cada decisión de la compuerta en esta conversación, la más reciente primero",
972
- "history.loading": "Cargando…",
973
- "history.empty": "Aún no hay aprobaciones en esta sesión",
974
- "history.error": "Error al cargar: ",
975
- "history.tag.auto": "Auto-permitido",
976
- "history.tag.ask": "Preguntado",
977
- "history.tag.deny": "Denegado",
978
- "card.denylistRestore": "Restaurar la blacklist preajustada",
979
- "card.denylist": "Blacklist (palabras clave deny, preajuste del plugin)",
980
- "card.denylistHint": "Una llamada cuyo texto contiene una palabra clave (subcadena sin distinguir mayúsculas) se deniega de plano, antes de whitelist / concesiones / LLM. La lista preajustada se hereda de dsh-approval-gate y está activa desde la instalación; edítala con libertad — vacía o vaciada vuelve al preajuste (restauración de un clic disponible), así la blacklist nunca se apaga en silencio.",
981
- "card.denylistEmpty": "Blacklist vacía (ninguna llamada es vetada por la capa de palabras clave)",
982
- "card.denylistPlaceholder": "Nueva palabra clave, p. ej. drop database",
983
- "card.denylistAdd": "Añadir",
984
- "card.denylistRemove": "Eliminar esta palabra clave",
985
- "card.denylistTagPreset": "Preajuste",
986
- "card.denylistTagCustom": "Personalizado",
987
- "card.riskSediment": "riskSediment — sedimentación del aprendizaje: las muestras que llegan al umbral se vuelven permisos deterministas",
988
- "card.riskSedimentHint": "Activada, cuando una clave tool|category alcanza el umbral las huellas de sus muestras se sedimentan en reglas de permiso — una coincidencia exacta misma herramienta/mismo destino permite sin otra llamada al LLM (sigue funcionando con llmAssist apagado).",
989
- "card.sediment": "Sedimento de aprendizaje (reglas de permiso deterministas)",
990
- "card.sedimentHint": "Reglas sedimentadas de confirmaciones humanas: solo una coincidencia exacta de huella permite, jamás un destino distinto. Puedes terminar un aprendizaje o eliminar una muestra.",
991
- "card.sedimentEmpty": "Aún no hay reglas sedimentadas (las muestras que llegan al umbral aparecen aquí)",
992
- "card.sedimentCount": "confirmadas %n/%t",
993
- "card.sedimentStop": "Terminar",
994
- "card.sedimentStopTitle": "Terminar este aprendizaje (borra el conteo y las muestras)",
995
- "card.sedimentStopConfirm": "¿Terminar el aprendizaje de «%k»? Se borrarán su conteo y todas sus muestras.",
996
- "card.sedimentSampleRemove": "Eliminar esta muestra sedimentada",
997
- "card.llmSource": "Fuente del receptor",
998
- "card.llmSourceCustom": "API propia (compatible con OpenAI)",
999
- "card.llmSourceHost": "Usar el modelo por defecto actual de la sesión (servicio llm de DSH)",
1000
- "card.llmSourceHostHint": "Por defecto la graduación de aprobación corre sobre el grupo de modelos que la sesión DSH usa ahora (el valor efectivo se muestra abajo); deja Provider / Model vacíos para seguir la sesión, o elige del desplegable o escribe para fijar uno — tus grupos personalizados configurados en DSH (p. ej. local-35b) aparecen automáticamente.",
1001
- "card.llmProvider": "Provider (vacío = seguir la selección de la sesión)",
1002
- "card.llmProviderPlaceholder": "Seguir la selección de la sesión",
1003
- "card.llmModelHostPlaceholder": "Vacío = seguir la selección de la sesión",
1004
- "card.llmResolved": "Vigente ahora: %p / %m",
1005
- "card.llmPreset": "Preajustes de endpoint (rellena los campos; sigue siendo editable)",
1006
- "card.llmPresetChoose": "Elegir un preajuste…",
1007
- "card.llmPresetPublic": "Preajustes de API públicas",
1008
- "card.llmPresetHost": "Grupos de modelos DSH (seleccionar uno cambia al receptor de sesión)",
1009
- "card.networkEnabled": "Intercepción de red (networkEnabled)",
1010
- "card.networkEnabledHint": "Activada, arranca un proxy local que intercepta las conexiones salientes de los subprocesos y aplica reglas allow/deny. Apagada = cero cambio de comportamiento.",
1011
- "card.watch": "Recarga en caliente de reglas (watch)",
1012
- "card.watchHint": "Activada, vigila automáticamente los archivos de reglas y aplica los cambios al instante, sin recarga manual.",
1013
- "card.networkRebindNote": "Surte efecto al instante — el proxy se reconecta solo, sin recargar el plugin.",
1014
- "card.networkInjectEnv": "Inyectar variables de entorno del proxy (networkInjectEnv)",
1015
- "card.networkInjectEnvHint": "Escribe HTTP(S)_PROXY / ALL_PROXY para los subprocesos (y limpia NO_PROXY). Apagada = el listener corre pero no cambia ningún entorno de proceso.",
1016
- "card.healthStale": "Ruta del host no disponible (404) — la mitad node sigue siendo una build antigua; reinicia dsh por completo (no solo la página del navegador) y reintenta.",
1017
- "card.healthTest": "Prueba de salud",
1018
- "card.healthRunning": "Probando…",
1019
- "card.healthOk": "OK (%ms ms)",
1020
- "card.healthFail": "Falló: ",
1021
- "history.tag.learned": "Aprendido",
1022
- "history.tag.manualApproved": "Aprobado",
1023
- "history.tag.manualRejected": "Rechazado",
1024
- "history.tag.manualCancelled": "Cancelado",
1025
- "history.tag.standDown": "Compuerta desactivada",
1026
- "history.learnedProgress": "aprendido %n/%t",
1027
- "history.snapshots": "instantáneas diff",
1028
- "history.clearSession": "Vaciar esta sesión",
1029
- "history.clearAll": "Vaciar todo",
1030
- "history.clearSessionTitle": "Vaciar solo las instantáneas diff de esta sesión (las demás sesiones quedan intactas)",
1031
- "history.clearAllTitle": "Vaciar las instantáneas diff de todas las sesiones, incluidas las nunca revisadas — pide una segunda confirmación",
1032
- "history.clearSessionConfirm": "¿Vaciar las instantáneas diff de esta sesión? Solo se eliminan los datos de comparación producidos por las aprobaciones de esta sesión; los registros de aprobación quedan.",
1033
- "history.clearAllConfirm": "¿Vaciar las instantáneas diff de TODAS las sesiones? Esto puede borrar comparaciones que otras sesiones aún no han visto, y no se puede deshacer.",
1034
- "history.diffTitle": "Comparación de cambios de archivos",
1035
- "history.diffClose": "Cerrar",
1036
- "history.diffLoading": "Cargando…",
1037
- "history.diffEmpty": "Sin cambios de contenido (o el archivo no se puede leer)",
1038
- "history.diffLoadFail": "Error al cargar el diff: ",
1039
- "history.diffRevert": "Deshacer este cambio",
1040
- "history.diffReverting": "Enviando…",
1041
- "history.diffRevertDone": "Instrucción de reversión enviada",
1042
- "history.diffRevertSent": "Instrucción de reversión enviada a la conversación; la IA restaurará el archivo según la instrucción",
1043
- "history.diffRevertFail": "Error al enviar: ",
1044
- "history.diffNoSnapshot": "Este evento no tiene instantánea para ese archivo",
1045
- "history.diffChipTitle": "Ver la comparación de cambios de este archivo",
1046
- "history.diffStats": "+%a / -%r líneas cambiadas · %c sin cambios",
1047
- "history.diffGone": " (el archivo ya no existe)",
1048
- "history.diffHidden": "%n líneas sin modificar",
1049
- "card.dryRun": "Prueba de reglas",
1050
- "card.dryRunHint": "Juzga una llamada contra las reglas vigentes. No ejecuta ninguna herramienta ni escribe ninguna regla — comprueba una regla aquí antes de cambiarla.",
1051
- "card.dryRunTool": "Herramienta",
1052
- "card.dryRunCommand": "Comando / argumentos",
1053
- "card.dryRunCommandPlaceholder": "git push --force origin main",
1054
- "card.dryRunTest": "Probar",
1055
- "card.dryRunRunning": "Juzgando…",
1056
- "card.dryRunEmpty": "Nada probado todavía. Rellena herramienta y comando y pulsa Probar para ver cómo se juzgaría.",
1057
- "card.dryRunVerdict": "Veredicto",
1058
- "card.dryRunAllow": "permitir",
1059
- "card.dryRunAsk": "preguntar",
1060
- "card.dryRunDeny": "denegar",
1061
- "card.dryRunRule": "Regla coincidente",
1062
- "card.dryRunRuleNone": "ninguna regla coincidió; defaultAction: %d",
1063
- "card.dryRunDimensions": "Dimensiones",
1064
- "card.dryRunReason": "Motivo",
1065
- "card.dryRunNote": "«Regla coincidente» viene de la capa de reglas. Cuando el veredicto lo produce un rechazo duro anterior o la blacklist de palabras clave no hay índice de regla — las dimensiones listadas son entonces solo orientativas.",
1066
- "card.dryRunNoRules": "(0 reglas cargadas — revisa la ruta del rulesFile)",
1067
- "card.dryRunFail": "La prueba falló: ",
1068
- "card.dryRunStale": "ruta inexistente (¿plugin sin reiniciar?)",
1069
- "card.rules": "Reglas integradas de Settings",
1070
- "card.rulesHint": "Las reglas que la compuerta carga ahora mismo (del campo de configuración `rules` de la entrada de este plugin; sin configurar recurre al archivo de reglas). Solo lectura — aquí no se puede cambiar nada. Para cambiar reglas usa la whitelist de abajo o la página de ajustes.",
1071
- "card.rulesRefresh": "Actualizar",
1072
- "card.rulesLoading": "Leyendo…",
1073
- "card.rulesPath": "Origen",
1074
- "card.rulesNone": "sin reglas configuradas",
1075
- "card.rulesCounts": "Reglas",
1076
- "card.rulesDefault": "defaultAction",
1077
- "card.rulesStats": "%b bytes · %l líneas",
1078
- "card.rulesMissing": "Reglas no encontradas. La compuerta vuelve a sus valores integrados por defecto.",
1079
- "card.rulesTruncated": "Reglas grandes; se muestran solo los primeros 512 KB.",
1080
- "card.rulesError": "Error al cargar: ",
1081
- "card.rulesStale": "ruta inexistente (¿plugin sin reiniciar?)",
1082
- "card.rulesNote": "Solo lectura: este panel no puede modificar las reglas. La sección whitelist y «permitir siempre» se escriben de vuelta en la configuración de entrada de este plugin."
13
+ /**
14
+ * Combined dictionary map for all 9 active languages, registered in ONE
15
+ * `locale.register(NS, dictionaries)` call. The map form is deliberately left
16
+ * with its inferred literal type instead of a loose
17
+ * `Record<string, Record<string, string>>` annotation: `dsh-perm-gate` is a
18
+ * declared namespace in `LocaleNamespaceMap`, so the locale service resolves
19
+ * the TYPED `register(ns, dicts)` overload, which requires the built-in
20
+ * `zh`/`en` keys to be statically present (an index signature would not
21
+ * guarantee them and fails the assignment). The runtime still iterates every
22
+ * entry of the map and accepts any BCP 47-style locale id, so the seven
23
+ * extension languages activate through the very same call.
24
+ */
25
+ const dictionaries = {
26
+ zh: {
27
+ "card.title": "自动审查",
28
+ "section.title": "自动审查门",
29
+ "card.description": "独立审批档位:与只读 / 完全权限 / 白名单平行,在会话权限下拉中显示为「自动审查」。前端只暴露一个开关;后台四个审批策略可组合、由此处设置决定,仍对 P0 硬拒绝保持 fail-closed。",
30
+ "card.permissive": "启用自动审查档位",
31
+ "card.permissiveHint": "关闭时门禁行为与此前完全一致。",
32
+ "card.scopeNote": "档位作用域:本门禁仅在会话权限档位为 %scope 时生效。其他档位下整个门禁停用(含 P0 硬拒绝),由所选档位自身的策略接管;停用时会写入一条 stand-down 事件,并在输入框上方常驻一条提示条。",
33
+ "card.strategies": "后台审批策略(至少一个生效;可组合)",
34
+ "card.strategy.trustAutoAllow": "trustAutoAllow — 作用域内安全操作自动放行,危险/未知转 ask(中间档基线)",
35
+ "card.strategy.alwaysConfirm": "alwaysConfirm — 一律逐次 ask;审批面板「允许控件」含两个扩展按钮:「本会话重复允许该类」(会话语限次 grant)与「允许所有类型」(命令词持久迁入 rulesFile 白名单)",
36
+ "card.strategy.llmAssist": "llmAssist — 自定义 LLM 按风险分级裁决:safe 自动放行;deletion/credential/remote/system/bulk 硬风险维持人工确认(分类器永不拒绝,只负责升级判断);neutral 进入确认学习;失败/超时回退人工",
37
+ "card.strategy.trustEscalation": "trustEscalation — 沙箱升级免确认:本门禁已放行的调用,其工具内部发起的沙箱提权(shell / pwsh / edit 的 sandbox_permissions)由门禁直接批准,不再弹窗",
38
+ "card.strategy.trustEscalationHint": "沙箱提权审批发生在 tools/pre-execute 之后、工具体内部,门禁原本看不到它,因此「LLM 判安全已自动放行」的调用仍会弹窗要求你批准提权。开启后,仅对门禁确实放行过的那一次调用(按 callId 精确匹配)免确认,其余一律照旧走人工;目标模式非 workspace-write / danger-full-access 或理由无法识别时同样回退人工。关闭后提权保持人工把关。",
39
+ "card.readonly": "只读",
40
+ "card.unavailable": "设置命名空间不可用:请确认 dsh-perm-gate 已装配进 profile。",
41
+ "card.llmReceiver": "llmAssist 接收 LLM(OpenAI 兼容,可用自定义 API)",
42
+ "card.llmEndpoint": "Endpoint(BaseURL,如 https://api.openai.com/v1)",
43
+ "card.llmModel": "Model(模型 id,如 deepseek-chat)",
44
+ "card.llmKey": "API Key(保密)",
45
+ "card.llmKeyHidden": "未设置 · 首次输入后以 * 显示",
46
+ "card.llmKeyMasked": "已设置 · 输入新值覆盖",
47
+ "card.llmKeyOverwrite": "已设置保密值,输入新值并失焦即可覆盖。",
48
+ "card.llmKeyClear": "清除",
49
+ "card.allowlist": "白名单(allow,rulesFile 同步)",
50
+ "card.allowlistHint": "命中 allow 段的命令模式直接放行;逐条添加或删除,实时写入 rulesFile 并重载。",
51
+ "card.allowlistPresetNote": "预置白名单的等价物是 trustAutoAllow 策略(工作区内安全操作自动放行),在上方策略区开启;本列表镜像你的 rulesFile allow 段,初始为空属正常。",
52
+ "card.allowlistEmpty": "无白名单规则",
53
+ "card.allowlistPlaceholder": "新命令模式,如 npm test*",
54
+ "card.allowlistAdd": "添加",
55
+ "card.allowlistRemove": "删除该模式",
56
+ "card.allowlistBulk": "批量编辑",
57
+ "card.allowlistBulkHide": "收起批量编辑",
58
+ "card.allowlistSave": "保存白名单",
59
+ "card.riskLearning": "riskLearning — 裁决学习:neutral 风险操作经人工确认并执行后计数,满阈值且操作指纹一致时自动放行",
60
+ "card.riskLearningHint": "学习状态为插件自有数据($DSH_HOME/perm-gate/learning.json),不写入你的 YAML 规则文件;不同目标永不复用放行。默认关闭。",
61
+ "card.riskThreshold": "自动放行所需人工确认次数(1–10)",
62
+ "notice.label": "权限门禁动态",
63
+ "history.label": "审批记录",
64
+ "history.title": "权限审批记录",
65
+ "history.subtitle": "本会话中门禁的每次判定轨迹(最新在上)",
66
+ "history.loading": "加载中…",
67
+ "history.empty": "本会话暂无审批记录",
68
+ "history.error": "加载失败:",
69
+ "history.tag.auto": "自动放行",
70
+ "history.tag.ask": "转人工",
71
+ "history.tag.deny": "拒绝",
72
+ "card.denylistRestore": "恢复预置黑名单",
73
+ "card.denylist": "黑名单(deny 关键词,插件预置)",
74
+ "card.denylistHint": "命中关键词(大小写不敏感子串)的调用直接拒绝,先于白名单/授权/LLM。预置列表继承自 dsh-approval-gate,安装即生效;可增删,清空或删除后可用「恢复预置黑名单」一键还原(空列表等同预置,黑名单常开)。",
75
+ "card.denylistEmpty": "黑名单为空(所有调用都不会被关键词层拒绝)",
76
+ "card.denylistPlaceholder": "新关键词,如 drop database",
77
+ "card.denylistAdd": "添加",
78
+ "card.denylistRemove": "删除该关键词",
79
+ "card.denylistTagPreset": "预置",
80
+ "card.denylistTagCustom": "自定义",
81
+ "card.riskSediment": "riskSediment — 学习沉淀:满阈值的确认样本成为确定性放行规则",
82
+ "card.riskSedimentHint": "开启后,同一「工具|类别」确认满阈值,其样本指纹即沉淀为放行规则——同工具同目标精确命中时直接放行,不再过 LLM(关闭 llmAssist 也继续生效)。",
83
+ "card.sediment": "学习沉淀(确定性放行规则)",
84
+ "card.sedimentHint": "来自人工确认的沉淀规则:样本指纹命中才放行,不同目标永不复用。可终止单个学习或删除单条样本。",
85
+ "card.sedimentEmpty": "暂无沉淀规则(确认满阈值的样本会出现在这里)",
86
+ "card.sedimentCount": "已确认 %n/%t",
87
+ "card.sedimentStop": "终止",
88
+ "card.sedimentStopTitle": "终止该学习(删除计数与样本)",
89
+ "card.sedimentStopConfirm": "终止「%k」的学习?将删除其确认计数与全部样本。",
90
+ "card.sedimentSampleRemove": "删除该沉淀样本",
91
+ "card.llmSource": "接收 LLM 来源",
92
+ "card.llmSourceCustom": "自定义 API(OpenAI 兼容)",
93
+ "card.llmSourceHost": "使用会话当前默认模型(DSH llm 服务)",
94
+ "card.llmSourceHostHint": "默认使用 DSH 会话当前选中的模型组执行审批判定(下方显示当前生效值);Provider / Model 留空即跟随会话,也可从下拉选择或手动输入固定下来——你在 DSH 里配置的自定义组(如 local-35b)会自动出现在下拉中。",
95
+ "card.llmProvider": "Provider(留空 = 跟随会话当前选择)",
96
+ "card.llmProviderPlaceholder": "跟随会话当前选择",
97
+ "card.llmModelHostPlaceholder": "留空 = 跟随会话当前选择",
98
+ "card.llmResolved": "当前生效:%p / %m",
99
+ "card.llmPreset": "预置端点(选择后自动填充,可再编辑)",
100
+ "card.llmPresetChoose": "选择预置…",
101
+ "card.llmPresetPublic": "公共 API 预置",
102
+ "card.llmPresetHost": "DSH 模型组(选择后自动切换为会话模式)",
103
+ "card.networkEnabled": "网络拦截(networkEnabled)",
104
+ "card.networkEnabledHint": "开启后启动本地代理拦截子进程出站连接,按规则放行/阻断。关闭则零行为变更。",
105
+ "card.watch": "规则热重载(watch)",
106
+ "card.watchHint": "开启后自动监听规则文件变更并即时生效,无需手动 reload。",
107
+ "card.networkRebindNote": "切换后立即生效(自动重绑,无需重载插件)。",
108
+ "card.networkInjectEnv": "注入代理环境变量(networkInjectEnv)",
109
+ "card.networkInjectEnvHint": "开启后为子进程写入 HTTP(S)_PROXY / ALL_PROXY(并清空 NO_PROXY)。关闭则只启动监听、不改变任何进程环境。",
110
+ "card.healthStale": "宿主路由不可用(404)——node 半区仍是旧版本,请完全重启 dsh(而非仅刷新页面)后重试。",
111
+ "card.healthTest": "健康测试",
112
+ "card.healthRunning": "测试中…",
113
+ "card.healthOk": "正常(%ms ms)",
114
+ "card.healthFail": "失败:",
115
+ "history.tag.learned": "学习沉淀",
116
+ "history.tag.manualApproved": "人工通过",
117
+ "history.tag.manualRejected": "人工拒绝",
118
+ "history.tag.manualCancelled": "人工取消",
119
+ "history.tag.standDown": "门禁停用",
120
+ "history.learnedProgress": "学习 %n/%t",
121
+ "history.snapshots": "diff 快照",
122
+ "history.clearSession": "仅清本会话",
123
+ "history.clearAll": "清空全部",
124
+ "history.clearSessionTitle": "仅清除本会话的 diff 快照(不影响其他会话)",
125
+ "history.clearAllTitle": "清空全部会话(含其他会话未查看过的)diff 快照,需二次确认",
126
+ "history.clearSessionConfirm": "确定清除【本会话】的 diff 快照?仅删除本会话审批产生的改动对比数据,不影响审批记录本身。",
127
+ "history.clearAllConfirm": "确定清除【全部会话】的 diff 快照?这可能删除其他会话还没看过的改动对比记录,且不可恢复。",
128
+ "history.diffTitle": "文件改动对比",
129
+ "history.diffClose": "关闭",
130
+ "history.diffLoading": "加载中…",
131
+ "history.diffEmpty": "该文件无内容变化(或文件不可读)",
132
+ "history.diffLoadFail": "加载 diff 失败:",
133
+ "history.diffRevert": "撤销此改动",
134
+ "history.diffReverting": "发送中…",
135
+ "history.diffRevertDone": "已发送撤销指令",
136
+ "history.diffRevertSent": "撤销指令已发送到对话框,AI 将按指令恢复文件",
137
+ "history.diffRevertFail": "发送失败:",
138
+ "history.diffNoSnapshot": "该事件没有此文件的快照",
139
+ "history.diffChipTitle": "查看该文件改动对比",
140
+ "history.diffStats": "+%a / -%r 行变更 · %c 行未变",
141
+ "history.diffGone": "(文件当前已不存在)",
142
+ "history.diffHidden": "%n 行未修改",
143
+ "card.dryRun": "规则测试",
144
+ "card.dryRunHint": "用当前生效的规则判定一次调用,不执行任何工具、不写入任何规则。改规则前先在这里验一遍。",
145
+ "card.dryRunTool": "工具名",
146
+ "card.dryRunCommand": "命令 / 参数",
147
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
148
+ "card.dryRunTest": "测试",
149
+ "card.dryRunRunning": "判定中…",
150
+ "card.dryRunEmpty": "尚未测试。填入工具名与命令,点「测试」查看会怎么裁决。",
151
+ "card.dryRunVerdict": "裁决",
152
+ "card.dryRunAllow": "放行",
153
+ "card.dryRunAsk": "人工确认",
154
+ "card.dryRunDeny": "拒绝",
155
+ "card.dryRunRule": "命中规则",
156
+ "card.dryRunRuleNone": "无规则命中,走 defaultAction:%d",
157
+ "card.dryRunDimensions": "约束维度",
158
+ "card.dryRunReason": "原因",
159
+ "card.dryRunNote": "「命中规则」来自规则层。若裁决由更早的硬拒绝或关键词黑名单产生,则不会有规则序号——那种情况下列出的维度仅供参考。",
160
+ "card.dryRunNoRules": "(当前加载了 0 条规则:检查 rulesFile 路径)",
161
+ "card.dryRunFail": "测试失败:",
162
+ "card.dryRunStale": "路由不存在(插件未重启?)",
163
+ "card.rules": "Settings 内置规则",
164
+ "card.rulesHint": "门禁此刻实际加载的规则(来自本插件入口的 rules 配置字段;未配置时回退到规则文件)。只读——这里改不了任何东西。要改规则,用下面的白名单,或在设置页中编辑。",
165
+ "card.rulesRefresh": "刷新",
166
+ "card.rulesLoading": "读取中…",
167
+ "card.rulesPath": "来源",
168
+ "card.rulesNone": "未配置规则",
169
+ "card.rulesCounts": "规则",
170
+ "card.rulesDefault": "defaultAction",
171
+ "card.rulesStats": "%b 字节 · %l 行",
172
+ "card.rulesMissing": "规则不存在。门禁会退到内置默认。",
173
+ "card.rulesTruncated": "规则过大,仅显示前 512 KB。",
174
+ "card.rulesError": "加载失败:",
175
+ "card.rulesStale": "路由不存在(插件未重启?)",
176
+ "card.rulesNote": "此处只读:面板无法修改规则;白名单段与“始终允许”会写回本插件的入口配置。"
177
+ },
178
+ en: {
179
+ "card.title": "自动审查 (Auto review)",
180
+ "section.title": "Auto-Review Gate",
181
+ "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.",
182
+ "card.permissive": "Enable the 自动审查 tier",
183
+ "card.permissiveHint": "When off, the gate behaves exactly as before.",
184
+ "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.",
185
+ "card.strategies": "Backend approval strategies (at least one effective; combinable)",
186
+ "card.strategy.trustAutoAllow": "trustAutoAllow — safe in-scope ops auto-allow; dangerous/unknown ask (baseline middle tier)",
187
+ "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)",
188
+ "card.strategy.llmAssist": "llmAssist — a custom LLM grades risk: safe auto-allows; deletion/credential/remote/system/bulk hard risks keep the human ask (the classifier never denies — it only escalates); neutral enters confirm-learning; failure/timeout falls back to the human",
189
+ "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",
190
+ "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.",
191
+ "card.readonly": "Read-only",
192
+ "card.unavailable": "Settings namespace unavailable: make sure dsh-perm-gate is assembled into this profile.",
193
+ "card.llmReceiver": "llmAssist receiving LLM (OpenAI-compatible; any custom API)",
194
+ "card.llmEndpoint": "Endpoint (BaseURL, e.g. https://api.openai.com/v1)",
195
+ "card.llmModel": "Model (model id, e.g. deepseek-chat)",
196
+ "card.llmKey": "API Key (secret)",
197
+ "card.llmKeyHidden": "Unset · shown as * after first entry",
198
+ "card.llmKeyMasked": "Set · type a new value to overwrite",
199
+ "card.llmKeyOverwrite": "A secret is stored; type a new value and blur to overwrite it.",
200
+ "card.llmKeyClear": "Clear",
201
+ "card.allowlist": "Whitelist (allow, synced to rulesFile)",
202
+ "card.allowlistHint": "Patterns in the allow section allow directly; add or remove entries one by one, written to the rulesFile and reloaded live.",
203
+ "card.allowlistPresetNote": "The preset-whitelist equivalent is the trustAutoAllow strategy (safe in-scope ops auto-allow), enabled in the strategies section above; this list mirrors your rulesFile allow section and legitimately starts empty.",
204
+ "card.allowlistEmpty": "No whitelist rules",
205
+ "card.allowlistPlaceholder": "New command pattern, e.g. npm test*",
206
+ "card.allowlistAdd": "Add",
207
+ "card.allowlistRemove": "Remove this pattern",
208
+ "card.allowlistBulk": "Bulk edit",
209
+ "card.allowlistBulkHide": "Hide bulk edit",
210
+ "card.allowlistSave": "Save whitelist",
211
+ "card.riskLearning": "riskLearning — verdict learning: neutral-risk asks that the human approves and that execute count up; the same operation auto-allows at threshold with a matching fingerprint",
212
+ "card.riskLearningHint": "Learning state is plugin-owned ($DSH_HOME/perm-gate/learning.json) and never written into your YAML rules; a different target never reuses authority. Off by default.",
213
+ "card.riskThreshold": "Human confirmations required before auto-allow (1–10)",
214
+ "notice.label": "Permission gate activity",
215
+ "history.label": "Approvals",
216
+ "history.title": "Permission approval records",
217
+ "history.subtitle": "Every gate decision in this conversation, newest first",
218
+ "history.loading": "Loading…",
219
+ "history.empty": "No approval records in this session yet",
220
+ "history.error": "Failed to load: ",
221
+ "history.tag.auto": "Auto-allowed",
222
+ "history.tag.ask": "Asked",
223
+ "history.tag.deny": "Denied",
224
+ "card.denylistRestore": "Restore preset blacklist",
225
+ "card.denylist": "Blacklist (deny keywords, plugin preset)",
226
+ "card.denylistHint": "A call whose text contains a keyword (case-insensitive substring) is denied outright, before whitelist / grants / LLM. The preset list is inherited from dsh-approval-gate and active from install; edit freely — empty or cleared falls back to the preset (one-click restore available), so the blacklist never silently turns off.",
227
+ "card.denylistEmpty": "Blacklist empty (no call is vetoed by the keyword layer)",
228
+ "card.denylistPlaceholder": "New keyword, e.g. drop database",
229
+ "card.denylistAdd": "Add",
230
+ "card.denylistRemove": "Remove this keyword",
231
+ "card.denylistTagPreset": "Preset",
232
+ "card.denylistTagCustom": "Custom",
233
+ "card.riskSediment": "riskSediment — learning sedimentation: threshold-reached samples become deterministic auto-allows",
234
+ "card.riskSedimentHint": "When on, once a tool|category key reaches the threshold its sample fingerprints are sedimented into allow rules — an exact same-tool same-target hit allows outright without another LLM call (keeps working with llmAssist off).",
235
+ "card.sediment": "Learning sediment (deterministic allow rules)",
236
+ "card.sedimentHint": "Rules sedimented from human confirmations: only an exact fingerprint hit allows, never a different target. Terminate one learning or remove one sample.",
237
+ "card.sedimentEmpty": "No sedimented rules yet (samples reaching the threshold appear here)",
238
+ "card.sedimentCount": "confirmed %n/%t",
239
+ "card.sedimentStop": "Terminate",
240
+ "card.sedimentStopTitle": "Terminate this learning (drops the count and samples)",
241
+ "card.sedimentStopConfirm": "Terminate learning for '%k'? Its count and samples are dropped.",
242
+ "card.sedimentSampleRemove": "Remove this sedimented sample",
243
+ "card.llmSource": "Receiver source",
244
+ "card.llmSourceCustom": "Custom API (OpenAI-compatible)",
245
+ "card.llmSourceHost": "Use the session's current default model (DSH llm service)",
246
+ "card.llmSourceHostHint": "By default approval grading runs on the model group the DSH session currently uses (the effective value is shown below); leave Provider / Model empty to follow the session, or pick from the dropdown or type to pin one — custom groups you configured in DSH (e.g. local-35b) appear automatically.",
247
+ "card.llmProvider": "Provider (empty = follow the session selection)",
248
+ "card.llmProviderPlaceholder": "Follow the session selection",
249
+ "card.llmModelHostPlaceholder": "Empty = follow the session selection",
250
+ "card.llmResolved": "Effective now: %p / %m",
251
+ "card.llmPreset": "Endpoint presets (fills the fields; still editable)",
252
+ "card.llmPresetChoose": "Pick a preset…",
253
+ "card.llmPresetPublic": "Public API presets",
254
+ "card.llmPresetHost": "DSH model groups (selecting one switches to the session receiver)",
255
+ "card.networkEnabled": "Network interception (networkEnabled)",
256
+ "card.networkEnabledHint": "When on, starts a local proxy that intercepts outbound subprocess connections and applies allow/deny rules. Off = zero behavior change.",
257
+ "card.watch": "Rule hot-reload (watch)",
258
+ "card.watchHint": "When on, automatically watches rule files for changes and applies them instantly without manual reload.",
259
+ "card.networkRebindNote": "Takes effect immediately — the proxy rebinds automatically, no plugin reload needed.",
260
+ "card.networkInjectEnv": "Inject proxy environment (networkInjectEnv)",
261
+ "card.networkInjectEnvHint": "Writes HTTP(S)_PROXY / ALL_PROXY for subprocesses (and clears NO_PROXY). Off = the listener runs but no process environment is changed.",
262
+ "card.healthStale": "Host route unavailable (404) — the node half is still an old build; fully restart dsh (not just the browser page) and retry.",
263
+ "card.healthTest": "Health test",
264
+ "card.healthRunning": "Testing…",
265
+ "card.healthOk": "OK (%ms ms)",
266
+ "card.healthFail": "Failed: ",
267
+ "history.tag.learned": "Learned",
268
+ "history.tag.manualApproved": "Approved",
269
+ "history.tag.manualRejected": "Rejected",
270
+ "history.tag.manualCancelled": "Cancelled",
271
+ "history.tag.standDown": "Gate off",
272
+ "history.learnedProgress": "learned %n/%t",
273
+ "history.snapshots": "diff snapshots",
274
+ "history.clearSession": "Clear this session",
275
+ "history.clearAll": "Clear all",
276
+ "history.clearSessionTitle": "Clear only this session’s diff snapshots (other sessions untouched)",
277
+ "history.clearAllTitle": "Clear diff snapshots for every session, including ones never reviewed — asks again",
278
+ "history.clearSessionConfirm": "Clear this session’s diff snapshots? Only the change-comparison data produced by this session’s approvals is removed; the approval records stay.",
279
+ "history.clearAllConfirm": "Clear diff snapshots for ALL sessions? This may delete change comparisons other sessions have not reviewed yet, and cannot be undone.",
280
+ "history.diffTitle": "File change comparison",
281
+ "history.diffClose": "Close",
282
+ "history.diffLoading": "Loading…",
283
+ "history.diffEmpty": "No content change (or the file is unreadable)",
284
+ "history.diffLoadFail": "Failed to load diff: ",
285
+ "history.diffRevert": "Revert this change",
286
+ "history.diffReverting": "Sending…",
287
+ "history.diffRevertDone": "Revert instruction sent",
288
+ "history.diffRevertSent": "Revert instruction sent to the conversation; the AI will restore the file accordingly",
289
+ "history.diffRevertFail": "Send failed: ",
290
+ "history.diffNoSnapshot": "This event has no snapshot for that file",
291
+ "history.diffChipTitle": "View this file’s change comparison",
292
+ "history.diffStats": "+%a / -%r lines changed · %c unchanged",
293
+ "history.diffGone": " (file no longer exists)",
294
+ "history.diffHidden": "%n unmodified lines",
295
+ "card.dryRun": "Rule test",
296
+ "card.dryRunHint": "Judge one call against the rules in force. Runs no tool and writes no rule — check a rule here before you change it.",
297
+ "card.dryRunTool": "Tool",
298
+ "card.dryRunCommand": "Command / arguments",
299
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
300
+ "card.dryRunTest": "Test",
301
+ "card.dryRunRunning": "Judging…",
302
+ "card.dryRunEmpty": "Nothing tested yet. Fill in a tool and command, then press Test to see how it would be judged.",
303
+ "card.dryRunVerdict": "Verdict",
304
+ "card.dryRunAllow": "allow",
305
+ "card.dryRunAsk": "ask",
306
+ "card.dryRunDeny": "deny",
307
+ "card.dryRunRule": "Matched rule",
308
+ "card.dryRunRuleNone": "no rule matched; defaultAction: %d",
309
+ "card.dryRunDimensions": "Dimensions",
310
+ "card.dryRunReason": "Reason",
311
+ "card.dryRunNote": "\"Matched rule\" comes from the rule layer. When the verdict was produced by an earlier hard-deny or by the keyword blacklist there is no rule index — the dimensions listed are then informational only.",
312
+ "card.dryRunNoRules": "(0 rules loaded — check the rulesFile path)",
313
+ "card.dryRunFail": "Test failed: ",
314
+ "card.dryRunStale": "route missing (plugin not restarted?)",
315
+ "card.rules": "Settings Built-in Rules",
316
+ "card.rulesHint": "The rules the gate is loading right now (from this plugin entry's `rules` config field; falls back to the rules file when unset). Read-only — nothing here can change them. To change rules, use the allowlist below or edit them on the settings page.",
317
+ "card.rulesRefresh": "Refresh",
318
+ "card.rulesLoading": "Reading…",
319
+ "card.rulesPath": "Source",
320
+ "card.rulesNone": "no rules configured",
321
+ "card.rulesCounts": "Rules",
322
+ "card.rulesDefault": "defaultAction",
323
+ "card.rulesStats": "%b bytes · %l lines",
324
+ "card.rulesMissing": "Rules not found. The gate falls back to its built-in defaults.",
325
+ "card.rulesTruncated": "Rules are large; showing the first 512 KB only.",
326
+ "card.rulesError": "Load failed: ",
327
+ "card.rulesStale": "route missing (plugin not restarted?)",
328
+ "card.rulesNote": "Read-only: this panel cannot modify the rules. The allowlist section and \"allow always\" write back to this plugin's entry config."
329
+ },
330
+ ja: {
331
+ "card.title": "自动审查(自動審査)ティア",
332
+ "section.title": "自動審査ゲート",
333
+ "card.description": "read-only / full-access / whitelist と並ぶ独立の承認モード。セッション権限ピッカーでは「自动审查」と表示されます。フロントは単一スイッチのみ。バックエンドの 4 つの承認戦略はこのパネルで組み合わせ可能で、P0 ハード拒否に対して依然 fail-closed。",
334
+ "card.permissive": "自动审查ティアを有効化",
335
+ "card.permissiveHint": "オフのときは以前と完全に同じ動作です。",
336
+ "card.scopeNote": "プリセットスコープ: このゲートはセッションの権限プリセットが %scope のときだけ動作します。それ以外のプリセットでは P0 ハード拒否を含むゲート全体が停止し、選択されたプリセット自身のポリシーが引き継ぎます。停止時は stand-down イベントを 1 件記録し、入力欄の上に常駐表示します。",
337
+ "card.strategies": "バックエンド承認戦略(少なくとも 1 つ有効、組み合わせ可)",
338
+ "card.strategy.trustAutoAllow": "trustAutoAllow — スコープ内の安全操作は自動許可、危険/不明は ask(中間ティアのベースライン)",
339
+ "card.strategy.alwaysConfirm": "alwaysConfirm — すべて ask。承認パネルの「許可コントロール」に2つの拡張ボタン:「このセッションで当該種別を繰り返し許可」(セッション限次 grant)と「すべての発生を許可」(コマンド語を rulesFile の許可リストへ永続化)",
340
+ "card.strategy.llmAssist": "llmAssist — カスタム LLM がリスクを判定:safe は自動許可、deletion/credential/remote/system/bulk のハードリスクは人手確認を維持(分類器は決して拒否せず、判断をエスカレートするだけ)、neutral は確認学習へ、失敗/タイムアウトは人手にフォールバック",
341
+ "card.strategy.trustEscalation": "trustEscalation — サンドボックス昇格を確認なしで許可:本ゲートが既に許可した呼び出しについて、shell / pwsh / edit がツール内部から要求するサンドボックス拡張をゲートが承認し、プロンプトを出しません",
342
+ "card.strategy.trustEscalationHint": "サンドボックス昇格の承認要求は tools/pre-execute の後、ツール本体の内部で発行されるためゲートには見えず、LLM が safe と判定して自動許可した呼び出しでも拡張の承認を求められていました。オンにすると、ゲートが実際に許可した同一呼び出し(callId で厳密一致)だけがプロンプトを省略し、それ以外は従来どおり人手に回ります。理由を認識できない場合や対象が workspace-write / danger-full-access 以外の場合も同様です。オフにすると拡張は常に人手の判断となります。",
343
+ "card.readonly": "読み取り専用",
344
+ "card.unavailable": "設定名前空間が利用できません:dsh-perm-gate がこの profile に組み込まれているか確認してください。",
345
+ "card.llmReceiver": "llmAssist 受信 LLM(OpenAI 互換、カスタム API 可)",
346
+ "card.llmEndpoint": "Endpoint(BaseURL、例:https://api.openai.com/v1)",
347
+ "card.llmModel": "Model(モデル id、例:deepseek-chat)",
348
+ "card.llmKey": "API Key(機密)",
349
+ "card.llmKeyHidden": "未設定・初回入力後は * 表示",
350
+ "card.llmKeyMasked": "設定済み・新しい値を入力すると上書き",
351
+ "card.llmKeyOverwrite": "機密値が保存されています。新しい値を入力してフォーカスを外すと上書きします。",
352
+ "card.llmKeyClear": "クリア",
353
+ "card.allowlist": "許可リスト(allow、rulesFile と同期)",
354
+ "card.allowlistHint": "allow セクションに一致したパターンは直接許可。1 件ずつ追加・削除でき、rulesFile に即時書き込み&再読込します。",
355
+ "card.allowlistPresetNote": "プリセット許可リストの等価物は trustAutoAllow 戦略(スコープ内の安全操作を自動許可)で、上の戦略セクションで有効化します。このリストは rulesFile の allow を反映するため、初期は空で正常です。",
356
+ "card.allowlistEmpty": "許可ルールはありません",
357
+ "card.allowlistPlaceholder": "新しいコマンドパターン、例:npm test*",
358
+ "card.allowlistAdd": "追加",
359
+ "card.allowlistRemove": "このパターンを削除",
360
+ "card.allowlistBulk": "一括編集",
361
+ "card.allowlistBulkHide": "一括編集を閉じる",
362
+ "card.allowlistSave": "許可リストを保存",
363
+ "card.riskLearning": "riskLearning — 裁決学習:neutral リスク操作が人手で承認され実行されるとカウントし、しきい値到達後は同一操作(指紋一致)のみ自動許可",
364
+ "card.riskLearningHint": "学習状態はプラグイン所有($DSH_HOME/perm-gate/learning.json)で、YAML ルールには書き込みません。別の対象は決して権限を再利用しません。既定はオフ。",
365
+ "card.riskThreshold": "自動許可に必要な人手確認回数(1–10)",
366
+ "notice.label": "権限ゲートの活動",
367
+ "history.label": "承認記録",
368
+ "history.title": "権限承認記録",
369
+ "history.subtitle": "このセッションでのゲート判定のすべて(新しい順)",
370
+ "history.loading": "読み込み中…",
371
+ "history.empty": "このセッションにはまだ承認記録がありません",
372
+ "history.error": "読み込み失敗:",
373
+ "history.tag.auto": "自動許可",
374
+ "history.tag.ask": "人手へ",
375
+ "history.tag.deny": "拒否",
376
+ "card.denylistRestore": "プリセットの拒否キーワードに戻す",
377
+ "card.denylist": "拒否キーワード(deny、プリセット)",
378
+ "card.denylistHint": "キーワード(大小文字を区別しない部分一致)を含む呼び出しは、許可リスト / 許可 / LLM より先に拒否されます。プリセットは dsh-approval-gate から継承し、導入直後から有効。空または未設定ならプリセットを適用(ワンクリック復元対応)し、黑名単が静かに無効化されることはありません。",
379
+ "card.denylistEmpty": "拒否キーワードは空です(キーワード層で拒否される呼び出しはありません)",
380
+ "card.denylistPlaceholder": "新しいキーワード、例:drop database",
381
+ "card.denylistAdd": "追加",
382
+ "card.denylistRemove": "このキーワードを削除",
383
+ "card.denylistTagPreset": "プリセット",
384
+ "card.denylistTagCustom": "カスタム",
385
+ "card.riskSediment": "riskSediment — 学習沈殿:しきい値に達したサンプルが決定論的な自動許可ルールに",
386
+ "card.riskSedimentHint": "オンにすると、tool|category キーの確認がしきい値に達した時点でサンプル指紋が許可ルールとして沈殿します。同一ツール・同一ターゲットの正確一致は LLM を経由せず自動許可(llmAssist オフでも継続)。",
387
+ "card.sediment": "学習沈殿(決定論的許可ルール)",
388
+ "card.sedimentHint": "人手確認から沈殿したルール:指紋が正確に一致したときのみ許可。別の対象は決して再利用しません。学習の終止やサンプル削除ができます。",
389
+ "card.sedimentEmpty": "沈殿ルールはまだありません(しきい値に達したサンプルがここに表示されます)",
390
+ "card.sedimentCount": "確認済み %n/%t",
391
+ "card.sedimentStop": "終止",
392
+ "card.sedimentStopTitle": "この学習を終止する(カウントとサンプルを削除)",
393
+ "card.sedimentStopConfirm": "「%k」の学習を終止しますか?カウントと全サンプルを削除します。",
394
+ "card.sedimentSampleRemove": "この沈殿サンプルを削除",
395
+ "card.llmSource": "受信 LLM のソース",
396
+ "card.llmSourceCustom": "カスタム API(OpenAI 互換)",
397
+ "card.llmSourceHost": "セッションの現在の既定モデルを使用(DSH llm サービス)",
398
+ "card.llmSourceHostHint": "既定では DSH セッションで現在选择中のモデルグループで審査判定を実行します(下に現在の実効値を表示)。Provider / Model を空のままにするとセッションに追従し、ドロップダウンから選ぶか手入力で固定できます——DSH で設定したカスタムグループ(例:local-35b)は自動的にリストに現れます。",
399
+ "card.llmProvider": "Provider(空 = セッションの現在の選択に追従)",
400
+ "card.llmProviderPlaceholder": "セッションの現在の選択に追従",
401
+ "card.llmModelHostPlaceholder": "空 = セッションの現在の選択に追従",
402
+ "card.llmResolved": "現在の実効値:%p / %m",
403
+ "card.llmPreset": "プリセットエンドポイント(選択で自動入力、後から編集可)",
404
+ "card.llmPresetChoose": "プリセットを選択…",
405
+ "card.llmPresetPublic": "公開 API プリセット",
406
+ "card.llmPresetHost": "DSH モデルグループ(選択するとセッション受信へ切替)",
407
+ "card.networkEnabled": "ネットワーク傍受(networkEnabled)",
408
+ "card.networkEnabledHint": "オンにするとローカルプロキシが起動し、サブプロセスの送信接続を傍受して許可/拒否ルールを適用します。オフにすると動作変更なし。",
409
+ "card.watch": "ルールホットリロード(watch)",
410
+ "card.watchHint": "オンにするとルールファイルの変更を自動監視し、手動リロードなしで即座に反映されます。",
411
+ "card.networkRebindNote": "切り替えは即時反映されます(自動で再バインド、プラグイン再読み込み不要)。",
412
+ "card.networkInjectEnv": "プロキシ環境変数の注入(networkInjectEnv)",
413
+ "card.networkInjectEnvHint": "サブプロセスに HTTP(S)_PROXY / ALL_PROXY を書き込みます(NO_PROXY はクリア)。オフの場合はリスナーのみ起動し、プロセス環境は変更しません。",
414
+ "card.healthStale": "ホストルートが利用できません(404)——node 側が旧ビルドです。ページ再読み込みだけでなく dsh を完全に再起動してから再試行してください。",
415
+ "card.healthTest": "健全性テスト",
416
+ "card.healthRunning": "テスト中…",
417
+ "card.healthOk": "正常(%ms ms)",
418
+ "card.healthFail": "失敗:",
419
+ "history.tag.learned": "学習済み",
420
+ "history.tag.manualApproved": "人手承認",
421
+ "history.tag.manualRejected": "人手拒否",
422
+ "history.tag.manualCancelled": "人手キャンセル",
423
+ "history.tag.standDown": "ゲート停止",
424
+ "history.learnedProgress": "学習 %n/%t",
425
+ "history.snapshots": "diff スナップショット",
426
+ "history.clearSession": "このセッションのみ削除",
427
+ "history.clearAll": "すべて削除",
428
+ "history.clearSessionTitle": "このセッションの diff スナップショットのみ削除(他のセッションは対象外)",
429
+ "history.clearAllTitle": "全セッション(未確認のものも含む)の diff スナップショットを削除。再確認あり",
430
+ "history.clearSessionConfirm": "このセッションの diff スナップショットを削除しますか?本セッションの承認で生成された変更比較データのみを削除し、承認記録自体には影響しません。",
431
+ "history.clearAllConfirm": "【全セッション】の diff スナップショットを削除しますか?他のセッションがまだ確認していない変更比較も削除され、復元できません。",
432
+ "history.diffTitle": "ファイル変更の比較",
433
+ "history.diffClose": "閉じる",
434
+ "history.diffLoading": "読み込み中…",
435
+ "history.diffEmpty": "内容の変更なし(またはファイルを読み取れません)",
436
+ "history.diffLoadFail": "diff の読み込みに失敗:",
437
+ "history.diffRevert": "この変更を元に戻す",
438
+ "history.diffReverting": "送信中…",
439
+ "history.diffRevertDone": "取り消し指示を送信しました",
440
+ "history.diffRevertSent": "取り消し指示を会話に送信しました。AI が指示に従ってファイルを復元します",
441
+ "history.diffRevertFail": "送信に失敗:",
442
+ "history.diffNoSnapshot": "このイベントには該当ファイルのスナップショットがありません",
443
+ "history.diffChipTitle": "このファイルの変更比較を表示",
444
+ "history.diffStats": "+%a / -%r 行変更 · %c 行は未変更",
445
+ "history.diffGone": "(ファイルは現在存在しません)",
446
+ "history.diffHidden": "%n 行は未変更",
447
+ "card.dryRun": "ルールテスト",
448
+ "card.dryRunHint": "現在有効なルールで 1 回の呼び出しを判定します。ツールは実行せず、ルールも書き換えません。ルールを変更する前にここで確認してください。",
449
+ "card.dryRunTool": "ツール名",
450
+ "card.dryRunCommand": "コマンド / 引数",
451
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
452
+ "card.dryRunTest": "テスト",
453
+ "card.dryRunRunning": "判定中…",
454
+ "card.dryRunEmpty": "まだテストしていません。ツール名とコマンドを入力し「テスト」を押すと、どう判定されるか確認できます。",
455
+ "card.dryRunVerdict": "判定",
456
+ "card.dryRunAllow": "許可",
457
+ "card.dryRunAsk": "要確認",
458
+ "card.dryRunDeny": "拒否",
459
+ "card.dryRunRule": "一致したルール",
460
+ "card.dryRunRuleNone": "一致するルールなし。defaultAction:%d",
461
+ "card.dryRunDimensions": "制約次元",
462
+ "card.dryRunReason": "理由",
463
+ "card.dryRunNote": "「一致したルール」はルール層の結果です。判定がより前段のハード拒否やキーワードブラックリストによる場合、ルール番号はありません——その場合の次元表示は参考情報です。",
464
+ "card.dryRunNoRules": "(読み込まれたルールは 0 件:rulesFile のパスを確認してください)",
465
+ "card.dryRunFail": "テスト失敗:",
466
+ "card.dryRunStale": "ルートが存在しません(プラグイン未再起動?)",
467
+ "card.rules": "Settings 組み込みルール",
468
+ "card.rulesHint": "ゲートが今読み込んでいるルール(本プラグインエントリの rules 設定フィールドから。未設定時はルールファイルにフォールバック)。読み取り専用 — 変更するには下の許可リストか設定ページを使ってください。",
469
+ "card.rulesRefresh": "再読み込み",
470
+ "card.rulesLoading": "読み込み中…",
471
+ "card.rulesPath": "ソース",
472
+ "card.rulesNone": "ルールが設定されていません",
473
+ "card.rulesCounts": "ルール",
474
+ "card.rulesDefault": "defaultAction",
475
+ "card.rulesStats": "%b バイト · %l 行",
476
+ "card.rulesMissing": "ルールが存在しません。ゲートは組み込みの既定値にフォールバックします。",
477
+ "card.rulesTruncated": "ルールが大きいため、先頭 512 KB のみ表示します。",
478
+ "card.rulesError": "読み込みに失敗しました:",
479
+ "card.rulesStale": "ルートが存在しません(プラグイン未再起動?)",
480
+ "card.rulesNote": "読み取り専用:このパネルからルールは変更できません。許可リストと「常に許可」は本プラグインのエントリ設定に書き戻されます。"
481
+ },
482
+ ko: {
483
+ "card.title": "自动审查(자동 검토) 티어",
484
+ "section.title": "자동 검토 게이트",
485
+ "card.description": "read-only / full-access / whitelist와 나란한 독립 승인 모드. 세션 권한 선택기에서는 「自动审查」로 표시됩니다. 프론트는 단일 스위치만 노출. 백엔드의 네 가지 승인 전략은 이 패널에서 조합 가능하며 P0 하드 거부에 대해 여전히 fail-closed.",
486
+ "card.permissive": "自动审查 티어 활성화",
487
+ "card.permissiveHint": "꺼져 있으면 이전과 완전히 동일하게 동작합니다.",
488
+ "card.scopeNote": "프리셋 범위: 이 게이트는 세션 권한 프리셋이 %scope일 때만 동작합니다. 다른 프리셋에서는 P0 하드 거부를 포함한 게이트 전체가 정지하고, 선택된 프리셋 자체의 정책이 대신합니다. 정지 시 stand-down 이벤트가 1건 기록되고 입력창 위에 상시 표시됩니다.",
489
+ "card.strategies": "백엔드 승인 전략(하나 이상 유효, 조합 가능)",
490
+ "card.strategy.trustAutoAllow": "trustAutoAllow — 범위 내 안전 작업은 자동 허용, 위험/미확인은 ask(중간 티어 기준)",
491
+ "card.strategy.alwaysConfirm": "alwaysConfirm — 모든 경계를 ask. 승인 패널의「허용 컨트롤」에 두 개의 확장 버튼:「이 세션에서 해당 유형 반복 허용」(세션 제한 grant) 과「모든 발생 허용」(명령어를 rulesFile 허용 목록에 영구 추가)",
492
+ "card.strategy.llmAssist": "llmAssist — 사용자 지정 LLM이 위험을 판정: safe는 자동 허용, deletion/credential/remote/system/bulk 하드 위험은 사람 확인 유지(분류기는 결코 거부하지 않고 판단을 에스컬레이션만 합니다), neutral은 확인 학습으로, 실패/시간 초과는 사람에게 폴백",
493
+ "card.strategy.trustEscalation": "trustEscalation — 샌드박스 승격 무확인: 이 게이트가 이미 허용한 호출에 대해 shell / pwsh / edit 이 도구 내부에서 요청하는 샌드박스 확장을 게이트가 승인하고 프롬프트를 띄우지 않습니다",
494
+ "card.strategy.trustEscalationHint": "샌드박스 승격 승인 요청은 tools/pre-execute 이후 도구 본체 내부에서 발생하므로 게이트가 보지 못했고, LLM이 safe로 판정해 자동 허용한 호출에도 확장 승인을 물었습니다. 켜면 게이트가 실제로 허용한 동일 호출(callId 정확 일치)만 프롬프트를 건너뛰고, 나머지는 이전과 같이 사람에게 전달됩니다. 이유를 인식할 수 없거나 대상이 workspace-write / danger-full-access 가 아닌 경우도 마찬가지입니다. 끄면 확장은 항상 사람이 판단합니다.",
495
+ "card.readonly": "읽기 전용",
496
+ "card.unavailable": "설정 네임스페이스를 사용할 수 없습니다: dsh-perm-gate가 이 profile에 조립되었는지 확인하세요.",
497
+ "card.llmReceiver": "llmAssist 수신 LLM (OpenAI 호환, 사용자 지정 API 가능)",
498
+ "card.llmEndpoint": "Endpoint (BaseURL, 예: https://api.openai.com/v1)",
499
+ "card.llmModel": "Model (모델 id, 예: deepseek-chat)",
500
+ "card.llmKey": "API Key (비밀)",
501
+ "card.llmKeyHidden": "미설정 · 최초 입력 후 * 표시",
502
+ "card.llmKeyMasked": "설정됨 · 새 값을 입력하면 덮어씀",
503
+ "card.llmKeyOverwrite": "비밀 값이 저장되어 있습니다. 새 값을 입력하고 포커스를 벗어나면 덮어씁니다.",
504
+ "card.llmKeyClear": "지우기",
505
+ "card.allowlist": "허용 목록(allow, rulesFile과 동기화)",
506
+ "card.allowlistHint": "allow 섹션과 일치하는 패턴은 바로 허용됩니다. 항목을 하나씩 추가·삭제하면 rulesFile에 즉시 기록 후 다시 적재됩니다.",
507
+ "card.allowlistPresetNote": "프리셋 허용 목록의 등가물은 trustAutoAllow 전략(범위 내 안전 작업 자동 허용)으로, 위 전략 섹션에서 활성화합니다. 이 목록은 rulesFile의 allow를 반영하므로 초기에 비어 있는 것이 정상입니다.",
508
+ "card.allowlistEmpty": "허용 규칙이 없습니다",
509
+ "card.allowlistPlaceholder": "새 명령 패턴, 예: npm test*",
510
+ "card.allowlistAdd": "추가",
511
+ "card.allowlistRemove": "이 패턴 삭제",
512
+ "card.allowlistBulk": "일괄 편집",
513
+ "card.allowlistBulkHide": "일괄 편집 접기",
514
+ "card.allowlistSave": "허용 목록 저장",
515
+ "card.riskLearning": "riskLearning — 판정 학습: neutral 위험 요청이 사람의 승인과 실행까지 완료되면 카운트되며, 임계값 도달 후 동일 작업(지문 일치)만 자동 허용",
516
+ "card.riskLearningHint": "학습 상태는 플러그인 소유($DSH_HOME/perm-gate/learning.json)이며 YAML 규칙에는 기록하지 않습니다. 다른 대상은 권한을 재사용하지 않습니다. 기본값은 꺼짐.",
517
+ "card.riskThreshold": "자동 허용 전 필요한 사람 확인 횟수(1–10)",
518
+ "notice.label": "권한 게이트 활동",
519
+ "history.label": "승인 기록",
520
+ "history.title": "권한 승인 기록",
521
+ "history.subtitle": "이 세션에서 게이트가 판정한 모든 기록(최신 순)",
522
+ "history.loading": "불러오는 중…",
523
+ "history.empty": "이 세션에는 아직 승인 기록이 없습니다",
524
+ "history.error": "불러오기 실패: ",
525
+ "history.tag.auto": "자동 허용",
526
+ "history.tag.ask": "사람에게",
527
+ "history.tag.deny": "거부",
528
+ "card.denylistRestore": "프리셋 블랙리스트로 복원",
529
+ "card.denylist": "블랙리스트(deny 키워드, 플러그인 프리셋)",
530
+ "card.denylistHint": "키워드(대소문자 구분 없는 부분 일치)를 포함한 호출은 허용 목록/권한/LLM보다 먼저 거부됩니다. 프리셋은 dsh-approval-gate에서 계승되어 설치 즉시 활성화되며, 비어 있거나 미설정이면 프리셋이 적용됩니다(원클릭 복원 지원). 블랙리스트가 조용히 꺼지는 일은 없습니다.",
531
+ "card.denylistEmpty": "블랙리스트가 비어 있음(키워드 계층에서 거부되는 호출 없음)",
532
+ "card.denylistPlaceholder": "새 키워드, 예: drop database",
533
+ "card.denylistAdd": "추가",
534
+ "card.denylistRemove": "이 키워드 삭제",
535
+ "card.denylistTagPreset": "프리셋",
536
+ "card.denylistTagCustom": "사용자 지정",
537
+ "card.riskSediment": "riskSediment — 학습 침전: 임계값에 도달한 샘플이 결정론적 자동 허용 규칙으로",
538
+ "card.riskSedimentHint": "켜면 tool|category 키의 확인이 임계값에 도달한 시점에 샘플 지문이 허용 규칙으로 침전됩니다. 동일 도구·동일 대상의 정확 일치는 LLM 없이 바로 허용(llmAssist가 꺼져도 유지).",
539
+ "card.sediment": "학습 침전(결정론적 허용 규칙)",
540
+ "card.sedimentHint": "사람의 확인에서 침전된 규칙: 지문이 정확히 일치할 때만 허용되며 다른 대상은 절대 재사용하지 않습니다. 학습 종료 또는 샘플 삭제가 가능합니다.",
541
+ "card.sedimentEmpty": "아직 침전 규칙이 없습니다(임계값에 도달한 샘플이 여기에 표시됩니다)",
542
+ "card.sedimentCount": "확인 %n/%t",
543
+ "card.sedimentStop": "종료",
544
+ "card.sedimentStopTitle": "이 학습 종료(카운트와 샘플 삭제)",
545
+ "card.sedimentStopConfirm": "'%k' 학습을 종료합니까? 카운트와 모든 샘플이 삭제됩니다.",
546
+ "card.sedimentSampleRemove": "이 침전 샘플 삭제",
547
+ "card.llmSource": "수신 LLM 소스",
548
+ "card.llmSourceCustom": "사용자 지정 API(OpenAI 호환)",
549
+ "card.llmSourceHost": "세션의 현재 기본 모델 사용(DSH llm 서비스)",
550
+ "card.llmSourceHostHint": "기본적으로 DSH 세션에서 현재 선택한 모델 그룹으로 승인 판정을 수행합니다(아래에 실제 적용값 표시). Provider / Model을 비우면 세션 선택을 따르며, 드롭다운에서 선택하거나 직접 입력해 고정할 수 있습니다——DSH에 설정한 사용자 지정 그룹(예: local-35b)은 목록에 자동으로 나타납니다.",
551
+ "card.llmProvider": "Provider(비움 = 세션 현재 선택 따름)",
552
+ "card.llmProviderPlaceholder": "세션 현재 선택 따름",
553
+ "card.llmModelHostPlaceholder": "비움 = 세션 현재 선택 따름",
554
+ "card.llmResolved": "현재 적용: %p / %m",
555
+ "card.llmPreset": "프리셋 엔드포인트(선택 시 자동 채움, 이후 편집 가능)",
556
+ "card.llmPresetChoose": "프리셋 선택…",
557
+ "card.llmPresetPublic": "공개 API 프리셋",
558
+ "card.llmPresetHost": "DSH 모델 그룹(선택 시 세션 수신으로 전환)",
559
+ "card.networkEnabled": "네트워크 가로채기 (networkEnabled)",
560
+ "card.networkEnabledHint": "켜면 로컬 프록시가 시작되어 서브프로세스의 아웃바운드 연결을 가로채고 허용/거부 규칙을 적용합니다. 끄면 동작 변경 없음.",
561
+ "card.watch": "규칙 핫 리로드 (watch)",
562
+ "card.watchHint": "켜면 규칙 파일 변경을 자동 감시하여 수동 리로드 없이 즉시 적용됩니다.",
563
+ "card.networkRebindNote": "전환 즉시 적용됩니다(자동 재바인딩, 플러그인 리로드 불필요).",
564
+ "card.networkInjectEnv": "프록시 환경 변수 주입 (networkInjectEnv)",
565
+ "card.networkInjectEnvHint": "서브프로세스에 HTTP(S)_PROXY / ALL_PROXY를 기록합니다(NO_PROXY는 비움). 끄면 리스너만 실행되고 프로세스 환경은 변경되지 않습니다.",
566
+ "card.healthStale": "호스트 라우트를 사용할 수 없음(404)——node 측이 이전 빌드입니다. 페이지 새로고침이 아니라 dsh를 완전히 재시작한 후 다시 시도하세요.",
567
+ "card.healthTest": "상태 테스트",
568
+ "card.healthRunning": "테스트 중…",
569
+ "card.healthOk": "정상(%ms ms)",
570
+ "card.healthFail": "실패: ",
571
+ "history.tag.learned": "학습됨",
572
+ "history.tag.manualApproved": "사람 승인",
573
+ "history.tag.manualRejected": "사람 거부",
574
+ "history.tag.manualCancelled": "사람 취소",
575
+ "history.tag.standDown": "게이트 정지",
576
+ "history.learnedProgress": "학습 %n/%t",
577
+ "history.snapshots": "diff 스냅샷",
578
+ "history.clearSession": "이 세션만 삭제",
579
+ "history.clearAll": "전체 삭제",
580
+ "history.clearSessionTitle": "이 세션의 diff 스냅샷만 삭제(다른 세션은 그대로)",
581
+ "history.clearAllTitle": "모든 세션(아직 확인하지 않은 것 포함)의 diff 스냅샷을 삭제. 재확인 필요",
582
+ "history.clearSessionConfirm": "이 세션의 diff 스냅샷을 삭제합니까? 이 세션의 승인으로 생성된 변경 비교 데이터만 삭제하며 승인 기록 자체는 유지됩니다.",
583
+ "history.clearAllConfirm": "【모든 세션】의 diff 스냅샷을 삭제합니까? 다른 세션이 아직 확인하지 않은 변경 비교도 삭제되며 복구할 수 없습니다.",
584
+ "history.diffTitle": "파일 변경 비교",
585
+ "history.diffClose": "닫기",
586
+ "history.diffLoading": "불러오는 중…",
587
+ "history.diffEmpty": "내용 변경 없음(또는 파일을 읽을 수 없음)",
588
+ "history.diffLoadFail": "diff 불러오기 실패: ",
589
+ "history.diffRevert": "이 변경 되돌리기",
590
+ "history.diffReverting": "전송 중…",
591
+ "history.diffRevertDone": "되돌리기 지시를 보냈습니다",
592
+ "history.diffRevertSent": "되돌리기 지시를 대화로 보냈습니다. AI가 지시에 따라 파일을 복원합니다",
593
+ "history.diffRevertFail": "전송 실패: ",
594
+ "history.diffNoSnapshot": "이 이벤트에는 해당 파일의 스냅샷이 없습니다",
595
+ "history.diffChipTitle": "이 파일의 변경 비교 보기",
596
+ "history.diffStats": "+%a / -%r 행 변경 · %c 행 미변경",
597
+ "history.diffGone": " (파일이 더 이상 존재하지 않음)",
598
+ "history.diffHidden": "%n 행 미변경",
599
+ "card.dryRun": "규칙 테스트",
600
+ "card.dryRunHint": "현재 적용 중인 규칙으로 호출 한 건을 판정합니다. 도구를 실행하지 않고 규칙도 쓰지 않습니다. 규칙을 바꾸기 전에 여기서 먼저 확인하세요.",
601
+ "card.dryRunTool": "도구 이름",
602
+ "card.dryRunCommand": "명령 / 인수",
603
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
604
+ "card.dryRunTest": "테스트",
605
+ "card.dryRunRunning": "판정 중…",
606
+ "card.dryRunEmpty": "아직 테스트하지 않았습니다. 도구 이름과 명령을 입력하고 「테스트」를 누르면 어떻게 판정되는지 볼 수 있습니다.",
607
+ "card.dryRunVerdict": "판정",
608
+ "card.dryRunAllow": "허용",
609
+ "card.dryRunAsk": "확인 필요",
610
+ "card.dryRunDeny": "거부",
611
+ "card.dryRunRule": "일치한 규칙",
612
+ "card.dryRunRuleNone": "일치하는 규칙 없음, defaultAction: %d",
613
+ "card.dryRunDimensions": "제약 차원",
614
+ "card.dryRunReason": "이유",
615
+ "card.dryRunNote": "「일치한 규칙」은 규칙 계층의 결과입니다. 판정이 더 앞선 하드 거부나 키워드 블랙리스트에서 나온 경우 규칙 번호가 없습니다——그때의 차원 표시는 참고용입니다.",
616
+ "card.dryRunNoRules": "(로드된 규칙 0건: rulesFile 경로를 확인하세요)",
617
+ "card.dryRunFail": "테스트 실패: ",
618
+ "card.dryRunStale": "라우트가 없습니다(플러그인 미재시작?)",
619
+ "card.rules": "Settings 내장 규칙",
620
+ "card.rulesHint": "게이트가 지금 불러오는 규칙(이 플러그인 엔트리의 rules 설정 필드에서. 미설정 시 규칙 파일로 폴백). 읽기 전용 — 아래 허용 목록이나 설정 페이지에서 수정하세요.",
621
+ "card.rulesRefresh": "새로 고침",
622
+ "card.rulesLoading": "읽는 중…",
623
+ "card.rulesPath": "소스",
624
+ "card.rulesNone": "규칙이 설정되지 않았습니다",
625
+ "card.rulesCounts": "규칙",
626
+ "card.rulesDefault": "defaultAction",
627
+ "card.rulesStats": "%b 바이트 · %l 줄",
628
+ "card.rulesMissing": "규칙이 없습니다. 게이트는 내장 기본값으로 대체합니다.",
629
+ "card.rulesTruncated": "규칙이 커서 앞 512 KB만 표시합니다.",
630
+ "card.rulesError": "불러오기 실패: ",
631
+ "card.rulesStale": "라우트가 없습니다(플러그인 미재시작?)",
632
+ "card.rulesNote": "읽기 전용: 이 패널은 규칙을 수정할 수 없습니다. 허용 목록과 \"항상 허용\"은 이 플러그인의 엔트리 설정에 기록됩니다."
633
+ },
634
+ fr: {
635
+ "card.title": "Examen automatique",
636
+ "section.title": "Porte d'examen automatique",
637
+ "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.",
638
+ "card.permissive": "Activer le niveau d'examen automatique",
639
+ "card.permissiveHint": "Lorsqu'il est désactivé, la porte se comporte exactement comme avant.",
640
+ "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.",
641
+ "card.strategies": "Stratégies d'approbation backend (au moins une active ; combinables)",
642
+ "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)",
643
+ "card.strategy.alwaysConfirm": "alwaysConfirm — chaque passage demande ; les « contrôles d'autorisation » du panneau d'approbation ajoutent deux boutons étendus : « répéter l'autorisation pour ce type cette session » (grant borné de session) et « autoriser toutes les occurrences » (persistance du mot de commande dans la liste blanche rulesFile)",
644
+ "card.strategy.llmAssist": "llmAssist — un LLM personnalisé évalue le risque : safe → auto-autorisation ; deletion/credential/remote/system/bulk → risques durs maintiennent la demande humaine (le classifieur ne refuse jamais — il ne fait qu'escalader) ; neutral → apprentissage par confirmation ; échec/timeout → retour à l'humain",
645
+ "card.strategy.trustEscalation": "trustEscalation — escalade sandbox sans invite : pour un appel déjà autorisé par cette porte, l'élargissement sandbox initié par shell / pwsh / edit depuis l'intérieur de l'outil est approuvé ici au lieu de vous le demander",
646
+ "card.strategy.trustEscalationHint": "L'approbation d'escalade sandbox est demandée depuis l'intérieur du corps de l'outil, après tools/pre-execute, donc la porte ne la voit jamais — un appel que le LLM a jugé sûr et que la porte a auto-autorisé vous demandait quand même d'approuver l'élargissement. Activé, seul l'appel exact que la porte a autorisé (correspondance par callId) ignore l'invite ; tout le reste passe à l'humain comme avant, ainsi que toute raison non reconnue ou cible autre que workspace-write / danger-full-access. Désactivez pour garder l'élargissement sous contrôle humain.",
647
+ "card.readonly": "Lecture seule",
648
+ "card.unavailable": "Espace de noms des paramètres indisponible : assurez-vous que dsh-perm-gate est assemblé dans ce profil.",
649
+ "card.llmReceiver": "LLM récepteur llmAssist (compatible OpenAI ; API personnalisée possible)",
650
+ "card.llmEndpoint": "Endpoint (BaseURL, ex. https://api.openai.com/v1)",
651
+ "card.llmModel": "Model (identifiant du modèle, ex. deepseek-chat)",
652
+ "card.llmKey": "API Key (secret)",
653
+ "card.llmKeyHidden": "Non défini · affiché comme * après la première saisie",
654
+ "card.llmKeyMasked": "Défini · saisir une nouvelle valeur pour écraser",
655
+ "card.llmKeyOverwrite": "Une valeur secrète est stockée ; saisissez une nouvelle valeur et quittez le champ pour l'écraser.",
656
+ "card.llmKeyClear": "Effacer",
657
+ "card.allowlist": "Liste blanche (allow, synchronisée avec rulesFile)",
658
+ "card.allowlistHint": "Les motifs correspondant à la section allow sont directement autorisés ; ajoutez ou supprimez des entrées une par une, écrites dans rulesFile et rechargées en direct.",
659
+ "card.allowlistPresetNote": "L'équivalent de la liste blanche prédéfinie est la stratégie trustAutoAllow (opérations sûres dans le périmètre auto-autorisées), activée dans la section stratégies ci-dessus ; cette liste reflète votre section allow de rulesFile et démarre légitimement vide.",
660
+ "card.allowlistEmpty": "Aucune règle de liste blanche",
661
+ "card.allowlistPlaceholder": "Nouveau motif de commande, ex. npm test*",
662
+ "card.allowlistAdd": "Ajouter",
663
+ "card.allowlistRemove": "Supprimer ce motif",
664
+ "card.allowlistBulk": "Édition groupée",
665
+ "card.allowlistBulkHide": "Masquer l'édition groupée",
666
+ "card.allowlistSave": "Enregistrer la liste blanche",
667
+ "card.riskLearning": "riskLearning — apprentissage de verdict : les opérations à risque neutral approuvées et exécutées sont comptées ; la même opération est auto-autorisée au seuil avec une empreinte correspondante",
668
+ "card.riskLearningHint": "L'état d'apprentissage appartient au plugin ($DSH_HOME/perm-gate/learning.json) et n'est jamais écrit dans vos règles YAML ; une cible différente ne réutilise jamais l'autorité. Désactivé par défaut.",
669
+ "card.riskThreshold": "Confirmations humaines requises avant auto-autorisation (1–10)",
670
+ "notice.label": "Activité de la porte de permission",
671
+ "history.label": "Approbations",
672
+ "history.title": "Historique des approbations de permission",
673
+ "history.subtitle": "Chaque décision de la porte dans cette session, les plus récentes d'abord",
674
+ "history.loading": "Chargement…",
675
+ "history.empty": "Aucun enregistrement d'approbation dans cette session",
676
+ "history.error": "Échec du chargement : ",
677
+ "history.tag.auto": "Auto-autorisé",
678
+ "history.tag.ask": "Demandé",
679
+ "history.tag.deny": "Refusé",
680
+ "card.denylistRestore": "Restaurer la liste noire prédéfinie",
681
+ "card.denylist": "Liste noire (mots-clés deny, prédéfinis par le plugin)",
682
+ "card.denylistHint": "Un appel dont le texte contient un mot-clé (sous-chaîne insensible à la casse) est refusé immédiatement, avant liste blanche / grants / LLM. La liste prédéfinie hérite de dsh-approval-gate et est active dès l'installation ; modifiez librement — vide ou effacée, retour au préréglage (restauration en un clic), donc la liste noire ne se désactive jamais silencieusement.",
683
+ "card.denylistEmpty": "Liste noire vide (aucun appel n'est rejeté par la couche de mots-clés)",
684
+ "card.denylistPlaceholder": "Nouveau mot-clé, ex. drop database",
685
+ "card.denylistAdd": "Ajouter",
686
+ "card.denylistRemove": "Supprimer ce mot-clé",
687
+ "card.denylistTagPreset": "Prédéfini",
688
+ "card.denylistTagCustom": "Personnalisé",
689
+ "card.riskSediment": "riskSediment — sédimentation d'apprentissage : les échantillons ayant atteint le seuil deviennent des règles d'auto-autorisation déterministes",
690
+ "card.riskSedimentHint": "Activé, lorsqu'une clé tool|category atteint le seuil, ses empreintes d'échantillons se sédimentent en règles d'autorisation — une correspondance exacte même outil/même cible autorise directement sans autre appel LLM (continue de fonctionner avec llmAssist désactivé).",
691
+ "card.sediment": "Sédiment d'apprentissage (règles d'autorisation déterministes)",
692
+ "card.sedimentHint": "Règles sédimentées à partir des confirmations humaines : seule une correspondance exacte d'empreinte autorise, jamais une cible différente. Terminez un apprentissage ou supprimez un échantillon.",
693
+ "card.sedimentEmpty": "Aucune règle sédimentée pour l'instant (les échantillons atteignant le seuil apparaissent ici)",
694
+ "card.sedimentCount": "confirmé %n/%t",
695
+ "card.sedimentStop": "Terminer",
696
+ "card.sedimentStopTitle": "Terminer cet apprentissage (supprime le compteur et les échantillons)",
697
+ "card.sedimentStopConfirm": "Terminer l'apprentissage pour « %k » ? Son compteur et tous ses échantillons seront supprimés.",
698
+ "card.sedimentSampleRemove": "Supprimer cet échantillon sédimenté",
699
+ "card.llmSource": "Source du LLM récepteur",
700
+ "card.llmSourceCustom": "API personnalisée (compatible OpenAI)",
701
+ "card.llmSourceHost": "Utiliser le modèle par défaut actuel de la session (service DSH llm)",
702
+ "card.llmSourceHostHint": "Par défaut, l'évaluation d'approbation utilise le groupe de modèles actuellement sélectionné dans la session DSH (la valeur effective est affichée ci-dessous) ; laissez Provider / Model vide pour suivre la session, ou choisissez dans la liste déroulante ou saisissez pour épingler — vos groupes personnalisés configurés dans DSH (ex. local-35b) apparaissent automatiquement.",
703
+ "card.llmProvider": "Provider (vide = suit la sélection de session)",
704
+ "card.llmProviderPlaceholder": "Suit la sélection de session",
705
+ "card.llmModelHostPlaceholder": "Vide = suit la sélection de session",
706
+ "card.llmResolved": "Effectif actuellement : %p / %m",
707
+ "card.llmPreset": "Préréglages d'endpoint (remplit les champs ; modifiable ensuite)",
708
+ "card.llmPresetChoose": "Choisir un préréglage…",
709
+ "card.llmPresetPublic": "Préréglages d'API publiques",
710
+ "card.llmPresetHost": "Groupes de modèles DSH (la sélection bascule vers le récepteur de session)",
711
+ "card.networkEnabled": "Interception réseau (networkEnabled)",
712
+ "card.networkEnabledHint": "Activé, démarre un proxy local qui intercepte les connexions sortantes des sous-processus et applique les règles d'autorisation/refus. Désactivé = aucun changement de comportement.",
713
+ "card.watch": "Rechargement à chaud des règles (watch)",
714
+ "card.watchHint": "Activé, surveille automatiquement les modifications des fichiers de règles et les applique instantanément sans rechargement manuel.",
715
+ "card.networkRebindNote": "Prend effet immédiatement — le proxy se relie automatiquement, pas besoin de recharger le plugin.",
716
+ "card.networkInjectEnv": "Injecter les variables d'environnement proxy (networkInjectEnv)",
717
+ "card.networkInjectEnvHint": "Écrit HTTP(S)_PROXY / ALL_PROXY pour les sous-processus (et vide NO_PROXY). Désactivé = l'écouteur fonctionne mais aucun environnement de processus n'est modifié.",
718
+ "card.healthStale": "Route hôte indisponible (404) — la moitié node est encore une ancienne version ; redémarrez complètement dsh (pas seulement la page du navigateur) et réessayez.",
719
+ "card.healthTest": "Test de santé",
720
+ "card.healthRunning": "Test en cours…",
721
+ "card.healthOk": "OK (%ms ms)",
722
+ "card.healthFail": "Échec : ",
723
+ "history.tag.learned": "Appris",
724
+ "history.tag.manualApproved": "Approuvé",
725
+ "history.tag.manualRejected": "Rejeté",
726
+ "history.tag.manualCancelled": "Annulé",
727
+ "history.tag.standDown": "Porte inactive",
728
+ "history.learnedProgress": "appris %n/%t",
729
+ "history.snapshots": "instantanés diff",
730
+ "history.clearSession": "Effacer cette session",
731
+ "history.clearAll": "Tout effacer",
732
+ "history.clearSessionTitle": "Effacer uniquement les instantanés diff de cette session (les autres sessions ne sont pas affectées)",
733
+ "history.clearAllTitle": "Effacer les instantanés diff de toutes les sessions, y compris celles jamais consultées — demande une reconfirmation",
734
+ "history.clearSessionConfirm": "Effacer les instantanés diff de cette session ? Seules les données de comparaison produites par les approbations de cette session sont supprimées ; les enregistrements d'approbation restent.",
735
+ "history.clearAllConfirm": "Effacer les instantanés diff de TOUTES les sessions ? Cela peut supprimer des comparaisons que d'autres sessions n'ont pas encore consultées, et c'est irréversible.",
736
+ "history.diffTitle": "Comparaison des modifications de fichier",
737
+ "history.diffClose": "Fermer",
738
+ "history.diffLoading": "Chargement…",
739
+ "history.diffEmpty": "Aucun changement de contenu (ou fichier illisible)",
740
+ "history.diffLoadFail": "Échec du chargement du diff : ",
741
+ "history.diffRevert": "Annuler cette modification",
742
+ "history.diffReverting": "Envoi…",
743
+ "history.diffRevertDone": "Instruction d'annulation envoyée",
744
+ "history.diffRevertSent": "Instruction d'annulation envoyée à la conversation ; l'IA restaurera le fichier en conséquence",
745
+ "history.diffRevertFail": "Échec de l'envoi : ",
746
+ "history.diffNoSnapshot": "Cet événement n'a pas d'instantané pour ce fichier",
747
+ "history.diffChipTitle": "Voir la comparaison des modifications de ce fichier",
748
+ "history.diffStats": "+%a / -%r lignes modifiées · %c inchangées",
749
+ "history.diffGone": " (le fichier n'existe plus)",
750
+ "history.diffHidden": "%n lignes non modifiées",
751
+ "card.dryRun": "Test de règles",
752
+ "card.dryRunHint": "Juge un appel contre les règles en vigueur. N'exécute aucun outil et n'écrit aucune règle — vérifiez ici avant de modifier.",
753
+ "card.dryRunTool": "Outil",
754
+ "card.dryRunCommand": "Commande / arguments",
755
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
756
+ "card.dryRunTest": "Tester",
757
+ "card.dryRunRunning": "Évaluation…",
758
+ "card.dryRunEmpty": "Rien de testé pour l'instant. Renseignez un outil et une commande, puis appuyez sur Tester pour voir le verdict.",
759
+ "card.dryRunVerdict": "Verdict",
760
+ "card.dryRunAllow": "autoriser",
761
+ "card.dryRunAsk": "demander",
762
+ "card.dryRunDeny": "refuser",
763
+ "card.dryRunRule": "Règle correspondante",
764
+ "card.dryRunRuleNone": "aucune règle correspondante ; defaultAction : %d",
765
+ "card.dryRunDimensions": "Dimensions",
766
+ "card.dryRunReason": "Raison",
767
+ "card.dryRunNote": "« Règle correspondante » provient de la couche de règles. Quand le verdict est produit par un refus dur antérieur ou par la liste noire de mots-clés, il n'y a pas d'index de règle — les dimensions listées sont alors informatives.",
768
+ "card.dryRunNoRules": "(0 règles chargées — vérifiez le chemin rulesFile)",
769
+ "card.dryRunFail": "Échec du test : ",
770
+ "card.dryRunStale": "route manquante (plugin non redémarré ?)",
771
+ "card.rules": "Règles intégrées des paramètres",
772
+ "card.rulesHint": "Les règles que la porte charge actuellement (depuis le champ de configuration rules de cette entrée de plugin ; retour au fichier de règles si non configuré). Lecture seule — rien ici ne peut les modifier. Pour changer les règles, utilisez la liste blanche ci-dessous ou modifiez-les dans la page des paramètres.",
773
+ "card.rulesRefresh": "Actualiser",
774
+ "card.rulesLoading": "Lecture…",
775
+ "card.rulesPath": "Source",
776
+ "card.rulesNone": "aucune règle configurée",
777
+ "card.rulesCounts": "Règles",
778
+ "card.rulesDefault": "defaultAction",
779
+ "card.rulesStats": "%b octets · %l lignes",
780
+ "card.rulesMissing": "Règles introuvables. La porte retombe sur ses valeurs par défaut intégrées.",
781
+ "card.rulesTruncated": "Règles volumineuses ; affichage des 512 Ko premiers uniquement.",
782
+ "card.rulesError": "Échec du chargement : ",
783
+ "card.rulesStale": "route manquante (plugin non redémarré ?)",
784
+ "card.rulesNote": "Lecture seule : ce panneau ne peut pas modifier les règles. La section liste blanche et « toujours autoriser » écrivent dans la configuration d'entrée de ce plugin."
785
+ },
786
+ de: {
787
+ "card.title": "Automatische Überprüfung",
788
+ "section.title": "Automatische Prüfschleuse",
789
+ "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.",
790
+ "card.permissive": "Stufe „Automatische Überprüfung\" aktivieren",
791
+ "card.permissiveHint": "Wenn deaktiviert, verhält sich das Gate genau wie zuvor.",
792
+ "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.",
793
+ "card.strategies": "Backend-Genehmigungsstrategien (mindestens eine aktiv; kombinierbar)",
794
+ "card.strategy.trustAutoAllow": "trustAutoAllow — sichere Operationen im Bereich werden automatisch erlaubt; gefährliche/unbekannte → Nachfrage (mittlere Baseline-Stufe)",
795
+ "card.strategy.alwaysConfirm": "alwaysConfirm — jeder Übergang fragt; die „Erlaubnis-Steuerelemente\" des Genehmigungspanels erhalten zwei erweiterte Buttons: „Diesen Typ in dieser Sitzung wiederholt erlauben\" (begrenztes Sitzungs-Grant) und „Alle Vorkommen erlauben\" (Befehlswort dauerhaft in die rulesFile-Whitelist aufnehmen)",
796
+ "card.strategy.llmAssist": "llmAssist — ein benutzerdefiniertes LLM bewertet das Risiko: safe → automatische Erlaubnis; deletion/credential/remote/system/bulk → Hartrisiken behalten die menschliche Nachfrage (der Klassifizierer lehnt nie ab — er eskaliert nur); neutral → Bestätigungslernen; Fehler/Timeout → Rückfall auf den Menschen",
797
+ "card.strategy.trustEscalation": "trustEscalation — Sandbox-Eskalation ohne Eingabeaufforderung: Für einen bereits von diesem Gate erlaubten Aufruf wird die Sandbox-Erweiterung, die shell / pwsh / edit intern anfordert, hier genehmigt statt Sie zu fragen",
798
+ "card.strategy.trustEscalationHint": "Die Sandbox-Eskalationsgenehmigung wird nach tools/pre-execute im Tool-Körper angefordert, sodass das Gate sie nie sieht — ein vom LLM als sicher eingestufter und automatisch erlaubter Aufruf fragte Sie dennoch nach der Erweiterung. Aktiviert überspringt nur der exakte vom Gate erlaubte Aufruf (übereinstimmend per callId) die Eingabeaufforderung; alles andere geht wie zuvor zum Menschen, ebenso jeder unerkannte Grund oder ein anderes Ziel als workspace-write / danger-full-access. Deaktivieren, um die Erweiterung unter menschlicher Kontrolle zu halten.",
799
+ "card.readonly": "Nur-Lesen",
800
+ "card.unavailable": "Einstellungs-Namensraum nicht verfügbar: Stellen Sie sicher, dass dsh-perm-gate in diesem Profil zusammengebaut ist.",
801
+ "card.llmReceiver": "llmAssist empfangendes LLM (OpenAI-kompatibel; jede benutzerdefinierte API)",
802
+ "card.llmEndpoint": "Endpoint (BaseURL, z. B. https://api.openai.com/v1)",
803
+ "card.llmModel": "Model (Modell-ID, z. B. deepseek-chat)",
804
+ "card.llmKey": "API Key (geheim)",
805
+ "card.llmKeyHidden": "Nicht gesetzt · nach erster Eingabe als * angezeigt",
806
+ "card.llmKeyMasked": "Gesetzt · neuen Wert eingeben zum Überschreiben",
807
+ "card.llmKeyOverwrite": "Ein geheimer Wert ist gespeichert; geben Sie einen neuen Wert ein und verlassen Sie das Feld zum Überschreiben.",
808
+ "card.llmKeyClear": "Löschen",
809
+ "card.allowlist": "Whitelist (allow, mit rulesFile synchronisiert)",
810
+ "card.allowlistHint": "Muster im allow-Abschnitt werden direkt erlaubt; Einträge einzeln hinzufügen oder entfernen, live in rulesFile geschrieben und neu geladen.",
811
+ "card.allowlistPresetNote": "Das Äquivalent der Voreinstellungs-Whitelist ist die trustAutoAllow-Strategie (sichere Operationen im Bereich automatisch erlaubt), aktiviert im Strategiebereich oben; diese Liste spiegelt Ihren rulesFile-allow-Abschnitt und startet berechtigterweise leer.",
812
+ "card.allowlistEmpty": "Keine Whitelist-Regeln",
813
+ "card.allowlistPlaceholder": "Neues Befehlsmuster, z. B. npm test*",
814
+ "card.allowlistAdd": "Hinzufügen",
815
+ "card.allowlistRemove": "Dieses Muster entfernen",
816
+ "card.allowlistBulk": "Massenbearbeitung",
817
+ "card.allowlistBulkHide": "Massenbearbeitung ausblenden",
818
+ "card.allowlistSave": "Whitelist speichern",
819
+ "card.riskLearning": "riskLearning — Verdiktlernen: neutrale Risikoanfragen, die der Mensch genehmigt und die ausgeführt werden, zählen hoch; dieselbe Operation wird am Schwellenwert mit passendem Fingerabdruck automatisch erlaubt",
820
+ "card.riskLearningHint": "Der Lernzustand gehört dem Plugin ($DSH_HOME/perm-gate/learning.json) und wird nie in Ihre YAML-Regeln geschrieben; ein anderes Ziel nutzt nie die Autorität wieder. Standardmäßig deaktiviert.",
821
+ "card.riskThreshold": "Menschliche Bestätigungen vor automatischer Erlaubnis erforderlich (1–10)",
822
+ "notice.label": "Berechtigungs-Gate-Aktivität",
823
+ "history.label": "Genehmigungen",
824
+ "history.title": "Berechtigungsgenehmigungsprotokolle",
825
+ "history.subtitle": "Jede Gate-Entscheidung in dieser Sitzung, neueste zuerst",
826
+ "history.loading": "Wird geladen…",
827
+ "history.empty": "Noch keine Genehmigungsprotokolle in dieser Sitzung",
828
+ "history.error": "Laden fehlgeschlagen: ",
829
+ "history.tag.auto": "Automatisch erlaubt",
830
+ "history.tag.ask": "Angefragt",
831
+ "history.tag.deny": "Abgelehnt",
832
+ "card.denylistRestore": "Standard-Blacklist wiederherstellen",
833
+ "card.denylist": "Blacklist (deny-Schlüsselwörter, Plugin-Standard)",
834
+ "card.denylistHint": "Ein Aufruf, dessen Text ein Schlüsselwort enthält (Groß-/Kleinschreibung-unempfindliche Teilzeichenkette), wird sofort abgelehnt, vor Whitelist / Grants / LLM. Die Standardliste erbt von dsh-approval-gate und ist ab Installation aktiv; frei bearbeitbar — leer oder geleert fällt auf den Standard zurück (Ein-Klick-Wiederherstellung verfügbar), sodass die Blacklist sich nie stillschweigend abschaltet.",
835
+ "card.denylistEmpty": "Blacklist leer (kein Aufruf wird von der Schlüsselwortschicht abgelehnt)",
836
+ "card.denylistPlaceholder": "Neues Schlüsselwort, z. B. drop database",
837
+ "card.denylistAdd": "Hinzufügen",
838
+ "card.denylistRemove": "Dieses Schlüsselwort entfernen",
839
+ "card.denylistTagPreset": "Standard",
840
+ "card.denylistTagCustom": "Benutzerdefiniert",
841
+ "card.riskSediment": "riskSediment — Lernsedimentation: Proben am Schwellenwert werden zu deterministischen Auto-Erlaubnisregeln",
842
+ "card.riskSedimentHint": "Wenn aktiv, werden bei Erreichen des Schwellenwerts für einen tool|category-Schlüssel dessen Proben-Fingerabdrücke als Erlaubnisregeln sedimentiert — eine exakte Übereinstimmung bei gleichem Tool und gleichem Ziel erlaubt direkt ohne weiteren LLM-Aufruf (funktioniert weiter mit deaktiviertem llmAssist).",
843
+ "card.sediment": "Lernsediment (deterministische Erlaubnisregeln)",
844
+ "card.sedimentHint": "Aus menschlichen Bestätigungen sedimentierte Regeln: Nur eine exakte Fingerabdruckübereinstimmung erlaubt, nie ein anderes Ziel. Beenden Sie ein Lernen oder entfernen Sie eine Probe.",
845
+ "card.sedimentEmpty": "Noch keine sedimentierten Regeln (Proben am Schwellenwert erscheinen hier)",
846
+ "card.sedimentCount": "bestätigt %n/%t",
847
+ "card.sedimentStop": "Beenden",
848
+ "card.sedimentStopTitle": "Dieses Lernen beenden (Zähler und Proben löschen)",
849
+ "card.sedimentStopConfirm": "Lernen für „%k\" beenden? Zähler und alle Proben werden gelöscht.",
850
+ "card.sedimentSampleRemove": "Diese sedimentierte Probe entfernen",
851
+ "card.llmSource": "Empfängerquelle",
852
+ "card.llmSourceCustom": "Benutzerdefinierte API (OpenAI-kompatibel)",
853
+ "card.llmSourceHost": "Das aktuelle Standardmodell der Sitzung verwenden (DSH-llm-Dienst)",
854
+ "card.llmSourceHostHint": "Standardmäßig läuft die Genehmigungsbewertung auf der Modellgruppe, die die DSH-Sitzung aktuell verwendet (der effektive Wert wird unten angezeigt); Provider / Model leer lassen, um der Sitzung zu folgen, oder aus dem Dropdown wählen bzw. eingeben zum Festlegen — Ihre in DSH konfigurierten benutzerdefinierten Gruppen (z. B. local-35b) erscheinen automatisch.",
855
+ "card.llmProvider": "Provider (leer = folgt der Sitzungsauswahl)",
856
+ "card.llmProviderPlaceholder": "Folgt der Sitzungsauswahl",
857
+ "card.llmModelHostPlaceholder": "Leer = folgt der Sitzungsauswahl",
858
+ "card.llmResolved": "Aktuell wirksam: %p / %m",
859
+ "card.llmPreset": "Endpoint-Voreinstellungen (füllt die Felder; weiter bearbeitbar)",
860
+ "card.llmPresetChoose": "Voreinstellung wählen…",
861
+ "card.llmPresetPublic": "Öffentliche API-Voreinstellungen",
862
+ "card.llmPresetHost": "DSH-Modellgruppen (Auswahl wechselt zum Sitzungsempfänger)",
863
+ "card.networkEnabled": "Netzwerk-Interception (networkEnabled)",
864
+ "card.networkEnabledHint": "Wenn aktiv, startet ein lokaler Proxy, der ausgehende Verbindungen von Unterprozessen abfängt und Erlaubnis-/Ablehnungsregeln anwendet. Deaktiviert = keine Verhaltensänderung.",
865
+ "card.watch": "Regel-Hot-Reload (watch)",
866
+ "card.watchHint": "Wenn aktiv, werden Regeldateien automatisch auf Änderungen überwacht und sofort ohne manuelles Neuladen angewendet.",
867
+ "card.networkRebindNote": "Wirkt sofort — der Proxy bindet sich automatisch neu, kein Plugin-Reload nötig.",
868
+ "card.networkInjectEnv": "Proxy-Umgebungsvariablen injizieren (networkInjectEnv)",
869
+ "card.networkInjectEnvHint": "Schreibt HTTP(S)_PROXY / ALL_PROXY für Unterprozesse (und leert NO_PROXY). Deaktiviert = der Listener läuft, aber keine Prozessumgebung wird geändert.",
870
+ "card.healthStale": "Host-Route nicht verfügbar (404) — die Node-Hälfte ist noch ein alter Build; starten Sie dsh vollständig neu (nicht nur die Browserseite) und versuchen Sie es erneut.",
871
+ "card.healthTest": "Zustandstest",
872
+ "card.healthRunning": "Wird getestet…",
873
+ "card.healthOk": "OK (%ms ms)",
874
+ "card.healthFail": "Fehlgeschlagen: ",
875
+ "history.tag.learned": "Gelernt",
876
+ "history.tag.manualApproved": "Genehmigt",
877
+ "history.tag.manualRejected": "Abgelehnt",
878
+ "history.tag.manualCancelled": "Abgebrochen",
879
+ "history.tag.standDown": "Gate aus",
880
+ "history.learnedProgress": "gelernt %n/%t",
881
+ "history.snapshots": "Diff-Snapshots",
882
+ "history.clearSession": "Diese Sitzung löschen",
883
+ "history.clearAll": "Alle löschen",
884
+ "history.clearSessionTitle": "Nur die Diff-Snapshots dieser Sitzung löschen (andere Sitzungen unberührt)",
885
+ "history.clearAllTitle": "Diff-Snapshots aller Sitzungen löschen, einschließlich nie überprüfter — fragt erneut",
886
+ "history.clearSessionConfirm": "Diff-Snapshots dieser Sitzung löschen? Nur die von dieser Sitzung erzeugten Änderungsvergleichsdaten werden entfernt; die Genehmigungsprotokolle bleiben.",
887
+ "history.clearAllConfirm": "Diff-Snapshots ALLER Sitzungen löschen? Dies kann Änderungsvergleiche löschen, die andere Sitzungen noch nicht überprüft haben, und ist unwiderruflich.",
888
+ "history.diffTitle": "Dateiänderungsvergleich",
889
+ "history.diffClose": "Schließen",
890
+ "history.diffLoading": "Wird geladen…",
891
+ "history.diffEmpty": "Keine Inhaltsänderung (oder Datei unlesbar)",
892
+ "history.diffLoadFail": "Diff-Laden fehlgeschlagen: ",
893
+ "history.diffRevert": "Diese Änderung zurücksetzen",
894
+ "history.diffReverting": "Wird gesendet…",
895
+ "history.diffRevertDone": "Zurücksetzungsanweisung gesendet",
896
+ "history.diffRevertSent": "Zurücksetzungsanweisung an die Konversation gesendet; die KI stellt die Datei entsprechend wieder her",
897
+ "history.diffRevertFail": "Senden fehlgeschlagen: ",
898
+ "history.diffNoSnapshot": "Dieses Ereignis hat keinen Snapshot für diese Datei",
899
+ "history.diffChipTitle": "Änderungsvergleich dieser Datei anzeigen",
900
+ "history.diffStats": "+%a / -%r Zeilen geändert · %c unverändert",
901
+ "history.diffGone": " (Datei existiert nicht mehr)",
902
+ "history.diffHidden": "%n unveränderte Zeilen",
903
+ "card.dryRun": "Regeltest",
904
+ "card.dryRunHint": "Beurteilt einen Aufruf gegen die aktuellen Regeln. Führt kein Tool aus und schreibt keine Regel — prüfen Sie hier, bevor Sie ändern.",
905
+ "card.dryRunTool": "Werkzeug",
906
+ "card.dryRunCommand": "Befehl / Argumente",
907
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
908
+ "card.dryRunTest": "Testen",
909
+ "card.dryRunRunning": "Wird bewertet…",
910
+ "card.dryRunEmpty": "Noch nichts getestet. Geben Sie ein Werkzeug und einen Befehl ein und drücken Sie Testen, um das Verdikt zu sehen.",
911
+ "card.dryRunVerdict": "Verdikt",
912
+ "card.dryRunAllow": "erlauben",
913
+ "card.dryRunAsk": "anfragen",
914
+ "card.dryRunDeny": "ablehnen",
915
+ "card.dryRunRule": "Übereinstimmende Regel",
916
+ "card.dryRunRuleNone": "keine Regel gefunden; defaultAction: %d",
917
+ "card.dryRunDimensions": "Dimensionen",
918
+ "card.dryRunReason": "Begründung",
919
+ "card.dryRunNote": "„Übereinstimmende Regel\" stammt aus der Regelebene. Wenn das Verdikt von einer früheren Hartablehnung oder der Schlüsselwort-Blacklist erzeugt wurde, gibt es keinen Regelindex — die aufgelisteten Dimensionen sind dann nur informativ.",
920
+ "card.dryRunNoRules": "(0 Regeln geladen — prüfen Sie den rulesFile-Pfad)",
921
+ "card.dryRunFail": "Test fehlgeschlagen: ",
922
+ "card.dryRunStale": "Route fehlt (Plugin nicht neu gestartet?)",
923
+ "card.rules": "Einstellungen: Integrierte Regeln",
924
+ "card.rulesHint": "Die Regeln, die das Gate gerade lädt (aus dem rules-Konfigurationsfeld dieses Plugin-Eintrags; Fallback auf die Regeldatei, wenn nicht konfiguriert). Nur-Lesen — nichts hier kann sie ändern. Um Regeln zu ändern, verwenden Sie die Whitelist unten oder bearbeiten Sie sie auf der Einstellungsseite.",
925
+ "card.rulesRefresh": "Aktualisieren",
926
+ "card.rulesLoading": "Wird gelesen…",
927
+ "card.rulesPath": "Quelle",
928
+ "card.rulesNone": "keine Regeln konfiguriert",
929
+ "card.rulesCounts": "Regeln",
930
+ "card.rulesDefault": "defaultAction",
931
+ "card.rulesStats": "%b Bytes · %l Zeilen",
932
+ "card.rulesMissing": "Regeln nicht gefunden. Das Gate fällt auf seine integrierten Standardwerte zurück.",
933
+ "card.rulesTruncated": "Regeln sind groß; nur die ersten 512 KB werden angezeigt.",
934
+ "card.rulesError": "Laden fehlgeschlagen: ",
935
+ "card.rulesStale": "Route fehlt (Plugin nicht neu gestartet?)",
936
+ "card.rulesNote": "Nur-Lesen: Dieses Panel kann die Regeln nicht ändern. Der Whitelist-Abschnitt und „Immer erlauben\" schreiben in die Konfiguration dieses Plugin-Eintrags zurück."
937
+ },
938
+ it: {
939
+ "card.title": "Revisione automatica",
940
+ "section.title": "Portale di revisione automatica",
941
+ "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.",
942
+ "card.permissive": "Abilita il livello di revisione automatica",
943
+ "card.permissiveHint": "Quando disattivato, il portale si comporta esattamente come prima.",
944
+ "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.",
945
+ "card.strategies": "Strategie di approvazione backend (almeno una attiva; combinabili)",
946
+ "card.strategy.trustAutoAllow": "trustAutoAllow — le operazioni sicure nell'ambito sono auto-consentite; pericolose/sconosciute → richiesta (livello intermedio di base)",
947
+ "card.strategy.alwaysConfirm": "alwaysConfirm — ogni attraversamento chiede; i «controlli di consenso» del pannello di approvazione aggiungono due pulsanti estesi: «ripeti consenso per questo tipo in questa sessione» (grant limitato alla sessione) e «consenti tutte le occorrenze» (persistenza della parola comando nella whitelist rulesFile)",
948
+ "card.strategy.llmAssist": "llmAssist — un LLM personalizzato valuta il rischio: safe → auto-consenso; deletion/credential/remote/system/bulk → rischi rigidi mantengono la richiesta umana (il classificatore non rifiuta mai — scala solo); neutral → apprendimento per conferma; fallimento/timeout → ritorno all'umano",
949
+ "card.strategy.trustEscalation": "trustEscalation — escalation sandbox senza prompt: per una chiamata già consentita da questo portale, l'ampliamento sandbox avviato internamente da shell / pwsh / edit è approvato qui invece di chiedere a te",
950
+ "card.strategy.trustEscalationHint": "L'approvazione dell'escalation sandbox è richiesta dall'interno del corpo dello strumento, dopo tools/pre-execute, quindi il portale non la vede mai — una chiamata che il LLM ha giudicato sicura e che il portale ha auto-consentito ti chiedeva comunque di approvare l'ampliamento. Attivato, solo la chiamata esatta consentita dal portale (corrispondenza per callId) salta il prompt; tutto il resto va all'umano come prima, così come ogni motivo non riconosciuto o un target diverso da workspace-write / danger-full-access. Disattiva per mantenere l'ampliamento sotto controllo umano.",
951
+ "card.readonly": "Sola lettura",
952
+ "card.unavailable": "Namespace delle impostazioni non disponibile: assicurati che dsh-perm-gate sia assemblato in questo profilo.",
953
+ "card.llmReceiver": "LLM ricevente llmAssist (compatibile OpenAI; qualsiasi API personalizzata)",
954
+ "card.llmEndpoint": "Endpoint (BaseURL, es. https://api.openai.com/v1)",
955
+ "card.llmModel": "Model (ID modello, es. deepseek-chat)",
956
+ "card.llmKey": "API Key (segreta)",
957
+ "card.llmKeyHidden": "Non impostata · mostrata come * dopo il primo inserimento",
958
+ "card.llmKeyMasked": "Impostata · inserisci un nuovo valore per sovrascrivere",
959
+ "card.llmKeyOverwrite": "Un valore segreto è memorizzato; inserisci un nuovo valore e lascia il campo per sovrascriverlo.",
960
+ "card.llmKeyClear": "Cancella",
961
+ "card.allowlist": "Whitelist (allow, sincronizzata con rulesFile)",
962
+ "card.allowlistHint": "I pattern nella sezione allow consentono direttamente; aggiungi o rimuovi voci una alla volta, scritte in rulesFile e ricaricate in tempo reale.",
963
+ "card.allowlistPresetNote": "L'equivalente della whitelist predefinita è la strategia trustAutoAllow (operazioni sicure nell'ambito auto-consentite), attivata nella sezione strategie sopra; questa lista rispecchia la tua sezione allow di rulesFile e inizia legittimamente vuota.",
964
+ "card.allowlistEmpty": "Nessuna regola whitelist",
965
+ "card.allowlistPlaceholder": "Nuovo pattern di comando, es. npm test*",
966
+ "card.allowlistAdd": "Aggiungi",
967
+ "card.allowlistRemove": "Rimuovi questo pattern",
968
+ "card.allowlistBulk": "Modifica in blocco",
969
+ "card.allowlistBulkHide": "Nascondi modifica in blocco",
970
+ "card.allowlistSave": "Salva whitelist",
971
+ "card.riskLearning": "riskLearning — apprendimento del verdetto: le operazioni a rischio neutral approvate ed eseguite vengono conteggiate; la stessa operazione è auto-consentita alla soglia con impronta corrispondente",
972
+ "card.riskLearningHint": "Lo stato di apprendimento appartiene al plugin ($DSH_HOME/perm-gate/learning.json) e non viene mai scritto nelle tue regole YAML; un target diverso non riutilizza mai l'autorità. Disattivato per impostazione predefinita.",
973
+ "card.riskThreshold": "Conferme umane richieste prima dell'auto-consenso (1–10)",
974
+ "notice.label": "Attività del portale autorizzazioni",
975
+ "history.label": "Approvazioni",
976
+ "history.title": "Registro approvazioni autorizzazioni",
977
+ "history.subtitle": "Ogni decisione del portale in questa sessione, più recenti prima",
978
+ "history.loading": "Caricamento…",
979
+ "history.empty": "Nessun record di approvazione in questa sessione",
980
+ "history.error": "Caricamento non riuscito: ",
981
+ "history.tag.auto": "Auto-consentito",
982
+ "history.tag.ask": "Richiesto",
983
+ "history.tag.deny": "Rifiutato",
984
+ "card.denylistRestore": "Ripristina blacklist predefinita",
985
+ "card.denylist": "Blacklist (parole chiave deny, preset plugin)",
986
+ "card.denylistHint": "Una chiamata il cui testo contiene una parola chiave (sottostringa senza distinzione maiuscole/minuscole) è rifiutata immediatamente, prima di whitelist / grant / LLM. La lista predefinita eredita da dsh-approval-gate ed è attiva dall'installazione; modifica liberamente — vuota o cancellata torna al predefinito (ripristino con un clic), quindi la blacklist non si disattiva mai silenziosamente.",
987
+ "card.denylistEmpty": "Blacklist vuota (nessuna chiamata è respinta dal livello parole chiave)",
988
+ "card.denylistPlaceholder": "Nuova parola chiave, es. drop database",
989
+ "card.denylistAdd": "Aggiungi",
990
+ "card.denylistRemove": "Rimuovi questa parola chiave",
991
+ "card.denylistTagPreset": "Preset",
992
+ "card.denylistTagCustom": "Personalizzato",
993
+ "card.riskSediment": "riskSediment — sedimentazione dell'apprendimento: i campioni alla soglia diventano regole deterministiche di auto-consenso",
994
+ "card.riskSedimentHint": "Attivato, quando una chiave tool|category raggiunge la soglia le sue impronte campione si sedimentano in regole di consenso — una corrispondenza esatta stesso strumento/stesso target consente direttamente senza altra chiamata LLM (continua a funzionare con llmAssist disattivato).",
995
+ "card.sediment": "Sedimento di apprendimento (regole deterministiche di consenso)",
996
+ "card.sedimentHint": "Regole sedimentate dalle conferme umane: solo una corrispondenza esatta dell'impronta consente, mai un target diverso. Termina un apprendimento o rimuovi un campione.",
997
+ "card.sedimentEmpty": "Nessuna regola sedimentata per ora (i campioni che raggiungono la soglia appaiono qui)",
998
+ "card.sedimentCount": "confermato %n/%t",
999
+ "card.sedimentStop": "Termina",
1000
+ "card.sedimentStopTitle": "Termina questo apprendimento (elimina conteggio e campioni)",
1001
+ "card.sedimentStopConfirm": "Terminare l'apprendimento per «%k»? Il conteggio e tutti i campioni saranno eliminati.",
1002
+ "card.sedimentSampleRemove": "Rimuovi questo campione sedimentato",
1003
+ "card.llmSource": "Sorgente LLM ricevente",
1004
+ "card.llmSourceCustom": "API personalizzata (compatibile OpenAI)",
1005
+ "card.llmSourceHost": "Usa il modello predefinito attuale della sessione (servizio DSH llm)",
1006
+ "card.llmSourceHostHint": "Per impostazione predefinita la valutazione dell'approvazione usa il gruppo di modelli attualmente selezionato nella sessione DSH (il valore effettivo è mostrato sotto); lascia Provider / Model vuoto per seguire la sessione, oppure scegli dal menu a tendina o digita per fissare — i gruppi personalizzati configurati in DSH (es. local-35b) appaiono automaticamente.",
1007
+ "card.llmProvider": "Provider (vuoto = segue la selezione della sessione)",
1008
+ "card.llmProviderPlaceholder": "Segue la selezione della sessione",
1009
+ "card.llmModelHostPlaceholder": "Vuoto = segue la selezione della sessione",
1010
+ "card.llmResolved": "Attualmente effettivo: %p / %m",
1011
+ "card.llmPreset": "Preset endpoint (compila i campi; ancora modificabile)",
1012
+ "card.llmPresetChoose": "Scegli un preset…",
1013
+ "card.llmPresetPublic": "Preset API pubbliche",
1014
+ "card.llmPresetHost": "Gruppi modelli DSH (la selezione passa al ricevitore di sessione)",
1015
+ "card.networkEnabled": "Intercettazione rete (networkEnabled)",
1016
+ "card.networkEnabledHint": "Attivato, avvia un proxy locale che intercetta le connessioni in uscita dei sottoprocessi e applica regole di consenso/rifiuto. Disattivato = nessun cambiamento di comportamento.",
1017
+ "card.watch": "Ricaricamento a caldo regole (watch)",
1018
+ "card.watchHint": "Attivato, monitora automaticamente le modifiche ai file di regole e le applica istantaneamente senza ricaricamento manuale.",
1019
+ "card.networkRebindNote": "Ha effetto immediato — il proxy si ricollega automaticamente, nessun ricaricamento del plugin necessario.",
1020
+ "card.networkInjectEnv": "Inietta variabili d'ambiente proxy (networkInjectEnv)",
1021
+ "card.networkInjectEnvHint": "Scrive HTTP(S)_PROXY / ALL_PROXY per i sottoprocessi (e svuota NO_PROXY). Disattivato = il listener è in esecuzione ma nessun ambiente di processo viene modificato.",
1022
+ "card.healthStale": "Route host non disponibile (404) — la metà node è ancora una build vecchia; riavvia completamente dsh (non solo la pagina del browser) e riprova.",
1023
+ "card.healthTest": "Test di salute",
1024
+ "card.healthRunning": "Test in corso…",
1025
+ "card.healthOk": "OK (%ms ms)",
1026
+ "card.healthFail": "Fallito: ",
1027
+ "history.tag.learned": "Appreso",
1028
+ "history.tag.manualApproved": "Approvato",
1029
+ "history.tag.manualRejected": "Rifiutato",
1030
+ "history.tag.manualCancelled": "Annullato",
1031
+ "history.tag.standDown": "Portale inattivo",
1032
+ "history.learnedProgress": "appreso %n/%t",
1033
+ "history.snapshots": "snapshot diff",
1034
+ "history.clearSession": "Cancella questa sessione",
1035
+ "history.clearAll": "Cancella tutto",
1036
+ "history.clearSessionTitle": "Cancella solo gli snapshot diff di questa sessione (altre sessioni intatte)",
1037
+ "history.clearAllTitle": "Cancella gli snapshot diff di tutte le sessioni, incluse quelle mai revisionate — chiede di nuovo",
1038
+ "history.clearSessionConfirm": "Cancellare gli snapshot diff di questa sessione? Solo i dati di confronto prodotti dalle approvazioni di questa sessione sono rimossi; i record di approvazione restano.",
1039
+ "history.clearAllConfirm": "Cancellare gli snapshot diff di TUTTE le sessioni? Potrebbe eliminare confronti che altre sessioni non hanno ancora revisionato, ed è irreversibile.",
1040
+ "history.diffTitle": "Confronto modifiche file",
1041
+ "history.diffClose": "Chiudi",
1042
+ "history.diffLoading": "Caricamento…",
1043
+ "history.diffEmpty": "Nessuna modifica al contenuto (o file illeggibile)",
1044
+ "history.diffLoadFail": "Caricamento diff non riuscito: ",
1045
+ "history.diffRevert": "Annulla questa modifica",
1046
+ "history.diffReverting": "Invio…",
1047
+ "history.diffRevertDone": "Istruzione di ripristino inviata",
1048
+ "history.diffRevertSent": "Istruzione di ripristino inviata alla conversazione; l'IA ripristinerà il file di conseguenza",
1049
+ "history.diffRevertFail": "Invio non riuscito: ",
1050
+ "history.diffNoSnapshot": "Questo evento non ha uno snapshot per quel file",
1051
+ "history.diffChipTitle": "Visualizza il confronto modifiche di questo file",
1052
+ "history.diffStats": "+%a / -%r righe modificate · %c invariate",
1053
+ "history.diffGone": " (il file non esiste più)",
1054
+ "history.diffHidden": "%n righe non modificate",
1055
+ "card.dryRun": "Test regole",
1056
+ "card.dryRunHint": "Giudica una chiamata contro le regole in vigore. Non esegue alcun strumento e non scrive alcuna regola — verifica qui prima di modificare.",
1057
+ "card.dryRunTool": "Strumento",
1058
+ "card.dryRunCommand": "Comando / argomenti",
1059
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
1060
+ "card.dryRunTest": "Testa",
1061
+ "card.dryRunRunning": "Valutazione…",
1062
+ "card.dryRunEmpty": "Nulla di testato finora. Inserisci uno strumento e un comando, poi premi Testa per vedere il verdetto.",
1063
+ "card.dryRunVerdict": "Verdetto",
1064
+ "card.dryRunAllow": "consenti",
1065
+ "card.dryRunAsk": "richiedi",
1066
+ "card.dryRunDeny": "rifiuta",
1067
+ "card.dryRunRule": "Regola corrispondente",
1068
+ "card.dryRunRuleNone": "nessuna regola corrispondente; defaultAction: %d",
1069
+ "card.dryRunDimensions": "Dimensioni",
1070
+ "card.dryRunReason": "Motivo",
1071
+ "card.dryRunNote": "«Regola corrispondente» proviene dal livello regole. Quando il verdetto è prodotto da un rifiuto rigido precedente o dalla blacklist di parole chiave non c'è indice di regola — le dimensioni elencate sono solo informative.",
1072
+ "card.dryRunNoRules": "(0 regole caricate — controlla il percorso rulesFile)",
1073
+ "card.dryRunFail": "Test non riuscito: ",
1074
+ "card.dryRunStale": "route mancante (plugin non riavviato?)",
1075
+ "card.rules": "Regole integrate delle impostazioni",
1076
+ "card.rulesHint": "Le regole che il portale sta caricando ora (dal campo di configurazione rules di questa voce plugin; fallback al file regole se non configurato). Sola lettura — nulla qui può modificarle. Per cambiare le regole, usa la whitelist sotto o modificale nella pagina impostazioni.",
1077
+ "card.rulesRefresh": "Aggiorna",
1078
+ "card.rulesLoading": "Lettura…",
1079
+ "card.rulesPath": "Sorgente",
1080
+ "card.rulesNone": "nessuna regola configurata",
1081
+ "card.rulesCounts": "Regole",
1082
+ "card.rulesDefault": "defaultAction",
1083
+ "card.rulesStats": "%b byte · %l righe",
1084
+ "card.rulesMissing": "Regole non trovate. Il portale ripiega sui suoi valori predefiniti integrati.",
1085
+ "card.rulesTruncated": "Regole grandi; mostrati solo i primi 512 KB.",
1086
+ "card.rulesError": "Caricamento non riuscito: ",
1087
+ "card.rulesStale": "route mancante (plugin non riavviato?)",
1088
+ "card.rulesNote": "Sola lettura: questo pannello non può modificare le regole. La sezione whitelist e «consenti sempre» scrivono nella configurazione di questa voce plugin."
1089
+ },
1090
+ ru: {
1091
+ "card.title": "Автоматическая проверка",
1092
+ "section.title": "Шлюз автоматической проверки",
1093
+ "card.description": "Независимый уровень одобрения: параллельно с «только чтение» / «полный доступ» / «белый список», отображается как «Автоматическая проверка» в выборе разрешений сессии. Фронтенд предоставляет один переключатель; четыре серверные стратегии одобрения комбинируемы через эту панель и остаются fail-closed против жёсткого отказа P0.",
1094
+ "card.permissive": "Включить уровень автоматической проверки",
1095
+ "card.permissiveHint": "Когда выключено, шлюз ведёт себя точно так же, как раньше.",
1096
+ "card.scopeNote": "Область пресета: этот шлюз действует только когда пресет разрешений сессии — %scope. При любом другом пресете весь шлюз отключается — включая жёсткий отказ P0 — и политика выбранного пресета берёт управление на себя. Отключение записывает событие и оставляет постоянную полоску над полем ввода.",
1097
+ "card.strategies": "Серверные стратегии одобрения (хотя бы одна активна; комбинируемы)",
1098
+ "card.strategy.trustAutoAllow": "trustAutoAllow — безопасные операции в области автоматически разрешаются; опасные/неизвестные → запрос (базовый средний уровень)",
1099
+ "card.strategy.alwaysConfirm": "alwaysConfirm — каждый переход спрашивает; «элементы управления разрешением» панели одобрения получают две расширенные кнопки: «повторно разрешить этот тип в этой сессии» (ограниченный grant сессии) и «разрешить все вхождения» (постоянное добавление слова команды в белый список rulesFile)",
1100
+ "card.strategy.llmAssist": "llmAssist — пользовательская LLM оценивает риск: safe → авто-разрешение; deletion/credential/remote/system/bulk → жёсткие риски сохраняют запрос к человеку (классификатор никогда не отказывает — только эскалирует); neutral → обучение подтверждением; сбой/таймаут → возврат к человеку",
1101
+ "card.strategy.trustEscalation": "trustEscalation — эскалация песочницы без запроса: для вызова, уже разрешённого этим шлюзом, расширение песочницы, инициированное shell / pwsh / edit изнутри инструмента, одобряется здесь вместо запроса к вам",
1102
+ "card.strategy.trustEscalationHint": "Запрос на одобрение эскалации песочницы происходит изнутри тела инструмента после tools/pre-execute, поэтому шлюз его не видит — вызов, который LLM оценила как безопасный и шлюз авто-разрешил, всё равно спрашивал вас об одобрении расширения. При включении только точный вызов, разрешённый шлюзом (совпадение по callId), пропускает запрос; всё остальное идёт к человеку как раньше, равно как и любая нераспознанная причина или цель, отличная от workspace-write / danger-full-access. Выключите, чтобы расширение оставалось под контролем человека.",
1103
+ "card.readonly": "Только чтение",
1104
+ "card.unavailable": "Пространство имён настроек недоступно: убедитесь, что dsh-perm-gate собран в этом профиле.",
1105
+ "card.llmReceiver": "Принимающая LLM для llmAssist (совместимая с OpenAI; любой пользовательский API)",
1106
+ "card.llmEndpoint": "Endpoint (BaseURL, напр. https://api.openai.com/v1)",
1107
+ "card.llmModel": "Model (идентификатор модели, напр. deepseek-chat)",
1108
+ "card.llmKey": "API Key (секрет)",
1109
+ "card.llmKeyHidden": "Не задан · после первого ввода отображается как *",
1110
+ "card.llmKeyMasked": "Задан · введите новое значение для перезаписи",
1111
+ "card.llmKeyOverwrite": "Секретное значение сохранено; введите новое значение и покиньте поле для перезаписи.",
1112
+ "card.llmKeyClear": "Очистить",
1113
+ "card.allowlist": "Белый список (allow, синхронизирован с rulesFile)",
1114
+ "card.allowlistHint": "Паттерны в секции allow разрешаются напрямую; добавляйте или удаляйте записи по одной, они записываются в rulesFile и перезагружаются в реальном времени.",
1115
+ "card.allowlistPresetNote": "Эквивалент пресетного белого списка — стратегия trustAutoAllow (безопасные операции в области автоматически разрешаются), активируется в секции стратегий выше; этот список отражает вашу секцию allow в rulesFile и закономерно начинается пустым.",
1116
+ "card.allowlistEmpty": "Нет правил белого списка",
1117
+ "card.allowlistPlaceholder": "Новый паттерн команды, напр. npm test*",
1118
+ "card.allowlistAdd": "Добавить",
1119
+ "card.allowlistRemove": "Удалить этот паттерн",
1120
+ "card.allowlistBulk": "Массовое редактирование",
1121
+ "card.allowlistBulkHide": "Скрыть массовое редактирование",
1122
+ "card.allowlistSave": "Сохранить белый список",
1123
+ "card.riskLearning": "riskLearning — обучение вердиктам: операции с нейтральным риском, одобренные человеком и выполненные, подсчитываются; та же операция автоматически разрешается при достижении порога с совпадающим отпечатком",
1124
+ "card.riskLearningHint": "Состояние обучения принадлежит плагину ($DSH_HOME/perm-gate/learning.json) и никогда не записывается в ваши YAML-правила; другая цель никогда не переиспользует полномочие. По умолчанию выключено.",
1125
+ "card.riskThreshold": "Подтверждений человеком перед авто-разрешением (1–10)",
1126
+ "notice.label": "Активность шлюза разрешений",
1127
+ "history.label": "Одобрения",
1128
+ "history.title": "Журнал одобрений разрешений",
1129
+ "history.subtitle": "Каждое решение шлюза в этой сессии, последние сверху",
1130
+ "history.loading": "Загрузка…",
1131
+ "history.empty": "В этой сессии пока нет записей одобрения",
1132
+ "history.error": "Ошибка загрузки: ",
1133
+ "history.tag.auto": "Авто-разрешено",
1134
+ "history.tag.ask": "Запрошено",
1135
+ "history.tag.deny": "Отклонено",
1136
+ "card.denylistRestore": "Восстановить пресетный чёрный список",
1137
+ "card.denylist": "Чёрный список (ключевые слова deny, пресет плагина)",
1138
+ "card.denylistHint": "Вызов, текст которого содержит ключевое слово (подстрока без учёта регистра), немедленно отклоняется — до белого списка / grants / LLM. Пресетный список наследуется от dsh-approval-gate и активен с момента установки; редактируйте свободно — пустой или очищенный возвращается к пресету (восстановление в один клик), поэтому чёрный список никогда не отключается молча.",
1139
+ "card.denylistEmpty": "Чёрный список пуст (ни один вызов не отклоняется на уровне ключевых слов)",
1140
+ "card.denylistPlaceholder": "Новое ключевое слово, напр. drop database",
1141
+ "card.denylistAdd": "Добавить",
1142
+ "card.denylistRemove": "Удалить это ключевое слово",
1143
+ "card.denylistTagPreset": "Пресет",
1144
+ "card.denylistTagCustom": "Пользовательское",
1145
+ "card.riskSediment": "riskSediment — седиментация обучения: образцы, достигшие порога, становятся детерминированными правилами авто-разрешения",
1146
+ "card.riskSedimentHint": "При включении, когда ключ tool|category достигает порога, отпечатки его образцов оседают в правила разрешения — точное совпадение тот же инструмент/та же цель разрешает напрямую без нового вызова LLM (продолжает работать с выключенным llmAssist).",
1147
+ "card.sediment": "Осадок обучения (детерминированные правила разрешения)",
1148
+ "card.sedimentHint": "Правила, осевшие из подтверждений человека: только точное совпадение отпечатка разрешает, никогда другая цель. Завершите одно обучение или удалите один образец.",
1149
+ "card.sedimentEmpty": "Пока нет осаждённых правил (образцы, достигшие порога, появятся здесь)",
1150
+ "card.sedimentCount": "подтверждено %n/%t",
1151
+ "card.sedimentStop": "Завершить",
1152
+ "card.sedimentStopTitle": "Завершить это обучение (удаляет счётчик и образцы)",
1153
+ "card.sedimentStopConfirm": "Завершить обучение для «%k»? Его счётчик и все образцы будут удалены.",
1154
+ "card.sedimentSampleRemove": "Удалить этот осаждённый образец",
1155
+ "card.llmSource": "Источник принимающей LLM",
1156
+ "card.llmSourceCustom": "Пользовательский API (совместимый с OpenAI)",
1157
+ "card.llmSourceHost": "Использовать текущую модель по умолчанию сессии (сервис DSH llm)",
1158
+ "card.llmSourceHostHint": "По умолчанию оценка одобрения выполняется на группе моделей, выбранной в текущей сессии DSH (эффективное значение показано ниже); оставьте Provider / Model пустым, чтобы следовать за сессией, или выберите из выпадающего списка / введите для фиксации — ваши пользовательские группы, настроенные в DSH (напр. local-35b), появляются автоматически.",
1159
+ "card.llmProvider": "Provider (пусто = следует выбору сессии)",
1160
+ "card.llmProviderPlaceholder": "Следует выбору сессии",
1161
+ "card.llmModelHostPlaceholder": "Пусто = следует выбору сессии",
1162
+ "card.llmResolved": "Текущий эффективный: %p / %m",
1163
+ "card.llmPreset": "Пресеты эндпоинтов (заполняет поля; далее редактируемо)",
1164
+ "card.llmPresetChoose": "Выбрать пресет…",
1165
+ "card.llmPresetPublic": "Пресеты публичных API",
1166
+ "card.llmPresetHost": "Группы моделей DSH (выбор переключает на приёмник сессии)",
1167
+ "card.networkEnabled": "Перехват сети (networkEnabled)",
1168
+ "card.networkEnabledHint": "При включении запускается локальный прокси, перехватывающий исходящие соединения подпроцессов и применяющий правила разрешения/отказа. Выключено = никаких изменений поведения.",
1169
+ "card.watch": "Горячая перезагрузка правил (watch)",
1170
+ "card.watchHint": "При включении автоматически отслеживает изменения файлов правил и применяет их мгновенно без ручной перезагрузки.",
1171
+ "card.networkRebindNote": "Вступает в силу немедленно — прокси автоматически перепривязывается, перезагрузка плагина не нужна.",
1172
+ "card.networkInjectEnv": "Инъекция переменных окружения прокси (networkInjectEnv)",
1173
+ "card.networkInjectEnvHint": "Записывает HTTP(S)_PROXY / ALL_PROXY для подпроцессов (и очищает NO_PROXY). Выключено = слушатель работает, но окружение процессов не изменяется.",
1174
+ "card.healthStale": "Маршрут хоста недоступен (404) — половина node всё ещё старой сборки; полностью перезапустите dsh (не только страницу браузера) и повторите.",
1175
+ "card.healthTest": "Тест состояния",
1176
+ "card.healthRunning": "Тестирование…",
1177
+ "card.healthOk": "OK (%ms мс)",
1178
+ "card.healthFail": "Ошибка: ",
1179
+ "history.tag.learned": "Изучено",
1180
+ "history.tag.manualApproved": "Одобрено",
1181
+ "history.tag.manualRejected": "Отклонено",
1182
+ "history.tag.manualCancelled": "Отменено",
1183
+ "history.tag.standDown": "Шлюз выключен",
1184
+ "history.learnedProgress": "изучено %n/%t",
1185
+ "history.snapshots": "снимки diff",
1186
+ "history.clearSession": "Очистить эту сессию",
1187
+ "history.clearAll": "Очистить всё",
1188
+ "history.clearSessionTitle": "Очистить только снимки diff этой сессии (другие сессии не затрагиваются)",
1189
+ "history.clearAllTitle": "Очистить снимки diff всех сессий, включая непросмотренные — запрашивает подтверждение",
1190
+ "history.clearSessionConfirm": "Очистить снимки diff этой сессии? Удаляются только данные сравнения, созданные одобрениями этой сессии; записи одобрений остаются.",
1191
+ "history.clearAllConfirm": "Очистить снимки diff ВСЕХ сессий? Это может удалить сравнения, которые другие сессии ещё не просмотрели, и необратимо.",
1192
+ "history.diffTitle": "Сравнение изменений файла",
1193
+ "history.diffClose": "Закрыть",
1194
+ "history.diffLoading": "Загрузка…",
1195
+ "history.diffEmpty": "Нет изменений содержимого (или файл нечитаем)",
1196
+ "history.diffLoadFail": "Ошибка загрузки diff: ",
1197
+ "history.diffRevert": "Отменить это изменение",
1198
+ "history.diffReverting": "Отправка…",
1199
+ "history.diffRevertDone": "Инструкция отмены отправлена",
1200
+ "history.diffRevertSent": "Инструкция отмены отправлена в диалог; ИИ восстановит файл соответственно",
1201
+ "history.diffRevertFail": "Ошибка отправки: ",
1202
+ "history.diffNoSnapshot": "У этого события нет снимка для данного файла",
1203
+ "history.diffChipTitle": "Просмотреть сравнение изменений этого файла",
1204
+ "history.diffStats": "+%a / -%r строк изменено · %c без изменений",
1205
+ "history.diffGone": " (файл больше не существует)",
1206
+ "history.diffHidden": "%n неизменённых строк",
1207
+ "card.dryRun": "Тест правил",
1208
+ "card.dryRunHint": "Оценивает один вызов по действующим правилам. Не выполняет инструмент и не записывает правило — проверьте здесь перед изменением.",
1209
+ "card.dryRunTool": "Инструмент",
1210
+ "card.dryRunCommand": "Команда / аргументы",
1211
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
1212
+ "card.dryRunTest": "Тестировать",
1213
+ "card.dryRunRunning": "Оценка…",
1214
+ "card.dryRunEmpty": "Пока ничего не протестировано. Укажите инструмент и команду, затем нажмите «Тестировать», чтобы увидеть вердикт.",
1215
+ "card.dryRunVerdict": "Вердикт",
1216
+ "card.dryRunAllow": "разрешить",
1217
+ "card.dryRunAsk": "запросить",
1218
+ "card.dryRunDeny": "отклонить",
1219
+ "card.dryRunRule": "Совпавшее правило",
1220
+ "card.dryRunRuleNone": "ни одно правило не совпало; defaultAction: %d",
1221
+ "card.dryRunDimensions": "Измерения",
1222
+ "card.dryRunReason": "Причина",
1223
+ "card.dryRunNote": "«Совпавшее правило» происходит из уровня правил. Когда вердикт выработан более ранним жёстким отказом или чёрным списком ключевых слов, индекса правила нет — перечисленные измерения носят справочный характер.",
1224
+ "card.dryRunNoRules": "(загружено 0 правил — проверьте путь rulesFile)",
1225
+ "card.dryRunFail": "Ошибка теста: ",
1226
+ "card.dryRunStale": "маршрут отсутствует (плагин не перезапущен?)",
1227
+ "card.rules": "Встроенные правила настроек",
1228
+ "card.rulesHint": "Правила, которые шлюз загружает сейчас (из поля конфигурации rules этой записи плагина; откат к файлу правил, если не настроено). Только чтение — ничто здесь не может их изменить. Чтобы изменить правила, используйте белый список ниже или редактируйте на странице настроек.",
1229
+ "card.rulesRefresh": "Обновить",
1230
+ "card.rulesLoading": "Чтение…",
1231
+ "card.rulesPath": "Источник",
1232
+ "card.rulesNone": "правила не настроены",
1233
+ "card.rulesCounts": "Правила",
1234
+ "card.rulesDefault": "defaultAction",
1235
+ "card.rulesStats": "%b байт · %l строк",
1236
+ "card.rulesMissing": "Правила не найдены. Шлюз откатывается к встроенным значениям по умолчанию.",
1237
+ "card.rulesTruncated": "Правила большие; показаны только первые 512 КБ.",
1238
+ "card.rulesError": "Ошибка загрузки: ",
1239
+ "card.rulesStale": "маршрут отсутствует (плагин не перезапущен?)",
1240
+ "card.rulesNote": "Только чтение: эта панель не может изменять правила. Секция белого списка и «всегда разрешать» записывают обратно в конфигурацию этой записи плагина."
1241
+ },
1242
+ es: {
1243
+ "card.title": "Revisión automática",
1244
+ "section.title": "Compuerta de revisión automática",
1245
+ "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.",
1246
+ "card.permissive": "Habilitar el nivel de revisión automática",
1247
+ "card.permissiveHint": "Cuando está desactivado, la compuerta se comporta exactamente igual que antes.",
1248
+ "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.",
1249
+ "card.strategies": "Estrategias de aprobación del backend (al menos una activa; combinables)",
1250
+ "card.strategy.trustAutoAllow": "trustAutoAllow — las operaciones seguras dentro del ámbito se auto-permiten; peligrosas/desconocidas → consulta (nivel intermedio base)",
1251
+ "card.strategy.alwaysConfirm": "alwaysConfirm — cada cruce pregunta; los «controles de permiso» del panel de aprobación reciben dos botones extendidos: «repetir permiso para este tipo esta sesión» (grant acotado de sesión) y «permitir todas las ocurrencias» (persistir la palabra de comando en la lista blanca de rulesFile)",
1252
+ "card.strategy.llmAssist": "llmAssist — un LLM personalizado evalúa el riesgo: safe → auto-permiso; deletion/credential/remote/system/bulk → riesgos duros mantienen la consulta humana (el clasificador nunca deniega — solo escala); neutral → aprendizaje por confirmación; fallo/timeout → retorno al humano",
1253
+ "card.strategy.trustEscalation": "trustEscalation — escalado de sandbox sin aviso: para una llamada ya permitida por esta compuerta, la ampliación de sandbox iniciada internamente por shell / pwsh / edit se aprueba aquí en lugar de preguntarte",
1254
+ "card.strategy.trustEscalationHint": "La aprobación del escalado de sandbox se solicita desde dentro del cuerpo de la herramienta, después de tools/pre-execute, así que la compuerta nunca la ve — una llamada que el LLM calificó como segura y que la compuerta auto-permitió aun así te pedía aprobar la ampliación. Activado, solo la llamada exacta que la compuerta permitió (coincidencia por callId) omite el aviso; todo lo demás va al humano como antes, al igual que cualquier motivo no reconocido o un destino distinto de workspace-write / danger-full-access. Desactívalo para mantener la ampliación bajo control humano.",
1255
+ "card.readonly": "Solo lectura",
1256
+ "card.unavailable": "Espacio de nombres de configuración no disponible: asegúrate de que dsh-perm-gate esté ensamblado en este perfil.",
1257
+ "card.llmReceiver": "LLM receptor de llmAssist (compatible con OpenAI; cualquier API personalizada)",
1258
+ "card.llmEndpoint": "Endpoint (BaseURL, ej. https://api.openai.com/v1)",
1259
+ "card.llmModel": "Model (id del modelo, ej. deepseek-chat)",
1260
+ "card.llmKey": "API Key (secreta)",
1261
+ "card.llmKeyHidden": "No establecida · se muestra como * tras la primera entrada",
1262
+ "card.llmKeyMasked": "Establecida · introduce un nuevo valor para sobrescribir",
1263
+ "card.llmKeyOverwrite": "Hay un valor secreto almacenado; introduce un nuevo valor y sal del campo para sobrescribirlo.",
1264
+ "card.llmKeyClear": "Borrar",
1265
+ "card.allowlist": "Lista blanca (allow, sincronizada con rulesFile)",
1266
+ "card.allowlistHint": "Los patrones en la sección allow se permiten directamente; agrega o elimina entradas una por una, se escriben en rulesFile y se recargan en vivo.",
1267
+ "card.allowlistPresetNote": "El equivalente de la lista blanca predefinida es la estrategia trustAutoAllow (operaciones seguras en el ámbito auto-permitidas), habilitada en la sección de estrategias de arriba; esta lista refleja tu sección allow de rulesFile y legítimamente comienza vacía.",
1268
+ "card.allowlistEmpty": "Sin reglas de lista blanca",
1269
+ "card.allowlistPlaceholder": "Nuevo patrón de comando, ej. npm test*",
1270
+ "card.allowlistAdd": "Agregar",
1271
+ "card.allowlistRemove": "Eliminar este patrón",
1272
+ "card.allowlistBulk": "Edición masiva",
1273
+ "card.allowlistBulkHide": "Ocultar edición masiva",
1274
+ "card.allowlistSave": "Guardar lista blanca",
1275
+ "card.riskLearning": "riskLearning — aprendizaje de veredictos: las operaciones de riesgo neutral aprobadas por el humano y ejecutadas se contabilizan; la misma operación se auto-permite al umbral con huella coincidente",
1276
+ "card.riskLearningHint": "El estado de aprendizaje pertenece al plugin ($DSH_HOME/perm-gate/learning.json) y nunca se escribe en tus reglas YAML; un destino diferente nunca reutiliza la autoridad. Desactivado por defecto.",
1277
+ "card.riskThreshold": "Confirmaciones humanas requeridas antes del auto-permiso (1–10)",
1278
+ "notice.label": "Actividad de la compuerta de permisos",
1279
+ "history.label": "Aprobaciones",
1280
+ "history.title": "Registro de aprobaciones de permisos",
1281
+ "history.subtitle": "Cada decisión de la compuerta en esta sesión, más recientes primero",
1282
+ "history.loading": "Cargando…",
1283
+ "history.empty": "Aún no hay registros de aprobación en esta sesión",
1284
+ "history.error": "Error al cargar: ",
1285
+ "history.tag.auto": "Auto-permitido",
1286
+ "history.tag.ask": "Consultado",
1287
+ "history.tag.deny": "Denegado",
1288
+ "card.denylistRestore": "Restaurar lista negra predefinida",
1289
+ "card.denylist": "Lista negra (palabras clave deny, preajuste del plugin)",
1290
+ "card.denylistHint": "Una llamada cuyo texto contiene una palabra clave (subcadena sin distinción de mayúsculas) se deniega de inmediato, antes que lista blanca / grants / LLM. La lista predefinida hereda de dsh-approval-gate y está activa desde la instalación; edita libremente — vacía o borrada vuelve al preajuste (restauración con un clic), así que la lista negra nunca se desactiva silenciosamente.",
1291
+ "card.denylistEmpty": "Lista negra vacía (ninguna llamada es vetada por la capa de palabras clave)",
1292
+ "card.denylistPlaceholder": "Nueva palabra clave, ej. drop database",
1293
+ "card.denylistAdd": "Agregar",
1294
+ "card.denylistRemove": "Eliminar esta palabra clave",
1295
+ "card.denylistTagPreset": "Preajuste",
1296
+ "card.denylistTagCustom": "Personalizado",
1297
+ "card.riskSediment": "riskSediment — sedimentación del aprendizaje: las muestras que alcanzan el umbral se convierten en reglas deterministas de auto-permiso",
1298
+ "card.riskSedimentHint": "Activado, cuando una clave tool|category alcanza el umbral sus huellas de muestra se sedimentan en reglas de permiso — una coincidencia exacta misma herramienta/mismo destino permite directamente sin otra llamada LLM (sigue funcionando con llmAssist desactivado).",
1299
+ "card.sediment": "Sedimento de aprendizaje (reglas deterministas de permiso)",
1300
+ "card.sedimentHint": "Reglas sedimentadas a partir de confirmaciones humanas: solo una coincidencia exacta de huella permite, nunca un destino diferente. Termina un aprendizaje o elimina una muestra.",
1301
+ "card.sedimentEmpty": "Aún no hay reglas sedimentadas (las muestras que alcanzan el umbral aparecen aquí)",
1302
+ "card.sedimentCount": "confirmado %n/%t",
1303
+ "card.sedimentStop": "Terminar",
1304
+ "card.sedimentStopTitle": "Terminar este aprendizaje (elimina el contador y las muestras)",
1305
+ "card.sedimentStopConfirm": "¿Terminar el aprendizaje de «%k»? Se eliminarán su contador y todas las muestras.",
1306
+ "card.sedimentSampleRemove": "Eliminar esta muestra sedimentada",
1307
+ "card.llmSource": "Fuente del LLM receptor",
1308
+ "card.llmSourceCustom": "API personalizada (compatible con OpenAI)",
1309
+ "card.llmSourceHost": "Usar el modelo predeterminado actual de la sesión (servicio DSH llm)",
1310
+ "card.llmSourceHostHint": "Por defecto la evaluación de aprobación se ejecuta en el grupo de modelos seleccionado actualmente en la sesión DSH (el valor efectivo se muestra abajo); deja Provider / Model vacío para seguir la sesión, o elige del desplegable o escribe para fijar — tus grupos personalizados configurados en DSH (ej. local-35b) aparecen automáticamente.",
1311
+ "card.llmProvider": "Provider (vacío = sigue la selección de sesión)",
1312
+ "card.llmProviderPlaceholder": "Sigue la selección de sesión",
1313
+ "card.llmModelHostPlaceholder": "Vacío = sigue la selección de sesión",
1314
+ "card.llmResolved": "Efectivo ahora: %p / %m",
1315
+ "card.llmPreset": "Preajustes de endpoint (rellena los campos; aún editable)",
1316
+ "card.llmPresetChoose": "Elegir un preajuste…",
1317
+ "card.llmPresetPublic": "Preajustes de API públicas",
1318
+ "card.llmPresetHost": "Grupos de modelos DSH (seleccionar uno cambia al receptor de sesión)",
1319
+ "card.networkEnabled": "Intercepción de red (networkEnabled)",
1320
+ "card.networkEnabledHint": "Activado, inicia un proxy local que intercepta las conexiones salientes de los subprocesos y aplica reglas de permiso/denegación. Desactivado = cero cambio de comportamiento.",
1321
+ "card.watch": "Recarga en caliente de reglas (watch)",
1322
+ "card.watchHint": "Activado, vigila automáticamente los cambios en los archivos de reglas y los aplica instantáneamente sin recarga manual.",
1323
+ "card.networkRebindNote": "Surte efecto de inmediato — el proxy se re-vincula automáticamente, sin necesidad de recargar el plugin.",
1324
+ "card.networkInjectEnv": "Inyectar variables de entorno del proxy (networkInjectEnv)",
1325
+ "card.networkInjectEnvHint": "Escribe HTTP(S)_PROXY / ALL_PROXY para los subprocesos (y vacía NO_PROXY). Desactivado = el listener funciona pero no se modifica ningún entorno de proceso.",
1326
+ "card.healthStale": "Ruta del host no disponible (404) — la mitad node sigue siendo una compilación antigua; reinicia completamente dsh (no solo la página del navegador) e inténtalo de nuevo.",
1327
+ "card.healthTest": "Prueba de salud",
1328
+ "card.healthRunning": "Probando…",
1329
+ "card.healthOk": "OK (%ms ms)",
1330
+ "card.healthFail": "Fallido: ",
1331
+ "history.tag.learned": "Aprendido",
1332
+ "history.tag.manualApproved": "Aprobado",
1333
+ "history.tag.manualRejected": "Rechazado",
1334
+ "history.tag.manualCancelled": "Cancelado",
1335
+ "history.tag.standDown": "Compuerta inactiva",
1336
+ "history.learnedProgress": "aprendido %n/%t",
1337
+ "history.snapshots": "instantáneas diff",
1338
+ "history.clearSession": "Borrar esta sesión",
1339
+ "history.clearAll": "Borrar todo",
1340
+ "history.clearSessionTitle": "Borrar solo las instantáneas diff de esta sesión (otras sesiones intactas)",
1341
+ "history.clearAllTitle": "Borrar las instantáneas diff de todas las sesiones, incluidas las no revisadas — pide confirmación",
1342
+ "history.clearSessionConfirm": "¿Borrar las instantáneas diff de esta sesión? Solo se eliminan los datos de comparación generados por las aprobaciones de esta sesión; los registros de aprobación permanecen.",
1343
+ "history.clearAllConfirm": "¿Borrar las instantáneas diff de TODAS las sesiones? Esto puede eliminar comparaciones que otras sesiones aún no han revisado, y es irreversible.",
1344
+ "history.diffTitle": "Comparación de cambios de archivo",
1345
+ "history.diffClose": "Cerrar",
1346
+ "history.diffLoading": "Cargando…",
1347
+ "history.diffEmpty": "Sin cambios de contenido (o archivo ilegible)",
1348
+ "history.diffLoadFail": "Error al cargar diff: ",
1349
+ "history.diffRevert": "Revertir este cambio",
1350
+ "history.diffReverting": "Enviando…",
1351
+ "history.diffRevertDone": "Instrucción de reversión enviada",
1352
+ "history.diffRevertSent": "Instrucción de reversión enviada a la conversación; la IA restaurará el archivo en consecuencia",
1353
+ "history.diffRevertFail": "Error de envío: ",
1354
+ "history.diffNoSnapshot": "Este evento no tiene instantánea para ese archivo",
1355
+ "history.diffChipTitle": "Ver la comparación de cambios de este archivo",
1356
+ "history.diffStats": "+%a / -%r líneas cambiadas · %c sin cambios",
1357
+ "history.diffGone": " (el archivo ya no existe)",
1358
+ "history.diffHidden": "%n líneas sin modificar",
1359
+ "card.dryRun": "Prueba de reglas",
1360
+ "card.dryRunHint": "Juzga una llamada contra las reglas vigentes. No ejecuta ninguna herramienta ni escribe ninguna regla — comprueba aquí antes de cambiar.",
1361
+ "card.dryRunTool": "Herramienta",
1362
+ "card.dryRunCommand": "Comando / argumentos",
1363
+ "card.dryRunCommandPlaceholder": "git push --force origin main",
1364
+ "card.dryRunTest": "Probar",
1365
+ "card.dryRunRunning": "Evaluando…",
1366
+ "card.dryRunEmpty": "Nada probado aún. Rellena una herramienta y un comando, luego pulsa Probar para ver el veredicto.",
1367
+ "card.dryRunVerdict": "Veredicto",
1368
+ "card.dryRunAllow": "permitir",
1369
+ "card.dryRunAsk": "consultar",
1370
+ "card.dryRunDeny": "denegar",
1371
+ "card.dryRunRule": "Regla coincidente",
1372
+ "card.dryRunRuleNone": "ninguna regla coincidió; defaultAction: %d",
1373
+ "card.dryRunDimensions": "Dimensiones",
1374
+ "card.dryRunReason": "Razón",
1375
+ "card.dryRunNote": "«Regla coincidente» proviene de la capa de reglas. Cuando el veredicto fue producido por una denegación dura anterior o por la lista negra de palabras clave no hay índice de regla — las dimensiones listadas son solo informativas.",
1376
+ "card.dryRunNoRules": "(0 reglas cargadas — comprueba la ruta de rulesFile)",
1377
+ "card.dryRunFail": "Prueba fallida: ",
1378
+ "card.dryRunStale": "ruta ausente (¿plugin no reiniciado?)",
1379
+ "card.rules": "Reglas integradas de configuración",
1380
+ "card.rulesHint": "Las reglas que la compuerta está cargando ahora (del campo de configuración rules de esta entrada de plugin; retroceso al archivo de reglas si no está configurado). Solo lectura — nada aquí puede modificarlas. Para cambiar reglas, usa la lista blanca de abajo o edítalas en la página de configuración.",
1381
+ "card.rulesRefresh": "Actualizar",
1382
+ "card.rulesLoading": "Leyendo…",
1383
+ "card.rulesPath": "Origen",
1384
+ "card.rulesNone": "sin reglas configuradas",
1385
+ "card.rulesCounts": "Reglas",
1386
+ "card.rulesDefault": "defaultAction",
1387
+ "card.rulesStats": "%b bytes · %l líneas",
1388
+ "card.rulesMissing": "Reglas no encontradas. La compuerta retrocede a sus valores predeterminados integrados.",
1389
+ "card.rulesTruncated": "Reglas grandes; se muestran solo los primeros 512 KB.",
1390
+ "card.rulesError": "Error de carga: ",
1391
+ "card.rulesStale": "ruta ausente (¿plugin no reiniciado?)",
1392
+ "card.rulesNote": "Solo lectura: este panel no puede modificar las reglas. La sección de lista blanca y «permitir siempre» escriben de vuelta en la configuración de esta entrada de plugin."
1393
+ }
1083
1394
  };
1084
1395
  //#endregion
1085
1396
  //#region src/deny-defaults.ts
@@ -3916,34 +4227,42 @@ window.__ModuleLoader__.load({
3916
4227
  });
3917
4228
  }
3918
4229
  //#endregion
4230
+ //#region src/client/compat.ts
4231
+ /**
4232
+ * Resolve the plugin's durable settings scope through whichever service this
4233
+ * host line carries, then hand it to `onScope` exactly once.
4234
+ *
4235
+ * Both variants are scoped sub-injects: on a host without the service the fiber
4236
+ * waits forever WITHOUT blocking the plugin's other faces (same posture as the
4237
+ * notice strip's slot-inject when its slot holder is absent).
4238
+ */
4239
+ function resolveSettingsScope(ctx, namespace, onScope) {
4240
+ ctx.inject(["configForms"], (configForms) => {
4241
+ onScope(configForms.get(namespace));
4242
+ });
4243
+ ctx.inject(["settingsScope"], (settingsScope) => {
4244
+ onScope(settingsScope.bind({ namespace }));
4245
+ });
4246
+ }
4247
+ //#endregion
3919
4248
  //#region src/client/index.ts
3920
4249
  /** The profile entry id of this plugin — the `configForms` key (kept in lockstep with cordis.patch.yml). */
3921
4250
  const PERMISSIVE_NS = "dsh-perm-gate";
3922
- /** Services required by the browser half. */
3923
- const inject = [
3924
- "slots",
3925
- "locale",
3926
- "configForms"
3927
- ];
4251
+ /** Services required by the browser half — generation-neutral only.
4252
+ *
4253
+ * `configForms` (0.1.7+) / `settingsScope` (≤0.1.5) are generation-exclusive
4254
+ * durable-settings faces: keeping either in the plugin-level inject list would
4255
+ * leave the WHOLE browser half fiber PENDING on the other line (strict ctx
4256
+ * proxy throws on undeclared service reads; pending fiber = silent death).
4257
+ * They resolve through scoped sub-injects in compat.ts instead, and the card
4258
+ * registrations below move into the resolution callback. */
4259
+ const inject = ["slots", "locale"];
3928
4260
  /**
3929
4261
  * Client plugin body: dictionaries plus the settings page registration.
3930
4262
  * @param ctx - client root context.
3931
4263
  */
3932
4264
  function apply(ctx) {
3933
- ctx.effect(() => {
3934
- const disposers = [
3935
- ctx.locale.register(NS, {
3936
- zh,
3937
- en
3938
- }),
3939
- ctx.locale.register(NS, "fr", fr),
3940
- ctx.locale.register(NS, "de", de),
3941
- ctx.locale.register(NS, "it", it),
3942
- ctx.locale.register(NS, "ru", ru),
3943
- ctx.locale.register(NS, "es", es)
3944
- ];
3945
- return () => disposers.forEach((dispose) => dispose());
3946
- }, "dsh-perm-gate: dictionaries");
4265
+ if (ctx.locale) ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-perm-gate: dictionaries");
3947
4266
  ctx.slots.inject("conversation.input.dock", function* () {
3948
4267
  yield ctx.slots.register({
3949
4268
  name: "conversation.input.dock",
@@ -3964,26 +4283,24 @@ window.__ModuleLoader__.load({
3964
4283
  }, HistoryView);
3965
4284
  });
3966
4285
  const t = ctx.locale.bind(NS);
3967
- ctx.slots.inject("settings.section", function* () {
3968
- yield ctx.slots.register({
3969
- name: "settings.section",
3970
- id: PERMISSIVE_NS,
3971
- order: 30,
3972
- label: () => t("section.title"),
4286
+ resolveSettingsScope(ctx, PERMISSIVE_NS, (scope) => {
4287
+ ctx.slots.inject("settings.section", function* () {
4288
+ yield ctx.slots.register({
4289
+ name: "settings.section",
4290
+ id: PERMISSIVE_NS,
4291
+ order: 30,
4292
+ label: () => t("section.title"),
4293
+ locale: NS,
4294
+ inject: () => ({ scope })
4295
+ }, PermissiveCard);
4296
+ });
4297
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
4298
+ name: "plugins.bundle.config",
4299
+ key: "dsh-perm-gate",
3973
4300
  locale: NS,
3974
- inject: () => {
3975
- return { scope: ctx.configForms.get(PERMISSIVE_NS) };
3976
- }
3977
- }, PermissiveCard);
4301
+ inject: () => ({ scope })
4302
+ }, PermissiveCard));
3978
4303
  });
3979
- ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
3980
- name: "plugins.bundle.config",
3981
- key: "dsh-perm-gate",
3982
- locale: NS,
3983
- inject: () => {
3984
- return { scope: ctx.configForms.get(PERMISSIVE_NS) };
3985
- }
3986
- }, PermissiveCard));
3987
4304
  }
3988
4305
  //#endregion
3989
4306
  exports.apply = apply;