dsh-eval-guard 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/ISSUES.md +239 -0
  2. package/README.md +48 -18
  3. package/assets/neural-cifar/workspace.zip +0 -0
  4. package/assets/neural-cpp/workspace.zip +0 -0
  5. package/assets/neural-fp32/workspace.zip +0 -0
  6. package/assets/neural-pendulum/SPEC.md +43 -35
  7. package/assets/neural-pendulum/TASK.md +139 -33
  8. package/assets/neural-pendulum/rl-feasibility/README.md +60 -0
  9. package/assets/neural-pendulum/rl-feasibility/RESULTS.md +370 -0
  10. package/assets/neural-pendulum/rl-feasibility/evaluate.py +107 -0
  11. package/assets/neural-pendulum/rl-feasibility/finalize_run.py +168 -0
  12. package/assets/neural-pendulum/rl-feasibility/models.py +124 -0
  13. package/assets/neural-pendulum/rl-feasibility/obs.py +205 -0
  14. package/assets/neural-pendulum/rl-feasibility/plot_selected.py +55 -0
  15. package/assets/neural-pendulum/rl-feasibility/reference_controller.py +112 -0
  16. package/assets/neural-pendulum/rl-feasibility/results/curves_selected.png +0 -0
  17. package/assets/neural-pendulum/rl-feasibility/results/reference_controller.json +417 -0
  18. package/assets/neural-pendulum/rl-feasibility/results/summary.json +1599 -0
  19. package/assets/neural-pendulum/rl-feasibility/results/table.md +39 -0
  20. package/assets/neural-pendulum/rl-feasibility/sim.py +324 -0
  21. package/assets/neural-pendulum/rl-feasibility/summarize.py +175 -0
  22. package/assets/neural-pendulum/rl-feasibility/task.py +342 -0
  23. package/assets/neural-pendulum/rl-feasibility/test_contract.py +188 -0
  24. package/assets/neural-pendulum/rl-feasibility/train_ppo.py +403 -0
  25. package/assets/neural-pendulum/rl-feasibility/train_sac.py +406 -0
  26. package/assets/neural-pendulum/sim.cpp +13 -2
  27. package/assets/neural-pendulum/sim_eval +0 -0
  28. package/assets/neural-pendulum/workspace.zip +0 -0
  29. package/eval/README.md +19 -9
  30. package/eval/bomberman.py +5 -3
  31. package/eval/commit0.py +10 -3
  32. package/eval/common.py +169 -5
  33. package/eval/ctf.py +4 -2
  34. package/eval/featurebench.py +12 -8
  35. package/eval/land.py +4 -2
  36. package/eval/legacy/bomberman.py +5 -2
  37. package/eval/legacy/ctf.py +5 -2
  38. package/eval/legacy/featurebench.py +55 -15
  39. package/eval/legacy/land.py +6 -3
  40. package/eval/legacy/swe_bench_pro.py +57 -16
  41. package/eval/legacy/tank.py +2 -2
  42. package/eval/legacy/warehouse.py +4 -4
  43. package/eval/neural-cpp.py +9 -6
  44. package/eval/neural-fp32.py +17 -5
  45. package/eval/swe-bench-pro.py +11 -7
  46. package/eval/tank.py +11 -2
  47. package/eval/warehouse.py +17 -1
  48. package/lib/client/index.js +56 -29
  49. package/lib/client/index.js.map +1 -1
  50. package/lib/index.js +883 -298
  51. package/native/dsh-eval-supervisor +0 -0
  52. package/native/supervisor.c +232 -36
  53. package/neural-cifar-eval/README.md +5 -0
  54. package/neural-cifar-eval/common.py +13 -0
  55. package/neural-cifar-eval/ops_eval.py +17 -5
  56. package/neural-cifar-eval/run_eval.py +42 -32
  57. package/neural-pendulum-eval/README.md +62 -0
  58. package/neural-pendulum-eval/ops_eval.py +603 -0
  59. package/neural-pendulum-eval/run_eval.py +218 -137
  60. package/neural-pendulum-eval/sim_eval +0 -0
  61. package/package.json +7 -11
  62. package/assets/neural-pendulum/hidden/test_infra.cpp +0 -631
  63. package/assets/neural-pendulum/include/nn/nn.hpp +0 -88
