Skip to content

feat(plugins): pass approve rule keys to approval gate - #59163

Closed
doncazper wants to merge 1 commit into
NousResearch:mainfrom
doncazper:codex/hermes-plugin-rule-key
Closed

feat(plugins): pass approve rule keys to approval gate#59163
doncazper wants to merge 1 commit into
NousResearch:mainfrom
doncazper:codex/hermes-plugin-rule-key

Conversation

@doncazper

Copy link
Copy Markdown
Contributor

Summary

Restores the pre_tool_call approve escalation lane from #58698 on top of current main after the revert in #59131, and fixes #59067 by preserving plugin-supplied approval rule keys.

Plugins can now return an approve directive with an optional rule_key, for example:

return {
    "action": "approve",
    "message": "Write to ~/.ssh requires approval",
    "rule_key": "write_file:ssh",
}

resolve_pre_tool_block() passes that validated non-empty string through to request_tool_approval(). Missing, empty, whitespace-only, or non-string rule_key values fall back to the prior rule_key=tool_name behavior.

Why

Before this fix, all approve rules for a given tool shared one allowlist key because resolve_pre_tool_block() hardcoded rule_key=tool_name. An [a]lways approval for one write_file rule could therefore auto-approve unrelated write_file rules. This PR lets plugins define per-rule allowlist granularity while keeping backward compatibility.

Fixes #59067.

Validation

  • python3 -m pytest tests/hermes_cli/test_plugins.py -q failed because the system Python lacks pytest.
  • uv run --extra dev pytest tests/hermes_cli/test_plugins.py -q -> 116 passed.
  • uv run --extra dev pytest tests/tools/test_request_tool_approval.py -q -> 13 passed.
  • uv run --extra dev pytest tests/run_agent/test_tool_call_guardrail_runtime.py -q -> 9 passed.
  • uv run --extra dev pytest tests/run_agent/test_run_agent.py::TestConcurrentToolExecution -q -> 39 passed.
  • uv run --extra dev pytest tests/test_model_tools.py -q -> 31 passed.
  • uv run --extra dev ruff check agent/agent_runtime_helpers.py agent/tool_executor.py hermes_cli/plugins.py model_tools.py tests/hermes_cli/test_plugins.py tests/run_agent/test_run_agent.py tests/run_agent/test_tool_call_guardrail_runtime.py tests/tools/test_request_tool_approval.py tools/approval.py -> passed.

@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/tools Tool registry, model_tools, toolsets comp/plugins Plugin system and bundled plugins area/auth Authentication, OAuth, credential pools sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data P3 Low — cosmetic, nice to have labels Jul 5, 2026
@rudidev08

Copy link
Copy Markdown

This is good for CLI. Is there a follow up somewhere for same for desktop and gateways?

@doncazper

Copy link
Copy Markdown
Contributor Author

Good call. I do not see a separate follow-up filed yet.

This PR is intentionally keeping the change at the shared plugin/runtime approval boundary: resolve_pre_tool_block() now carries plugin rule_key through the agent/tool execution paths into request_tool_approval(). So gateway executions that go through those shared runtime dispatch paths should get the same allowlist granularity.

I would keep desktop-specific UX/config parity as a follow-up rather than widen this PR: verify the desktop/gateway approval surfaces display and persist the plugin-provided rule key cleanly, and add coverage around that surface if it has a separate approval path.

@doncazper

Copy link
Copy Markdown
Contributor Author

Follow-up opened for the Desktop/TUI/gateway parity piece: #59298.

It keeps the approval allowlist grain visible across gateway clients by emitting pattern_keys/allowlist_key plus plugin rule_key, then preserving and displaying that metadata in Desktop and TUI approval prompts. It is stacked on this PR until #59163 lands.

@doncazper
doncazper force-pushed the codex/hermes-plugin-rule-key branch from 67f4a04 to 55bf4ac Compare July 6, 2026 19:44
@doncazper

Copy link
Copy Markdown
Contributor Author

Rebased this on current main and re-ran focused plugin/runtime approval verification.

Verified locally:

  • uv run --extra dev pytest tests/hermes_cli/test_plugins.py tests/run_agent/test_run_agent.py tests/run_agent/test_tool_call_guardrail_runtime.py tests/tools/test_request_tool_approval.py -q -> 552 passed
  • uv run --extra dev ruff check agent/agent_runtime_helpers.py agent/tool_executor.py hermes_cli/plugins.py model_tools.py tools/approval.py tests/hermes_cli/test_plugins.py tests/run_agent/test_run_agent.py tests/run_agent/test_tool_call_guardrail_runtime.py tests/tools/test_request_tool_approval.py -> passed
  • python -m py_compile agent/agent_runtime_helpers.py agent/tool_executor.py hermes_cli/plugins.py model_tools.py tools/approval.py -> passed

@teknium1

teknium1 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Merged via #60504 - your commit was cherry-picked onto current main with your authorship preserved, plus a follow-up wiring the gateway notify round-trip into _run_approval_gate so plugin-escalated approvals actually send the interactive prompt on Discord/Telegram/Slack (#59413). Credit also to @kshitijk4poor for the original #58698 design this re-lands. Thanks!

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

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Pass rule_key through from pre_tool_call approve directive to request_tool_approval

4 participants