feat: add support for Windows-only tools and enhance platform checks
Some checks failed
CI / test (push) Failing after 10s
Some checks failed
CI / test (push) Failing after 10s
This commit is contained in:
14
src/agent.py
14
src/agent.py
@@ -6,6 +6,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@@ -331,6 +332,7 @@ OBSERVATION_TOOL_NAMES = VISUAL_TOOL_NAMES | WINDOW_TOOL_NAMES | DIALOG_TOOL_NAM
|
|||||||
"clipboard_get",
|
"clipboard_get",
|
||||||
"get_cursor_position",
|
"get_cursor_position",
|
||||||
}
|
}
|
||||||
|
WINDOWS_ONLY_TOOL_NAMES = WINDOW_TOOL_NAMES | DIALOG_TOOL_NAMES | UI_ELEMENT_TOOL_NAMES
|
||||||
MAX_ACTION_SIGNATURE_ATTEMPTS = 3
|
MAX_ACTION_SIGNATURE_ATTEMPTS = 3
|
||||||
MAX_STABLE_OBSERVATION_STEPS = 3
|
MAX_STABLE_OBSERVATION_STEPS = 3
|
||||||
FINISH_LIKELY_OBSERVATION_TOOLS = {"see_screen", "enhance", "get_active_window", "detect_dialog"}
|
FINISH_LIKELY_OBSERVATION_TOOLS = {"see_screen", "enhance", "get_active_window", "detect_dialog"}
|
||||||
@@ -1022,9 +1024,10 @@ class ScreenJobAgent:
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
optional_native_tools = WINDOW_TOOL_NAMES | DIALOG_TOOL_NAMES | UI_ELEMENT_TOOL_NAMES
|
optional_native_tools = set(WINDOWS_ONLY_TOOL_NAMES)
|
||||||
if not self._native_control_tools_enabled():
|
if self._is_windows_host() and not self._native_control_tools_enabled():
|
||||||
optional_native_tools = optional_native_tools - {"get_active_window"}
|
optional_native_tools = optional_native_tools - {"get_active_window"}
|
||||||
|
if not self._is_windows_host() or not self._native_control_tools_enabled():
|
||||||
return [
|
return [
|
||||||
tool
|
tool
|
||||||
for tool in all_tools
|
for tool in all_tools
|
||||||
@@ -1080,6 +1083,9 @@ class ScreenJobAgent:
|
|||||||
def _sorted_prohibited_key_combos(self) -> list[str]:
|
def _sorted_prohibited_key_combos(self) -> list[str]:
|
||||||
return sorted(self.prohibited_key_combos)
|
return sorted(self.prohibited_key_combos)
|
||||||
|
|
||||||
|
def _is_windows_host(self) -> bool:
|
||||||
|
return sys.platform.startswith("win")
|
||||||
|
|
||||||
def _native_automation_mode(self) -> str:
|
def _native_automation_mode(self) -> str:
|
||||||
mode = str(self.options.native_automation_mode or "prefer").strip().lower()
|
mode = str(self.options.native_automation_mode or "prefer").strip().lower()
|
||||||
if mode not in {"off", "prefer", "require_fallback"}:
|
if mode not in {"off", "prefer", "require_fallback"}:
|
||||||
@@ -1087,7 +1093,7 @@ class ScreenJobAgent:
|
|||||||
return mode
|
return mode
|
||||||
|
|
||||||
def _native_control_tools_enabled(self) -> bool:
|
def _native_control_tools_enabled(self) -> bool:
|
||||||
return self._native_automation_mode() != "off"
|
return self._is_windows_host() and self._native_automation_mode() != "off"
|
||||||
|
|
||||||
def _active_app_identity(self, window: dict[str, Any] | None) -> str:
|
def _active_app_identity(self, window: dict[str, Any] | None) -> str:
|
||||||
if not isinstance(window, dict):
|
if not isinstance(window, dict):
|
||||||
@@ -3753,6 +3759,8 @@ class ScreenJobAgent:
|
|||||||
def _dispatch_tool(self, name: str, args: dict[str, Any]) -> dict[str, Any]:
|
def _dispatch_tool(self, name: str, args: dict[str, Any]) -> dict[str, Any]:
|
||||||
if name in self.disabled_tools:
|
if name in self.disabled_tools:
|
||||||
return {"ok": False, "error": f"Tool '{name}' is disabled for this job."}
|
return {"ok": False, "error": f"Tool '{name}' is disabled for this job."}
|
||||||
|
if not self._is_windows_host() and name in WINDOWS_ONLY_TOOL_NAMES:
|
||||||
|
return {"ok": False, "error": f"Tool '{name}' is only available on Windows."}
|
||||||
finish_likely_result = self._check_finish_likely_gate(name, args)
|
finish_likely_result = self._check_finish_likely_gate(name, args)
|
||||||
if finish_likely_result is not None:
|
if finish_likely_result is not None:
|
||||||
return finish_likely_result
|
return finish_likely_result
|
||||||
|
|||||||
@@ -752,6 +752,23 @@ def test_tool_schemas_hide_optional_native_tools_when_mode_off(tmp_path: Path, m
|
|||||||
assert "list_ui_elements" not in schemas
|
assert "list_ui_elements" not in schemas
|
||||||
|
|
||||||
|
|
||||||
|
def test_tool_schemas_hide_windows_only_tools_on_non_windows_host(tmp_path: Path, monkeypatch) -> None:
|
||||||
|
agent = _build_agent(tmp_path, monkeypatch)
|
||||||
|
monkeypatch.setattr(agent_module.sys, "platform", "linux")
|
||||||
|
|
||||||
|
schemas = {tool["name"]: tool for tool in agent._tool_schemas()}
|
||||||
|
|
||||||
|
assert "get_active_window" not in schemas
|
||||||
|
assert "list_windows" not in schemas
|
||||||
|
assert "detect_dialog" not in schemas
|
||||||
|
assert "list_ui_elements" not in schemas
|
||||||
|
|
||||||
|
result = agent._dispatch_tool("get_active_window", {})
|
||||||
|
|
||||||
|
assert result["ok"] is False
|
||||||
|
assert result["error"] == "Tool 'get_active_window' is only available on Windows."
|
||||||
|
|
||||||
|
|
||||||
def test_list_windows_returns_structured_surface_metadata(tmp_path: Path, monkeypatch) -> None:
|
def test_list_windows_returns_structured_surface_metadata(tmp_path: Path, monkeypatch) -> None:
|
||||||
agent = _build_agent(tmp_path, monkeypatch)
|
agent = _build_agent(tmp_path, monkeypatch)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
|||||||
Reference in New Issue
Block a user