@miller-tech/uap 1.148.7 → 1.148.9

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.
@@ -52,7 +52,7 @@ export async function listPolicyChoices() {
52
52
  return builtins
53
53
  .map((b) => {
54
54
  const inst = byName.get(normPolicyKey(b.name));
55
- const level = (inst?.level ?? b.level ?? 'OPTIONAL').toUpperCase();
55
+ const level = (inst?.level ?? b.level ?? 'REQUIRED').toUpperCase();
56
56
  return {
57
57
  name: b.name,
58
58
  category: inst?.category ?? b.category,
@@ -59,7 +59,7 @@ export function parsePolicyMeta(md) {
59
59
  return m ? m[1].trim() : dflt;
60
60
  };
61
61
  return {
62
- level: grab('Level', 'OPTIONAL'),
62
+ level: grab('Level', 'REQUIRED'),
63
63
  category: grab('Category', 'custom'),
64
64
  stage: grab('Enforcement Stage', 'pre-exec'),
65
65
  };
@@ -90,7 +90,7 @@ export function buildPolicyMatrix(builtins, installed) {
90
90
  builtin: existing?.builtin ?? false,
91
91
  installed: true,
92
92
  enabled: p.isActive,
93
- level: p.level ?? existing?.level ?? 'OPTIONAL',
93
+ level: p.level ?? existing?.level ?? 'REQUIRED',
94
94
  stage: p.enforcementStage ?? existing?.stage ?? 'pre-exec',
95
95
  category: p.category ?? existing?.category ?? 'custom',
96
96
  id: p.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.148.7",
3
+ "version": "1.148.9",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,6 +22,7 @@ files (deliver protects those itself), and tooling dot-dirs.
22
22
  """
23
23
  from __future__ import annotations
24
24
  import os
25
+ import re
25
26
  import sys
26
27
  from pathlib import Path
27
28
 
@@ -35,6 +36,25 @@ SOURCE_EXTS = (
35
36
  ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs",
36
37
  ".py", ".go", ".rs", ".java", ".rb", ".php", ".cs", ".swift", ".kt",
37
38
  ".c", ".cc", ".cpp", ".h", ".hpp",
39
+ # WEB deliverables are source too. Omitting these let an ENTIRE class of
40
+ # deliverable (single-file web apps, static sites, templates) escape the gate:
41
+ # a 34KB single-file HTML app returned "not source code" -> allowed -> zero
42
+ # routing, zero deliver, zero validation (observed live). The completion gate's
43
+ # own code-change detector already counts html/css/vue/svelte as code, so the
44
+ # two halves of the system disagreed about what "source" means until now.
45
+ ".html", ".htm", ".css", ".scss", ".sass", ".less",
46
+ ".vue", ".svelte", ".astro",
47
+ )
48
+
49
+ # GUI-browser launchers. A model with no validation tooling in reach tries to
50
+ # "check its work" by opening a browser (observed: 11 xdg-open/firefox/chromium
51
+ # attempts in 40 min, spawning windows on the operator's desktop) — which proves
52
+ # nothing and burns turns. Redirect it to `uap verify`, which renders headlessly
53
+ # and runs the real visual + behavioral gates.
54
+ BROWSER_BINS = (
55
+ "xdg-open", "firefox", "chromium", "chromium-browser", "google-chrome",
56
+ "google-chrome-stable", "chrome", "sensible-browser", "x-www-browser",
57
+ "microsoft-edge", "open",
38
58
  )
39
59
 
40
60
  # NOTE: `.worktrees/` is deliberately NOT exempt here. A real `uap deliver` run
@@ -148,8 +168,79 @@ def _local_mode() -> str:
148
168
  return "advisory" if adv not in {"0", "off", "false", "no", ""} else "block"
149
169
 
150
170
 
171
+ BASH_OPS = {"Bash", "bash", "run_bash", "shell"}
172
+
173
+ # A bash command that WRITES a source file: `> f.ts`, `>> f.ts`, `tee f.ts`,
174
+ # `sed -i ... f.ts`. Without this, Edit/Write gating is trivially bypassable —
175
+ # `cat > app.js <<EOF` writes source with no deliver run and no validation.
176
+ _EXT_ALT = "|".join(e.lstrip(".") for e in SOURCE_EXTS)
177
+ _BASH_WRITE_RE = re.compile(
178
+ r"(?:>>?\s*|tee\s+(?:-a\s+)?|sed\s+-i[^\s]*\s+(?:[^|;&]*\s)?)"
179
+ r"['\"]?([^\s'\"|;&>]+\.(?:" + _EXT_ALT + r"))\b",
180
+ re.IGNORECASE,
181
+ )
182
+ # A browser LAUNCH (not a lookup: `which firefox` / `command -v chrome` are fine).
183
+ _BROWSER_RE = re.compile(
184
+ r"(?:^|[;&|]|\&\&|\|\|)\s*(?:nohup\s+)?(" + "|".join(BROWSER_BINS) + r")\b",
185
+ re.IGNORECASE,
186
+ )
187
+ _LOOKUP_RE = re.compile(r"^\s*(?:which|command\s+-v|type|whereis)\b", re.IGNORECASE)
188
+
189
+
190
+ def _handle_bash(args: dict) -> None:
191
+ """Gate bash so it can't bypass delivery enforcement (source writes) and so a
192
+ model stops trying to 'verify' by opening a GUI browser."""
193
+ cmd = str(args.get("command") or args.get("cmd") or args.get("script") or "")
194
+ if not cmd.strip():
195
+ emit(True, "bash: empty command")
196
+ return
197
+
198
+ # Browser launch → redirect to the real (headless) validation path. Always
199
+ # blocked: opening a window proves nothing and cannot gate a DONE claim.
200
+ if not _LOOKUP_RE.match(cmd) and _BROWSER_RE.search(cmd):
201
+ emit(
202
+ False,
203
+ "BLOCKED: do not open a GUI browser to check your work — it proves nothing "
204
+ "and cannot validate anything. Run `uap verify` instead: it renders the page "
205
+ "headlessly and runs the REAL visual + behavioral gates (screenshots land in "
206
+ ".uap/visual). Use that to see whether the UI actually works.",
207
+ )
208
+ return
209
+
210
+ # Source write via shell → same rules as an Edit/Write: route through deliver.
211
+ if os.environ.get("UAP_DELIVER_ACTIVE") == "1":
212
+ emit(True, "bash: inside a deliver-driven run")
213
+ return
214
+ if os.environ.get("UAP_DELIVER_BYPASS") == "1":
215
+ emit(True, "bash: UAP_DELIVER_BYPASS override set")
216
+ return
217
+ m = _BASH_WRITE_RE.search(cmd)
218
+ if m:
219
+ target = m.group(1)
220
+ mode = os.environ.get("UAP_ENFORCE_DELIVERY", "block").lower()
221
+ if mode == "block" and _is_local_model_session() and _local_mode() == "advisory":
222
+ mode = "advisory"
223
+ msg = (
224
+ f"BLOCKED: do not write source via the shell ('{target}'). Writing files with "
225
+ "a redirect/heredoc/sed/tee bypasses the delivery gate. To create or change "
226
+ "code, call the `deliver` tool (or run: uap deliver \"<one-line description>\"). "
227
+ "Do NOT retry this command."
228
+ )
229
+ if mode == "block":
230
+ emit(False, msg, route="deliver", deliverHint=f"implement the intended change to {target}")
231
+ return
232
+ print(f"[delivery-enforcement advisory] {msg}", file=sys.stderr)
233
+ emit(True, "bash advisory: source-write nudge logged")
234
+ return
235
+
236
+ emit(True, "bash: no source write or browser launch")
237
+
238
+
151
239
  def main() -> None:
152
240
  op, args = parse_cli()
241
+ if op in BASH_OPS:
242
+ _handle_bash(args)
243
+ return
153
244
  if op not in EDIT_OPS:
154
245
  emit(True, "not a file-edit operation")
155
246
  return
@@ -1,7 +1,7 @@
1
1
  # adr-guard
2
2
 
3
3
  **Category**: safety
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: adr, architecture, invariants, write-gate
7
7
 
@@ -1,7 +1,7 @@
1
1
  # coord-overlap
2
2
 
3
3
  **Category**: workflow
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: agents, coordination, parallelism
7
7
 
@@ -1,7 +1,7 @@
1
1
  # delivery-enforcement
2
2
 
3
3
  **Category**: safety
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: uap, delivery, deliver, convergence, enforcement
7
7
 
@@ -1,5 +1,7 @@
1
1
  # Enforcement Self-Protect
2
2
 
3
+ **Level**: REQUIRED
4
+
3
5
  The delivery gate exists to route coding through `uap deliver`. A weak local
4
6
  model that hits the block tends to try to ESCAPE it rather than comply
5
7
  (observed: "set enforcement to advisory", "use UAP_DELIVER_BYPASS=1"). This
@@ -1,7 +1,7 @@
1
1
  # local-build-before-push
2
2
 
3
3
  **Category**: quality
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: build, push, ci, docker, compile-gate
7
7
 
@@ -1,7 +1,7 @@
1
1
  # mcp-router-first
2
2
 
3
3
  **Category**: custom
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: mcp, router, tokens, context
7
7
 
@@ -1,7 +1,7 @@
1
1
  # parallel-reads
2
2
 
3
3
  **Category**: custom
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: performance, parallelism, exploration
7
7
 
@@ -1,7 +1,7 @@
1
1
  # pay2u-architecture-rules
2
2
 
3
3
  **Category**: safety
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: pay2u, architecture, adr, invariants, example-pack
7
7
 
@@ -1,7 +1,7 @@
1
1
  # pay2u-enforcement-hooks
2
2
 
3
3
  **Category**: workflow
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: pay2u, hooks, enforcement, example-pack
7
7
 
@@ -1,7 +1,7 @@
1
1
  # pay2u-quick-reference
2
2
 
3
3
  **Category**: custom
4
- **Level**: RECOMMENDED
4
+ **Level**: REQUIRED
5
5
  **Enforcement Stage**: pre-exec
6
6
  **Tags**: pay2u, reference, clusters, example-pack
7
7
 
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+ """delivery-enforcement must gate WEB source (.html/.css/...) and BASH.
3
+
4
+ Two live escapes this closes:
5
+ 1. `.html` was not in SOURCE_EXTS, so a single-file web app — the actual
6
+ deliverable — returned "not source code" and was allowed: zero routing, zero
7
+ deliver, zero validation (observed: a 34KB rubiks-cube.html built ungated).
8
+ 2. Bash was ungated entirely, so `cat > app.js <<EOF` bypassed the whole gate;
9
+ and a model with no validation tooling in reach kept launching a GUI browser
10
+ (xdg-open/firefox) to "check its work", which proves nothing.
11
+ """
12
+ import json, os, subprocess, sys, unittest
13
+ from pathlib import Path
14
+
15
+ ENF = Path(__file__).resolve().parents[3] / "src" / "policies" / "enforcers" / "delivery_enforcement.py"
16
+
17
+
18
+ def run(op, args, env=None):
19
+ e = {**os.environ, "UAP_ENFORCE_DELIVERY": "block", "UAP_INFERENCE_ENDPOINT": "http://172.17.0.1:8080/v1"}
20
+ for k in ("UAP_DELIVER_ACTIVE", "UAP_DELIVER_BYPASS", "UAP_DELIVER_LOCAL_MODE", "UAP_DELIVER_LOCAL_ADVISORY"):
21
+ e.pop(k, None)
22
+ if env:
23
+ e.update(env)
24
+ p = subprocess.run([sys.executable, str(ENF), "--operation", op, "--args", json.dumps(args)],
25
+ capture_output=True, text=True, env=e, cwd=str(ENF.parents[3]))
26
+ return p.returncode, p.stdout + p.stderr
27
+
28
+
29
+ class WebSourceTest(unittest.TestCase):
30
+ def test_html_is_source_and_routes(self):
31
+ code, out = run("Write", {"file_path": "app.html", "content": "<html>" + "x" * 2000})
32
+ self.assertEqual(code, 2, out) # blocked
33
+ self.assertIn("route", out) # → deliver
34
+
35
+ def test_css_is_source(self):
36
+ code, _ = run("Write", {"file_path": "src/app.css", "content": "a{}" * 500})
37
+ self.assertEqual(code, 2)
38
+
39
+ def test_vue_svelte_are_source(self):
40
+ for f in ("src/App.vue", "src/App.svelte"):
41
+ code, _ = run("Write", {"file_path": f, "content": "x" * 1000})
42
+ self.assertEqual(code, 2, f)
43
+
44
+ def test_non_source_still_allowed(self):
45
+ code, out = run("Write", {"file_path": "notes.md", "content": "hello"})
46
+ self.assertEqual(code, 0, out)
47
+
48
+
49
+ class BashGateTest(unittest.TestCase):
50
+ def test_gui_browser_launch_is_blocked_and_redirected_to_verify(self):
51
+ code, out = run("Bash", {"command": "xdg-open /tmp/app.html"})
52
+ self.assertEqual(code, 2, out)
53
+ self.assertIn("uap verify", out) # redirected to the real validation path
54
+
55
+ def test_firefox_launch_blocked(self):
56
+ code, _ = run("Bash", {"command": "nohup firefox /tmp/a.html &"})
57
+ self.assertEqual(code, 2)
58
+
59
+ def test_browser_LOOKUP_is_not_blocked(self):
60
+ # `which firefox` is a capability probe, not a launch — must not false-block.
61
+ code, _ = run("Bash", {"command": "which firefox chromium 2>/dev/null"})
62
+ self.assertEqual(code, 0)
63
+
64
+ def test_bash_source_write_routes_to_deliver(self):
65
+ code, out = run("Bash", {"command": "cat > src/app.js <<EOF\nconsole.log(1)\nEOF"})
66
+ self.assertEqual(code, 2, out)
67
+ self.assertIn("route", out)
68
+
69
+ def test_sed_inplace_on_source_blocked(self):
70
+ code, _ = run("Bash", {"command": "sed -i s/a/b/ src/main.ts"})
71
+ self.assertEqual(code, 2)
72
+
73
+ def test_benign_bash_allowed(self):
74
+ code, out = run("Bash", {"command": "ls -la && npm test"})
75
+ self.assertEqual(code, 0, out)
76
+
77
+ def test_deliver_active_bypasses_bash_gate(self):
78
+ code, _ = run("Bash", {"command": "cat > src/app.js <<EOF\nx\nEOF"},
79
+ env={"UAP_DELIVER_ACTIVE": "1"})
80
+ self.assertEqual(code, 0) # deliver's own shell writes must pass
81
+
82
+
83
+ if __name__ == "__main__":
84
+ unittest.main()
@@ -58,7 +58,9 @@ class TestDeliveryEnforcementWorktree(unittest.TestCase):
58
58
  )
59
59
 
60
60
  def test_worktree_nonsource_still_allowed(self):
61
- self.assertTrue(run(".worktrees/001-x/css/styles.css", self.root))
61
+ # NOTE: .css is now SOURCE (web deliverables are gated) — use a genuinely
62
+ # non-source file to keep this test's intent (non-source is not routed).
63
+ self.assertTrue(run(".worktrees/001-x/docs/NOTES.md", self.root))
62
64
 
63
65
  def test_direct_source_still_blocked(self):
64
66
  self.assertFalse(run("src/game.js", self.root))