@@ -15,15 +15,12 @@ import sys
15
15
  import tempfile
16
16
  from pathlib import Path
17
17
 
18
- from common import EVAL_ROOT, clean_copy, emit, fail, parse_workspace
18
+ from common import EVAL_ROOT, child_env, clean_copy, emit, fail, parse_workspace
19
19
 
20
20
 
21
21
  def run(cmd: list[str], cwd: Path, timeout: int = 600) -> subprocess.CompletedProcess[str]:
22
- env = dict(os.environ)
23
- env.pop('PYTHONPATH', None)
24
- env.pop('PYTHONHOME', None)
25
22
  return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True,
26
- encoding='utf-8', errors='replace', env=env, timeout=timeout)
23
+ encoding='utf-8', errors='replace', env=child_env(), timeout=timeout)
27
24
 
28
25
 
29
26
  def main() -> int:
@@ -31,6 +28,9 @@ def main() -> int:
31
28
  hidden = EVAL_ROOT.parent / 'assets' / 'neural-cpp' / 'hidden' / 'test_neural.cpp'
32
29
  if not hidden.is_file():
33
30
  return fail({'task': 'neural-cpp', 'ok': False, 'error': 'hidden test missing'})
31
+ # Snapshot the hidden test BEFORE the candidate build runs: an agent-authored
32
+ # CMakeLists can otherwise overwrite the asset and substitute a passing test.
33
+ hidden_source = hidden.read_bytes()
34
34
 
35
35
  with tempfile.TemporaryDirectory(prefix='neural-cpp-eval-') as td:
36
36
  root = Path(td)
@@ -67,9 +67,12 @@ def main() -> int:
67
67
  if not lib.is_file():
68
68
  return fail({'task': 'neural-cpp', 'ok': False, 'error': 'build/libnn.a missing'})
69
69
 
70
+ # Compile the in-memory snapshot, not the on-disk asset the build could edit.
71
+ hidden_copy = root / 'test_neural.cpp'
72
+ hidden_copy.write_bytes(hidden_source)
70
73
  exe = root / 'test_neural'
71
74
  cc = run([compiler, '-std=c++17', '-O3', '-pthread',
72
- '-I', str(source / 'include'), str(hidden), str(lib),
75
+ '-I', str(source / 'include'), str(hidden_copy), str(lib),
73
76
  '-lm', '-o', str(exe)], root, 300)
74
77
  if cc.returncode != 0:
75
78
  return fail({'task': 'neural-cpp', 'ok': False, 'error': 'hidden test compile failed',
@@ -20,7 +20,7 @@ import numpy as np
20
20
  import torch
21
21
  import torch.nn.functional as F
22
22
 
23
- from common import EVAL_ROOT, clean_copy, emit, fail, parse_workspace
23
+ from common import EVAL_ROOT, child_env, clean_copy, emit, fail, parse_workspace
24
24
 
25
25
  torch.set_num_threads(1)
26
26
  torch.set_float32_matmul_precision('highest')
@@ -29,6 +29,9 @@ torch.backends.mkldnn.deterministic = True
29
29
  torch.manual_seed(1234)
30
30
 
31
31
  SEEDS = (11, 22, 33)
32
+ # A single CLI invocation budget and a total budget for the whole oracle run.
33
+ INVOCATION_TIMEOUT_SECONDS = 60
34
+ OPS_DEADLINE_SECONDS = 1800
32
35
 
33
36
 
34
37
  def write_tensor(path: Path, t: torch.Tensor) -> None:
@@ -65,11 +68,12 @@ def build_candidate(workspace: Path, root: Path) -> Path:
65
68
  shutil.rmtree(build, ignore_errors=True)
66
69
  build.mkdir(parents=True)
67
70
  cfg = subprocess.run(['cmake', '-S', '.', '-B', 'build', '-DCMAKE_BUILD_TYPE=Release'],
68
- cwd=str(source), capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=180)
71
+ cwd=str(source), capture_output=True, text=True, encoding='utf-8', errors='replace',
72
+ timeout=180, env=child_env())
69
73
  if cfg.returncode != 0:
70
74
  raise RuntimeError('cmake configure failed\n' + (cfg.stdout + cfg.stderr)[-4000:])
71
75
  made = subprocess.run(['cmake', '--build', 'build', '-j4'], cwd=str(source), capture_output=True,
72
- text=True, encoding='utf-8', errors='replace', timeout=1800)
76
+ text=True, encoding='utf-8', errors='replace', timeout=1800, env=child_env())
73
77
  if made.returncode != 0:
