@miller-tech/uap 2.1.5 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "start": "node dist/bin/cli.js",
22
22
  "test": "vitest",
23
23
  "test:ci": "vitest run",
24
- "test:enforcers": "UAP_PROXY_ENV_AUTOLOAD=0 python3 -m unittest tools.agents.tests.test_enforcement_self_protect tools.agents.tests.test_conditional_destructive_verbs tools.agents.tests.test_live_deliver_kill tools.agents.tests.test_schema_diff_gate tools.agents.tests.test_schema_diff_inline tools.agents.tests.test_stream_telemetry tools.agents.tests.test_project_telemetry_events tools.agents.tests.test_workdir_scope_enforcer tools.agents.tests.test_gate_evidence tools.agents.tests.test_repeat_call_guard tools.agents.tests.test_repeat_guard tools.agents.tests.test_gate_integrity tools.agents.tests.test_gate_failclosed_indirection tools.agents.tests.test_gate_failclosed_schema_diff tools.agents.tests.test_expert_review_pr_scope tools.agents.tests.test_path_containment tools.agents.tests.test_path_normalizer_hardened tools.agents.tests.test_sandbox_tool_strip tools.agents.tests.test_proxy_env_loader tools.agents.tests.test_stream_required_tool tools.agents.tests.test_tool_call_wrapper_profiles tools.agents.tests.test_tool_convert_cache tools.agents.tests.test_doubling_break tools.agents.tests.test_error_loop_ignores_correctives tools.agents.tests.test_anthropic_proxy_tool_arg_types tools.agents.tests.test_attractor_detection tools.agents.tests.test_client_disconnect tools.agents.tests.test_confidence_escalation tools.agents.tests.test_coordination_ban tools.agents.tests.test_coordination_early_ban tools.agents.tests.test_cycle_break_exploration tools.agents.tests.test_deferral_break tools.agents.tests.test_deliver_autoroute tools.agents.tests.test_delivery_enforcement_all_langs tools.agents.tests.test_delivery_enforcement_exemptions tools.agents.tests.test_delivery_enforcement_filepath tools.agents.tests.test_delivery_enforcement_write_complexity tools.agents.tests.test_fastpath_config tools.agents.tests.test_delivery_enforcement_web_and_bash tools.agents.tests.test_disconnect_watcher tools.agents.tests.test_empty_maxtokens_recovery tools.agents.tests.test_empty_tool_loop_break tools.agents.tests.test_enforcer_escape_hatches tools.agents.tests.test_error_loop_break tools.agents.tests.test_finalize_suppression tools.agents.tests.test_malformed_unclosed_think tools.agents.tests.test_mandate_beats_recon tools.agents.tests.test_mandate_deliver tools.agents.tests.test_overflow_truncate_count_tokens tools.agents.tests.test_passthrough_oauth tools.agents.tests.test_project_telemetry tools.agents.tests.test_proxy_auth_headers tools.agents.tests.test_prune_preserve_force_write tools.agents.tests.test_recon_deliver_gate tools.agents.tests.test_session_admission tools.agents.tests.test_stream_heartbeat tools.agents.tests.test_stuck_break_reattach tools.agents.tests.test_stuck_break_hard tools.agents.tests.test_turn_count_breaker_periodic tools.agents.tests.test_upstream_chokepoint tools.agents.tests.test_vision_passthrough tools.agents.tests.test_worktree_required tools.agents.tests.test_enforcer_suite_coverage tools.agents.tests.test_validate_plan_gate tools.agents.tests.test_validate_plan_inside_project tools.agents.tests.test_anthropic_proxy_streaming tools.agents.tests.test_non_stream_resp_log tools.agents.tests.test_delivery_enforcement_worktree tools.agents.tests.test_output_token_ceilings tools.agents.tests.test_tool_narrowing_core tools.agents.tests.test_models_context_window tools.agents.tests.test_thinking_template_kwargs tools.agents.tests.test_cycle_break_wait_poll tools.agents.tests.test_upstream_model_aliases tools.agents.tests.test_stream_thinking_deltas",
24
+ "test:enforcers": "UAP_PROXY_ENV_AUTOLOAD=0 python3 -m unittest tools.agents.tests.test_enforcement_self_protect tools.agents.tests.test_conditional_destructive_verbs tools.agents.tests.test_live_deliver_kill tools.agents.tests.test_schema_diff_gate tools.agents.tests.test_schema_diff_inline tools.agents.tests.test_stream_telemetry tools.agents.tests.test_project_telemetry_events tools.agents.tests.test_workdir_scope_enforcer tools.agents.tests.test_gate_evidence tools.agents.tests.test_repeat_call_guard tools.agents.tests.test_repeat_guard tools.agents.tests.test_gate_integrity tools.agents.tests.test_gate_failclosed_indirection tools.agents.tests.test_gate_failclosed_schema_diff tools.agents.tests.test_expert_review_pr_scope tools.agents.tests.test_path_containment tools.agents.tests.test_path_normalizer_hardened tools.agents.tests.test_sandbox_tool_strip tools.agents.tests.test_proxy_env_loader tools.agents.tests.test_stream_required_tool tools.agents.tests.test_tool_call_wrapper_profiles tools.agents.tests.test_tool_convert_cache tools.agents.tests.test_doubling_break tools.agents.tests.test_error_loop_ignores_correctives tools.agents.tests.test_anthropic_proxy_tool_arg_types tools.agents.tests.test_anthropic_sse_schema tools.agents.tests.test_attractor_detection tools.agents.tests.test_client_disconnect tools.agents.tests.test_confidence_escalation tools.agents.tests.test_coordination_ban tools.agents.tests.test_coordination_early_ban tools.agents.tests.test_cycle_break_exploration tools.agents.tests.test_deferral_break tools.agents.tests.test_deliver_autoroute tools.agents.tests.test_delivery_enforcement_all_langs tools.agents.tests.test_delivery_enforcement_exemptions tools.agents.tests.test_delivery_enforcement_filepath tools.agents.tests.test_delivery_enforcement_write_complexity tools.agents.tests.test_fastpath_config tools.agents.tests.test_delivery_enforcement_web_and_bash tools.agents.tests.test_disconnect_watcher tools.agents.tests.test_empty_maxtokens_recovery tools.agents.tests.test_empty_tool_loop_break tools.agents.tests.test_enforcer_escape_hatches tools.agents.tests.test_error_loop_break tools.agents.tests.test_finalize_suppression tools.agents.tests.test_malformed_unclosed_think tools.agents.tests.test_mandate_beats_recon tools.agents.tests.test_mandate_deliver tools.agents.tests.test_overflow_truncate_count_tokens tools.agents.tests.test_passthrough_oauth tools.agents.tests.test_project_telemetry tools.agents.tests.test_proxy_auth_headers tools.agents.tests.test_prune_preserve_force_write tools.agents.tests.test_recon_deliver_gate tools.agents.tests.test_session_admission tools.agents.tests.test_stream_heartbeat tools.agents.tests.test_stuck_break_reattach tools.agents.tests.test_stuck_break_hard tools.agents.tests.test_turn_count_breaker_periodic tools.agents.tests.test_upstream_chokepoint tools.agents.tests.test_vision_passthrough tools.agents.tests.test_worktree_required tools.agents.tests.test_enforcer_suite_coverage tools.agents.tests.test_validate_plan_gate tools.agents.tests.test_validate_plan_inside_project tools.agents.tests.test_anthropic_proxy_streaming tools.agents.tests.test_non_stream_resp_log tools.agents.tests.test_delivery_enforcement_worktree tools.agents.tests.test_output_token_ceilings tools.agents.tests.test_tool_narrowing_core tools.agents.tests.test_models_context_window tools.agents.tests.test_thinking_template_kwargs tools.agents.tests.test_cycle_break_wait_poll tools.agents.tests.test_upstream_model_aliases tools.agents.tests.test_stream_thinking_deltas",
25
25
  "test:coverage": "vitest --coverage",
