@miller-tech/uap 2.16.7 → 2.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/config/capacity-policy.json +33 -0
  2. package/config/model-profiles/qwen38.json +32 -33
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/bin/cli.js +57 -0
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/capacity/policy.d.ts +32 -1
  7. package/dist/capacity/policy.d.ts.map +1 -1
  8. package/dist/capacity/policy.js +123 -4
  9. package/dist/capacity/policy.js.map +1 -1
  10. package/dist/capacity/probe.d.ts +26 -0
  11. package/dist/capacity/probe.d.ts.map +1 -1
  12. package/dist/capacity/probe.js +84 -3
  13. package/dist/capacity/probe.js.map +1 -1
  14. package/dist/cli/inference.d.ts +4 -0
  15. package/dist/cli/inference.d.ts.map +1 -1
  16. package/dist/cli/inference.js +74 -8
  17. package/dist/cli/inference.js.map +1 -1
  18. package/dist/cli/merge-gate.d.ts +59 -0
  19. package/dist/cli/merge-gate.d.ts.map +1 -0
  20. package/dist/cli/merge-gate.js +238 -0
  21. package/dist/cli/merge-gate.js.map +1 -0
  22. package/dist/cli/merge-queue.d.ts +13 -0
  23. package/dist/cli/merge-queue.d.ts.map +1 -1
  24. package/dist/cli/merge-queue.js +48 -2
  25. package/dist/cli/merge-queue.js.map +1 -1
  26. package/dist/cli/mission.d.ts +25 -0
  27. package/dist/cli/mission.d.ts.map +1 -0
  28. package/dist/cli/mission.js +225 -0
  29. package/dist/cli/mission.js.map +1 -0
  30. package/dist/cli/orch.d.ts +27 -0
  31. package/dist/cli/orch.d.ts.map +1 -0
  32. package/dist/cli/orch.js +70 -0
  33. package/dist/cli/orch.js.map +1 -0
  34. package/dist/cli/systemd-services.d.ts.map +1 -1
  35. package/dist/cli/systemd-services.js +44 -0
  36. package/dist/cli/systemd-services.js.map +1 -1
  37. package/dist/delivery/merge-gate.d.ts +168 -0
  38. package/dist/delivery/merge-gate.d.ts.map +1 -0
  39. package/dist/delivery/merge-gate.js +309 -0
  40. package/dist/delivery/merge-gate.js.map +1 -0
  41. package/dist/inference/analysis.d.ts +69 -3
  42. package/dist/inference/analysis.d.ts.map +1 -1
  43. package/dist/inference/analysis.js +123 -7
  44. package/dist/inference/analysis.js.map +1 -1
  45. package/dist/inference/probe.d.ts +38 -1
  46. package/dist/inference/probe.d.ts.map +1 -1
  47. package/dist/inference/probe.js +303 -30
  48. package/dist/inference/probe.js.map +1 -1
  49. package/dist/inference/strata.d.ts +95 -0
  50. package/dist/inference/strata.d.ts.map +1 -0
  51. package/dist/inference/strata.js +167 -0
  52. package/dist/inference/strata.js.map +1 -0
  53. package/dist/mission/database.d.ts +29 -0
  54. package/dist/mission/database.d.ts.map +1 -0
  55. package/dist/mission/database.js +131 -0
  56. package/dist/mission/database.js.map +1 -0
  57. package/dist/mission/ledger.d.ts +109 -0
  58. package/dist/mission/ledger.d.ts.map +1 -0
  59. package/dist/mission/ledger.js +294 -0
  60. package/dist/mission/ledger.js.map +1 -0
  61. package/dist/mission/poll-cycle.d.ts +73 -0
  62. package/dist/mission/poll-cycle.d.ts.map +1 -0
  63. package/dist/mission/poll-cycle.js +100 -0
  64. package/dist/mission/poll-cycle.js.map +1 -0
  65. package/dist/mission/poll-executor.d.ts +91 -0
  66. package/dist/mission/poll-executor.d.ts.map +1 -0
  67. package/dist/mission/poll-executor.js +328 -0
  68. package/dist/mission/poll-executor.js.map +1 -0
  69. package/dist/models/types.d.ts.map +1 -1
  70. package/dist/models/types.js +33 -27
  71. package/dist/models/types.js.map +1 -1
  72. package/dist/self-tuning/profiles/qwen38.d.ts +13 -14
  73. package/dist/self-tuning/profiles/qwen38.d.ts.map +1 -1
  74. package/dist/self-tuning/profiles/qwen38.js +17 -18
  75. package/dist/self-tuning/profiles/qwen38.js.map +1 -1
  76. package/dist/types/mission.d.ts +80 -0
  77. package/dist/types/mission.d.ts.map +1 -0
  78. package/dist/types/mission.js +12 -0
  79. package/dist/types/mission.js.map +1 -0
  80. package/docs/architecture/adr/0003-mission-orchestrator.md +99 -0
  81. package/docs/architecture/adr/0004-deterministic-merge-gate.md +127 -0
  82. package/docs/guides/CAPACITY_POLICY.md +49 -0
  83. package/docs/guides/INFERENCE_HEALTH.md +65 -8
  84. package/docs/guides/MISSIONS.md +170 -0
  85. package/docs/guides/PROXY.md +2 -0
  86. package/docs/reports/2026-10-02-plane-admin-fail-closed.md +70 -0
  87. package/docs/reports/2026-10-02-plane-admin-protect-session.md +116 -0
  88. package/docs/visual/merge-gate.html +404 -0
  89. package/docs/visual/mission-orchestrator.html +472 -0
  90. package/package.json +2 -2
  91. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  92. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  93. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  94. package/src/policies/enforcers/plane_admin_protect.py +133 -0
  95. package/src/policies/schemas/policies/plane-admin-protect.md +55 -0
  96. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  97. package/templates/hooks/uap-policy-gate.sh +33 -0
  98. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  99. package/tools/agents/scripts/anthropic_proxy.py +493 -40
  100. package/tools/agents/tests/test_anthropic_proxy_streaming.py +171 -0
  101. package/tools/agents/tests/test_gate_failclosed_plane_admin.py +248 -0
  102. package/tools/agents/tests/test_plane_admin_protect.py +124 -0
  103. package/tools/agents/tests/test_stream_heartbeat.py +77 -0
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+ """plane-admin-protect enforcer: agents must not reset/override the Plane admin
3
+ account credentials unless the owner explicitly approved via a waiver file.
4
+
5
+ Observed (2026-09-24 and 2026-09-30): agents independently ran
6
+ `docker exec plane-app-api-1 python manage.py ... set_password admin@pay2u.com.au`
7
+ and overwrote the owner's password TWICE. The owner had to ask for a reset.
8
+ UAP memory stores a PROTECT note; this enforcer makes that note mechanical.
9
+
10
+ Scope (Bash/bash/run_bash/shell operations only):
11
+ - Any shell command that targets the Plane stack (plane-app-api-1, plane-app,
12
+ 134.199.173.65, /opt/plane, plane-db) AND performs a credential mutation
13
+ (reset_password / set_password / changepassword / createsuperuser / passwd)
14
+ is refused.
15
+
16
+ Escape hatch (owner approval, mirrors the policies/waivers convention used by
17
+ the expert-review gate): write `policies/waivers/plane-admin-reset` containing
18
+ a line `expires: YYYY-MM-DD` (today or later), then retry. The waiver is a
19
+ file the OWNER writes; an agent writing it without an explicit owner request
20
+ in the current session violates the policy even though the gate would pass.
21
+
22
+ Known limits. This is a text gate over a language with unbounded ways to say
23
+ the same thing. It scans the command with `scannable_command()`, which strips
24
+ heredoc bodies and multi-word quoted blobs so a command that merely NAMES a
25
+ credential verb in prose (a commit message, a `uap memory store`, a script fed
26
+ over a heredoc) is not refused. The observed incident — `docker exec ...
27
+ changepassword ...` — is caught because `exec` hands text to a shell, so
28
+ `scannable_command` leaves that payload intact. A mutation quoted inside a
29
+ plain `ssh host "..."` with no shell-exec token, or routed through `eval`/
30
+ base64, evades every text rule by construction. Treat this as raising the cost,
31
+ not as a boundary.
32
+ """
33
+ from __future__ import annotations
34
+
35
+ import re
36
+ import sys
37
+ from datetime import date
38
+ from pathlib import Path
39
+
40
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
41
+ from _common import emit, parse_cli, repo_root, scannable_command # noqa: E402
42
+
43
+ WAIVER_PATH = "policies/waivers/plane-admin-reset"
44
+
45
+ SHELL_OPS = {"bash", "run_bash", "shell", "execute_command", "terminal"}
46
+
47
+ # Substring match, so `plane-app` already covers plane-app-api-1/-worker/-beat;
48
+ # the specific spellings are listed for documentation, not because they match
49
+ # anything the generic one would not.
50
+ TARGETS = (
51
+ "plane-app",
52
+ "134.199.173.65",
53
+ "/opt/plane",
54
+ "plane-db",
55
+ )
56
+
57
+ MUTATIONS = (
58
+ "reset_password",
59
+ "set_password",
60
+ "changepassword",
61
+ "createsuperuser",
62
+ "passwd",
63
+ )
64
+
65
+
66
+ def _waiver_expiry(waiver: Path) -> tuple[bool, str]:
67
+ """(valid, detail) for a waiver file that exists.
68
+
69
+ A waiver is only honoured when it parses an `expires: YYYY-MM-DD` line that
70
+ is today or later. An unreadable file is NOT treated as absent — the gate
71
+ fails closed rather than crashing, so a permission problem cannot be used to
72
+ slip past it (or to mask it as an internal error).
73
+ """
74
+ try:
75
+ text = waiver.read_text()
76
+ except OSError as e:
77
+ return False, f"waiver file exists but could not be read ({e.strerror or e})"
78
+ m = re.search(r"expires:\s*(\d{4}-\d{2}-\d{2})", text)
79
+ if not m:
80
+ return False, "waiver file is missing an 'expires: YYYY-MM-DD' line"
81
+ try:
82
+ expires = date.fromisoformat(m.group(1))
83
+ except ValueError:
84
+ return False, f"waiver 'expires: {m.group(1)}' is not a valid date"
85
+ if expires >= date.today():
86
+ return True, f"owner waiver valid until {m.group(1)}"
87
+ return False, f"waiver expired on {m.group(1)}"
88
+
89
+
90
+ def main() -> None:
91
+ operation, args = parse_cli()
92
+ if operation.lower() not in SHELL_OPS:
93
+ emit(True, "plane-admin-protect: not a shell operation")
94
+
95
+ raw = str(args.get("command") or "")
96
+ if not raw:
97
+ emit(True, "plane-admin-protect: no command payload")
98
+ cmd = scannable_command(raw).lower()
99
+
100
+ if not any(t in cmd for t in TARGETS):
101
+ emit(True, "plane-admin-protect: command does not target the Plane stack")
102
+
103
+ verb = next((v for v in MUTATIONS if v in cmd), None)
104
+ if verb is None:
105
+ emit(True, "plane-admin-protect: no credential mutation verb")
106
+
107
+ waiver = repo_root() / WAIVER_PATH
108
+ if waiver.exists():
109
+ valid, detail = _waiver_expiry(waiver)
110
+ if valid:
111
+ emit(True, f"plane-admin-protect: {detail}")
112
+ emit(
113
+ False,
114
+ f"plane-admin-protect: {detail} — refresh it with the owner's approval.",
115
+ route="owner-waiver",
116
+ waiverHint=f"owner writes {WAIVER_PATH} with a line 'expires: YYYY-MM-DD'",
117
+ )
118
+
119
+ emit(
120
+ False,
121
+ "plane-admin-protect: refusing credential mutation on the Plane stack "
122
+ f"(matched target + verb {verb!r}). The Plane admin account "
123
+ "admin@pay2u.com.au (droplet 134.199.173.65, /opt/plane/plane-app) must "
124
+ "NOT be reset/overridden unless the owner explicitly asks. Owner "
125
+ "approval: write policies/waivers/plane-admin-reset with a line "
126
+ "'expires: YYYY-MM-DD' and retry.",
127
+ route="owner-waiver",
128
+ waiverHint=f"owner writes {WAIVER_PATH} with a line 'expires: YYYY-MM-DD'",
129
+ )
130
+
131
+
132
+ if __name__ == "__main__":
133
+ main()
@@ -0,0 +1,55 @@
1
+ # plane-admin-protect
2
+
3
+ **Category**: safety
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: pre-exec
6
+ **Default**: on
7
+ **Tags**: plane, credentials, admin, safety, droplet, owner-waiver
8
+
9
+ ## Rule
10
+
11
+ A `Bash`/`run_bash`/`shell` command that **both** targets the Plane stack **and**
12
+ performs a credential mutation is blocked:
13
+
14
+ - Targets (substring match): `plane-app` (covers `plane-app-api-1`/`-worker`/`-beat`),
15
+ `134.199.173.65`, `/opt/plane`, `plane-db`.
16
+ - Mutation verbs: `reset_password`, `set_password`, `changepassword`,
17
+ `createsuperuser`, `passwd`.
18
+
19
+ The Plane admin account `admin@pay2u.com.au` (droplet `134.199.173.65`,
20
+ `/opt/plane/plane-app`) must **not** be reset or overridden unless the owner
21
+ explicitly asks for it in the current session.
22
+
23
+ **Allowed**: read-only Plane commands (`kubectl logs plane-app-worker`,
24
+ `docker exec plane-app-api-1 python manage.py showmigrations`, …) and any command
25
+ that merely *names* a credential verb in prose — a commit message, a
26
+ `uap memory store`, a script fed over a heredoc. The scan uses
27
+ `scannable_command()`, which strips heredoc bodies and multi-word quoted blobs,
28
+ so prose that mentions the verbs is not refused.
29
+
30
+ **Escape hatch (owner approval).** Write `policies/waivers/plane-admin-reset`
31
+ containing a line `expires: YYYY-MM-DD` (today or later), then retry. The waiver
32
+ is a file the OWNER writes; an agent writing it without an explicit owner request
33
+ in the current session violates the policy even though the gate would pass. A
34
+ waiver that is expired, missing its `expires:` line, or unreadable fails the gate
35
+ closed (refused), never allowed and never a crash.
36
+
37
+ **Known limits.** This is a text gate over a language with unbounded ways to say
38
+ the same thing. A mutation quoted inside a plain `ssh host "..."` with no
39
+ shell-exec token, or routed through `eval`/base64, evades every text rule by
40
+ construction. Treat this as raising the cost, not as a boundary.
41
+
42
+ ## Why
43
+
44
+ Observed live (2026-09-24 and 2026-09-30): agents independently ran
45
+ `docker exec plane-app-api-1 python manage.py ... set_password admin@pay2u.com.au`
46
+ and overwrote the owner's password TWICE. The owner had to ask for a reset. UAP
47
+ memory stores a PROTECT note; this enforcer makes that note mechanical rather
48
+ than a reminder an agent can forget or route around.
49
+
50
+ ## Enforcement
51
+
52
+ Python enforcer `plane_admin_protect.py` (Bash/run_bash/shell ops only). Emits a
53
+ specific remediation: the refusal names the matched target and verb and points at
54
+ the owner-waiver path. Install with `uap policy install plane-admin-protect`,
55
+ which auto-attaches the enforcer from `src/policies/enforcers/plane_admin_protect.py`.
@@ -399,6 +399,9 @@ fail_closed() {
399
399
  schema_diff_gate)
