@miller-tech/uap 1.20.9 → 1.20.11

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.20.9",
3
+ "version": "1.20.11",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -147,10 +147,10 @@ PROXY_TOOL_STATE_FORCED_BUDGET = int(
147
147
  )
148
148
  PROXY_TOOL_STATE_AUTO_BUDGET = int(os.environ.get("PROXY_TOOL_STATE_AUTO_BUDGET", "2"))
149
149
  PROXY_TOOL_STATE_STAGNATION_THRESHOLD = int(
150
- os.environ.get("PROXY_TOOL_STATE_STAGNATION_THRESHOLD", "9")
150
+ os.environ.get("PROXY_TOOL_STATE_STAGNATION_THRESHOLD", "5")
151
151
  )
152
152
  PROXY_TOOL_STATE_CYCLE_WINDOW = int(
153
- os.environ.get("PROXY_TOOL_STATE_CYCLE_WINDOW", "8")
153
+ os.environ.get("PROXY_TOOL_STATE_CYCLE_WINDOW", "4")
154
154
  )
155
155
  PROXY_TOOL_STATE_FINALIZE_THRESHOLD = int(
156
156
  os.environ.get("PROXY_TOOL_STATE_FINALIZE_THRESHOLD", "18")
@@ -280,9 +280,6 @@ PROXY_FORCED_TOOL_DAMPENER_REJECTIONS = int(
280
280
  PROXY_TOOL_STARVATION_THRESHOLD = int(
281
281
  os.environ.get("PROXY_TOOL_STARVATION_THRESHOLD", "5")
282
282
  )
283
- PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD = float(
284
- os.environ.get("PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD", "0.70")
285
- )
286
283
  PROXY_SESSION_CONTAMINATION_BREAKER = os.environ.get(
287
284
  "PROXY_SESSION_CONTAMINATION_BREAKER", "on"
288
285
  ).lower() not in {
@@ -2056,7 +2053,7 @@ def _resolve_state_machine_tool_choice(
2056
2053
  monitor.tool_state_stagnation_streak,
2057
2054
  monitor.tool_state_review_cycles,
2058
2055
  )
2059
- return "auto", reason
2056
+ return "required", reason
2060
2057
 
2061
2058
  if monitor.tool_state_forced_budget_remaining <= 0:
2062
2059
  monitor.set_tool_turn_phase("review", reason="forced_budget_exhausted")
@@ -2071,7 +2068,7 @@ def _resolve_state_machine_tool_choice(
2071
2068
  "TOOL STATE MACHINE: forced budget exhausted, entering review (cycles=%d)",
2072
2069
  monitor.tool_state_review_cycles,
2073
2070
  )
2074
- return "auto", "forced_budget_exhausted"
2071
+ return "required", "forced_budget_exhausted"
2075
2072
 
2076
2073
  monitor.tool_state_forced_budget_remaining -= 1
2077
2074
  return "required", "act"
@@ -2091,7 +2088,7 @@ def _resolve_state_machine_tool_choice(
2091
2088
  1, PROXY_TOOL_STATE_FORCED_BUDGET // 2
2092
2089
  )
2093
2090
  return "required", "review_complete"
2094
- return "auto", "review"
2091
+ return "required", "review"
2095
2092
 
2096
2093
  if monitor.tool_turn_phase == "finalize":
2097
2094
  if monitor.tool_state_auto_budget_remaining <= 0:
@@ -2388,22 +2385,6 @@ def build_openai_request(
2388
2385
  if not has_tool_results:
2389
2386
  monitor.reset_tool_turn_state(reason="no_tool_results")
2390
2387
 
2391
- # CONTEXT-AWARE RELAXATION: when context utilization is high and
2392
- # tool_choice was forced to required, relax to auto to let the model
2393
- # emit shorter text responses instead of consuming more tokens.
2394
- if openai_body.get("tool_choice") == "required":
2395
- ctx_utilization = (
2396
- monitor.last_input_tokens / monitor.context_window
2397
- if monitor.context_window > 0
2398
- else 0.0
2399
- )
2400
- if ctx_utilization >= PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD:
2401
- openai_body["tool_choice"] = "auto"
2402
- logger.warning(
2403
- "CONTEXT-AWARE RELAXATION: tool_choice=auto (utilization=%.1f%% >= %.0f%% threshold)",
2404
- ctx_utilization * 100,
2405
- PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD * 100,
2406
- )
2407
2388
 
2408
2389
  if PROXY_DISABLE_THINKING_ON_TOOL_TURNS:
2409
2390
  openai_body["enable_thinking"] = False
@@ -1861,7 +1861,8 @@ class TestToolTurnControls(unittest.TestCase):
1861
1861
 
1862
1862
  self.assertEqual(openai_1.get("tool_choice"), "required")
1863
1863
  self.assertEqual(openai_2.get("tool_choice"), "required")
1864
- self.assertEqual(openai_3.get("tool_choice"), "auto")
1864
+ # Review phase now keeps required to prevent end-turn escape
1865
+ self.assertEqual(openai_3.get("tool_choice"), "required")
1865
1866
  finally:
1866
1867
  setattr(proxy, "PROXY_TOOL_STATE_MACHINE", old_state)
1867
1868
  setattr(proxy, "PROXY_TOOL_STATE_MIN_MESSAGES", old_min_msgs)
@@ -1938,7 +1939,8 @@ class TestToolTurnControls(unittest.TestCase):
1938
1939
  }
1939
1940
 
1940
1941
  openai = proxy.build_openai_request(body, monitor)
1941
- self.assertEqual(openai.get("tool_choice"), "auto")
1942
+ # Review phase now keeps required to prevent end-turn escape
1943
+ self.assertEqual(openai.get("tool_choice"), "required")
1942
1944
  self.assertEqual(monitor.tool_turn_phase, "review")
1943
1945
  finally:
1944
1946
  setattr(proxy, "PROXY_TOOL_STATE_MACHINE", old_state)
@@ -2067,7 +2069,8 @@ class TestToolTurnControls(unittest.TestCase):
2067
2069
  }
2068
2070
 
2069
2071
  openai = proxy.build_openai_request(body, monitor)
2070
- self.assertEqual(openai.get("tool_choice"), "auto")
2072
+ # Review phase now keeps required to prevent end-turn escape
2073
+ self.assertEqual(openai.get("tool_choice"), "required")
2071
2074
  self.assertEqual(monitor.tool_turn_phase, "review")
2072
2075
  self.assertEqual(monitor.tool_state_review_cycles, 1)
2073
2076
  finally:
@@ -3166,44 +3169,6 @@ class TestToolStarvationBreaker(unittest.TestCase):
3166
3169
  self.assertIn("tools", result)
3167
3170
 
3168
3171
 
3169
- class TestContextAwareRelaxation(unittest.TestCase):
3170
- """Tests for context-aware tool_choice relaxation."""
3171
-
3172
- def test_relaxes_at_high_utilization(self):
3173
- monitor = proxy.SessionMonitor()
3174
- monitor.context_window = 100000
3175
- monitor.last_input_tokens = 75000 # 75% > 70% threshold
3176
- body = {
3177
- "model": "qwen3.5",
3178
- "messages": [
3179
- {"role": "user", "content": "hello"},
3180
- {"role": "assistant", "content": "text only"},
3181
- {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "x", "content": "ok"}]},
3182
- ],
3183
- "tools": [{"name": "Bash", "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}}}],
3184
- }
3185
- result = proxy.build_openai_request(body, monitor)
3186
- # Should be auto, not required
3187
- self.assertEqual(result.get("tool_choice"), "auto")
3188
-
3189
- def test_no_relaxation_below_threshold(self):
3190
- monitor = proxy.SessionMonitor()
3191
- monitor.context_window = 100000
3192
- monitor.last_input_tokens = 50000 # 50% < 70%
3193
- body = {
3194
- "model": "qwen3.5",
3195
- "messages": [
3196
- {"role": "user", "content": "hello"},
3197
- {"role": "assistant", "content": "text only"},
3198
- {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "x", "content": "ok"}]},
3199
- ],
3200
- "tools": [{"name": "Bash", "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}}}],
3201
- }
3202
- result = proxy.build_openai_request(body, monitor)
3203
- # Should still be required (state machine forces it)
3204
- self.assertEqual(result.get("tool_choice"), "required")
3205
-
3206
-
3207
3172
  if __name__ == "__main__":
3208
3173
  unittest.main()
3209
3174