"""Conservative mapping from Hermes tools to HUX capabilities.""" from __future__ import annotations from dataclasses import dataclass @dataclass(frozen=True) class ToolPolicy: """The HUX gate inputs for one tool family.""" capability: str risk: str external: bool EXACT = { "read_file": ToolPolicy("read_files", "low", False), "read_many_files": ToolPolicy("read_files", "low", False), "list_directory": ToolPolicy("read_files", "low", False), "search_files": ToolPolicy("read_files", "low", False), "session_search": ToolPolicy("read_files", "low", False), "tool_search": ToolPolicy("read_files", "low", False), "write_file": ToolPolicy("write_files", "medium", False), "patch": ToolPolicy("write_files", "medium", False), "memory": ToolPolicy("memory_write", "medium", False), "delegate_task": ToolPolicy("delegate", "high", False), "terminal": ToolPolicy("shell", "high", True), # The Python sandbox is isolated by design; it is internal shell work, # unlike the real terminal above. "python": ToolPolicy("shell", "medium", False), "python_sandbox": ToolPolicy("shell", "medium", False), # Core assistant faculties are reads, not side effects. "skills": ToolPolicy("read_files", "low", False), "clarify": ToolPolicy("read_files", "low", False), "todo": ToolPolicy("read_files", "low", False), "vision": ToolPolicy("read_files", "low", False), # Browsing is the product; it is network, not an unknown side effect. "browser": ToolPolicy("network", "medium", True), "web": ToolPolicy("network", "medium", True), "web_search": ToolPolicy("network", "medium", True), # Image generation produces an artifact through the trusted broker. "image_gen": ToolPolicy("artifact_write", "medium", False), # Read-only cluster visibility; RBAC and the handler both exclude secrets. "cluster_read": ToolPolicy("read_files", "low", False), } PREFIXES = ( (("skill_", "skills_", "todo_", "clarify_"), ToolPolicy("read_files", "low", False)), (("web_", "browser_", "http_", "mcp_"), ToolPolicy("network", "medium", True)), (("send_", "mail_", "email_", "slack_", "discord_", "telegram_"), ToolPolicy("send_message", "high", True)), (("kubectl_", "flux_", "deploy_", "release_"), ToolPolicy("deploy", "high", True)), (("image_",), ToolPolicy("artifact_write", "medium", False)), (("video_", "vision_"), ToolPolicy("read_files", "low", False)), (("artifact_",), ToolPolicy("artifact_write", "medium", False)), (("memory_",), ToolPolicy("memory_write", "medium", False)), (("delegate_", "subagent_"), ToolPolicy("delegate", "high", False)), (("write_", "edit_", "file_"), ToolPolicy("write_files", "medium", False)), ) UNKNOWN = ToolPolicy("external_side_effect", "high", True) def classify(tool_name: str) -> ToolPolicy: """Return a known mapping, treating every unknown tool as high-risk external.""" name = tool_name.strip().lower() if isinstance(tool_name, str) else "" if name in EXACT: return EXACT[name] for prefixes, policy in PREFIXES: if name.startswith(prefixes): return policy return UNKNOWN