400
400
  echo "[UAP policy gate] FAIL-CLOSED: this commit touches watched schema paths but the schema-diff enforcer could not run (${why}). Blocked so a broken/absent gate can't become a bypass. Restore it with: uap policy verify --repair (or uap policy install schema-diff-gate if the manifest is gone; npm run build if dist/ is missing). Operator override: UAP_SELF_PROTECT_OFF=1 in the environment." >&2
401
401
  ;;
402
+ plane_admin_protect)
403
+ echo "[UAP policy gate] FAIL-CLOSED: this shell command can mutate the plane-admin control surface but the plane_admin_protect enforcer could not run (${why}). Blocked so a broken/absent gate can't become a bypass. Restore it with: uap policy verify --repair (or uap policy install plane-admin-protect if the manifest is gone). Operator override: UAP_SELF_PROTECT_OFF=1 in the environment." >&2
404
+ ;;
402
405
  *)
403
406
  echo "[UAP policy gate] FAIL-CLOSED: this operation touches the enforcement control surface but the self-protect enforcer could not run (${why}). Blocked so a broken/absent gate can't become a bypass. (Operator override: UAP_SELF_PROTECT_OFF=1.)" >&2
404
407
  ;;
@@ -524,13 +527,43 @@ print("1" if (inv and not inert) else "0")
524
527
  # refused every commit and push in the session with no working remedy.
