fix(cli): persist YOLO mode across --resume
A session's YOLO bypass lived only in the in-memory
tools.approval._session_yolo set (or the process-frozen --yolo env
var), so resuming a session in a fresh process silently reverted the
user's /yolo ON — dangerous commands started prompting again.
Persist a yolo_mode flag in the session row's model_config JSON and
restore it on every CLI resume path:
- SessionDB.set_session_yolo() merges the flag into model_config
(same lineage-preserving merge as update_session_runtime_lock);
SessionDB.session_yolo_enabled() reads it back, false on any parse
failure.
- /yolo toggle persists ON and OFF through the new helper; the
compression/branch session-id rotation carries the flag onto the
continuation row.
- --yolo launches record the flag at session creation (agent_init),
and a /yolo toggled before the lazily-created row exists is carried
into the creation-time model_config (_ensure_db_session).
- HermesCLI._restore_session_yolo() re-enables the bypass on startup
--resume/-c, the deferred init path, and mid-chat /resume, with a
visible '⚡ YOLO mode restored from session' notice. No-op under a
frozen process-wide --yolo and never enables on absent/garbage flags.
This commit is contained in:
@@ -1580,6 +1580,17 @@ def init_agent(
|
||||
"reasoning_config": reasoning_config,
|
||||
"max_tokens": max_tokens,
|
||||
}
|
||||
# Persist a process-scoped --yolo launch into the session row so a later
|
||||
# `hermes --resume <id>` can restore the bypass (CLI resume paths read
|
||||
# model_config.yolo_mode back via SessionDB.session_yolo_enabled).
|
||||
# Session-scoped /yolo toggles persist separately through
|
||||
# SessionDB.set_session_yolo at toggle time.
|
||||
try:
|
||||
from tools.approval import _YOLO_MODE_FROZEN
|
||||
if _YOLO_MODE_FROZEN:
|
||||
agent._session_init_model_config["yolo_mode"] = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# In-memory todo list for task planning (one per agent/session)
|
||||
from tools.todo_tool import TodoStore
|
||||
|
||||
@@ -7334,6 +7334,44 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
else:
|
||||
self._console_print(f"[dim]{_escape(msg)}[/dim]")
|
||||
|
||||
def _restore_session_yolo(self, session_meta: dict, *, quiet: bool = False) -> None:
|
||||
"""Re-enable YOLO bypass on resume when the session had it on.
|
||||
|
||||
Companion to ``_restore_session_cwd`` — called from every resume path
|
||||
(startup ``--resume``/``-c`` and mid-chat ``/resume``). The persisted
|
||||
flag lives in the session row's ``model_config.yolo_mode`` (written by
|
||||
``/yolo`` toggles and ``--yolo`` launches); without this restore the
|
||||
in-memory ``tools.approval._session_yolo`` set starts empty in a fresh
|
||||
process and the user's bypass silently reverts.
|
||||
|
||||
No-op when the flag is absent/false, when YOLO is already active for
|
||||
this session (idempotent across repeated resume paths), or when the
|
||||
process was itself launched with ``--yolo`` (frozen bypass already
|
||||
covers everything).
|
||||
"""
|
||||
try:
|
||||
from hermes_state import SessionDB
|
||||
from tools.approval import (
|
||||
_YOLO_MODE_FROZEN,
|
||||
enable_session_yolo,
|
||||
is_session_yolo_enabled,
|
||||
)
|
||||
except Exception:
|
||||
return
|
||||
if _YOLO_MODE_FROZEN:
|
||||
return
|
||||
if not SessionDB.session_yolo_enabled(session_meta):
|
||||
return
|
||||
session_key = self.session_id or "default"
|
||||
if is_session_yolo_enabled(session_key):
|
||||
return
|
||||
enable_session_yolo(session_key)
|
||||
msg = "⚡ YOLO mode restored from session — all commands auto-approved. /yolo to turn off."
|
||||
if quiet:
|
||||
print(msg, file=sys.stderr)
|
||||
else:
|
||||
self._console_print(f"[dim]{_escape(msg)}[/dim]")
|
||||
|
||||
|
||||
|
||||
def _render_resume_history_panel_lines(self, panel) -> list[str]:
|
||||
@@ -10820,6 +10858,12 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
if is_session_yolo_enabled(old_session_id):
|
||||
enable_session_yolo(new_session_id)
|
||||
disable_session_yolo(old_session_id)
|
||||
# Carry the persisted flag onto the continuation row so a later
|
||||
# `hermes --resume <new_id>` restores the bypass too. getattr
|
||||
# guard: tests call this unbound against a minimal stand-in.
|
||||
_persist = getattr(self, "_persist_session_yolo", None)
|
||||
if _persist:
|
||||
_persist(new_session_id, True)
|
||||
|
||||
def _is_session_yolo_active(self) -> bool:
|
||||
"""Whether YOLO bypass is currently enabled for this CLI session.
|
||||
@@ -10871,19 +10915,45 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
)
|
||||
|
||||
session_key = self.session_id or "default"
|
||||
# ``getattr`` guard: tests exercise this method unbound against a
|
||||
# minimal stand-in object (see tests/cli/test_cli_yolo_toggle.py);
|
||||
# persistence is best-effort either way.
|
||||
_persist = getattr(self, "_persist_session_yolo", None)
|
||||
if is_session_yolo_enabled(session_key):
|
||||
disable_session_yolo(session_key)
|
||||
if _persist:
|
||||
_persist(session_key, False)
|
||||
_cprint(
|
||||
f" ⚠ YOLO mode {_Colors.BOLD}{_Colors.RED}OFF{_Colors.RESET}"
|
||||
" — dangerous commands will require approval."
|
||||
)
|
||||
else:
|
||||
enable_session_yolo(session_key)
|
||||
if _persist:
|
||||
_persist(session_key, True)
|
||||
_cprint(
|
||||
f" ⚡ YOLO mode {_Colors.BOLD}{_Colors.GREEN}ON{_Colors.RESET}"
|
||||
" — all commands auto-approved. Use with caution."
|
||||
)
|
||||
|
||||
def _persist_session_yolo(self, session_key: str, enabled: bool) -> None:
|
||||
"""Persist the YOLO flag to the session row so --resume restores it.
|
||||
|
||||
Best-effort: the in-memory toggle is authoritative for this process;
|
||||
persistence only affects a future ``hermes --resume``. Skipped when the
|
||||
session store is unavailable or the row doesn't exist yet (the row is
|
||||
created lazily on the first turn — ``_toggle_yolo`` before any chat
|
||||
writes nothing, and the launch-time ``--yolo`` flag is carried into the
|
||||
creation-time model_config instead).
|
||||
"""
|
||||
db = getattr(self, "_session_db", None)
|
||||
if db is None or not session_key or session_key == "default":
|
||||
return
|
||||
try:
|
||||
db.set_session_yolo(session_key, enabled)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -429,6 +429,7 @@ class CLIAgentSetupMixin:
|
||||
f"({msg_count} user message{'s' if msg_count != 1 else ''}, {len(restored)} total messages)"
|
||||
)
|
||||
self._restore_session_cwd(session_meta, quiet=_quiet_mode)
|
||||
self._restore_session_yolo(session_meta, quiet=_quiet_mode)
|
||||
else:
|
||||
if _quiet_mode:
|
||||
print(
|
||||
@@ -640,6 +641,7 @@ class CLIAgentSetupMixin:
|
||||
f"{len(restored)} total messages)[/]"
|
||||
)
|
||||
self._restore_session_cwd(session_meta)
|
||||
self._restore_session_yolo(session_meta)
|
||||
else:
|
||||
accent_color = _accent_hex()
|
||||
self._console_print(
|
||||
|
||||
@@ -1055,6 +1055,12 @@ class CLICommandsMixin:
|
||||
# and a no-op when the session recorded no cwd. See #38562.
|
||||
self._restore_session_cwd(session_meta)
|
||||
|
||||
# Restore the target session's persisted YOLO bypass. Any bypass the
|
||||
# PREVIOUS session had toggled on stops applying automatically because
|
||||
# the approval session key just changed. Same contract as a startup
|
||||
# --resume.
|
||||
self._restore_session_yolo(session_meta)
|
||||
|
||||
def _handle_sessions_command(self, cmd_original: str) -> None:
|
||||
"""Handle /sessions [list|<id_or_title>] — browse or resume previous sessions.
|
||||
|
||||
|
||||
@@ -4220,6 +4220,64 @@ class SessionDB(SessionSearchMixin, SessionSchemaMixin, SessionPortabilityMixin)
|
||||
)
|
||||
self._execute_write(_do)
|
||||
|
||||
def set_session_yolo(self, session_id: str, enabled: bool) -> None:
|
||||
"""Persist the per-session YOLO bypass flag into ``model_config``.
|
||||
|
||||
Merges ``yolo_mode`` into the existing ``model_config`` JSON (same
|
||||
merge discipline as ``update_session_runtime_lock`` so lineage
|
||||
markers like ``_branched_from`` / ``_delegate_from`` survive). The
|
||||
CLI resume paths read this flag back so a ``/yolo ON`` toggle — or a
|
||||
``--yolo`` launch — survives ``hermes --resume`` into a fresh
|
||||
process. No-op when the session row doesn't exist yet; the
|
||||
creation-time ``model_config`` carries the flag for ``--yolo``
|
||||
launches.
|
||||
"""
|
||||
if not session_id:
|
||||
return
|
||||
|
||||
def _do(conn):
|
||||
row = conn.execute(
|
||||
"SELECT model_config FROM sessions WHERE id = ?",
|
||||
(session_id,),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return
|
||||
raw = row["model_config"] if isinstance(row, sqlite3.Row) else row[0]
|
||||
config: Dict[str, Any] = {}
|
||||
if isinstance(raw, str) and raw.strip():
|
||||
try:
|
||||
parsed = json.loads(raw)
|
||||
if isinstance(parsed, dict):
|
||||
config = parsed
|
||||
except Exception:
|
||||
config = {}
|
||||
elif isinstance(raw, dict):
|
||||
config = dict(raw)
|
||||
config["yolo_mode"] = bool(enabled)
|
||||
conn.execute(
|
||||
"UPDATE sessions SET model_config = ? WHERE id = ?",
|
||||
(json.dumps(config), session_id),
|
||||
)
|
||||
self._execute_write(_do)
|
||||
|
||||
@staticmethod
|
||||
def session_yolo_enabled(session_meta: Optional[Dict[str, Any]]) -> bool:
|
||||
"""Read the persisted YOLO flag off a session row dict.
|
||||
|
||||
Accepts the dict returned by ``get_session`` (``model_config`` is a
|
||||
JSON string) or an already-parsed dict. Returns False on any parse
|
||||
failure — resume must never enable the bypass by accident.
|
||||
"""
|
||||
raw = (session_meta or {}).get("model_config")
|
||||
if isinstance(raw, str):
|
||||
try:
|
||||
raw = json.loads(raw)
|
||||
except Exception:
|
||||
return False
|
||||
if not isinstance(raw, dict):
|
||||
return False
|
||||
return bool(raw.get("yolo_mode"))
|
||||
|
||||
def update_session_billing_route(
|
||||
self,
|
||||
session_id: str,
|
||||
|
||||
+14
-1
@@ -630,11 +630,24 @@ class AIAgent:
|
||||
_profile_for_session = None
|
||||
except Exception:
|
||||
_profile_for_session = None
|
||||
# Carry the live YOLO bypass into the creation-time model_config so
|
||||
# a session whose /yolo was toggled BEFORE the row existed (the row
|
||||
# is created lazily on the first turn) still persists the flag for
|
||||
# `hermes --resume`. set_session_yolo() no-ops on a missing row, so
|
||||
# this is the only chance to record a pre-first-turn toggle.
|
||||
_init_model_config = self._session_init_model_config
|
||||
try:
|
||||
from tools.approval import is_session_yolo_enabled
|
||||
if is_session_yolo_enabled(self.session_id):
|
||||
_init_model_config = dict(_init_model_config or {})
|
||||
_init_model_config["yolo_mode"] = True
|
||||
except Exception:
|
||||
pass
|
||||
self._session_db.create_session(
|
||||
session_id=self.session_id,
|
||||
source=source,
|
||||
model=self.model,
|
||||
model_config=self._session_init_model_config,
|
||||
model_config=_init_model_config,
|
||||
system_prompt=self._cached_system_prompt,
|
||||
user_id=None,
|
||||
parent_session_id=self._parent_session_id,
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
"""Regression tests: YOLO mode persists across ``hermes --resume``.
|
||||
|
||||
Pre-fix bug: the ``/yolo`` toggle (and the process-start ``--yolo`` flag)
|
||||
lived only in the in-memory ``tools.approval._session_yolo`` set / the
|
||||
frozen env var. Resuming a session in a fresh process silently reverted the
|
||||
bypass — dangerous commands started prompting again even though the user had
|
||||
YOLO on for that session.
|
||||
|
||||
The fix persists a ``yolo_mode`` flag inside the session row's
|
||||
``model_config`` JSON:
|
||||
|
||||
- ``SessionDB.set_session_yolo`` merges the flag (preserving lineage markers
|
||||
like ``_branched_from``), written by the CLI ``/yolo`` toggle.
|
||||
- ``AIAgent._ensure_db_session`` carries a live session bypass (or a frozen
|
||||
``--yolo`` launch, via agent_init) into the creation-time model_config.
|
||||
- ``HermesCLI._restore_session_yolo`` reads the flag on every resume path
|
||||
and re-enables the in-memory bypass.
|
||||
"""
|
||||
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
import tools.approval as approval_module
|
||||
from cli import HermesCLI
|
||||
from hermes_state import SessionDB
|
||||
|
||||
|
||||
SESSION_ID = "yolo_persist_session"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _hermetic_yolo(monkeypatch):
|
||||
monkeypatch.delenv("HERMES_YOLO_MODE", raising=False)
|
||||
monkeypatch.setattr(approval_module, "_YOLO_MODE_FROZEN", False)
|
||||
approval_module.clear_session(SESSION_ID)
|
||||
yield
|
||||
approval_module.clear_session(SESSION_ID)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db(tmp_path):
|
||||
d = SessionDB(db_path=tmp_path / "state.db")
|
||||
yield d
|
||||
try:
|
||||
d.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class TestSessionDbYoloFlag:
|
||||
def test_set_and_read_round_trip(self, db):
|
||||
db.create_session(session_id=SESSION_ID, source="cli", model="m")
|
||||
db.set_session_yolo(SESSION_ID, True)
|
||||
meta = db.get_session(SESSION_ID)
|
||||
assert SessionDB.session_yolo_enabled(meta) is True
|
||||
|
||||
db.set_session_yolo(SESSION_ID, False)
|
||||
meta = db.get_session(SESSION_ID)
|
||||
assert SessionDB.session_yolo_enabled(meta) is False
|
||||
|
||||
def test_merge_preserves_existing_model_config_keys(self, db):
|
||||
db.create_session(
|
||||
session_id=SESSION_ID,
|
||||
source="cli",
|
||||
model="m",
|
||||
model_config={"max_iterations": 42, "_branched_from": "parent_x"},
|
||||
)
|
||||
db.set_session_yolo(SESSION_ID, True)
|
||||
meta = db.get_session(SESSION_ID)
|
||||
config = json.loads(meta["model_config"])
|
||||
assert config["yolo_mode"] is True
|
||||
assert config["max_iterations"] == 42
|
||||
assert config["_branched_from"] == "parent_x"
|
||||
|
||||
def test_missing_row_is_noop(self, db):
|
||||
# Row doesn't exist yet (lazy creation) — must not raise or create.
|
||||
db.set_session_yolo("does_not_exist", True)
|
||||
assert db.get_session("does_not_exist") is None
|
||||
|
||||
def test_creation_time_model_config_flag_reads_back(self, db):
|
||||
db.create_session(
|
||||
session_id=SESSION_ID,
|
||||
source="cli",
|
||||
model="m",
|
||||
model_config={"yolo_mode": True},
|
||||
)
|
||||
meta = db.get_session(SESSION_ID)
|
||||
assert SessionDB.session_yolo_enabled(meta) is True
|
||||
|
||||
def test_reader_is_false_on_garbage(self):
|
||||
assert SessionDB.session_yolo_enabled(None) is False
|
||||
assert SessionDB.session_yolo_enabled({}) is False
|
||||
assert SessionDB.session_yolo_enabled({"model_config": None}) is False
|
||||
assert SessionDB.session_yolo_enabled({"model_config": "not json {"}) is False
|
||||
assert SessionDB.session_yolo_enabled({"model_config": "[1,2]"}) is False
|
||||
assert (
|
||||
SessionDB.session_yolo_enabled({"model_config": '{"yolo_mode": false}'})
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def _stand_in(session_id=SESSION_ID, session_db=None):
|
||||
return SimpleNamespace(
|
||||
session_id=session_id,
|
||||
_session_db=session_db,
|
||||
_console_print=lambda *a, **k: None,
|
||||
)
|
||||
|
||||
|
||||
class TestRestoreSessionYolo:
|
||||
def test_restore_enables_bypass_when_flag_set(self):
|
||||
stand_in = _stand_in()
|
||||
meta = {"id": SESSION_ID, "model_config": '{"yolo_mode": true}'}
|
||||
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is False
|
||||
HermesCLI._restore_session_yolo(stand_in, meta)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is True
|
||||
|
||||
def test_restore_noop_when_flag_absent(self):
|
||||
stand_in = _stand_in()
|
||||
meta = {"id": SESSION_ID, "model_config": '{"max_iterations": 10}'}
|
||||
|
||||
HermesCLI._restore_session_yolo(stand_in, meta)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is False
|
||||
|
||||
def test_restore_noop_when_meta_empty(self):
|
||||
stand_in = _stand_in()
|
||||
HermesCLI._restore_session_yolo(stand_in, {})
|
||||
HermesCLI._restore_session_yolo(stand_in, None)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is False
|
||||
|
||||
def test_restore_idempotent_when_already_enabled(self):
|
||||
stand_in = _stand_in()
|
||||
approval_module.enable_session_yolo(SESSION_ID)
|
||||
meta = {"id": SESSION_ID, "model_config": '{"yolo_mode": true}'}
|
||||
# Should not raise or print duplicate banners; state stays enabled.
|
||||
HermesCLI._restore_session_yolo(stand_in, meta)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is True
|
||||
|
||||
def test_restore_skipped_under_frozen_process_yolo(self):
|
||||
stand_in = _stand_in()
|
||||
meta = {"id": SESSION_ID, "model_config": '{"yolo_mode": true}'}
|
||||
with patch.object(approval_module, "_YOLO_MODE_FROZEN", True):
|
||||
HermesCLI._restore_session_yolo(stand_in, meta)
|
||||
# Frozen bypass already covers everything — the session set is
|
||||
# untouched (avoids persisting a session-scoped bypass the user
|
||||
# only asked for at process scope).
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is False
|
||||
|
||||
|
||||
class TestToggleYoloPersists:
|
||||
def test_toggle_writes_flag_through_session_db(self):
|
||||
db = MagicMock()
|
||||
stand_in = SimpleNamespace(session_id=SESSION_ID, _session_db=db)
|
||||
# Bind the real persist helper so the toggle's getattr finds it.
|
||||
stand_in._persist_session_yolo = (
|
||||
lambda key, enabled: HermesCLI._persist_session_yolo(
|
||||
stand_in, key, enabled
|
||||
)
|
||||
)
|
||||
|
||||
with patch("cli._cprint"):
|
||||
HermesCLI._toggle_yolo(stand_in) # ON
|
||||
db.set_session_yolo.assert_called_once_with(SESSION_ID, True)
|
||||
|
||||
with patch("cli._cprint"):
|
||||
HermesCLI._toggle_yolo(stand_in) # OFF
|
||||
db.set_session_yolo.assert_called_with(SESSION_ID, False)
|
||||
|
||||
def test_toggle_survives_missing_session_db(self):
|
||||
stand_in = SimpleNamespace(session_id=SESSION_ID, _session_db=None)
|
||||
stand_in._persist_session_yolo = (
|
||||
lambda key, enabled: HermesCLI._persist_session_yolo(
|
||||
stand_in, key, enabled
|
||||
)
|
||||
)
|
||||
with patch("cli._cprint"):
|
||||
HermesCLI._toggle_yolo(stand_in) # must not raise
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is True
|
||||
|
||||
def test_toggle_still_works_without_persist_helper(self):
|
||||
# Back-compat with the minimal stand-in used by older tests.
|
||||
stand_in = SimpleNamespace(session_id=SESSION_ID)
|
||||
with patch("cli._cprint"):
|
||||
HermesCLI._toggle_yolo(stand_in)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is True
|
||||
|
||||
|
||||
class TestEndToEndPersistAndRestore:
|
||||
def test_full_round_trip_through_real_db(self, db):
|
||||
"""Toggle ON in 'process 1', restore in 'process 2' (fresh in-memory
|
||||
approval state), and verify a dangerous command auto-approves."""
|
||||
db.create_session(session_id=SESSION_ID, source="cli", model="m")
|
||||
|
||||
# Process 1: user toggles /yolo ON — persisted to the row.
|
||||
cli_one = SimpleNamespace(session_id=SESSION_ID, _session_db=db)
|
||||
cli_one._persist_session_yolo = (
|
||||
lambda key, enabled: HermesCLI._persist_session_yolo(
|
||||
cli_one, key, enabled
|
||||
)
|
||||
)
|
||||
with patch("cli._cprint"):
|
||||
HermesCLI._toggle_yolo(cli_one)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is True
|
||||
|
||||
# Simulate process exit: in-memory approval state is gone.
|
||||
approval_module.clear_session(SESSION_ID)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is False
|
||||
|
||||
# Process 2: --resume reads the row and restores the bypass.
|
||||
meta = db.get_session(SESSION_ID)
|
||||
cli_two = _stand_in(session_db=db)
|
||||
HermesCLI._restore_session_yolo(cli_two, meta)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is True
|
||||
|
||||
token = approval_module.set_current_session_key(SESSION_ID)
|
||||
try:
|
||||
result = approval_module.check_all_command_guards(
|
||||
"rm -rf /tmp/scratch-xyzzy", "local",
|
||||
)
|
||||
assert result["approved"] is True
|
||||
finally:
|
||||
approval_module.reset_current_session_key(token)
|
||||
|
||||
def test_toggle_off_round_trip(self, db):
|
||||
"""OFF must persist too — a resumed session must not resurrect a
|
||||
bypass the user explicitly turned off."""
|
||||
db.create_session(
|
||||
session_id=SESSION_ID,
|
||||
source="cli",
|
||||
model="m",
|
||||
model_config={"yolo_mode": True},
|
||||
)
|
||||
cli_one = SimpleNamespace(session_id=SESSION_ID, _session_db=db)
|
||||
cli_one._persist_session_yolo = (
|
||||
lambda key, enabled: HermesCLI._persist_session_yolo(
|
||||
cli_one, key, enabled
|
||||
)
|
||||
)
|
||||
approval_module.enable_session_yolo(SESSION_ID)
|
||||
with patch("cli._cprint"):
|
||||
HermesCLI._toggle_yolo(cli_one) # OFF
|
||||
approval_module.clear_session(SESSION_ID)
|
||||
|
||||
meta = db.get_session(SESSION_ID)
|
||||
cli_two = _stand_in(session_db=db)
|
||||
HermesCLI._restore_session_yolo(cli_two, meta)
|
||||
assert approval_module.is_session_yolo_enabled(SESSION_ID) is False
|
||||
Reference in New Issue
Block a user