@miller-tech/uap 1.15.3 → 1.15.5

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.15.3",
3
+ "version": "1.15.5",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1789,6 +1789,7 @@ _TOOL_ARG_MARKERS = (
1789
1789
  "<tool_call",
1790
1790
  "</tool_call",
1791
1791
  "<function=",
1792
+ "</function",
1792
1793
  "</think>",
1793
1794
  )
1794
1795
 
@@ -1817,6 +1818,7 @@ def _strip_tool_markup_artifacts(text: str) -> str:
1817
1818
  cleaned = re.sub(r"</?tool_call[^>]*>", "", cleaned, flags=re.IGNORECASE)
1818
1819
  cleaned = re.sub(r"</?think>", "", cleaned, flags=re.IGNORECASE)
1819
1820
  cleaned = re.sub(r"<function=[^>]*>", "", cleaned, flags=re.IGNORECASE)
1821
+ cleaned = re.sub(r"</function>", "", cleaned, flags=re.IGNORECASE)
1820
1822
  return cleaned.strip()
1821
1823
 
1822
1824
 
@@ -2284,7 +2286,13 @@ def _looks_malformed_tool_payload(text: str) -> bool:
2284
2286
  if _contains_tool_call_apology(text):
2285
2287
  return True
2286
2288
 
2287
- primary_markers = ("</parameter", "<parameter", "<tool_call", "<function=")
2289
+ primary_markers = (
2290
+ "</parameter",
2291
+ "<parameter",
2292
+ "<tool_call",
2293
+ "<function=",
2294
+ "</function",
2295
+ )
2288
2296
  if any(marker in lowered for marker in primary_markers):
2289
2297
  return True
2290
2298
 
@@ -122,6 +122,28 @@ class TestMalformedToolGuardrail(unittest.TestCase):
122
122
  }
123
123
  self.assertTrue(proxy._is_malformed_tool_response(openai_resp, anthropic_body))
124
124
 
125
+ def test_detects_closing_function_tag_payload(self):
126
+ openai_resp = {
127
+ "choices": [
128
+ {
129
+ "finish_reason": "stop",
130
+ "message": {
131
+ "content": (
132
+ "Bash(find /home/cogtek/dev/miller-tech/universal-agent-protocol -maxdepth 1 "
133
+ '\\( -type f -name "*.md" -o -type f -name "*.json" \\)\n'
134
+ "</function>"
135
+ ),
136
+ "tool_calls": [],
137
+ },
138
+ }
139
+ ]
140
+ }
141
+ anthropic_body = {
142
+ "tools": [{"name": "Bash", "input_schema": {"type": "object"}}],
143
+ "messages": [{"role": "user", "content": "list root docs/json files"}],
144
+ }
145
+ self.assertTrue(proxy._is_malformed_tool_response(openai_resp, anthropic_body))
146
+
125
147
  def test_detects_think_tag_with_repeated_policy_phrase(self):
126
148
  openai_resp = {
127
149
  "choices": [
@@ -611,6 +633,39 @@ class TestMalformedToolGuardrail(unittest.TestCase):
611
633
  self.assertEqual(issue.kind, "malformed_payload")
612
634
  self.assertIn("malformed pseudo tool payload", issue.reason)
613
635
 
636
+ def test_preflight_flags_closing_function_tag_inside_arguments(self):
637
+ old_preflight = getattr(proxy, "PROXY_TOOL_ARGS_PREFLIGHT")
638
+ try:
639
+ setattr(proxy, "PROXY_TOOL_ARGS_PREFLIGHT", True)
640
+ openai_resp = {
641
+ "choices": [
642
+ {
643
+ "finish_reason": "tool_calls",
644
+ "message": {
645
+ "content": "",
646
+ "tool_calls": [
647
+ {
648
+ "id": "call_1",
649
+ "function": {
650
+ "name": "Bash",
651
+ "arguments": '{"command":"pwd\\n</function>"}',
652
+ },
653
+ }
654
+ ],
655
+ },
656
+ }
657
+ ]
658
+ }
659
+ anthropic_body = {
660
+ "tools": [{"name": "Bash", "input_schema": {"type": "object"}}]
661
+ }
662
+
663
+ issue = proxy._classify_tool_response_issue(openai_resp, anthropic_body)
664
+ self.assertEqual(issue.kind, "invalid_tool_args")
665
+ self.assertIn("malformed markup fragments", issue.reason)
666
+ finally:
667
+ setattr(proxy, "PROXY_TOOL_ARGS_PREFLIGHT", old_preflight)
668
+
614
669
  def test_required_tool_turn_without_tool_call_is_flagged(self):
615
670
  openai_resp = {
616
671
  "choices": [
@@ -661,6 +716,34 @@ class TestMalformedToolGuardrail(unittest.TestCase):
661
716
  self.assertNotIn("</think>", args)
662
717
  self.assertNotIn("</parameter>", args)
663
718
 
719
+ def test_markup_repair_strips_closing_function_tag(self):
720
+ openai_resp = {
721
+ "choices": [
722
+ {
723
+ "finish_reason": "tool_calls",
724
+ "message": {
725
+ "content": "",
726
+ "tool_calls": [
727
+ {
728
+ "id": "call_1",
729
+ "function": {
730
+ "name": "Bash",
731
+ "arguments": '{"command":"pwd\\n</function>"}',
732
+ },
733
+ }
734
+ ],
735
+ },
736
+ }
737
+ ]
738
+ }
739
+
740
+ repaired, count = proxy._repair_tool_call_markup(openai_resp)
741
+ self.assertEqual(count, 1)
742
+ args = json.loads(
743
+ repaired["choices"][0]["message"]["tool_calls"][0]["function"]["arguments"]
744
+ )
745
+ self.assertEqual(args["command"], "pwd")
746
+
664
747
  def test_markup_repair_recovers_json_after_tag_stripping(self):
665
748
  openai_resp = {
666
749
  "choices": [