74
78
  raise RuntimeError('build failed\n' + (made.stdout + made.stderr)[-4000:])
75
79
  binary = build / 'nn_task'
@@ -87,7 +91,8 @@ class Case:
87
91
  self.flops = flops
88
92
 
89
93
 
90
- def run_operation(name: str, threshold: float, cases: list[Case], binary: Path, root: Path) -> dict:
94
+ def run_operation(name: str, threshold: float, cases: list[Case], binary: Path, root: Path,
95
+ invocation_timeout: float = INVOCATION_TIMEOUT_SECONDS) -> dict:
91
96
  op_dir = root / 'ops' / name
92
97
  op_dir.mkdir(parents=True, exist_ok=True)
93
98
  executable = True
@@ -111,7 +116,8 @@ def run_operation(name: str, threshold: float, cases: list[Case], binary: Path,
111
116
  started = time.perf_counter()
112
117
  try:
113
118
  proc = subprocess.run(cmd, cwd=str(case_dir), capture_output=True, text=True,
114
- encoding='utf-8', errors='replace', timeout=300)
119
+ encoding='utf-8', errors='replace', timeout=invocation_timeout,
120
+ env=child_env())
115
121
  except subprocess.TimeoutExpired:
116
122
  executable = False
117
123
  first_error = first_error or 'timeout'
@@ -459,7 +465,13 @@ def main() -> int:
459
465
  except Exception as exc:
460
466
  return fail({'task': 'neural-fp32', 'ok': False, 'error': str(exc)})
461
467
  rows = []
468
+ deadline = time.monotonic() + OPS_DEADLINE_SECONDS
462
469
  for name, threshold, builder in OPERATIONS:
470
+ if time.monotonic() > deadline:
471
+ rows.append({'name': name, 'executable': False, 'passed': False,
472
+ 'weighted_mean_relative_error': None, 'threshold': threshold,
473
+ 'speed_gflops': None, 'error': 'evaluation deadline exceeded'})
474
+ continue
463
475
  try:
464
476
  cases = builder()
465
477
  row = run_operation(name, threshold, cases, binary, root)
@@ -3,9 +3,12 @@
3
3
  from __future__ import annotations
4
4
  import os, tempfile
5
5
  from pathlib import Path
6
- from common import EVAL_ROOT, candidate_row, clean_copy, emit, fail, parse_workspace, run_legacy
6
+ from common import EVAL_ROOT, candidate_row, clean_copy, emit, fail, parse_workspace, restore_baseline, run_legacy, verify_venv
7
7
 
8
8
  workspace = parse_workspace()
9
+ problem = verify_venv(workspace)
10
+ if problem is not None:
11
+ raise SystemExit(fail({'task': 'swe-bench-pro', 'ok': False, 'error': problem}))
9
12
  with tempfile.TemporaryDirectory(prefix='swe-pro-eval-') as td:
10
13
  temp = Path(td)
11
14
  candidate = temp / 'candidate' / 'workspace'
@@ -13,13 +16,14 @@ with tempfile.TemporaryDirectory(prefix='swe-pro-eval-') as td:
13
16
  clean_copy(workspace, candidate, ignore_venv=True)
14
17
  venv = workspace / '.venv'
15
18
  if venv.is_dir():
16
- os.symlink(venv, candidate / '.venv')
19
+ try:
20
+ os.symlink(venv, candidate / '.venv')
21
+ except FileExistsError:
22
+ pass
23
+ restore_baseline(candidate)
17
24
  result = run_legacy(EVAL_ROOT / 'legacy' / 'swe_bench_pro.py', ['--submissions', str(temp)], temp / 'out')
18
25
  if not result.get('ok'):
