@miller-tech/uap 1.119.0 → 1.119.1
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 +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +4 -1
- package/tools/agents/tests/test_stuck_break_reattach.py +67 -0
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -4341,11 +4341,14 @@ def _maybe_inject_stuck_break(openai_body: dict, monitor: "SessionMonitor") -> N
|
|
|
4341
4341
|
"operator the single blocking question in one sentence. Take a DIFFERENT "
|
|
4342
4342
|
"action now."
|
|
4343
4343
|
)
|
|
4344
|
-
msgs = openai_body.get("messages")
|
|
4344
|
+
msgs = openai_body.get("messages")
|
|
4345
|
+
if not isinstance(msgs, list):
|
|
4346
|
+
msgs = []
|
|
4345
4347
|
if msgs and msgs[0].get("role") == "system":
|
|
4346
4348
|
msgs[0]["content"] = (msgs[0].get("content") or "") + directive
|
|
4347
4349
|
else:
|
|
4348
4350
|
msgs.insert(0, {"role": "system", "content": directive.strip()})
|
|
4351
|
+
openai_body["messages"] = msgs # reattach in case messages was empty/absent
|
|
4349
4352
|
logger.warning("STUCK-BREAK: forced terminal turn (%s, fires=%d)", reason, monitor.stuck_break_fires)
|
|
4350
4353
|
|
|
4351
4354
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Regression: the STUCK-BREAK injector built `msgs = openai_body.get("messages")
|
|
3
|
+
or []`, which returns a DETACHED empty list when messages is [], so the injected
|
|
4
|
+
directive never landed in the outbound request. Real requests always carry
|
|
5
|
+
messages, but the injector must be robust — it now reattaches the list.
|
|
6
|
+
Mirrors the fix already applied to _maybe_inject_deferral_break.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import importlib.util
|
|
10
|
+
import unittest
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _load_proxy_module():
|
|
15
|
+
proxy_path = Path(__file__).resolve().parents[1] / "scripts" / "anthropic_proxy.py"
|
|
16
|
+
spec = importlib.util.spec_from_file_location("anthropic_proxy", proxy_path)
|
|
17
|
+
assert spec is not None and spec.loader is not None
|
|
18
|
+
module = importlib.util.module_from_spec(spec)
|
|
19
|
+
spec.loader.exec_module(module)
|
|
20
|
+
return module
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
proxy = _load_proxy_module()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _firing_monitor():
|
|
27
|
+
mon = proxy.SessionMonitor()
|
|
28
|
+
# Trip the self-reported-stuck signal so the injector fires.
|
|
29
|
+
mon.self_stuck_streak = proxy.PROXY_STUCK_TEXT_THRESHOLD + 1
|
|
30
|
+
return mon
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestStuckBreakReattach(unittest.TestCase):
|
|
34
|
+
def test_empty_messages_list_still_receives_directive(self):
|
|
35
|
+
mon = _firing_monitor()
|
|
36
|
+
body = {"tool_choice": "required", "messages": []}
|
|
37
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
38
|
+
self.assertTrue(body["messages"], "directive must land in the request")
|
|
39
|
+
self.assertEqual(body["messages"][0]["role"], "system")
|
|
40
|
+
self.assertIn("STOP", body["messages"][0]["content"])
|
|
41
|
+
self.assertEqual(body["tool_choice"], "auto") # coercion released
|
|
42
|
+
|
|
43
|
+
def test_absent_messages_key_is_created(self):
|
|
44
|
+
mon = _firing_monitor()
|
|
45
|
+
body = {"tool_choice": "required"} # no "messages" key at all
|
|
46
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
47
|
+
self.assertIn("messages", body)
|
|
48
|
+
self.assertIn("STOP", body["messages"][0]["content"])
|
|
49
|
+
|
|
50
|
+
def test_existing_system_message_is_appended(self):
|
|
51
|
+
mon = _firing_monitor()
|
|
52
|
+
body = {"messages": [{"role": "system", "content": "SYS"}]}
|
|
53
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
54
|
+
self.assertEqual(len(body["messages"]), 1)
|
|
55
|
+
self.assertTrue(body["messages"][0]["content"].startswith("SYS"))
|
|
56
|
+
self.assertIn("STOP", body["messages"][0]["content"])
|
|
57
|
+
|
|
58
|
+
def test_does_not_fire_when_not_stuck(self):
|
|
59
|
+
mon = proxy.SessionMonitor() # no streak
|
|
60
|
+
body = {"tool_choice": "required", "messages": []}
|
|
61
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
62
|
+
self.assertEqual(body["messages"], [])
|
|
63
|
+
self.assertEqual(body["tool_choice"], "required")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
unittest.main()
|