@miller-tech/uap 1.94.0 → 1.95.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 (76) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/benchmarks/benchmark.d.ts +4 -4
  3. package/dist/benchmarks/paired/types.d.ts +2 -2
  4. package/dist/bin/cli.js +12 -2
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/cli/deliver.d.ts +5 -0
  7. package/dist/cli/deliver.d.ts.map +1 -1
  8. package/dist/cli/deliver.js +232 -12
  9. package/dist/cli/deliver.js.map +1 -1
  10. package/dist/cli/task.d.ts +1 -1
  11. package/dist/cli/task.d.ts.map +1 -1
  12. package/dist/cli/task.js +33 -0
  13. package/dist/cli/task.js.map +1 -1
  14. package/dist/coordination/reactor.d.ts.map +1 -1
  15. package/dist/coordination/reactor.js +14 -0
  16. package/dist/coordination/reactor.js.map +1 -1
  17. package/dist/delivery/applier.d.ts.map +1 -1
  18. package/dist/delivery/applier.js +3 -0
  19. package/dist/delivery/applier.js.map +1 -1
  20. package/dist/delivery/auto-mine.d.ts +27 -0
  21. package/dist/delivery/auto-mine.d.ts.map +1 -0
  22. package/dist/delivery/auto-mine.js +73 -0
  23. package/dist/delivery/auto-mine.js.map +1 -0
  24. package/dist/delivery/auto-optimizer.d.ts +6 -2
  25. package/dist/delivery/auto-optimizer.d.ts.map +1 -1
  26. package/dist/delivery/auto-optimizer.js +12 -5
  27. package/dist/delivery/auto-optimizer.js.map +1 -1
  28. package/dist/delivery/convergence-loop.d.ts +45 -0
  29. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  30. package/dist/delivery/convergence-loop.js +81 -2
  31. package/dist/delivery/convergence-loop.js.map +1 -1
  32. package/dist/delivery/decompose.d.ts +45 -0
  33. package/dist/delivery/decompose.d.ts.map +1 -0
  34. package/dist/delivery/decompose.js +122 -0
  35. package/dist/delivery/decompose.js.map +1 -0
  36. package/dist/delivery/escalation.d.ts +2 -0
  37. package/dist/delivery/escalation.d.ts.map +1 -1
  38. package/dist/delivery/escalation.js +5 -0
  39. package/dist/delivery/escalation.js.map +1 -1
  40. package/dist/delivery/index.d.ts +5 -1
  41. package/dist/delivery/index.d.ts.map +1 -1
  42. package/dist/delivery/index.js +4 -0
  43. package/dist/delivery/index.js.map +1 -1
  44. package/dist/delivery/run-state.d.ts +44 -0
  45. package/dist/delivery/run-state.d.ts.map +1 -0
  46. package/dist/delivery/run-state.js +137 -0
  47. package/dist/delivery/run-state.js.map +1 -0
  48. package/dist/delivery/task-sync.d.ts +47 -0
  49. package/dist/delivery/task-sync.d.ts.map +1 -0
  50. package/dist/delivery/task-sync.js +148 -0
  51. package/dist/delivery/task-sync.js.map +1 -0
  52. package/dist/mcp-router/tools/deliver.d.ts +48 -0
  53. package/dist/mcp-router/tools/deliver.d.ts.map +1 -1
  54. package/dist/mcp-router/tools/deliver.js +64 -4
  55. package/dist/mcp-router/tools/deliver.js.map +1 -1
  56. package/dist/observability/halo-exporter.d.ts +4 -3
  57. package/dist/observability/halo-exporter.d.ts.map +1 -1
  58. package/dist/observability/halo-exporter.js +9 -5
  59. package/dist/observability/halo-exporter.js.map +1 -1
  60. package/dist/state/manifest.d.ts +34 -0
  61. package/dist/state/manifest.d.ts.map +1 -0
  62. package/dist/state/manifest.js +152 -0
  63. package/dist/state/manifest.js.map +1 -0
  64. package/dist/state/reactor-inject.d.ts +14 -0
  65. package/dist/state/reactor-inject.d.ts.map +1 -0
  66. package/dist/state/reactor-inject.js +25 -0
  67. package/dist/state/reactor-inject.js.map +1 -0
  68. package/dist/tasks/service.d.ts +7 -0
  69. package/dist/tasks/service.d.ts.map +1 -1
  70. package/dist/tasks/service.js +23 -0
  71. package/dist/tasks/service.js.map +1 -1
  72. package/package.json +1 -1
  73. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  74. package/src/policies/enforcers/delivery_enforcement.py +1 -1
  75. package/templates/hooks/deliver_autoroute.py +60 -10
  76. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
@@ -12,12 +12,14 @@ the block carries route == "deliver", the helper:
12
12
  3. Prints the (possibly annotated) block message on stdout for the hook to
13
13
  surface to the agent.
14
14
 
