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 os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
@@ -331,6 +332,7 @@ OBSERVATION_TOOL_NAMES = VISUAL_TOOL_NAMES | WINDOW_TOOL_NAMES | DIALOG_TOOL_NAM
|
||||
"clipboard_get",
|
||||
"get_cursor_position",
|
||||
}
|
||||
WINDOWS_ONLY_TOOL_NAMES = WINDOW_TOOL_NAMES | DIALOG_TOOL_NAMES | UI_ELEMENT_TOOL_NAMES
|
||||
MAX_ACTION_SIGNATURE_ATTEMPTS = 3
|
||||
MAX_STABLE_OBSERVATION_STEPS = 3
|
||||
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
|
||||
if not self._native_control_tools_enabled():
|
||||
optional_native_tools = set(WINDOWS_ONLY_TOOL_NAMES)
|
||||
if self._is_windows_host() and not self._native_control_tools_enabled():
|
||||
optional_native_tools = optional_native_tools - {"get_active_window"}
|
||||
if not self._is_windows_host() or not self._native_control_tools_enabled():
|
||||
return [
|
||||
tool
|
||||
for tool in all_tools
|
||||
@@ -1080,6 +1083,9 @@ class ScreenJobAgent:
|
||||
def _sorted_prohibited_key_combos(self) -> list[str]:
|
||||
return sorted(self.prohibited_key_combos)
|
||||
|
||||
def _is_windows_host(self) -> bool:
|
||||
return sys.platform.startswith("win")
|
||||
|
||||
def _native_automation_mode(self) -> str:
|
||||
mode = str(self.options.native_automation_mode or "prefer").strip().lower()
|
||||
if mode not in {"off", "prefer", "require_fallback"}:
|
||||
@@ -1087,7 +1093,7 @@ class ScreenJobAgent:
|
||||
return mode
|
||||
|
||||
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:
|
||||
if not isinstance(window, dict):
|
||||
@@ -3753,6 +3759,8 @@ class ScreenJobAgent:
|
||||
def _dispatch_tool(self, name: str, args: dict[str, Any]) -> dict[str, Any]:
|
||||
if name in self.disabled_tools:
|
||||
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)
|
||||
if finish_likely_result is not None:
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
agent = _build_agent(tmp_path, monkeypatch)
|
||||
monkeypatch.setattr(
|
||||
|
||||
Reference in New Issue
Block a user