Skip to content

Comments

feat: Replace terminal dangerous-command block with HITL approval and add Safe Mode in Settings#1310

Open
spider-yamet wants to merge 26 commits intoeigent-ai:mainfrom
spider-yamet:feat/replace-strict-command-prohibition
Open

feat: Replace terminal dangerous-command block with HITL approval and add Safe Mode in Settings#1310
spider-yamet wants to merge 26 commits intoeigent-ai:mainfrom
spider-yamet:feat/replace-strict-command-prohibition

Conversation

@spider-yamet
Copy link
Contributor

Related issue

Closes #1306

Summary

Replaces the Terminal Toolkit’s hard block of “dangerous” commands with a Human-in-the-Loop (HITL) approval flow and adds a Safe Mode setting so users can opt in.

Changes

HITL for dangerous commands

  • When a dangerous command is detected and Safe Mode is on, the user is offered three choices:
    • Yes – approve this command once
    • All Yes in this task – approve all subsequent dangerous commands in the current task
    • No – reject the command
  • Frontend shows the prompt and calls /chat/{id}/terminal-approval with the chosen option; backend enforces approval before running the command and supports “approve all in task” per task.

Safe Mode in Settings

  • Settings → Permissions: new Safe Mode toggle with hint:
    “With Safe Mode active, Eigent will pause and seek explicit approval whenever high-risk system operations are detected.”
  • Default: off. When on, the HITL flow above is used for the configured dangerous-command list.

Backend

  • Dangerous command list (triggers HITL when Safe Mode is on): system (e.g. sudo, su, reboot, shutdown), file (e.g. rm, chown, mount), disk (e.g. dd, mkfs, fdisk), process (e.g. service, systemctl), network (e.g. iptables, ifconfig), cron (e.g. crontab, at), user/kernel (e.g. useradd, modprobe), and related commands as specified.
  • Non-Docker mode: cd is validated so the agent cannot leave the designated working_directory.
  • Task lock includes approved_all_dangerous_commands and a queue for terminal approval; reset on new task.

Frontend

  • Permissions tab and Safe Mode UI; preference stored in localStorage and sent as safe_mode in the start-task request.

Other

  • Pre-commit: skip backend checks when uv is not installed so commits succeed without uv (message suggests installing uv for backend checks).

Testing

  • Verified Safe Mode off: dangerous commands run without approval.
  • Verified Safe Mode on: dangerous commands trigger the three-option prompt; Yes / All Yes in task / No behave as described.
  • Verified Permissions UI: toggle and hint render; state persists and is sent to backend.
  • Verified cd outside working_directory in non-Docker mode is rejected.

@spider-yamet
Copy link
Contributor Author

@bytecii @Wendong-Fan Could you please review my PR?

Copy link
Collaborator

@bytecii bytecii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we can move this to camel to make a special safe mode such as strict. cc @Wendong-Fan

@Wendong-Fan
Copy link
Contributor

IMO we can move this to camel to make a special safe mode such as strict. cc @Wendong-Fan

thanks @bytecii , the idea behind this issue is to implement a human-in-the-loop mechanism for safer code execution, which would involve the HumanToolkit. Since CAMEL serves more as a modular framework, it might be more appropriate to implement this feature in Eigent as an application layer. What do you think?

@Wendong-Fan Wendong-Fan requested review from 4pmtong and fengju0213 and removed request for 4pmtong February 21, 2026 15:37
@Wendong-Fan Wendong-Fan added this to the Sprint 16 milestone Feb 21, 2026
@spider-yamet
Copy link
Contributor Author

spider-yamet commented Feb 22, 2026

@Wendong-Fan @bytecii could you please review this pr?
I see this PR remains opening for several days.

Regards

bytecii and others added 8 commits February 23, 2026 00:09
- Move HitlOptions and ApprovalRequest models from chat.py to app/hitl/__init__.py
- Remove unnecessary hasattr guard on auto_approve (always initialized)
- Remove issue reference comment and docstring comments
- Update all imports across controllers, services, and tests
- Move _request_user_approval from AbstractToolkit to TerminalToolkit
  (only terminal uses approval today)
- Remove debug print statements from shell_exec
- Move import time to top-level in terminal_toolkit
- Fix approval endpoint docstring
- Remove module docstring from terminal_command.py
- Add comment for effective executable check
- Update test to subclass TerminalToolkit directly
options.project_id,
Agents.browser_agent,
working_directory=working_directory,
safe_mode=True,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this as this is not configured. And by default if the HITL not specified, we still use the safe_mode=True for the terminaltoolkit

return None
return "Operation rejected by user. The task is being stopped."

async def shell_exec(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to async

- Remove unused logger from abstract_toolkit
- Move _thread_local from AbstractToolkit to TerminalToolkit
- Add missing threading.local() definition
- Add Args/Returns to terminal_command.py functions
- Use ApprovalAction.reject enum instead of raw string
- Fix ApprovalAction and ActionCommandApprovalData docstrings
- Add comment for auto_approve reset at task start
# the HITL approval flow (Camel's upstream shell_exec is
# sync-only with no async variant available).
if wrap is not func:
async_wrapper.__wrapped__ = func
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need someone helps to take a look at this

bytecii and others added 2 commits February 23, 2026 19:26
_get_terminal_approval() now reads from task_lock every call instead of
caching in __init__, so toggling the setting between tasks takes effect
immediately. SupplementChat carries hitl_options to the improve endpoint,
and the localStorage key is extracted to a shared constant.
@bytecii
Copy link
Collaborator

bytecii commented Feb 24, 2026

Update the PR including backend and ui redesign
In general, making the HITL as an option in the settings and make it extensible for future possible approval

image image
  • Added a _request_user_approval() method on AbstractToolkit that any toolkit can call to pause execution and wait for user approval via SSE.
  • Now only support the TerminalToolkit uses it to gate dangerous commands (rm, sudo, kill, etc.) behind a frontend toggle in the new HITL settings page, with per-agent approval queues so multi-agent tasks don't block each other.
  • The auto approval is defined for each task per agent and each task will fetch the terminal toolkit hitl settings to check whether need to let user input the approval for potential dangerous terminal command

tested with and without terminal approval in the HITL + approval / auto approval / reject with & without dark mode
cc @Wendong-Fan @fengju0213

spider-yamet and others added 4 commits February 24, 2026 13:08
…L approvals

- Replace approval_input Queue with pending_approvals dict of asyncio.Future
- Each _request_user_approval call gets a unique approval_id (agent_hex),
  so concurrent commands from the same agent no longer compete for one slot
- Fix str,Enum f-string bug: use concatenation so Agents.developer_agent
  produces "developer_agent_..." not "Agents.developer_agent_..."
- Frontend: replace activeApproval single slot with approvalQueue[] array;
  pushApproval/shiftApproval/clearAllApprovals manage the queue
- Controller routes approve_once by approval_id, auto_approve/reject by agent
- Add design comments to resolve_approval and resolve_all_approvals_for_agent
- Update all tests; add concurrent same-agent and Enum regression tests
async def _request_user_approval(self, action_data) -> str | None:
"""Send a command approval request to the frontend and wait.

Flow::
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is the flow

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Replace strict command prohibition in Terminal Toolkit with structured Human-in-the-Loop verification

3 participants