15
- Default (autoroute off): just logs the intent + returns the message unchanged —
16
- no behavior change. The hook still blocks (exit 2); this only enriches the block.
15
+ Autoroute is ON by default (UAP_DELIVER_AUTOROUTE=0 to disable): the blocked
16
+ intent is logged AND routed. The hook still blocks (exit 2); this only enriches
17
+ the block message and kicks off the sanctioned path in the background.
17
18
  """
18
19
  import argparse
19
20
  import json
20
21
  import os
22
+ import re
21
23
  import sys
22
24
  import time
23
25
  from pathlib import Path
@@ -28,8 +30,11 @@ UAP_DIR = ".uap"
28
30
 
29
31
 
30
32
  def _autoroute_enabled() -> bool:
31
- v = os.environ.get("UAP_DELIVER_AUTOROUTE", "").lower()
32
- return v not in {"", "0", "off", "false", "no"}
33
+ # Default ON: a blocked source edit auto-routes into `uap deliver` in the
34
+ # background instead of dead-ending the agent. Opt out with
35
+ # UAP_DELIVER_AUTOROUTE=0/off/false/no.
36
+ v = os.environ.get("UAP_DELIVER_AUTOROUTE", "on").lower()
37
+ return v not in {"0", "off", "false", "no"}
33
38
 
34
39
 
35
40
  def decide(out: dict, tool: str, args: dict, autoroute_on: bool, seen_files: set) -> dict:
@@ -37,6 +42,11 @@ def decide(out: dict, tool: str, args: dict, autoroute_on: bool, seen_files: set
37
42
  reason = out.get("reason", "")
38
43
  route = out.get("route")
39
44
  hint = out.get("deliverHint") or ""
45
+ # Older enforcers emitted a full command line ('uap deliver "..."'); the
46
+ # hint is the INSTRUCTION — unwrap so the mission text isn't a command.
47
+ m = re.fullmatch(r'uap deliver "(.+)"', hint.strip())
48
+ if m:
49
+ hint = m.group(1)
40
50
  file_path = args.get("file_path") or args.get("path") or args.get("target") or ""
41
51
 
42
52
  if route != "deliver":
@@ -49,7 +59,7 @@ def decide(out: dict, tool: str, args: dict, autoroute_on: bool, seen_files: set
49
59
  if spawn:
50
60
  message = reason + " [auto-routed to `uap deliver` — running in the background]"
51
61
  elif autoroute_on and file_path in seen_files:
52
- message = reason + " [already auto-routed to `uap deliver` this sessionwait for it]"
62
+ message = reason + " [already auto-routed to `uap deliver` for this filesee .uap/autoroute.log / pending-deliver.jsonl]"
53
63
  return {"message": message, "route": route, "spawn": spawn,
54
64
  "file_path": file_path, "hint": hint, "intent": intent}
55
65
 
@@ -65,16 +75,56 @@ def _load_seen(root: Path) -> set:
65
75
  return set()
66
76
 
67
77
 
78
+ LOCK_FILE = "autoroute.lock"
79
+ LOG_FILE = "autoroute.log"
80
+
81
+
82
+ def _pid_alive(pid: int) -> bool:
83
+ try:
84
+ os.kill(pid, 0)
85
+ return True
86
+ except Exception:
87
+ return False
88
+
89
+
90
+ def _acquire_slot(root: Path) -> bool:
91
+ """One in-flight autoroute deliver per repo: a lockfile holding the child
92
+ pid. Stale locks (dead pid) are reclaimed. Best-effort; failure = no slot."""
93
+ lock = root / UAP_DIR / LOCK_FILE
94
+ try:
95
+ if lock.exists():
96
+ pid = int(lock.read_text().strip() or "0")
97
+ if pid and _pid_alive(pid):
98
+ return False
99
+ return True
100
+ except Exception:
101
+ return False
102
+
103
+
68
104
  def _spawn_deliver(root: Path, hint: str) -> None:
69
- """Spawn `uap deliver "<hint>"` fully detached. Best-effort; never raises."""
105
+ """Spawn `uap deliver -- "<hint>"` fully detached. Best-effort; never
106
+ raises. The hint is passed after `--` (a pure operand — no flag smuggling),
107
+ bounded turn budget, output logged to .uap/autoroute.log for visibility."""
70
108
  import subprocess
109
+ if hint.startswith("-"):
110
+ return # never let a hint be parsed as a flag
111
+ if not _acquire_slot(root):
112
+ return # another autoroute mission is already converging in this repo
113
+ try:
114
+ log = (root / UAP_DIR / LOG_FILE).open("a")
115
+ except Exception:
116
+ log = subprocess.DEVNULL
71
117
  try:
72
- subprocess.Popen(
73
- ["uap", "deliver", hint],
118
+ proc = subprocess.Popen(
119
+ ["uap", "deliver", "--max-turns", "5", "--ceiling", "10", "--", hint],
74
120
  cwd=str(root),
75
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
121
+ stdout=log, stderr=log, stdin=subprocess.DEVNULL,
76
122
  start_new_session=True,
77
123
  )
124
+ try:
125
+ (root / UAP_DIR / LOCK_FILE).write_text(str(proc.pid))
126
+ except Exception:
127
+ pass
78
128
  except Exception:
79
129
  pass
80
130
 
@@ -111,7 +161,7 @@ def main() -> None:
111
161
  if d["spawn"]:
112
162
  try:
113
163
  with _seen_path(root).open("a") as f:
114
- f.write(d["file_path"] + "\n")
164
+ f.write(d["file_path"].replace("\n", " ").replace("\r", " ") + "\n")
115
165
  except Exception:
116
166
  pass
117
167
  _spawn_deliver(root, d["hint"])