26
26
  "bench": "vitest --config vitest.bench.config.ts",
27
27
  "lint": "eslint src --ext .ts",
@@ -12218,7 +12218,7 @@ async def stream_anthropic_message(
12218
12218
  tool_input = json.dumps(block.get("input", {}), separators=(",", ":"))
12219
12219
  yield (
12220
12220
  "event: content_block_start\n"
12221
- f"data: {json.dumps({'type': 'content_block_start', 'index': block_index, 'content_block': {'type': 'tool_use', 'id': tool_id, 'name': tool_name}})}\n\n"
12221
+ f"data: {json.dumps({'type': 'content_block_start', 'index': block_index, 'content_block': {'type': 'tool_use', 'id': tool_id, 'name': tool_name, 'input': {}}})}\n\n"
12222
12222
  )
12223
12223
  if tool_input:
12224
12224
  yield (
@@ -12507,7 +12507,7 @@ async def stream_anthropic_response(
12507
12507
  # Emit content_block_start for this tool_use
12508
12508
  yield (
12509
12509
  f"event: content_block_start\n"
12510
- f"data: {json.dumps({'type': 'content_block_start', 'index': this_block_index, 'content_block': {'type': 'tool_use', 'id': tc_id, 'name': fn.get('name', '')}})}\n\n"
12510
+ f"data: {json.dumps({'type': 'content_block_start', 'index': this_block_index, 'content_block': {'type': 'tool_use', 'id': tc_id, 'name': fn.get('name', ''), 'input': {}}})}\n\n"
12511
12511
  )
12512
12512
 
12513
12513
  # Emit initial arguments fragment (e.g. "{") that
@@ -12749,7 +12749,7 @@ async def stream_anthropic_response(
12749
12749
  }
12750
12750
  yield (
12751
12751
  f"event: content_block_start\n"
12752
- f"data: {json.dumps({'type': 'content_block_start', 'index': idx, 'content_block': {'type': 'tool_use', 'id': tc_id, 'name': tc_name}})}\n\n"
12752
+ f"data: {json.dumps({'type': 'content_block_start', 'index': idx, 'content_block': {'type': 'tool_use', 'id': tc_id, 'name': tc_name, 'input': {}}})}\n\n"
12753
12753
  )
12754
12754
  yield (
12755
12755
  f"event: content_block_delta\n"
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ """Every SSE event the proxy emits must satisfy the Anthropic wire schema.
3
+
4
+ 2026-09-13. The proxy emitted `content_block_start` for a tool_use WITHOUT the
5
+ `input` field:
6
+
7
+ {"type":"content_block_start","index":1,
8
+ "content_block":{"type":"tool_use","id":"...","name":"Read"}}
9
+
10
+ That is not valid per the Anthropic API, whose SDKs are generated from the same
11
+ schema — so Factory Droid (TypeScript SDK) rejected one event per tool-use turn
12
+ with "[DroidClient] Dropping malformed event" (measured: 42 drops against 45
13
+ tool-call responses). The client still recovered the call from the following
14
+ input_json_delta, but the start event — carrying the tool id and name — was lost.
15
+
16
+ These tests validate against the installed `anthropic` SDK's own pydantic models
17
+ rather than a hand-written expectation, so they track the real schema.
18
+ """
19
+
20
+ import json
21
+ import re
22
+ import unittest
23
+
24
+ try:
25
+ from anthropic.types.raw_content_block_start_event import RawContentBlockStartEvent
26
+ _HAVE_SDK = True
27
+ except Exception: # pragma: no cover - SDK not installed in this environment
28
+ _HAVE_SDK = False
29
+
30
+
31
+ TOOL_USE_START_RE = re.compile(
32
+ r"'content_block': \{'type': 'tool_use'[^}]*\}", re.S
33
+ )
34
+
35
+
36
+ def _proxy_source() -> str:
37
+ from pathlib import Path
38
+
39
+ return (
40
+ Path(__file__).resolve().parents[1] / "scripts" / "anthropic_proxy.py"
41
+ ).read_text()
42
+
43
+
44
+ class TestToolUseStartEventShape(unittest.TestCase):
45
+ def test_every_tool_use_start_site_sets_input(self):
46
+ """Static guard: a new emission site must not reintroduce the omission."""
47
+ src = _proxy_source()
48
+ sites = [
49
+ m.group(0)
50
+ for m in re.finditer(r"\{'type': 'tool_use',[^}]*\}", src)
51
+ if "content_block_start" in src[max(0, m.start() - 400):m.start()]
52
+ ]
53
+ self.assertGreater(len(sites), 0, "no tool_use content_block_start sites found")
54
+ missing = [s for s in sites if "'input'" not in s]
55
+ self.assertEqual(
56
+ missing,
57
+ [],
58
+ "tool_use content_block_start without `input` (clients drop it): "
59
+ f"{missing}",
60
+ )
61
+
62
+ @unittest.skipUnless(_HAVE_SDK, "anthropic SDK not installed")
63
+ def test_payload_without_input_is_rejected_by_the_real_schema(self):
64
+ """Documents *why* the field is required — this is the observed bug."""
65
+ bad = {
66
+ "type": "content_block_start",
67
+ "index": 1,
68
+ "content_block": {"type": "tool_use", "id": "abc", "name": "Read"},
69
+ }
70
+ with self.assertRaises(Exception):
71
+ RawContentBlockStartEvent.model_validate(bad)
72
+
73
+ @unittest.skipUnless(_HAVE_SDK, "anthropic SDK not installed")
74
+ def test_payload_with_input_validates(self):
75
+ good = {
76
+ "type": "content_block_start",
77
+ "index": 1,
78
+ "content_block": {
79
+ "type": "tool_use",
80
+ "id": "abc",
81
+ "name": "Read",
82
+ "input": {},
83
+ },
84
+ }
85
+ ev = RawContentBlockStartEvent.model_validate(good)
86
+ self.assertEqual(ev.content_block.type, "tool_use")
87
+
88
+ @unittest.skipUnless(_HAVE_SDK, "anthropic SDK not installed")
89
+ def test_thinking_and_text_start_events_validate(self):
90
+ """The other two block kinds the proxy opens must stay valid too."""
91
+ for cb in (
92
+ {"type": "thinking", "thinking": "", "signature": ""},
93
+ {"type": "text", "text": ""},
94
+ ):
95
+ with self.subTest(block=cb["type"]):
96
+ RawContentBlockStartEvent.model_validate(
97
+ {"type": "content_block_start", "index": 0, "content_block": cb}
98
+ )
99
+
100
+
101
+ if __name__ == "__main__":
102
+ unittest.main()