mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-21 14:22:19 +02:00
fix bridge text writes over binary artifacts
This commit is contained in:
@@ -305,6 +305,14 @@ def _client_bridge(client_runtime_context: Optional[Dict]) -> Optional[Dict]:
|
|||||||
|
|
||||||
_ROUTED_BRIDGE_TOOLS = TUI_ROUTED_BRIDGE_TOOL_NAMES
|
_ROUTED_BRIDGE_TOOLS = TUI_ROUTED_BRIDGE_TOOL_NAMES
|
||||||
_BRIDGE_TOOL_TIMEOUT_S = 900.0
|
_BRIDGE_TOOL_TIMEOUT_S = 900.0
|
||||||
|
# ``write_file`` transports UTF-8 source/text only. Keep this guard on the
|
||||||
|
# bridge route as well as the local WriteFileTool: native/Harbor runs use the
|
||||||
|
# bridge for workspace files, so otherwise a model can overwrite a verified
|
||||||
|
# PNG/PDF screenshot with a prose status message.
|
||||||
|
_TEXT_WRITE_BINARY_SUFFIXES = frozenset({
|
||||||
|
".bmp", ".gif", ".ico", ".jpeg", ".jpg", ".mp3", ".mp4", ".ogg",
|
||||||
|
".pdf", ".png", ".wav", ".webm", ".webp", ".zip",
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
async def _route_tool_via_bridge(tool: str, content: str, session_id: Optional[str], client_runtime_context: Optional[Dict]):
|
async def _route_tool_via_bridge(tool: str, content: str, session_id: Optional[str], client_runtime_context: Optional[Dict]):
|
||||||
@@ -660,6 +668,15 @@ async def _route_tool_via_bridge(tool: str, content: str, session_id: Optional[s
|
|||||||
"error": "write_file: path is required",
|
"error": "write_file: path is required",
|
||||||
"exit_code": 1,
|
"exit_code": 1,
|
||||||
}
|
}
|
||||||
|
if os.path.splitext(path)[1].casefold() in _TEXT_WRITE_BINARY_SUFFIXES:
|
||||||
|
return f"write_file: {path[:80]}", {
|
||||||
|
"error": (
|
||||||
|
f"write_file: refusing UTF-8 text for binary artifact path {path}. "
|
||||||
|
"Use Python or a format-specific creation tool, then inspect the result."
|
||||||
|
),
|
||||||
|
"exit_code": 1,
|
||||||
|
"binary_artifact_preserved": True,
|
||||||
|
}
|
||||||
return f"write_file: {path[:80]}", await _bridge_post(
|
return f"write_file: {path[:80]}", await _bridge_post(
|
||||||
bridge,
|
bridge,
|
||||||
"/write",
|
"/write",
|
||||||
|
|||||||
@@ -153,3 +153,32 @@ def test_scoped_execution_bridge_failure_logs_compact_warning(caplog) -> None:
|
|||||||
assert "Command timed out after 900 seconds" in result["error"]
|
assert "Command timed out after 900 seconds" in result["error"]
|
||||||
assert "Traceback" not in caplog.text
|
assert "Traceback" not in caplog.text
|
||||||
assert "Scoped execution bridge test-environment failed for tool=host_shell" in caplog.text
|
assert "Scoped execution bridge test-environment failed for tool=host_shell" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_tui_bridge_write_file_rejects_text_for_binary_artifact(monkeypatch) -> None:
|
||||||
|
"""The bridge path must preserve rendered media just like local writes."""
|
||||||
|
called = False
|
||||||
|
|
||||||
|
async def fake_post(*_args, **_kwargs):
|
||||||
|
nonlocal called
|
||||||
|
called = True
|
||||||
|
return {"output": "should not run", "exit_code": 0}
|
||||||
|
|
||||||
|
monkeypatch.setattr(tool_execution, "_client_bridge", lambda _context: {"url": "http://bridge", "token": "x"})
|
||||||
|
monkeypatch.setattr(tool_execution, "_bridge_post", fake_post)
|
||||||
|
|
||||||
|
async def invoke():
|
||||||
|
return await tool_execution._route_tool_via_bridge(
|
||||||
|
"write_file",
|
||||||
|
'{"path":"/tmp_workspace/results/screenshot.png","content":"The screenshot is complete."}',
|
||||||
|
"run-1",
|
||||||
|
{"surface": "odysseus-tui"},
|
||||||
|
)
|
||||||
|
|
||||||
|
description, result = asyncio.run(invoke())
|
||||||
|
|
||||||
|
assert description == "write_file: /tmp_workspace/results/screenshot.png"
|
||||||
|
assert result["exit_code"] == 1
|
||||||
|
assert result["binary_artifact_preserved"] is True
|
||||||
|
assert "binary artifact path" in result["error"]
|
||||||
|
assert called is False
|
||||||
|
|||||||
Reference in New Issue
Block a user