@miller-tech/uap 1.220.4 → 1.220.6
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/dist/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +1 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts +5 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +13 -2
- package/dist/cli/deliver.js.map +1 -1
- package/dist/config/settings-registry.d.ts.map +1 -1
- package/dist/config/settings-registry.js +5 -0
- package/dist/config/settings-registry.js.map +1 -1
- package/dist/delivery/agentic-executor.d.ts +13 -6
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +7 -6
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/dist/delivery/applier.d.ts +15 -4
- package/dist/delivery/applier.d.ts.map +1 -1
- package/dist/delivery/applier.js +39 -16
- package/dist/delivery/applier.js.map +1 -1
- package/dist/delivery/convergence-loop.d.ts +6 -0
- package/dist/delivery/convergence-loop.d.ts.map +1 -1
- package/dist/delivery/convergence-loop.js +20 -3
- package/dist/delivery/convergence-loop.js.map +1 -1
- package/package.json +2 -2
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.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 +31 -0
- package/tools/agents/tests/test_stuck_break_hard.py +87 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""STUCK-BREAK hard tier.
|
|
3
|
+
|
|
4
|
+
The advisory STUCK-BREAK (directive + tool_choice released to 'auto') leaves
|
|
5
|
+
every tool on the table, and a weak local model just issues the same call
|
|
6
|
+
again: observed live 2026-08-23 16:46-17:45, the identical bash call 13+ turns
|
|
7
|
+
in a row with fires=33 and never a prose reply. After
|
|
8
|
+
PROXY_STUCK_BREAK_HARD_FIRES fires the break must be HARD -- tools removed for
|
|
9
|
+
that turn, XML tool-call resurrection suppressed -- so the turn can only end in
|
|
10
|
+
prose and the client's agent loop actually terminates.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import importlib.util
|
|
14
|
+
import unittest
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _load_proxy_module():
|
|
19
|
+
proxy_path = Path(__file__).resolve().parents[1] / "scripts" / "anthropic_proxy.py"
|
|
20
|
+
spec = importlib.util.spec_from_file_location("anthropic_proxy", proxy_path)
|
|
21
|
+
assert spec is not None and spec.loader is not None
|
|
22
|
+
module = importlib.util.module_from_spec(spec)
|
|
23
|
+
spec.loader.exec_module(module)
|
|
24
|
+
return module
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
proxy = _load_proxy_module()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _looping_monitor(fires_so_far: int):
|
|
31
|
+
mon = proxy.SessionMonitor()
|
|
32
|
+
mon.self_stuck_streak = proxy.PROXY_STUCK_TEXT_THRESHOLD + 1
|
|
33
|
+
mon.stuck_break_fires = fires_so_far
|
|
34
|
+
return mon
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _body():
|
|
38
|
+
return {
|
|
39
|
+
"tool_choice": "required",
|
|
40
|
+
"tools": [{"type": "function", "function": {"name": "bash", "parameters": {}}}],
|
|
41
|
+
"messages": [{"role": "system", "content": "sys"}, {"role": "user", "content": "go"}],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TestStuckBreakHard(unittest.TestCase):
|
|
46
|
+
def test_below_hard_threshold_stays_advisory(self):
|
|
47
|
+
mon = _looping_monitor(fires_so_far=0)
|
|
48
|
+
body = _body()
|
|
49
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
50
|
+
self.assertEqual(mon.stuck_break_fires, 1)
|
|
51
|
+
self.assertIn("tools", body, "advisory tier keeps the tools")
|
|
52
|
+
self.assertEqual(body["tool_choice"], "auto")
|
|
53
|
+
self.assertFalse(mon.suppress_text_tool_extraction)
|
|
54
|
+
self.assertIn("STOP", body["messages"][0]["content"])
|
|
55
|
+
|
|
56
|
+
def test_hard_threshold_strips_tools_and_suppresses_xml_resurrection(self):
|
|
57
|
+
mon = _looping_monitor(fires_so_far=proxy.PROXY_STUCK_BREAK_HARD_FIRES - 1)
|
|
58
|
+
body = _body()
|
|
59
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
60
|
+
self.assertEqual(mon.stuck_break_fires, proxy.PROXY_STUCK_BREAK_HARD_FIRES)
|
|
61
|
+
self.assertNotIn("tools", body)
|
|
62
|
+
self.assertNotIn("tool_choice", body)
|
|
63
|
+
self.assertTrue(mon.suppress_text_tool_extraction)
|
|
64
|
+
self.assertIn("plain text", body["messages"][0]["content"])
|
|
65
|
+
self.assertIn("STOP", body["messages"][0]["content"]) # the advisory text still travels
|
|
66
|
+
|
|
67
|
+
def test_hard_tier_can_be_disabled(self):
|
|
68
|
+
saved = proxy.PROXY_STUCK_BREAK_HARD_FIRES
|
|
69
|
+
try:
|
|
70
|
+
proxy.PROXY_STUCK_BREAK_HARD_FIRES = 0
|
|
71
|
+
mon = _looping_monitor(fires_so_far=50)
|
|
72
|
+
body = _body()
|
|
73
|
+
proxy._maybe_inject_stuck_break(body, mon)
|
|
74
|
+
self.assertIn("tools", body)
|
|
75
|
+
self.assertFalse(mon.suppress_text_tool_extraction)
|
|
76
|
+
finally:
|
|
77
|
+
proxy.PROXY_STUCK_BREAK_HARD_FIRES = saved
|
|
78
|
+
|
|
79
|
+
def test_default_threshold_is_small(self):
|
|
80
|
+
# Three ignored breaks is already ~8 minutes of identical calls on the
|
|
81
|
+
# local executor; the default must not let a loop run for an hour.
|
|
82
|
+
self.assertGreater(proxy.PROXY_STUCK_BREAK_HARD_FIRES, 0)
|
|
83
|
+
self.assertLessEqual(proxy.PROXY_STUCK_BREAK_HARD_FIRES, 5)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
unittest.main()
|