mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-02 19:22:22 +01:00
feat(settings): add a tool approval mode with an explicit off (YOLO) option (#6304)
* feat: add optional tool approval mode Admin-configurable tool_approval_mode setting (ask/auto) for the post-external-context tool approval gate. - ask (default): existing approval flow, gate stays on - auto: bypasses the approval gate for all runs - same effect as ODYSSEUS_TOOL_APPROVAL_GATE=0. Documented honestly in the settings row copy and in THREAT_MODEL.md, per review on #6304. - value validated server-side ({ask, auto}, anything else -> 400) - no new env var: the setting wires to the existing gate control - tests: setting validation + _tool_approval_gate_bypassed behavior * feat: surface deployment gate state on Tool approval row Follow-up to StressTestor's re-review: when ODYSSEUS_TOOL_APPROVAL_GATE is set to a falsy value, the Tool approval row still read 'Always ask' even though the gate was off, misleading admins the other way. GET /api/auth/settings now returns tool_approval_gate_env_disabled (derived from TOOL_APPROVAL_GATE_ENABLED) for admins, and the settings row renders a red notice naming the env var and explaining that the gate is off for every run regardless of the stored setting. Non-admin/scrubbed responses do not expose the deployment state. Test covers both env values and the scrub path. * refactor: rename tool approval mode auto to off The gate-off value is now "off" ("Off (YOLO)" in the UI) so the auto-approve name stays available for the future judged mode. --------- Co-authored-by: Matej <matej@local>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import asyncio
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from fastapi import HTTPException
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -117,3 +118,62 @@ def test_manage_settings_tombstones_legacy_fallback_key(monkeypatch):
|
||||
|
||||
assert save_calls == []
|
||||
assert store["default_model_fallbacks"] == LEGACY_VALUE
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tool_approval_mode_accepts_only_supported_values(monkeypatch):
|
||||
store = dict(settings_mod.DEFAULT_SETTINGS)
|
||||
|
||||
monkeypatch.setattr(auth_routes, "migrate_from_settings", lambda: None)
|
||||
monkeypatch.setattr(auth_routes, "_load_settings", lambda: dict(store))
|
||||
|
||||
def save_settings(updated):
|
||||
store.clear()
|
||||
store.update(updated)
|
||||
|
||||
monkeypatch.setattr(auth_routes, "_save_settings", save_settings)
|
||||
|
||||
router = auth_routes.setup_auth_routes(_AuthManager())
|
||||
set_settings = _route(router, "/api/auth/settings", "POST")
|
||||
|
||||
response = await set_settings(
|
||||
_Request({"tool_approval_mode": "off"}, admin=True)
|
||||
)
|
||||
assert response["tool_approval_mode"] == "off"
|
||||
assert store["tool_approval_mode"] == "off"
|
||||
|
||||
response = await set_settings(
|
||||
_Request({"tool_approval_mode": "ask"}, admin=True)
|
||||
)
|
||||
assert response["tool_approval_mode"] == "ask"
|
||||
assert store["tool_approval_mode"] == "ask"
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await set_settings(
|
||||
_Request({"tool_approval_mode": "banana"}, admin=True)
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_settings_expose_gate_env_kill_switch_to_admins_only(monkeypatch):
|
||||
store = dict(settings_mod.DEFAULT_SETTINGS)
|
||||
|
||||
monkeypatch.setattr(auth_routes, "migrate_from_settings", lambda: None)
|
||||
monkeypatch.setattr(auth_routes, "_load_settings", lambda: dict(store))
|
||||
|
||||
router = auth_routes.setup_auth_routes(_AuthManager())
|
||||
get_settings = _route(router, "/api/auth/settings", "GET")
|
||||
|
||||
monkeypatch.setattr(auth_routes.tool_capabilities, "TOOL_APPROVAL_GATE_ENABLED", True)
|
||||
response = await get_settings(_Request(admin=True))
|
||||
assert response["tool_approval_gate_env_disabled"] is False
|
||||
|
||||
monkeypatch.setattr(auth_routes.tool_capabilities, "TOOL_APPROVAL_GATE_ENABLED", False)
|
||||
response = await get_settings(_Request(admin=True))
|
||||
assert response["tool_approval_gate_env_disabled"] is True
|
||||
# The stored setting itself is untouched by the env kill-switch.
|
||||
assert response["tool_approval_mode"] == "ask"
|
||||
|
||||
# Unauthenticated/scrubbed callers must not learn the deployment gate state.
|
||||
response = await get_settings(_Request())
|
||||
assert "tool_approval_gate_env_disabled" not in response
|
||||
|
||||
Reference in New Issue
Block a user