Coverage for src / osiris_cli / tool_policy.py: 0%
53 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-28 16:44 +0200
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-28 16:44 +0200
1import re
2from typing import Dict, List, Optional, Tuple
4from .session import SessionManager, session
5from .tools import tools
6from .tools_registry import tools_registry
8READ_ONLY_TOOL_SET = {
9 "read_file",
10 "list_directory",
11 "get_file_tree",
12 "search_codebase",
13}
15def _build_tool_name_map() -> Dict[str, str]:
16 names: Dict[str, str] = {}
17 for tool in tools_registry.list_tools():
18 key = tool.name.lower()
19 names[key] = tool.name
20 for tool_name in tools.tools.keys():
21 key = tool_name.lower()
22 names.setdefault(key, tool_name)
23 return names
26def _parse_tool_tokens(fragment: str, name_map: Dict[str, str]) -> List[str]:
27 tokens = re.findall(r"[a-zA-Z0-9_]+", fragment)
28 parsed: List[str] = []
29 for token in tokens:
30 canonical = name_map.get(token.lower())
31 if canonical and canonical not in parsed:
32 parsed.append(canonical)
33 return parsed
36def extract_tool_constraints(text: str) -> Tuple[List[str], List[str], bool]:
37 allowed: List[str] = []
38 blocked: List[str] = []
39 name_map = _build_tool_name_map()
40 header_pattern = re.compile(
41 r"(allowed tools|blocked tools|forbidden tools|allowed|blocked|forbidden)\s*:",
42 re.IGNORECASE
43 )
44 matches = list(header_pattern.finditer(text))
46 for idx, match in enumerate(matches):
47 header = match.group(1).lower()
48 section = "allowed" if "allow" in header else "blocked"
49 start = match.end()
50 end = matches[idx + 1].start() if idx + 1 < len(matches) else len(text)
51 segment = text[start:end]
52 tokens = _parse_tool_tokens(segment, name_map)
53 if section == "allowed":
54 allowed.extend(tokens)
55 else:
56 blocked.extend(tokens)
58 read_only_flag = bool(re.search(r"read[-\s]?only", text, re.IGNORECASE))
59 return allowed, blocked, read_only_flag
62def apply_tool_constraints(text: str, target_session: Optional[SessionManager] = None) -> bool:
63 session_obj = target_session or session
64 allowed, blocked, read_only_flag = extract_tool_constraints(text)
65 if read_only_flag and not allowed:
66 name_map = _build_tool_name_map()
67 allowed = [name_map[name] for name in READ_ONLY_TOOL_SET if name in name_map]
68 if not allowed and not blocked:
69 return False
70 policy_updated = session_obj.apply_tool_policy(
71 allowed=allowed if allowed else None,
72 forbidden=blocked if blocked else None
73 )
74 if policy_updated:
75 session_obj.add_activity("Applied tool policy from request.")
76 return policy_updated