@miller-tech/uap 1.148.11 → 1.148.12

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.148.11",
3
+ "version": "1.148.12",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -47,21 +47,36 @@ def decide(out: dict, tool: str, args: dict, autoroute_on: bool, seen_files: set
47
47
  m = re.fullmatch(r'uap deliver "(.+)"', hint.strip())
48
48
  if m:
49
49
  hint = m.group(1)
50
- file_path = args.get("file_path") or args.get("path") or args.get("target") or ""
50
+ # Accept the file-path key under ANY agent spelling. The enforcer was fixed
51
+ # for this long ago; autoroute was not — so for opencode (which sends
52
+ # `filePath`) file_path was always "", `spawn` was always False, and autoroute
53
+ # was INERT: the gate blocked the edit, logged the intent, told the model to
54
+ # call deliver… and deliver never ran. Observed live: 3 routed intents, 0
55
+ # deliver runs, 0 files changed — work blocked but never delivered.
56
+ file_path = (
57
+ args.get("file_path") or args.get("filePath") or args.get("path")
58
+ or args.get("target") or args.get("filename") or args.get("file") or ""
59
+ )
51
60
 
52
61
  if route != "deliver":
53
62
  return {"message": reason, "route": route, "spawn": False,
54
- "file_path": file_path, "hint": hint, "intent": None}
63
+ "file_path": file_path, "hint": hint, "dedup_key": "", "intent": None}
55
64
 
56
65
  intent = {"ts": int(time.time()), "tool": tool, "file_path": file_path, "hint": hint}
57
- spawn = bool(autoroute_on and hint and file_path and file_path not in seen_files)
66
+ # Dedup on the file when we have one, else on the hint itself. Requiring a
67
+ # file_path made an entire class unspawnable: a BASH-routed source-write
68
+ # (`cat > app.js <<EOF`) carries a `command`, not a path — so those intents
69
+ # were blocked and then silently dropped. The hint is what deliver actually
70
+ # runs, so it is the correct spawn key.
71
+ dedup_key = file_path or hint
72
+ spawn = bool(autoroute_on and hint and dedup_key and dedup_key not in seen_files)
58
73
  message = reason
59
74
  if spawn:
60
75
  message = reason + " [auto-routed to `uap deliver` — running in the background]"
61
- elif autoroute_on and file_path in seen_files:
62
- message = reason + " [already auto-routed to `uap deliver` for this file — see .uap/autoroute.log / pending-deliver.jsonl]"
76
+ elif autoroute_on and dedup_key and dedup_key in seen_files:
77
+ message = reason + " [already auto-routed to `uap deliver` for this change — see .uap/autoroute.log / pending-deliver.jsonl]"
63
78
  return {"message": message, "route": route, "spawn": spawn,
64
- "file_path": file_path, "hint": hint, "intent": intent}
79
+ "file_path": file_path, "hint": hint, "dedup_key": dedup_key, "intent": intent}
65
80
 
66
81
 
67
82
  def _seen_path(root: Path) -> Path:
@@ -160,8 +175,12 @@ def main() -> None:
160
175
 
161
176
  if d["spawn"]:
162
177
  try:
178
+ # Dedup on the SAME key `decide` gated on (file when present, else the
179
+ # hint) — writing file_path here would record "" for a bash-routed
180
+ # intent and never dedup it.
181
+ key = d.get("dedup_key") or d["file_path"] or d["hint"]
163
182
  with _seen_path(root).open("a") as f:
164
- f.write(d["file_path"].replace("\n", " ").replace("\r", " ") + "\n")
183
+ f.write(key.replace("\n", " ").replace("\r", " ") + "\n")
165
184
  except Exception:
166
185
  pass
167
186
  _spawn_deliver(root, d["hint"])
@@ -69,5 +69,53 @@ class LoggingTest(unittest.TestCase):
69
69
  self.assertEqual(rec["file_path"], "/repo/src/foo.ts")
70
70
 
71
71
 
72
+ class AgentKeySpellingTest(unittest.TestCase):
73
+ """autoroute must accept EVERY agent's file-path key.
74
+
75
+ The enforcer was fixed for this long ago; autoroute was not — so for opencode
76
+ (which sends `filePath`) file_path was always "", `spawn` was always False, and
77
+ autoroute was INERT: the gate blocked the edit, logged the intent, told the
78
+ model to call deliver… and deliver never ran. Observed live: 3 routed intents,
79
+ 0 deliver runs, 0 files changed — work blocked but never delivered. The old
80
+ tests only ever passed snake_case, which is why this went unnoticed.
81
+ """
82
+
83
+ def test_opencode_filePath_spawns(self):
84
+ d = mod.decide(BLOCK_OUT, "Write", {"filePath": "/repo/src/foo.ts"}, True, set())
85
+ self.assertTrue(d["spawn"])
86
+ self.assertEqual(d["dedup_key"], "/repo/src/foo.ts")
87
+
88
+ def test_claude_file_path_still_spawns(self):
89
+ d = mod.decide(BLOCK_OUT, "Write", {"file_path": "/repo/src/foo.ts"}, True, set())
90
+ self.assertTrue(d["spawn"])
91
+
92
+ def test_other_spellings(self):
93
+ for key in ("path", "target", "filename", "file"):
94
+ d = mod.decide(BLOCK_OUT, "Write", {key: "/repo/src/foo.ts"}, True, set())
95
+ self.assertTrue(d["spawn"], key)
96
+
97
+
98
+ class BashRoutedIntentTest(unittest.TestCase):
99
+ """A BASH-routed source-write carries a `command`, not a path — requiring
100
+ file_path made that whole class unspawnable (blocked, then silently dropped).
101
+ The hint is what deliver actually runs, so it is the correct spawn key."""
102
+
103
+ def test_bash_intent_spawns_on_hint_alone(self):
104
+ d = mod.decide(BLOCK_OUT, "Bash", {"command": "cat > src/app.js <<EOF"}, True, set())
105
+ self.assertTrue(d["spawn"])
106
+ self.assertEqual(d["file_path"], "") # genuinely has no path
107
+ self.assertEqual(d["dedup_key"], d["hint"]) # …so it dedups on the hint
108
+
109
+ def test_bash_intent_dedupes_on_hint(self):
110
+ d1 = mod.decide(BLOCK_OUT, "Bash", {"command": "cat > a.js <<EOF"}, True, set())
111
+ d2 = mod.decide(BLOCK_OUT, "Bash", {"command": "cat > a.js <<EOF"}, True, {d1["dedup_key"]})
112
+ self.assertTrue(d1["spawn"])
113
+ self.assertFalse(d2["spawn"]) # no double-spawn for the same change
114
+
115
+ def test_still_never_spawns_without_a_hint(self):
116
+ d = mod.decide({"reason": "x", "route": "deliver"}, "Bash", {"command": "cat > a.js"}, True, set())
117
+ self.assertFalse(d["spawn"])
118
+
119
+
72
120
  if __name__ == "__main__":
73
121
  unittest.main()