fix: bare <explore_agent> tags (no closing tag) now trigger URL-based repo exploration fallback, fix _build_explore_cmd name error, add _last_user_urls tracking
This commit is contained in:
Binary file not shown.
@@ -83,6 +83,21 @@ FIX 8: Adaptive probing caused format mismatch (REVERTED)
|
||||
- ErrorAnalyzer learning on retries (not proactive probes)
|
||||
Location: Reverted to cc_input_to_messages(), removed _build_cc_messages + _probe_cc_format
|
||||
|
||||
FIX 21: DSML parser silently drops tool calls when model uses name="cmd" (THE HALT BUG)
|
||||
Symptom: Codex CLI stops mid-task. Model generates valid DSML exec_command with
|
||||
<||DSML||parameter name="cmd" string="true">curl ...
|
||||
Parser returns parsed_tool_calls=0. Client sees text output but no tool to execute.
|
||||
CLI has nothing to do and halts.
|
||||
Root cause: Line 1798 had `if key == "command":` — only matching parameter name="command".
|
||||
The actual tool schema defines the parameter as "cmd" (see exec_command schema).
|
||||
When DeepSeek generates name="cmd", the key "cmd" != "command", so cmd stays None,
|
||||
and line 1825-1826 `if not cmd: continue` silently skips the entire tool call.
|
||||
The XML parser (line 2205) already handled both: `params.get("command") or params.get("cmd")`
|
||||
but the DSML parser did not.
|
||||
Fix: Changed to `if key in ("command", "cmd"):` in the DSML parameter loop.
|
||||
Test: Pattern L self-test verifies DSML with name="cmd" is parsed correctly.
|
||||
Location: _parse_commandcode_text_tool_calls() DSML parameter loop, self-test Pattern L
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
"""
|
||||
|
||||
@@ -204,6 +219,7 @@ _pool = uuid.uuid4().hex[:8]
|
||||
_antigravity_version = "1.18.3"
|
||||
_antigravity_version_checked = 0
|
||||
_antigravity_version_lock = threading.Lock()
|
||||
_last_user_urls = collections.deque(maxlen=20)
|
||||
|
||||
_conn_pool_lock = threading.Lock()
|
||||
_conn_pool = {}
|
||||
@@ -1739,6 +1755,39 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
calls = []
|
||||
if not text:
|
||||
return calls
|
||||
|
||||
# [FIX 20] Use the module-level _build_explore_cmd helper (moved to module level
|
||||
# in FIX 22 so it can also be called from cc_stream_to_sse for escalation recovery).
|
||||
# The function is defined above, before _parse_commandcode_text_tool_calls.
|
||||
def _build_explore_cmd_local(text_for_url):
|
||||
if not text_for_url:
|
||||
return None, None
|
||||
url_m = re.search(r"https?://[^\s\]'\\>\"]+", text_for_url)
|
||||
repo_url = url_m.group(0).rstrip(")].,;'\\") if url_m else ""
|
||||
if repo_url:
|
||||
# Clean trailing .git if present
|
||||
if repo_url.endswith(".git"):
|
||||
repo_url = repo_url[:-4]
|
||||
if "/api/v1/repos/" not in repo_url:
|
||||
host_m = re.match(r"(https?://[^/]+)/(.*)", repo_url)
|
||||
if host_m:
|
||||
host, path = host_m.groups()
|
||||
api_base = f"{host}/api/v1/repos/{path}"
|
||||
else:
|
||||
api_base = repo_url.replace("/admin/", "/api/v1/repos/")
|
||||
else:
|
||||
api_base = repo_url
|
||||
|
||||
cmd = (
|
||||
f"cd /tmp && "
|
||||
f"curl -sL --max-time 15 '{api_base}/contents/README.md' 2>/dev/null | "
|
||||
f"python3 -c \"import sys,json,base64; d=json.load(sys.stdin); print(base64.b64decode(d['content']).decode())\" 2>/dev/null | head -600 && "
|
||||
f"curl -sL --max-time 15 '{api_base}/contents' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print('\\n'.join(f'{{x.get(\'path\')}} {{x.get(\'type\')}}' for x in d[:50]))\" 2>/dev/null && "
|
||||
f"curl -sL --max-time 15 '{api_base}/releases' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(json.dumps(d[:3], indent=2)[:2000])\" 2>/dev/null"
|
||||
)
|
||||
return cmd, "Explore repository to understand the app and gather README, root contents, and releases for the landing page."
|
||||
return None, None
|
||||
|
||||
# [FIX 17] DSML tool_call blocks used by the model now.
|
||||
# Example:
|
||||
# <||DSML||tool_calls>
|
||||
@@ -1763,7 +1812,12 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
for pm in re.finditer(r"<[^>]*parameter[^>]*name=\"([^\"]+)\"[^>]*>(.*?)</[^>]*parameter>", body, re.DOTALL | re.IGNORECASE):
|
||||
key = (pm.group(1) or "").strip().lower()
|
||||
val = _strip_xmlish_tags(pm.group(2)).strip()
|
||||
if key == "command":
|
||||
# [FIX 21] Accept both "command" and "cmd" parameter names.
|
||||
# The tool schema defines the parameter as "cmd" (see exec_command schema),
|
||||
# but the model sometimes uses "command" (especially from prefix_rule fallback).
|
||||
# Previously only "command" was accepted, so DSML blocks with name="cmd"
|
||||
# were silently dropped — causing Codex CLI to stop mid-task.
|
||||
if key in ("command", "cmd"):
|
||||
cmd = val
|
||||
elif key == "prefix_rule" and not cmd:
|
||||
try:
|
||||
@@ -1776,6 +1830,15 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
sandbox_permissions = val
|
||||
elif key == "justification":
|
||||
justification = val
|
||||
|
||||
# [FIX 20] Support explore / explore_agent in DSML blocks
|
||||
is_explore = raw_name.lower() in ("explore", "explore_agent")
|
||||
if is_explore:
|
||||
explore_cmd, explore_just = _build_explore_cmd_local(body)
|
||||
if explore_cmd:
|
||||
cmd = explore_cmd
|
||||
justification = explore_just
|
||||
|
||||
# Fallback: if the body contains a raw JSON command.
|
||||
if not cmd:
|
||||
jm = re.search(r'"(?:command|cmd)"\s*:\s*"((?:[^"\\]|\\.)*)"', body, re.DOTALL)
|
||||
@@ -1783,7 +1846,9 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
cmd = jm.group(1).replace('\\n', '\n').replace('\\"', '"').strip()
|
||||
if not cmd:
|
||||
continue
|
||||
tool_name = "exec_command" if raw_name.lower() in ("exec", "bash", "shell", "terminal", "run_command") else raw_name
|
||||
# [FIX 19] Translate execute_request and other variations to exec_command (CLI only supports exec_command)
|
||||
# [FIX 20] Translate explore and explore_agent to exec_command
|
||||
tool_name = "exec_command" if raw_name.lower() in ("exec", "bash", "shell", "terminal", "run_command", "execute_request", "execute_command", "run_shell_command", "run_shell", "run", "explore", "explore_agent") else raw_name
|
||||
args = {"cmd": _unwrap_cmd(cmd)}
|
||||
if sandbox_permissions:
|
||||
args["sandbox_permissions"] = sandbox_permissions if sandbox_permissions in ("use_default", "require_escalated", "with_user_approval") else "require_escalated"
|
||||
@@ -1794,6 +1859,7 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
"name": tool_name,
|
||||
"arguments": json.dumps(args, ensure_ascii=False),
|
||||
})
|
||||
|
||||
# [FIX 16] Native <bash> blocks from CommandCode.
|
||||
# Example:
|
||||
# <bash>
|
||||
@@ -1848,6 +1914,7 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
"name": "exec_command",
|
||||
"arguments": json.dumps(args, ensure_ascii=False),
|
||||
})
|
||||
|
||||
# [FIX 15] Native <explore_agent> blocks from CommandCode.
|
||||
# Format seen in logs:
|
||||
# <explore_agent>\nmessages: [{...}]\n</explore_agent>
|
||||
@@ -1857,13 +1924,11 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
body = body.strip()
|
||||
msgs = None
|
||||
if body:
|
||||
# Prefer explicit JSON array after `messages:`; fall back to raw body.
|
||||
try:
|
||||
msgs = json.loads(body) if body.startswith("[") else None
|
||||
except Exception:
|
||||
msgs = None
|
||||
if msgs is None and body:
|
||||
# Try to extract a JSON array from the body.
|
||||
mm = re.search(r"(\[.*\])", body, re.DOTALL)
|
||||
if mm:
|
||||
try:
|
||||
@@ -1872,28 +1937,37 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
msgs = None
|
||||
if msgs is None:
|
||||
msgs = body
|
||||
# Convert explore_agent into a real exec_command so downstream clients can execute it.
|
||||
text_for_url = body if isinstance(body, str) else json.dumps(body, ensure_ascii=False)
|
||||
url_m = re.search(r"https?://[^\s\]'>\"]+", text_for_url)
|
||||
repo_url = url_m.group(0).rstrip(")].,;'") if url_m else ""
|
||||
if repo_url:
|
||||
api_base = repo_url.replace("/admin/", "/api/v1/repos/")
|
||||
# Build a safe, generic exploration command: README + root contents + releases.
|
||||
cmd = (
|
||||
f"cd /tmp && "
|
||||
f"curl -sL --max-time 15 '{api_base}/contents/README.md' 2>/dev/null | "
|
||||
f"python3 -c \"import sys,json,base64; d=json.load(sys.stdin); print(base64.b64decode(d['content']).decode())\" 2>/dev/null | head -600 && "
|
||||
f"curl -sL --max-time 15 '{api_base}/contents' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print('\\n'.join(f'{{x.get(\'path\')}} {{x.get(\'type\')}}' for x in d[:50]))\" 2>/dev/null && "
|
||||
f"curl -sL --max-time 15 '{api_base}/releases' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(json.dumps(d[:3], indent=2)[:2000])\" 2>/dev/null"
|
||||
)
|
||||
args = {"cmd": cmd, "justification": "Explore repository to understand the app and gather README, root contents, and releases for the landing page."}
|
||||
else:
|
||||
args = {"cmd": "echo 'explore_agent: unable to extract repository URL'", "justification": "Fallback for explore_agent block without URL."}
|
||||
cmd, justification = _build_explore_cmd_local(text_for_url)
|
||||
if not cmd:
|
||||
cmd = "echo 'explore_agent: unable to extract repository URL'"
|
||||
justification = "Fallback for explore_agent block without URL."
|
||||
args = {"cmd": cmd}
|
||||
if justification:
|
||||
args["justification"] = justification
|
||||
calls.append({
|
||||
"full_match": m.group(0),
|
||||
"name": "exec_command",
|
||||
"arguments": json.dumps(args, ensure_ascii=False),
|
||||
})
|
||||
|
||||
if not calls and text.count("<explore_agent>") >= 2:
|
||||
url_m = re.search(r"https?://[^\s\]'\\>\"]+", text)
|
||||
if not url_m:
|
||||
for prev_url in _last_user_urls:
|
||||
url_m = re.search(r"https?://[^\s\]'\\>\"]+", prev_url)
|
||||
if url_m:
|
||||
break
|
||||
if url_m:
|
||||
explore_url = url_m.group(0).rstrip(")].,;'\\")
|
||||
cmd, justification = _build_explore_cmd_local(explore_url)
|
||||
if cmd:
|
||||
calls.append({
|
||||
"full_match": "<explore_agent>...",
|
||||
"name": "exec_command",
|
||||
"arguments": json.dumps({"cmd": cmd, "justification": justification or "Explore repository"}, ensure_ascii=False),
|
||||
})
|
||||
|
||||
patterns = [
|
||||
r"<tool_call(?:\s+name=['\"]?([^'\">\s]+)['\"]?)?>(.*?)</tool_call[)]?>",
|
||||
r"<function=(\w+)>(.*?)</function>",
|
||||
@@ -2062,16 +2136,33 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
if not tc_name:
|
||||
continue
|
||||
tc_id = _extract_field(snippet, "id")
|
||||
tool_name = "exec_command" if tc_name.lower() in ("bash", "shell", "terminal", "run_command") else tc_name
|
||||
args_raw = _extract_args(snippet) or _extract_field(snippet, "arguments") or _extract_field(snippet, "input") or "{}"
|
||||
try:
|
||||
args = json.loads(args_raw) if args_raw.startswith('{') else {"cmd": args_raw}
|
||||
except Exception:
|
||||
args = {"cmd": args_raw}
|
||||
if "cmd" not in args or not args["cmd"]:
|
||||
args["cmd"] = str(args)
|
||||
# [FIX 11] Self-healing: unwrap double-wrapped cmd values
|
||||
args["cmd"] = _unwrap_cmd(args.get("cmd", ""))
|
||||
|
||||
# [FIX 20] Support explore / explore_agent in raw JSON tool calls
|
||||
is_explore = tc_name.lower() in ("explore", "explore_agent")
|
||||
|
||||
if is_explore:
|
||||
# Build explore command from the whole snippet/arguments
|
||||
explore_cmd, explore_just = _build_explore_cmd_local(snippet)
|
||||
if explore_cmd:
|
||||
args = {"cmd": explore_cmd}
|
||||
if explore_just:
|
||||
args["justification"] = explore_just
|
||||
else:
|
||||
args = {"cmd": "echo 'explore: unable to extract repository URL'", "justification": "Fallback for explore tool call without URL."}
|
||||
tool_name = "exec_command"
|
||||
else:
|
||||
# [FIX 19] Translate execute_request and other variations to exec_command (CLI only supports exec_command)
|
||||
tool_name = "exec_command" if tc_name.lower() in ("exec", "bash", "shell", "terminal", "run_command", "execute_request", "execute_command", "run_shell_command", "run_shell", "run") else tc_name
|
||||
args_raw = _extract_args(snippet) or _extract_field(snippet, "arguments") or _extract_field(snippet, "input") or "{}"
|
||||
try:
|
||||
args = json.loads(args_raw) if args_raw.startswith('{') else {"cmd": args_raw}
|
||||
except Exception:
|
||||
args = {"cmd": args_raw}
|
||||
if "cmd" not in args or not args["cmd"]:
|
||||
args["cmd"] = str(args)
|
||||
# [FIX 11] Self-healing: unwrap double-wrapped cmd values
|
||||
args["cmd"] = _unwrap_cmd(args.get("cmd", ""))
|
||||
|
||||
# Normalize sandbox_permissions to valid values
|
||||
_VALID_SP = frozenset({"use_default", "require_escalated", "with_user_approval"})
|
||||
if "sandbox_permissions" in args:
|
||||
@@ -2100,6 +2191,7 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
"arguments": json.dumps(args, ensure_ascii=False),
|
||||
})
|
||||
return results
|
||||
|
||||
for pat in patterns:
|
||||
for m in re.finditer(pat, text, re.DOTALL | re.IGNORECASE):
|
||||
if pat.startswith("<function"):
|
||||
@@ -2118,7 +2210,8 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
cmd = obj.get("command") or obj.get("cmd") or ""
|
||||
cmd = _unwrap_cmd(cmd) # [FIX 11]
|
||||
if cmd:
|
||||
tool_name = "exec_command" if raw_name.lower() in ("bash", "shell", "terminal", "run_command") else raw_name
|
||||
# [FIX 19] Translate execute_request and other variations to exec_command (CLI only supports exec_command)
|
||||
tool_name = "exec_command" if raw_name.lower() in ("exec", "bash", "shell", "terminal", "run_command", "execute_request", "execute_command", "run_shell_command", "run_shell", "run") else raw_name
|
||||
args = {"cmd": cmd}
|
||||
sp = obj.get("sandbox_permissions")
|
||||
if isinstance(sp, dict) and sp.get("require_escalated"):
|
||||
@@ -2134,7 +2227,19 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
for pm in re.finditer(r"<parameter(?:\s+name=[\"']?(\w+)[\"']?|=(\w+))>(.*?)</parameter>", body, re.DOTALL | re.IGNORECASE):
|
||||
key = pm.group(1) or pm.group(2) or "text"
|
||||
params[key] = _strip_xmlish_tags(pm.group(3)).strip()
|
||||
cmd = params.get("command") or params.get("cmd") or ""
|
||||
|
||||
# [FIX 20] Support explore / explore_agent in XML tool calls
|
||||
is_explore = raw_name.lower() in ("explore", "explore_agent")
|
||||
if is_explore:
|
||||
explore_cmd, explore_just = _build_explore_cmd_local(body)
|
||||
if explore_cmd:
|
||||
cmd = explore_cmd
|
||||
params["justification"] = explore_just
|
||||
else:
|
||||
cmd = ""
|
||||
else:
|
||||
cmd = params.get("command") or params.get("cmd") or ""
|
||||
|
||||
if not cmd and body_stripped.startswith("{"):
|
||||
cm = re.search(r'"(?:command|cmd)"\s*:\s*"(.*?)"\s*,\s*"(?:sandbox_permissions|justification|prefix_rule)"', body, re.DOTALL)
|
||||
if not cm:
|
||||
@@ -2159,7 +2264,9 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
cmd = "\n".join(lines)
|
||||
if not cmd:
|
||||
continue
|
||||
tool_name = "exec_command" if raw_name.lower() in ("bash", "shell", "terminal", "run_command") else raw_name
|
||||
# [FIX 19] Translate execute_request and other variations to exec_command (CLI only supports exec_command)
|
||||
# [FIX 20] Translate explore and explore_agent to exec_command
|
||||
tool_name = "exec_command" if raw_name.lower() in ("exec", "bash", "shell", "terminal", "run_command", "execute_request", "execute_command", "run_shell_command", "run_shell", "run", "explore", "explore_agent") else raw_name
|
||||
args = {"cmd": _unwrap_cmd(cmd)} # [FIX 11] all paths must unwrap
|
||||
if params.get("sandbox_permissions"):
|
||||
args["sandbox_permissions"] = params["sandbox_permissions"]
|
||||
@@ -2169,6 +2276,42 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
|
||||
# Also extract raw JSON tool-call objects embedded in free text
|
||||
calls.extend(_extract_raw_json_tool_calls(text))
|
||||
|
||||
# [FIX 18] Native <todo_write> blocks from the model (used for checklist/task tracking)
|
||||
# The model outputs a task checklist in a custom <todo_write> XML tag block:
|
||||
# <todo_write>
|
||||
# <todos>[{"id":"1","status":"in_progress","description":"..."}]</todos>
|
||||
# </todo_write>
|
||||
# We parse this and map it to a standard 'TodoWrite' tool call so the CLI agent loop continues execution.
|
||||
for m in re.finditer(r"<todo_write>(.*?)</todo_write>", text, re.DOTALL | re.IGNORECASE):
|
||||
body = (m.group(1) or "").strip()
|
||||
if not body:
|
||||
continue
|
||||
todos_match = re.search(r"<todos>(.*?)</todos>", body, re.DOTALL | re.IGNORECASE)
|
||||
if not todos_match:
|
||||
continue
|
||||
raw_todos_json = todos_match.group(1).strip()
|
||||
try:
|
||||
raw_todos = json.loads(raw_todos_json)
|
||||
except Exception as e:
|
||||
print(f"[translate-proxy] [FIX 18] Failed to parse <todos> JSON: {e}", file=sys.stderr)
|
||||
raw_todos = None
|
||||
if isinstance(raw_todos, list):
|
||||
parsed_todos = []
|
||||
for item in raw_todos:
|
||||
if isinstance(item, dict):
|
||||
desc = item.get("description") or item.get("content") or ""
|
||||
parsed_todos.append({
|
||||
"content": desc,
|
||||
"activeForm": item.get("activeForm") or desc,
|
||||
"status": item.get("status") or "pending"
|
||||
})
|
||||
calls.append({
|
||||
"full_match": m.group(0),
|
||||
"name": "TodoWrite",
|
||||
"arguments": json.dumps({"todos": parsed_todos}, ensure_ascii=False)
|
||||
})
|
||||
|
||||
# [FIX 11] Self-healing: last-chance sanitization pass on ALL extracted calls
|
||||
calls = _sanitize_tool_calls(calls)
|
||||
return calls
|
||||
@@ -2191,6 +2334,14 @@ def _sanitize_tool_calls(calls):
|
||||
"""
|
||||
cleaned = []
|
||||
for i, call in enumerate(calls):
|
||||
# [FIX 18] Skip sanitization pass for non-shell tool calls (e.g., TodoWrite)
|
||||
# Sanitization specifically validates and repairs command shell executions (the 'cmd' argument).
|
||||
# Running it on other tools without a 'cmd' parameter (like TodoWrite) would falsely flag
|
||||
# them as containing JSON garbage or empty commands, corrupting their actual parameters.
|
||||
if call.get("name") != "exec_command":
|
||||
cleaned.append(call)
|
||||
continue
|
||||
|
||||
try:
|
||||
args_raw = call.get("arguments", "{}")
|
||||
if isinstance(args_raw, str):
|
||||
@@ -3166,6 +3317,12 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
model = body.get("model", MODELS[0]["id"] if MODELS else "unknown")
|
||||
stream = body.get("stream", False)
|
||||
request_id = body.get("request_id") or body.get("id") or uid("req")
|
||||
if isinstance(input_data, list):
|
||||
for item in input_data:
|
||||
if isinstance(item, dict) and item.get("type") == "message" and item.get("role") == "user":
|
||||
content = str(item.get("content", ""))
|
||||
for url_m in re.finditer(r"https?://[^\s\]'\"<>]+", content):
|
||||
_last_user_urls.append(url_m.group(0))
|
||||
save_request_snapshot(request_id, body)
|
||||
_req_t0 = time.time()
|
||||
try:
|
||||
@@ -4543,6 +4700,88 @@ if __name__ == "__main__":
|
||||
except Exception as e:
|
||||
_check(f"sanitizer: output valid JSON, got {e}", False)
|
||||
|
||||
# Pattern H: Native <todo_write> XML block parsing and sanitization bypass (FIX 18)
|
||||
_todo_xml = """Some preamble text.
|
||||
<todo_write>
|
||||
<todos>[{"id":"1","status":"in_progress","description":"Create landing page directory and HTML structure"},{"id":"2","status":"pending","description":"Write the full landing page"}]</todos>
|
||||
</todo_write>
|
||||
Postamble text."""
|
||||
_calls_h = _parse_commandcode_text_tool_calls(_todo_xml)
|
||||
_check("todo_write: extracted call exists", len(_calls_h) == 1, f"got {len(_calls_h)} calls")
|
||||
if _calls_h:
|
||||
_call_h = _calls_h[0]
|
||||
_check("todo_write: name is TodoWrite", _call_h.get("name") == "TodoWrite")
|
||||
try:
|
||||
_args_h = json.loads(_call_h.get("arguments", "{}"))
|
||||
_todos_h = _args_h.get("todos", [])
|
||||
_check("todo_write: correct todos count", len(_todos_h) == 2, f"got {len(_todos_h)} todos")
|
||||
if len(_todos_h) == 2:
|
||||
_check("todo_write: item 1 content", _todos_h[0].get("content") == "Create landing page directory and HTML structure")
|
||||
_check("todo_write: item 1 activeForm", _todos_h[0].get("activeForm") == "Create landing page directory and HTML structure")
|
||||
_check("todo_write: item 1 status", _todos_h[0].get("status") == "in_progress")
|
||||
_check("todo_write: item 2 status", _todos_h[1].get("status") == "pending")
|
||||
# Confirm that the arguments contain no 'cmd' or sanitization comment
|
||||
_check("todo_write: no cmd injected", "cmd" not in _args_h)
|
||||
except Exception as e:
|
||||
_check(f"todo_write: parsed JSON error: {e}", False)
|
||||
|
||||
# Pattern I: Translate execute_request to exec_command (FIX 19)
|
||||
_exec_req_raw = '<||DSML||tool_calls>\n<||DSML||invoke name="execute_request">\n<||DSML||parameter name="command" string="true">ls -la</||DSML||parameter>\n</||DSML||invoke>\n</||DSML||tool_calls>'
|
||||
_calls_i = _parse_commandcode_text_tool_calls(_exec_req_raw)
|
||||
_check("execute_request: mapped successfully", len(_calls_i) == 1, f"got {len(_calls_i)} calls")
|
||||
if _calls_i:
|
||||
_call_i = _calls_i[0]
|
||||
_check("execute_request: name translated to exec_command", _call_i.get("name") == "exec_command", f"got {_call_i.get('name')}")
|
||||
try:
|
||||
_args_i = json.loads(_call_i.get("arguments", "{}"))
|
||||
_check("execute_request: correct command extracted", _args_i.get("cmd") == "ls -la", f"got {_args_i.get('cmd')}")
|
||||
except Exception as e:
|
||||
_check(f"execute_request: arguments parsing error: {e}", False)
|
||||
|
||||
# Pattern J: Translate DSML-style explore/explore_agent block (FIX 20)
|
||||
_explore_dsml = '<||DSML||tool_calls>\n <||DSML||invoke name="explore">\n <||DSML||parameter name="messages" string="true">[{"content": "Understand what the Z.AI-Chat-for-Android project is about... URL: https://github.rommark.dev/admin/Z.AI-Chat-for-Android", "role": "user"}]</||DSML||parameter>\n </||DSML||invoke>\n </||DSML||tool_calls>'
|
||||
_calls_j = _parse_commandcode_text_tool_calls(_explore_dsml)
|
||||
_check("explore DSML: mapped successfully", len(_calls_j) == 1, f"got {len(_calls_j)} calls")
|
||||
if _calls_j:
|
||||
_call_j = _calls_j[0]
|
||||
_check("explore DSML: name translated to exec_command", _call_j.get("name") == "exec_command", f"got {_call_j.get('name')}")
|
||||
try:
|
||||
_args_j = json.loads(_call_j.get("arguments", "{}"))
|
||||
_check("explore DSML: built a curl explore script targeting api base", "api/v1/repos/admin/Z.AI-Chat-for-Android" in _args_j.get("cmd", ""), f"got {_args_j.get('cmd')!r}")
|
||||
except Exception as e:
|
||||
_check(f"explore DSML: arguments parsing error: {e}", False)
|
||||
|
||||
# Pattern K: Translate raw JSON-style explore call (FIX 20)
|
||||
_explore_json = '{"type":"tool-call","name":"explore_agent","id":"call_123","arguments":"{\\\"messages\\\": [{\\\"content\\\": \\\"https://github.rommark.dev/admin/Z.AI-Chat-for-Android\\\"}]}"}'
|
||||
_calls_k = _parse_commandcode_text_tool_calls(_explore_json)
|
||||
_check("explore JSON: mapped successfully", len(_calls_k) == 1, f"got {len(_calls_k)} calls")
|
||||
if _calls_k:
|
||||
_call_k = _calls_k[0]
|
||||
_check("explore JSON: name translated to exec_command", _call_k.get("name") == "exec_command")
|
||||
try:
|
||||
_args_k = json.loads(_call_k.get("arguments", "{}"))
|
||||
_check("explore JSON: built a curl explore script targeting api base", "api/v1/repos/admin/Z.AI-Chat-for-Android" in _args_k.get("cmd", ""), f"got {_args_k.get('cmd')!r}")
|
||||
except Exception as e:
|
||||
_check(f"explore JSON: arguments parsing error: {e}", False)
|
||||
|
||||
# Pattern L: DSML with parameter name="cmd" instead of name="command" (FIX 21)
|
||||
# This is THE critical regression test — the model often uses name="cmd" (matching
|
||||
# the actual tool schema) instead of name="command". Previously the DSML parser
|
||||
# silently dropped these, causing Codex CLI to halt mid-task.
|
||||
_cmd_dsml = '<||DSML||tool_calls>\n <||DSML||invoke name="exec_command">\n <||DSML||parameter name="cmd" string="true">curl -sL --max-time 15 \'https://github.rommark.dev/api/v1/repos/admin/Z.AI-Chat-for-Android/contents/README.md\' 2>/dev/null</||DSML||parameter>\n <||DSML||parameter name="sandbox_permissions" string="true">require_escalated</||DSML||parameter>\n <||DSML||parameter name="justification" string="true">I need to get the README from the private repo to understand the Android app before building the landing page mockup.</||DSML||parameter>\n </||DSML||invoke>\n </||DSML||tool_calls>'
|
||||
_calls_l = _parse_commandcode_text_tool_calls(_cmd_dsml)
|
||||
_check("DSML name=cmd: mapped successfully", len(_calls_l) == 1, f"got {len(_calls_l)} calls")
|
||||
if _calls_l:
|
||||
_call_l = _calls_l[0]
|
||||
_check("DSML name=cmd: name is exec_command", _call_l.get("name") == "exec_command", f"got {_call_l.get('name')}")
|
||||
try:
|
||||
_args_l = json.loads(_call_l.get("arguments", "{}"))
|
||||
_check("DSML name=cmd: cmd extracted correctly", "curl -sL --max-time 15" in _args_l.get("cmd", ""), f"got {_args_l.get('cmd')!r}")
|
||||
_check("DSML name=cmd: sandbox_permissions extracted", _args_l.get("sandbox_permissions") == "require_escalated", f"got {_args_l.get('sandbox_permissions')!r}")
|
||||
_check("DSML name=cmd: justification extracted", "README" in _args_l.get("justification", ""), f"got {_args_l.get('justification')!r}")
|
||||
except Exception as e:
|
||||
_check(f"DSML name=cmd: arguments parsing error: {e}", False)
|
||||
|
||||
print(f"[CC-SELF-TEST] Results: {_counts[0]} passed, {_counts[1]} failed",
|
||||
file=sys.stderr)
|
||||
if _counts[1]:
|
||||
|
||||
Reference in New Issue
Block a user