feat: YOLO mode toggle — skip all approvals per session (#467)#1152
feat: YOLO mode toggle — skip all approvals per session (#467)#1152bergeouss wants to merge 6 commits intonesquena:masterfrom
Conversation
Fix: i18n locale structure restoredThe initial YOLO key insertions had several issues:
Fix: restored the original All 32 tests pass locally (24 YOLO + 4 Chinese locale + 4 language precedence). |
|
Thanks for the detailed write-up and the follow-up i18n fix, @bergeouss! The YOLO mode implementation looks comprehensive. Backend — the Frontend — three entry points (slash command, skip-all button on approval card, pill indicator) cover the natural discovery paths a user would take. The amber pill is a good visible indicator that the mode is active. State management — i18n fix — good that the duplicate-key and missing-locale issues were caught and fixed. The clean re-insertion approach (restore from master, then insert at correct positions) avoids accumulating diff noise. The 24 tests for the YOLO feature look solid. Closes #467. |
Hold — Needs Review and Clarification Before MergeYOLO mode is a substantial new feature that touches multiple sensitive areas of the codebase. Placing a hold until the following are addressed. 1. UX/Design Review Required — @aronprinsThis PR introduces two new persistent UI elements that need a design review against our progressive disclosure principle:
@aronprins — can you review the placement of both elements and confirm they meet our UX bar before this merges? 2. Author Clarification Needed — Session State BehaviorThe PR introduces session-scoped YOLO state, but the following questions are not answered in the PR description or code comments:
Please update the PR description (or add inline code comments) to explicitly document the answers to both questions. 3. i18n Duplicate Key Fix — Verification RequiredThe author mentioned that duplicate translation keys in the ru and zh-Hant locale files were fixed as part of this PR. Before merging, please confirm:
If the i18n fix is confirmed clean, note that explicitly in a follow-up comment. Summary of Blockers
Removing the hold once all three are addressed. Thanks for the thoughtful feature work — the core idea is solid, just want to make sure the edges are tight before this lands. |
Address reviewer feedback on nesquena#1152 (blocker nesquena#2): - Document page reload behavior: state PERSISTS (server-side in-memory) - Document cross-tab isolation: state is SHARED per session - Document server restart: state is LOST (in-memory only) - Correct misleading 'resets on page reload' comment in messages.js - Update backend route comment in routes.py
Review Feedback AddressedBlocker #2 — Session State Behavior Clarification ✅
Blocker #3 — i18n Duplicate Key Verification ✅
Blocker #1 — UX/Design ReviewNot addressed (awaiting @aronprins review — assigned to them). 🤖 AI-assisted via Hermes Agent |
|
Got any screenshots? |
…na#467) - Add POST /api/session/yolo endpoint (toggle enable/disable) - Add yolo_enabled field to GET /api/session/status - Add /yolo slash command with cmdYolo() function - Add YOLO pill indicator in composer footer (hidden by default) - Add 'Skip all this session' button on approval cards - Sync YOLO state across session load/switch via _fetchYoloState() - Add CSS for .yolo-pill (amber) and .approval-skip-all styles - Add i18n keys for all 6 locales (en, ru, es, de, zh, ko) - Add 24 tests covering endpoints, command, HTML, CSS, and i18n
…locales (nesquena#467) - Restore original i18n.js and re-insert YOLO keys cleanly - Add YOLO keys to zh (Simplified Chinese) locale - Add YOLO keys to zh-Hant (Traditional Chinese) locale - Fix duplicate key insertions in ru and zh-Hant blocks - Fix syntax errors (double commas) from previous insertions - All 7 locales + zh-Hant now have 8 YOLO-related keys - JS syntax validates clean
Address reviewer feedback on nesquena#1152 (blocker nesquena#2): - Document page reload behavior: state PERSISTS (server-side in-memory) - Document cross-tab isolation: state is SHARED per session - Document server restart: state is LOST (in-memory only) - Correct misleading 'resets on page reload' comment in messages.js - Update backend route comment in routes.py
…na#1152) The i18n.js locale blocks must be separated by double newlines (\n\n) for locale test regex patterns to match. Previous YOLO key insertions broke this spacing. All transitions (en->ru->es->de->zh->ko) restored.
24d58b0 to
8635365
Compare
Screenshots1. Approval Card — "Skip all this session" buttonThe new "⚡ Skip all this session" button is added at the bottom of the approval card, allowing users to toggle YOLO mode directly from the approval prompt: 2.
|
|
@aronprins looks pretty good to me, Aron can I get your blessing to move this forward to review? |
|
The slash command is fine. The text below the buttons isnt. Can it be added as another button? Regarding the item in the composer footer: not a fan... but with the knowledge ill be moving it in the near future: approved. |
+1 Agree, can we move the text below the buttons to be another button and then I'll move this forward for review, thanks! |
…na#1152) - Move skip-all button inside .approval-btns container alongside Approve/Deny buttons per reviewer feedback - Restyle from plain text link (.approval-skip-all) to styled button (.approval-btn.yolo) with amber accent matching YOLO pill - Update test to check for new .approval-btn.yolo CSS class - Add ⚡ icon to button for visual consistency
Review Feedback Addressed
🤖 AI-assisted via Hermes Agent |
…lse-fail
The /api/session/yolo endpoint relies on tools.approval.{enable,disable,
is_session}_yolo from hermes-agent. routes.py falls back to no-op lambdas
when the import fails — so without the agent:
* POST returns the input value echoed back ({"yolo_enabled": True}),
making POST-only tests false-pass.
* GET always returns False, breaking POST→GET round-trip tests like
test_yolo_post_persists_within_session, which is exactly the test
that's been failing on every CI run for over a day.
This is the same pattern used by other agent-dependent endpoints
(cron, skills) — mark the endpoint test classes with
@requires_agent_modules so they auto-skip when tools.approval is not
importable, and run normally in environments that have hermes-agent.
The 17 static-analysis tests (slash command registration, HTML elements,
CSS classes, i18n coverage) keep running everywhere — they don't need
the agent.
Local suite: 2564 passed, 7 skipped (the YOLO endpoint cluster) on
agent-less environments. Will run all 24 in CI if hermes-agent is wired,
or cleanly skip the 7 endpoint tests when not.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
nesquena
left a comment
There was a problem hiding this comment.
Review — end-to-end ✅ (approved after agent-skip marker pushed)
What this ships
@bergeouss's implementation of #467 — a YOLO mode toggle for the Web UI. Three entry points: /yolo slash command, "⚡ Skip all this session" button on every approval card, and a YOLO pill in the composer footer. State is server-side per-session, fetched on session load.
Traced against upstream hermes-agent
Pulled fresh nousresearch/hermes-agent tarball.
The agent already has the full plumbing at tools/approval.py:434-466:
enable_session_yolo(session_key)/disable_session_yolo(session_key)— module-level_session_yolo: set[str]lock-protectedis_session_yolo_enabled(session_key)— read pathis_current_session_yolo_enabled()— checked at run_agent.py:780, 905 before the dangerous-pattern check
Critically, the YOLO bypass does NOT defeat the hard floor: at tools/approval.py:131-167 there's a BLOCKED_DESTRUCTIVE set (rm -rf /, dd of=/dev/sda, mkfs.*, etc.) that's checked BEFORE the YOLO bypass. Even with /yolo enabled, those patterns still hit a hard rejection. ✅ Acceptable security model — YOLO skips the per-pattern allow-once / always prompt loop, not the destructive-floor block.
The PR's routes.py correctly imports the three functions from tools.approval (with a graceful no-op fallback when the agent isn't installed) and the POST handler also calls resolve_gateway_approval(sid, "once", resolve_all=True) to clear any pending approval card — so the user isn't stuck if they enable YOLO while a card is up.
What I caught — endpoint tests fail without the agent
CI has been red on every push for over a day with one specific failure:
test_yolo_post_persists_within_session FAILED — assert False is True
Root cause: when tools.approval is not importable (CI environments without hermes-agent), routes.py falls back to no-op lambdas (the existing pattern in this file):
except ImportError:
enable_session_yolo = lambda *a, **k: None
is_session_yolo_enabled = lambda *a, **k: FalsePOST returns {"yolo_enabled": <input>} echoed from the request body — so naive POST-only tests false-pass. But test_yolo_post_persists_within_session does POST→GET, and GET hits the False-returning lambda, so the round-trip catches the agent-less environment.
This is the same pattern other agent-dependent endpoints (cron, skills) handle via the existing @requires_agent_modules marker. The YOLO endpoint cluster needed it too.
What I pushed — 415d7f5
(Pushed to @bergeouss's fork via maintainer-edit.)
Marked TestYoloEndpointGet and TestYoloEndpointPost with @requires_agent_modules. Both class docstrings now explain the agent-dependence and the lambda-fallback false-pass mode that the round-trip test catches. Static-analysis tests (slash-command registration, HTML, CSS, i18n) keep running everywhere — they don't need the agent.
Local results:
- Without agent: 17 passed, 7 skipped (the YOLO endpoint cluster) — clean.
- With agent (production /
nesquena-hermesintegration): all 24 should run.
GitHub Actions hasn't auto-run CI on my fork push yet (likely needs maintainer approval after the previous string of failures). The fix is verified locally. CI will pick up on the next agent push or maintainer rerun.
End-to-end trace
/yolo slash command flow
commands.js:801-820 — cmdYolo():
- GET current state via
/api/session/yolo?session_id=<sid> - Toggle: POST with
{enabled: !current} - Update local
_yoloEnabled, refresh pill, toast message - If enabling, also call
hideApprovalCard(true)to dismiss any pending card
POST endpoint atomicity
if enabled:
enable_session_yolo(sid)
# Also resolve any pending approvals for this session so the
# agent doesn't stay stuck waiting on an already-dismissed card.
try:
from tools.approval import _pending as _p, _lock as _l
with _l:
_p.pop(sid, None)
except Exception:
pass
resolve_gateway_approval(sid, "once", resolve_all=True)
else:
disable_session_yolo(sid)When enabling: sets the YOLO flag, clears the pending-approval queue (so the agent proceeds), and resolves the visible approval card with "once" (immediately approves the current pending action). All three steps acquire the agent's _lock correctly. ✅
Lifecycle (PR docstring)
| Scenario | Behavior | Verified |
|---|---|---|
| Page reload | _fetchYoloState() re-syncs from backend |
sessions.js:178, 256 ✅ |
| Cross-tab | Both tabs poll same server flag | acceptable trade-off ✅ |
| Server restart | In-memory only — flag cleared | safe-by-default ✅ |
| Session switch | loadSession resets _yoloEnabled |
sessions.js:103 ✅ |
| Cross-session | Per-session flag in agent's _session_yolo set |
tools/approval.py ✅ |
_fetchYoloState integration points
Three call sites:
- sessions.js:178 —
loadSessionafterstartApprovalPolling - sessions.js:256 —
loadSessionreattach path - messages.js:128 —
send()after starting approval polling
Covers all three scenarios where a session becomes "active" on the client side. ✅
Edge-case trace
| Scenario | Expected | Actual |
|---|---|---|
| Click "Skip all" on approval card | Enable YOLO + approve current card "once" | ✅ POST handler |
Press /yolo with no active session |
"No active session" toast | ✅ commands.js:806 |
| Toggle YOLO on, then send dangerous command | Skipped per is_current_session_yolo_enabled() |
✅ agent path |
Toggle YOLO on, then send rm -rf / |
Hard-blocked by BLOCKED_DESTRUCTIVE |
✅ agent floor |
| Reload tab while YOLO active | Pill stays visible after _fetchYoloState() |
✅ |
| Two tabs, enable YOLO in tab A | Tab B's pill shows on next _fetchYoloState() (next session activity) |
✅ shared state |
| Server restart | All YOLO flags cleared | ✅ in-memory only |
| Session switch to a session without YOLO | _yoloEnabled=false then refetch true state |
✅ sessions.js:103 |
Tests
- PR's 24 new tests: 17 pass everywhere; 7 endpoint tests skip when agent unavailable (after my push).
- Local full suite: 2564 passed, 7 skipped + the existing agent-dependent skips, 1 PR-unrelated pre-existing failure (
test_sprint3.py::test_workspace_add_rejects_system_paths— the macOS quirk that v0.50.231 fixes; this PR is on top of master from before that landed). - CI: previously failing due to the missing skip marker; my push should turn it green when CI re-runs.
Other audit — confirmed correct
- JS syntax:
node --checkpasses oncommands.js,messages.js,i18n.js,sessions.js. - i18n coverage: PR adds 8 keys to 6 locales (en, ru, es, de, zh, ko) — wait, missing zh-Hant. Let me check... actually verified at i18n.js:3485-3492 — zh-Hant block IS included with full coverage. Total locales: 7 with the YOLO additions. ✅
- No XSS surface: pill and card label use textContent and
data-i18nattribute (translated server-side viaapplyLocaleToDOM). No raw HTML interpolation of user input. auth_errornot introduced here: this is the YOLO PR; auth_error is from a different flow (#1208). No overlap.- The PR's earlier review-cycle fixes (per the commit history) — moved skip-all button into
.approval-btns, restyled as button not link, double-newline locale fix — all already absorbed into the current shape.
Minor observations (non-blocking)
- The cross-tab "shared state" trade-off is correctly documented but worth highlighting in user-visible docs (or a tooltip): a power user might enable YOLO in a private tab and not realise their other open tabs of the same session also get the bypass. The current pill UI gives them visibility ("⚡ YOLO") so they should notice.
- The
_pending.pop(sid, None)direct-import workaround at routes.py:1432 reaches into the agent's private state. Cleaner would be a publicclear_pending(sid)helper intools/approval.py— but that requires an agent-side change. The current approach handles the gateway-resolve case correctly. - The
_updateYoloPillcallsapplyLocaleToDOM()on every state change — slightly wasteful since only the pill'stitleattr changes, but the function is fast and runs only on toggle. Acceptable.
Recommendation
Approved (with agent-skip marker pushed). Clean implementation that wires up #467's pre-existing backend plumbing across three discoverable UI affordances. The lifecycle docstring is accurate and the cross-tab shared-state trade-off is the right call. Hard-floor BLOCKED_DESTRUCTIVE patterns remain enforced, which is the right safety stance — YOLO bypasses the prompt loop, not the destructive block. My pushed @requires_agent_modules markers fix the long-standing CI failure that was blocking this PR. Parked at approval — ready for the release agent's merge/tag pipeline.
|
This PR has been rebased onto master and opened as #1210. The i18n.js conflicts were resolved (both master's new status keys and the PR's yolo keys kept). All 2811 tests pass. @bergeouss — thank you for the contribution, full credit preserved. Will merge #1210 in the next batch release. |
…OLO mode (#1211) fix+feat: batch v0.50.236 — OAuth providers fix, profile switch UX, YOLO mode (#1211) Merges PRs #1208, #1209, #1210 (#1152 rebased): - fix(providers): OAuth provider cards show correct Configured status in Settings. get_providers() was discarding has_key=True from _provider_has_key() for OAuth providers, hiding config.yaml tokens. Also fixed filter excluding all OAuth providers from the Settings panel. Surfaces auth_error string. (closes #1202) - ux(profiles): profile chip shows spinner and new name immediately on switch. Optimistic name update + .switching CSS class + chip disabled + finally cleanup. populateModelDropdown() and loadWorkspaceList() now parallelized via Promise.all. - feat: YOLO mode toggle — skip all approvals per session. /yolo slash command, "Skip all this session" button on approval cards, amber ⚡ pill indicator in composer footer. Session-scoped, in-memory. Full i18n: en, ru, es, de, zh, ko, zh-Hant. (closes #467) Original author: @bergeouss (PR #1152) Tests: 2837 passed (+50 new tests vs previous release) QA harness: 20/20 passed + all browser API checks passed
|
This PR has been merged via integration branch #1210 in v0.50.236. Thank you @bergeouss for the contribution — full credit preserved in the commit and CHANGELOG. |
…OLO mode (nesquena#1211) fix+feat: batch v0.50.236 — OAuth providers fix, profile switch UX, YOLO mode (nesquena#1211) Merges PRs nesquena#1208, nesquena#1209, nesquena#1210 (nesquena#1152 rebased): - fix(providers): OAuth provider cards show correct Configured status in Settings. get_providers() was discarding has_key=True from _provider_has_key() for OAuth providers, hiding config.yaml tokens. Also fixed filter excluding all OAuth providers from the Settings panel. Surfaces auth_error string. (closes nesquena#1202) - ux(profiles): profile chip shows spinner and new name immediately on switch. Optimistic name update + .switching CSS class + chip disabled + finally cleanup. populateModelDropdown() and loadWorkspaceList() now parallelized via Promise.all. - feat: YOLO mode toggle — skip all approvals per session. /yolo slash command, "Skip all this session" button on approval cards, amber ⚡ pill indicator in composer footer. Session-scoped, in-memory. Full i18n: en, ru, es, de, zh, ko, zh-Hant. (closes nesquena#467) Original author: @bergeouss (PR nesquena#1152) Tests: 2837 passed (+50 new tests vs previous release) QA harness: 20/20 passed + all browser API checks passed



Thinking Path
Issue #467 requests a YOLO mode that lets users skip all approval prompts for the current session. The backend already had the plumbing (
enable_session_yolo,disable_session_yolo,is_session_yolo_enabledintools/approval.py) — this PR wires it up on the frontend with three entry points:/yoloslash command — typed in the composer, toggles YOLO on/off for the active sessionYOLO State Lifecycle
The state is session-scoped and stored server-side in memory (not in localStorage or sessionStorage):
_fetchYoloState()on session loadloadSession()clears_yoloEnabledand fetches the new session stateThis is intentional: the server-side approach avoids localStorage sync issues and ensures consistency across page reloads. The cross-tab sharing is an acceptable trade-off since YOLO is a per-session power-user feature that the user explicitly enables.
What Changed
api/routes.py: AddedPOST /api/session/yoloendpoint (toggle enable/disable with pending approval resolution) +yolo_enabledfield inGET /api/session/statusstatic/commands.js: Added/yolocommand entry +cmdYolo()functionstatic/index.html: Added skip-all button on approval card + YOLO pill in composer footerstatic/messages.js: Added YOLO state management (_yoloEnabled,_fetchYoloState(),_updateYoloPill(),toggleYoloFromApproval()) + state fetch afterstartApprovalPollingin sendMessagestatic/sessions.js: Added_fetchYoloState()calls at allstartApprovalPollingcall sites + reset onloadSession()static/style.css: Added.yolo-pill(amber pill) and.approval-skip-all(link-style) CSSstatic/i18n.js: Added 8 YOLO-related keys for all 6 locales (en, ru, es, de, zh-Hant, ko)tests/test_issue467_yolo_mode_toggle.py: 24 tests — endpoint GET/POST, slash command registration, HTML elements, CSS classes, i18n coveragei18n Duplicate Key Verification
All 8 YOLO-specific keys (
cmd_yolo,yolo_no_session,yolo_enabled,yolo_disabled,yolo_pill_label,yolo_pill_title_active,approval_skip_all,approval_skip_all_title) are present exactly once in each of the 6 locales (en, ru, es, de, zh-Hant, ko). Verified via automated grep — no YOLO key duplicates exist.Note: there are pre-existing duplicate keys in
ruandzh-Hantlocale blocks (e.g.,failed,http,https,settings_label_model, etc.) that are unrelated to this PR. These pre-date this change and should be addressed in a separate cleanup PR.Closes #467