refactor: migrate Tool Permissions from Settings to /settings/tools

- Remove Tool Permissions form from settings.html; replace with a
  "Tool Settings →" link that redirects to /settings/tools
- Add Confirmation Gate section to tools_settings.html (allow/deny
  textareas) inside the same form as risk policy — one save covers all
- tools_settings.py save handler now writes allow/deny alongside
  max_risk/whitelist/blacklist into tool_policy.json
- Remove /settings/tool-policy POST route from settings.py (no longer needed)
- Remove get_tool_policy, save_tool_policy, CONFIRM_REQUIRED imports
  from settings.py (now owned by tools_settings.py)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-05-11 22:50:48 -04:00
parent 69ec2f667d
commit 8ab1942514
4 changed files with 56 additions and 60 deletions

View File

@@ -17,7 +17,7 @@ from fastapi.responses import HTMLResponse, RedirectResponse
from auth_utils import COOKIE_NAME, decode_token, get_tool_policy, save_tool_policy
from persona import list_user_personas
from tools import TOOL_CATEGORIES, TOOL_RISK
from tools import TOOL_CATEGORIES, TOOL_RISK, CONFIRM_REQUIRED
logger = logging.getLogger(__name__)
router = APIRouter()
@@ -124,6 +124,9 @@ def _tools_page(
html = html.replace("{{ tool_table_html }}", _build_tool_table(policy))
html = html.replace("{{ tool_risk_json }}", json.dumps(TOOL_RISK))
html = html.replace("{{ confirm_required_tools }}", _html.escape(", ".join(sorted(CONFIRM_REQUIRED))))
html = html.replace("{{ tool_allow }}", _html.escape("\n".join(policy.get("allow") or [])))
html = html.replace("{{ tool_deny }}", _html.escape("\n".join(policy.get("deny") or [])))
html = html.replace("{{ back_href }}", f"/{username}/{back_persona}" if back_persona else "/")
html = html.replace("{{ help_href }}", f"/help?persona={back_persona}" if back_persona else "/help")
@@ -167,7 +170,9 @@ async def save_tools(request: Request):
elif val == "blacklist":
blacklist.append(tool)
# Merge into existing policy (preserve allow/deny confirmation-gate fields)
allow_tools = [ln.strip() for ln in (form.get("allow_list") or "").splitlines() if ln.strip()]
deny_tools = [ln.strip() for ln in (form.get("deny_list") or "").splitlines() if ln.strip()]
policy = get_tool_policy(username)
if max_risk:
policy["max_risk"] = max_risk
@@ -176,11 +181,13 @@ async def save_tools(request: Request):
policy["whitelist"] = whitelist
policy["blacklist"] = blacklist
policy["allow"] = allow_tools
policy["deny"] = deny_tools
save_tool_policy(username, policy)
logger.info(
"tool policy saved for %s: max_risk=%s whitelist=%d blacklist=%d",
username, max_risk or "none", len(whitelist), len(blacklist),
"tool policy saved for %s: max_risk=%s whitelist=%d blacklist=%d allow=%d deny=%d",
username, max_risk or "none", len(whitelist), len(blacklist), len(allow_tools), len(deny_tools),
)
return HTMLResponse(_tools_page(
username, back_persona,