fix: normalize escaped Work artifacts

This commit is contained in:
wuyang
2026-07-26 14:03:01 +08:00
parent f149379cf7
commit 44fe0b3dd7
2 changed files with 173 additions and 3 deletions
+147 -2
View File
@@ -7,6 +7,7 @@ import re
import uuid
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from pathlib import PurePosixPath
from typing import Any
from agent_platform.auth import UserIdentity
@@ -72,6 +73,46 @@ VERIFICATION_TOOLS = {"read_file", "git_status", "git_diff", "exec", "poll_proce
EXECUTION_TOOLS = {"exec", "start_process", "poll_process"}
REPEAT_GUARDED_EXECUTION_TOOLS = {"exec", "start_process"}
MAX_CONSECUTIVE_CHECKPOINT_REJECTIONS = 3
CODE_FILE_SUFFIXES = {
".c",
".cc",
".cpp",
".css",
".go",
".h",
".hpp",
".html",
".java",
".js",
".jsx",
".php",
".py",
".rb",
".rs",
".sh",
".sql",
".svelte",
".ts",
".tsx",
".vue",
}
PLAIN_TEXT_FILE_SUFFIXES = {
".csv",
".json",
".jsonl",
".md",
".rst",
".toml",
".tsv",
".txt",
".xml",
".yaml",
".yml",
}
READ_ONLY_EXEC_COMMAND = re.compile(
r"^(?:cat|cut|diff|find|git\s+(?:diff|show|status)|grep|head|jq|ls|pwd|rg|sed\s+-n|stat|tail|wc)\b",
re.IGNORECASE,
)
def latest_user_text(messages: list[dict[str, Any]]) -> str:
@@ -156,6 +197,109 @@ def execution_call_key(name: str, arguments: dict[str, Any]) -> str | None:
return f"{name}\0{cwd}\0{command}"
def decode_code_layout_escapes(content: str) -> str:
output: list[str] = []
index = 0
quote = ""
triple = False
escaped = False
line_comment = False
while index < len(content):
if quote:
if escaped:
output.append(content[index])
escaped = False
index += 1
continue
if content[index] == "\\":
output.append(content[index])
escaped = True
index += 1
continue
delimiter = quote * (3 if triple else 1)
if content.startswith(delimiter, index):
output.append(delimiter)
index += len(delimiter)
quote = ""
triple = False
continue
output.append(content[index])
index += 1
continue
if content.startswith("\\r\\n", index):
output.append("\n")
index += 4
line_comment = False
continue
if content.startswith("\\n", index):
output.append("\n")
index += 2
line_comment = False
continue
if content.startswith("\\t", index):
output.append("\t")
index += 2
continue
if line_comment:
if content[index] == "\n":
line_comment = False
output.append(content[index])
index += 1
continue
if content.startswith("//", index) or content[index] == "#":
line_comment = True
delimiter = "//" if content.startswith("//", index) else "#"
output.append(delimiter)
index += len(delimiter)
continue
if content[index] in {'"', "'"}:
quote = content[index]
triple = content.startswith(quote * 3, index)
delimiter = quote * (3 if triple else 1)
output.append(delimiter)
index += len(delimiter)
continue
output.append(content[index])
index += 1
return "".join(output)
def normalize_write_file_content(path: str, content: str) -> str:
literal_newlines = content.count("\\n") + content.count("\\r\\n")
actual_newlines = content.count("\n")
if literal_newlines < 2 or literal_newlines <= max(2, actual_newlines * 2):
return content
suffix = PurePosixPath(path).suffix.lower()
if suffix in CODE_FILE_SUFFIXES:
return decode_code_layout_escapes(content)
if suffix in PLAIN_TEXT_FILE_SUFFIXES:
return content.replace("\\r\\n", "\n").replace("\\n", "\n").replace("\\t", "\t")
return content
def normalize_tool_arguments(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
if name != "write_file":
return arguments
path = arguments.get("path")
content = arguments.get("content")
if not isinstance(path, str) or not isinstance(content, str):
return arguments
normalized = normalize_write_file_content(path, content)
if normalized == content:
return arguments
return {**arguments, "content": normalized}
def is_substantive_execution(name: str, arguments: dict[str, Any]) -> bool:
if name == "start_process":
return True
if name != "exec":
return False
command = str(arguments.get("command", "")).strip()
return bool(command and not READ_ONLY_EXEC_COMMAND.match(command))
@dataclass(slots=True)
class RunRecorder:
store: RuntimeStore
@@ -382,10 +526,10 @@ class AgentLoop:
mutation_count += 1
elif name in VERIFICATION_TOOLS and mutation_count:
verified_mutation = mutation_count
if name in EXECUTION_TOOLS:
if is_substantive_execution(name, arguments):
successful_executions += 1
unresolved_execution_failure = None
elif name in EXECUTION_TOOLS:
elif is_substantive_execution(name, arguments):
unresolved_execution_failure = f"{name}: {public_tool_summary(result, 1000)}"
execution_failures.append(unresolved_execution_failure)
key = execution_call_key(name, arguments) if isinstance(arguments, dict) else None
@@ -458,6 +602,7 @@ class AgentLoop:
except (json.JSONDecodeError, ValueError) as exc:
parsed.append((index, call, name, {"__parse_error__": str(exc)}, False))
continue
arguments = normalize_tool_arguments(name, arguments)
metadata = TOOL_METADATA.get(name)
parallel = bool(metadata and metadata.parallel_safe)
if name == "delegate_task":
+26 -1
View File
@@ -7,7 +7,12 @@ from typing import Any
from agent_platform.auth import UserIdentity
from agent_platform.models import get_model_spec
from agent_platform.runtime.loop import AgentLoop, tool_event_details
from agent_platform.runtime.loop import (
AgentLoop,
is_substantive_execution,
normalize_write_file_content,
tool_event_details,
)
from agent_platform.runtime.tools import TOOL_METADATA
from agent_platform.store import RuntimeStore
@@ -20,6 +25,26 @@ def tool_call(call_id: str, name: str, arguments: dict[str, Any]) -> dict[str, A
}
def test_write_file_normalizes_double_escaped_code_layout() -> None:
escaped = r'import time\n\n# benchmark\ndef main():\n\tprint("keep\ninside")\n\nmain()\n'
assert normalize_write_file_content("benchmark.py", escaped) == (
'import time\n\n# benchmark\ndef main():\n\tprint("keep\\ninside")\n\nmain()\n'
)
def test_write_file_normalizes_double_escaped_plain_text() -> None:
assert normalize_write_file_content("report.md", r"# Report\n\nMeasured: 1.2 s\n") == (
"# Report\n\nMeasured: 1.2 s\n"
)
def test_read_only_shell_commands_do_not_satisfy_execution_evidence() -> None:
assert not is_substantive_execution("exec", {"command": "cat report.txt"})
assert not is_substantive_execution("exec", {"command": "sed -n '1,20p' script.py"})
assert is_substantive_execution("exec", {"command": "python3 script.py"})
assert is_substantive_execution("start_process", {"command": "python3 server.py"})
class ScriptedProvider:
def __init__(self, responses: list[dict[str, Any]]) -> None:
self.responses = responses