19
26
  raise SystemExit(fail({'task': 'swe-bench-pro', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
20
27
  row = candidate_row(result['summary'])
21
- passed = row.get('passed', 0)
22
- failed = row.get('failed', 0)
23
- errors = row.get('errors', 0)
24
- resolved = failed == 0 and errors == 0 and passed > 0
25
- emit({'task': 'swe-bench-pro', 'ok': True, 'score': 100.0 if resolved else 0.0, 'resolved': resolved, 'passRate': row.get('score', 0), 'passed': passed, 'failed': failed, 'errors': errors, 'status': row.get('status')})
28
+ resolved = row.get('resolved') is True
29
+ emit({'task': 'swe-bench-pro', 'ok': True, 'score': 100.0 if resolved else 0.0, 'resolved': resolved, 'passRate': row.get('score', 0), 'passed': row.get('passed', 0), 'failed': row.get('failed', 0), 'errors': row.get('errors', 0), 'f2pPassed': row.get('f2p_passed', 0), 'f2pTotal': row.get('f2p_total', 0), 'status': row.get('status')})
package/eval/tank.py CHANGED
@@ -16,5 +16,14 @@ with tempfile.TemporaryDirectory(prefix='tank-eval-') as td:
16
16
  result = run_legacy(EVAL_ROOT / 'legacy' / 'tank.py', ['--submissions', str(temp), '--arena', str(EVAL_ROOT / 'arena' / 'tank'), '--tier', 'quick'], temp / 'out')
17
17
  if not result.get('ok'):
18
18
  raise SystemExit(fail({'task': 'tank', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
19
- row = candidate_row(result['summary'])
20
- emit({'task': 'tank', 'ok': True, 'score': row.get('score', row.get('rating', row.get('theta', 0))), 'rating': row.get('rating', row.get('theta', 0)), 'wins': row.get('wins', 0), 'losses': row.get('losses', 0), 'draws': row.get('draws', 0)})
19
+ summary = result['summary']
20
+ row = candidate_row(summary)
21
+ if not row:
22
+ raise SystemExit(fail({'task': 'tank', 'ok': False, 'error': 'candidate row missing from evaluator summary'}))
23
+ wins = draws = losses = 0
24
+ for pair in summary.get('pairwise') or []:
25
+ if pair.get('a') == 'candidate':
26
+ wins += int(pair.get('wins', 0)); draws += int(pair.get('draws', 0)); losses += int(pair.get('losses', 0))
27
+ elif pair.get('b') == 'candidate':
28
+ wins += int(pair.get('losses', 0)); draws += int(pair.get('draws', 0)); losses += int(pair.get('wins', 0))
29
+ emit({'task': 'tank', 'ok': True, 'score': row.get('rating', 0), 'rating': row.get('rating', 0), 'wins': wins, 'losses': losses, 'draws': draws, 'games': row.get('games', 0)})
package/eval/warehouse.py CHANGED
@@ -17,4 +17,20 @@ with tempfile.TemporaryDirectory(prefix='warehouse-eval-') as td:
17
17
  if not result.get('ok'):
18
18
  raise SystemExit(fail({'task': 'warehouse', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
19
19
  row = candidate_row(result['summary'])
20
- emit({'task': 'warehouse', 'ok': True, 'score': row.get('speedup', 0), 'speedup': row.get('speedup', 0), 'correct': row.get('correct'), 'status': row.get('status')})
20
+ if not row:
21
+ raise SystemExit(fail({'task': 'warehouse', 'ok': False, 'error': 'candidate row missing from evaluator summary'}))
22
+ compile_error = row.get('compileError') or ''
23
+ correct = row.get('correct', 0)
24
+ total = row.get('total', 0)
25
+ status = 'compile-failed' if compile_error else ('ok' if correct == total and total else 'incorrect')
26
+ emit({
27
+ 'task': 'warehouse',
28
+ 'ok': True,
29
+ 'score': row.get('score', 0),
30
+ 'speedup': row.get('geomeanSpeedup', 0),
31
+ 'geomeanSpeedup': row.get('geomeanSpeedup', 0),
32
+ 'correct': correct,
33
+ 'total': total,
34
+ 'status': status,
35
+ 'error': compile_error,
36
+ })
@@ -285,7 +285,6 @@ window.__ModuleLoader__.load({
285
285
  };
286
286
  const selectTask = (next) => {
287
287
  setTaskId(next);
288
- save({ taskId: next });
289
288
  try {
290
289
  localStorage.setItem("evalGuard.taskId", next);
291
290
  } catch {}
@@ -296,21 +295,24 @@ window.__ModuleLoader__.load({
296
295
  setJobNotice("");
297
296
  if (workspaceId !== "") loadManual(workspaceId, next);
298
297
  const task = state?.tasks.find((item) => item.id === next);
299
- if (task === void 0) return;
298
+ if (task === void 0) {
299
+ save({ taskId: next });
300
+ return;
301
+ }
300
302
  const remotes = task.remotes ?? [];
301
303
  const cloneSource = draft.cloneSource === "custom" || remotes.some((remote) => remote.id === draft.cloneSource) ? draft.cloneSource : remotes[0]?.id ?? "github";
302
- setDraft((current) => ({
303
- ...current,
304
+ const patch = {
305
+ taskId: next,
304
306
  timeLimitMinutes: task.timeLimitMinutes,
305
307
  timeLimitEnabled: task.timeLimitEnabled,
306
308
  cloneSource
309
+ };
310
+ setDraft((current) => ({
311
+ ...current,
312
+ ...patch
307
313
  }));
308
314
  setNotice(t("settings.taskDefaults").replace("{minutes}", String(task.timeLimitMinutes)).replace("{state}", task.timeLimitEnabled ? t("settings.on") : t("settings.off")));
309
- save({
310
- timeLimitMinutes: task.timeLimitMinutes,
311
- timeLimitEnabled: task.timeLimitEnabled,
312
- cloneSource
313
- });
315
+ save(patch);
314
316
  };
315
317
  const toggleEnabled = (next) => {
316
318
  if (!next) {
@@ -472,22 +474,43 @@ window.__ModuleLoader__.load({
472
474
  }),
473
475
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
474
476
  style: box,
475
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
476
- style: row,
477
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
478
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
479
- type: "checkbox",
480
- checked: draft.enabled,
481
- disabled: !ready,
482
- onChange: (event) => toggleEnabled(event.target.checked)
483
- }),
484
- " ",
485
- t("settings.enabled")
486
- ] })
487
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
488
- style: note,
489
- children: t("settings.enabledHint")
490
- })]
477
+ children: [
478
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
479
+ style: row,
480
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
481
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
482
+ type: "checkbox",
483
+ checked: draft.enabled,
484
+ disabled: !ready,
485
+ onChange: (event) => toggleEnabled(event.target.checked)
486
+ }),
487
+ " ",
488
+ t("settings.enabled")
489
+ ] })
490
+ }),
491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
492
+ style: note,
493
+ children: t("settings.enabledHint")
494
+ }),
495
+ state?.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
496
+ style: {
497
+ ...note,
498
+ color: "#d99"
499
+ },
500
+ children: [
501
+ t("settings.enableError"),
502
+ ": ",
503
+ state.error
504
+ ]
505
+ }),
506
+ draft.enabled && state !== null && !state.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
507
+ style: {
508
+ ...note,
509
+ color: "#d99"
510
+ },
511
+ children: t("settings.enableInactive")
512
+ })
513
+ ]
491
514
  }),
