feat: tool risk policy UI + wiring through all orchestrators

- New /settings/tools page: max_risk selector (low/medium/high) + per-tool
  override dropdowns (Default / Force include / Force exclude) for all 58 tools
  grouped by category with color-coded risk badges; JS updates Auto status live
- get_tools_for_role() + get_openai_tools_for_role() now accept max_risk,
  whitelist, blacklist; _apply_risk_policy() handles the filtering logic
- get_risk_policy() helper in auth_utils reads from tool_policy.json
- Risk policy wired through orchestrator.py, openai_orchestrator.py,
  orchestrator_engine.py, nextcloud_talk.py, homeassistant.py
- Tools nav link added to settings.html and notifications.html
- CLAUDE.md and ARCH__SYSTEM.md updated: tool count 50→58, risk system docs,
  tool access control three-layer model documented

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-05-11 22:45:04 -04:00
parent c9c1ca7de6
commit 69ec2f667d
15 changed files with 584 additions and 27 deletions

View File

@@ -19,7 +19,7 @@ from datetime import datetime, timezone
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from auth_utils import get_user_gemini_key, get_user_role, get_tool_policy
from auth_utils import get_user_gemini_key, get_user_role, get_tool_policy, get_risk_policy
from config import settings
from context_loader import load_context
from persona import set_context, validate as validate_persona
@@ -224,6 +224,7 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
policy = get_tool_policy(user)
confirm_allow = set(policy.get("allow", []))
confirm_deny = set(policy.get("deny", []))
max_risk, risk_wl, risk_bl = get_risk_policy(user)
if orch_model and orch_model.get("type") == "local_openai":
result = await openai_orchestrator.run(
@@ -236,6 +237,9 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
tool_list=tool_list,
confirm_allow=confirm_allow,
confirm_deny=confirm_deny,
max_risk=max_risk,
risk_whitelist=risk_wl,
risk_blacklist=risk_bl,
)
else:
gemini_key = (
@@ -255,6 +259,9 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
confirm_allow=confirm_allow,
confirm_deny=confirm_deny,
max_rounds=orch_model.get("max_rounds") if orch_model else None,
max_risk=max_risk,
risk_whitelist=risk_wl,
risk_blacklist=risk_bl,
)
if result.checkpoint: