Skip to content

fix(cli): /yolo in chat must enable session bypass, not just set env var#33931

Merged
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/cli-yolo-session-toggle
May 28, 2026
Merged

fix(cli): /yolo in chat must enable session bypass, not just set env var#33931
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/cli-yolo-session-toggle

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

Fixes #33925.

The CLI's in-chat /yolo toggled os.environ["HERMES_YOLO_MODE"] but never bypassed approvals — that env var is captured once at module-import time into tools/approval.py:_YOLO_MODE_FROZEN (a deliberate security floor that stops prompt-injected skills from flipping the bypass mid-run), so any post-import flip is a silent no-op. The CLI handler also didn't call enable_session_yolo() the way the gateway and TUI handlers do, so the per-session bypass path couldn't fire either.

Net effect: the status bar showed ⚠ YOLO while every dangerous command still hit the approval prompt or got denied. Only hermes --yolo (set before tool imports), HERMES_YOLO_MODE=1 hermes ..., and hermes config set approvals.mode off actually bypassed.

Changes

  • cli.py:_toggle_yolo() — now calls enable_session_yolo(self.session_id) / disable_session_yolo(self.session_id) instead of mutating the env var. Matches gateway/run.py:_handle_yolo_command and tui_gateway/server.py key=="yolo".
  • cli.py:run_agent() (inside process_input) — binds set_current_session_key(self.session_id) around run_conversation() so is_current_session_yolo_enabled() resolves against the same key the toggle writes under. Resets in finally so reused threads don't see stale identity. Mirrors tui_gateway/server.py:3496-3812 and gateway/platforms/api_server.py:3658-3672.
  • cli.py:_is_session_yolo_active() — new helper that replaces the two bool(os.getenv("HERMES_YOLO_MODE")) reads in the status-bar builders (cli.py:3750, cli.py:3811). Reads the live session-yolo state and still honors _YOLO_MODE_FROZEN so hermes --yolo continues to light up the badge.
  • tests/cli/test_cli_yolo_toggle.py — 8 new regression tests covering: ON/OFF toggle, no env-var mutation, missing-session_id fallback to "default", two-session isolation, status-bar helper, _YOLO_MODE_FROZEN interaction, and an end-to-end check that check_all_command_guards actually auto-approves after the toggle.

The _YOLO_MODE_FROZEN security freeze itself is preserved — env-var-based opt-in still only works when set before process start, which is the documented contract for --yolo / HERMES_YOLO_MODE.

Test Plan

$ python -m pytest tests/cli/test_cli_yolo_toggle.py -v
============================== 8 passed in 0.19s ==============================

$ python -m pytest tests/tools/test_yolo_mode.py tests/tools/test_approval.py \
    tests/gateway/test_yolo_command.py tests/tools/test_cron_approval_mode.py \
    tests/cli/test_cli_init.py -q
............................................................................... 232 passed

$ python -m pytest tests/cli/test_cli_init.py tests/gateway/test_yolo_command.py -q
42 passed in 0.92s

Manual repro verified before/after on origin/main:

  • Before: hermes/yolo → status bar shows ⚠ YOLO → rm -rf /tmp/foo → still prompts.
  • After: hermes/yolo → status bar shows ⚠ YOLO → rm -rf /tmp/foo → auto-approved.

Related

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists labels May 28, 2026
The CLI's in-chat `/yolo` toggle mutated `os.environ["HERMES_YOLO_MODE"]`
but had no effect because `tools/approval.py:_YOLO_MODE_FROZEN` captures
that env var once at module-import time (a deliberate security floor that
keeps prompt-injected skills from flipping the bypass mid-run). By the
time the user reaches `/yolo` in a running CLI session, `tools.approval`
has already been imported, so the env flip after that is a silent no-op.

Result: `/yolo` advertised "⚠ YOLO" in the status bar while every
dangerous command still hit the approval prompt or got denied.  Only
`hermes --yolo` (set before tool imports), `HERMES_YOLO_MODE=1 hermes ...`,
and `hermes config set approvals.mode off` actually bypassed.

This patches the CLI to match what the gateway and TUI `/yolo` handlers
already do, plus mirrors the TUI's session-rename YOLO transfer:

* `_toggle_yolo()` now calls `enable_session_yolo(self.session_id)` /
  `disable_session_yolo(self.session_id)` instead of touching the env
  var.  Matches `gateway/run.py:_handle_yolo_command` and the
  `tui_gateway/server.py` key=="yolo" branch.
* Around each `run_conversation()` call, `run_agent()` now binds
  `set_current_session_key(self.session_id)` so
  `tools.approval.is_current_session_yolo_enabled()` resolves against
  the same key the toggle writes under, and resets it in `finally` so
  reused threads don't see stale identity.  Matches the
  `tui_gateway/server.py` and `gateway/platforms/api_server.py` binding
  pattern.
* New `_transfer_session_yolo()` helper carries YOLO bypass state
  across `self.session_id` reassignments — `/branch` forking into a
  new session id and the auto-compression sync that rotates into a
  fresh continuation session id.  Without this, the same UX failure
  mode the rest of this fix addresses (silent `/yolo` no-op) would
  reappear after a single `/branch` or auto-compression event.
  Mirrors `tui_gateway/server.py` ~line 1297-1305.
* New `_is_session_yolo_active()` helper replaces the two
  `bool(os.getenv("HERMES_YOLO_MODE"))` reads in the status-bar
  builders, so the badge reflects the actual bypass state.  Uses
  `getattr(self, "session_id", None)` so status-bar test fixtures
  that bypass `__init__` via `HermesCLI.__new__(HermesCLI)` don't
  trip `AttributeError` (the builders swallow exceptions silently
  and lose every field after the failure).  Still honors
  `_YOLO_MODE_FROZEN` so `hermes --yolo` keeps lighting it up.

The `_YOLO_MODE_FROZEN` security freeze is preserved — env-var-based
opt-in still only works when set before process start, which is the
documented contract for `--yolo` / `HERMES_YOLO_MODE`.

Closes NousResearch#33925
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI /yolo (in-chat) does not bypass dangerous command approvals — env var freeze + missing enable_session_yolo call

2 participants