525
528
  [[ "${UAP_SELF_PROTECT_OFF:-}" == "1" ]] && COMMIT_OP=0
526
529
 
530
+ # Is this a SHELL-EXECUTION tool call? plane_admin_protect guards the plane-admin
531
+ # control surface (policy registration, the plane-admin rows the gate reads out of
532
+ # policies.db, the .policy-tools/ enforcer copies), and that surface is reachable
533
+ # almost exclusively through the shell: `uap policy install/verify`, `sqlite3
534
+ # agents/data/memory/policies.db ...`, `rm -rf .policy-tools/...`. An Edit/Write
535
+ # of an ordinary source file never invokes it, so arming the fail-closed net there
536
+ # would turn a broken plane-admin enforcer into a blanket block on every edit in
537
+ # the session -- the same over-reach the COMMIT_OP scoping exists to avoid for
538
+ # schema_diff_gate. Keyed on the TOOL name rather than command text because the
539
+ # enforcer's own activation test is tool-based: a shell tool is the only one it is
540
+ # ever asked about.
541
+ SHELL_OP=0
542
+ case " $TOOL " in
543
+ *" Bash "*|*" bash "*|*" run_bash "*|*" BashTool "*|*" Shell "*|*" shell "*|*" Terminal "*|*" terminal "*|*" execute_command "*|*" run_command "*|*" exec "*|*" local_shell "*|*" command "*)
544
+ SHELL_OP=1
545
+ ;;
546
+ esac
547
+ # Same operator hatch as SEC_SENSITIVE/COMMIT_OP: UAP_SELF_PROTECT_OFF is
548
+ # documented as clearing the whole fail-closed net, so it has to clear this arm
549
+ # too -- otherwise the override named in the refusal is a no-op for exactly the
550
+ # case that prints it.
551
+ [[ "${UAP_SELF_PROTECT_OFF:-}" == "1" ]] && SHELL_OP=0
552
+
527
553
  # Which enforcers must fail CLOSED, and when. Called from `if`, never as the
528
554
  # left side of `&&`: this script runs under `set -e`, and `if` suspends it for
529
555
  # the whole condition.
556
+ #
557
+ # plane_admin_protect arms ONLY for shell-execution tools (SHELL_OP): its guarded
558
+ # surface is mutable through the shell and nothing else, so a missing or errored
559
+ # plane-admin enforcer on a Bash call is a live bypass of the admin controls,
560
+ # while the same broken enforcer on an Edit is unrelated to that surface and must
561
+ # keep failing open (the gate is fail-soft by design for what it is not guarding).
530
562
  must_fail_closed() {
531
563
  case "$1" in
532
564
  enforcement_self_protect) [[ "$SEC_SENSITIVE" == "1" ]] ;;
533
565
  schema_diff_gate) [[ "$COMMIT_OP" == "1" ]] ;;
566
+ plane_admin_protect) [[ "$SHELL_OP" == "1" ]] ;;
534
567
  *) false ;;
535
568
  esac
536
569
  }