492
515
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
493
516
  style: box,
@@ -1023,7 +1046,7 @@ window.__ModuleLoader__.load({
1023
1046
  "settings.nav": "评测守护",
1024
1047
  "settings.title": "评测守护",
1025
1048
  "settings.enabled": "启用评测守护",
1026
- "settings.enabledHint": "启用后,所有用户主对话只能读写自己的工作区,禁止联网、子代理与跨 agent 通信,并按任务时限强制终止。默认关闭。",
1049
+ "settings.enabledHint": "启用后,用户主对话的文件工具被限制在会话工作区(系统目录只读可见,/tmp 为工作区私有临时目录);禁止联网、子代理与跨 agent 通信;并按任务时限强制终止。默认关闭。",
1027
1050
  "settings.runningWarning": "当前有正在运行的对话。启用评测守护会立即约束它们,是否强行开启?",
1028
1051
  "settings.runningCancel": "放弃",
1029
1052
  "settings.runningForce": "强行开启",
@@ -1033,7 +1056,7 @@ window.__ModuleLoader__.load({
1033
1056
  "settings.parallelEvalHint": "启用后,安装任务时会在提示词中提醒 agent:运行环境可能因其他 agent 的行为而波动,需要自行适应。",
1034
1057
  "settings.minutes": "分钟",
1035
1058
  "settings.forbidden": "额外禁止路径",
1036
- "settings.forbiddenHint": "每行一个绝对路径;# 开头为注释;! 开头表示在禁止范围内放开该子路径;按行顺序生效。默认空。",
1059
+ "settings.forbiddenHint": "每行一个绝对路径;# 开头为注释;! 开头表示在禁止范围内放开该子路径;按行顺序生效;含 * 或 ? 的行按首个通配符之前的前缀生效。默认空。",
1037
1060
  "settings.blacklist": "工具黑名单",
1038
1061
  "settings.blacklistHint": "每行一个工具名;默认内容按当前已安装工具生成,不存在的工具会被忽略。",
1039
1062
  "settings.reset": "恢复默认黑名单",
@@ -1074,6 +1097,8 @@ window.__ModuleLoader__.load({
1074
1097
  "settings.recent": "最近事件",
1075
1098
  "settings.none": "暂无。",
1076
1099
  "settings.warningTitle": "评测守护警告",
1100
+ "settings.enableError": "启用失败",
1101
+ "settings.enableInactive": "开关已保存,但守护未生效:请查看上方错误信息并修复依赖后重试。",
1077
1102
  "settings.cifarProxy": "CIFAR 下载代理",
1078
1103
  "settings.cifarMirror": "CIFAR 下载镜像",
1079
1104
  "settings.close": "关闭"
@@ -1082,7 +1107,7 @@ window.__ModuleLoader__.load({
1082
1107
  "settings.nav": "Eval Guard",
1083
1108
  "settings.title": "Eval Guard",
1084
1109
  "settings.enabled": "Enable evaluation guard",
1085
- "settings.enabledHint": "When on, every user main conversation may only read/write its own workspace, with network, subagents and cross-agent channels blocked, and a wall-clock limit. Off by default.",
1110
+ "settings.enabledHint": "When on, user main conversations keep file tools inside their session workspace (system directories stay read-only visible; /tmp is a private workspace temp dir), network/subagents/cross-agent channels are blocked, and a wall-clock limit applies. Off by default.",
1086
1111
  "settings.runningWarning": "Conversations are running right now. Enabling the guard will constrain them immediately. Force enable?",
1087
1112
  "settings.runningCancel": "Cancel",
1088
1113
  "settings.runningForce": "Force enable",
@@ -1092,7 +1117,7 @@ window.__ModuleLoader__.load({
1092
1117
  "settings.parallelEvalHint": "When enabled, install prompts warn the agent that the runtime may fluctuate because other agents share the machine; it must adapt.",
1093
1118
  "settings.minutes": "minutes",
1094
1119
  "settings.forbidden": "Extra forbidden paths",
1095
- "settings.forbiddenHint": "One absolute path per line; # comments; ! re-allows a subpath; later lines win. Empty by default.",
1120
+ "settings.forbiddenHint": "One absolute path per line; # comments; ! re-allows a subpath; later lines win; a line with * or ? applies to the prefix before the first wildcard. Empty by default.",
1096
1121
  "settings.blacklist": "Tool blacklist",
1097
1122
  "settings.blacklistHint": "One tool name per line; the default is generated from the tools installed now and missing tools are ignored.",
1098
1123
  "settings.reset": "Reset default blacklist",
@@ -1135,6 +1160,8 @@ window.__ModuleLoader__.load({
1135
1160
  "settings.recent": "Recent events",
1136
1161
  "settings.none": "None.",
1137
1162
  "settings.warningTitle": "Eval Guard warning",
1163
+ "settings.enableError": "Enable failed",
1164
+ "settings.enableInactive": "The switch was saved, but the guard is not active. See the error above, fix the dependency, then retry.",
1138
1165
  "settings.close": "Close"
1139
1166
  };
1140
1167
  //#endregion