Enable Tool Permission HITL
Use this when you need to control which tools an agent can invoke, with human-in-the-loop approval for sensitive operations.
Prerequisites
Section titled “Prerequisites”- A working awaken agent runtime (see First Agent)
- Feature
permissionenabled on theawakencrate (enabled by default)
[dependencies]awaken = { git = "https://github.com/AwakenWorks/awaken", features = ["permission"] }tokio = { version = "1", features = ["full"] }serde_json = "1"- Register the permission plugin.
use std::sync::Arc;use awaken::engine::GenaiExecutor;use awaken::ext_permission::PermissionPlugin;use awaken::registry_spec::ModelSpec;use awaken::registry_spec::AgentSpec;use awaken::{AgentRuntimeBuilder, Plugin};
let mut agent_spec = AgentSpec::new("my-agent") .with_model_id("gpt-4o-mini") .with_system_prompt("You are a helpful assistant.") .with_hook_filter("permission");agent_spec.plugin_ids.push("permission".into());
let runtime = AgentRuntimeBuilder::new() .with_provider("openai", Arc::new(GenaiExecutor::new())) .with_model(ModelSpec::new("gpt-4o-mini", "openai", "gpt-4o-mini")) .with_agent_spec(agent_spec) .with_plugin("permission", Arc::new(PermissionPlugin) as Arc<dyn Plugin>) .build() .expect("failed to build runtime");The plugin registers a ToolGateHook that evaluates permission rules before every tool call.
plugin_ids loads the plugin; with_hook_filter("permission") keeps its hooks
active when the agent loads multiple plugins.
- Define permission rules inline.
use awaken::ext_permission::{PermissionRulesConfig, PermissionRuleEntry, ToolPermissionBehavior};
let config = PermissionRulesConfig { default_behavior: ToolPermissionBehavior::Ask, rules: vec![ PermissionRuleEntry { tool: "read_file".into(), behavior: ToolPermissionBehavior::Allow, scope: Default::default(), }, PermissionRuleEntry { tool: "file_*".into(), behavior: ToolPermissionBehavior::Ask, scope: Default::default(), }, PermissionRuleEntry { tool: "delete_*".into(), behavior: ToolPermissionBehavior::Deny, scope: Default::default(), }, ],};
let ruleset = config.into_ruleset().expect("invalid rules");-
Load rules from a YAML file (alternative).
Create
permissions.yaml:
default_behavior: askrules: - tool: "read_file" behavior: allow - tool: "Bash(npm *)" behavior: allow - tool: "file_*" behavior: ask - tool: "delete_*" behavior: deny - tool: "mcp__*" behavior: askLoad it in code:
use awaken::ext_permission::PermissionRulesConfig;
let config = PermissionRulesConfig::from_file("permissions.yaml") .expect("failed to load permissions");let ruleset = config.into_ruleset().expect("invalid rules");-
Configure via agent spec.
Permission rules can also be embedded in the agent spec using the
permissionplugin config key:
use awaken::registry_spec::AgentSpec;use awaken::ext_permission::{ PermissionConfigKey, PermissionRulesConfig, PermissionRuleEntry, ToolPermissionBehavior,};
let mut agent_spec = AgentSpec::new("my-agent") .with_model_id("gpt-4o-mini") .with_system_prompt("You are a helpful assistant.") .with_hook_filter("permission");agent_spec.plugin_ids.push("permission".into());
agent_spec.set_config::<PermissionConfigKey>(PermissionRulesConfig { default_behavior: ToolPermissionBehavior::Ask, rules: vec![ PermissionRuleEntry { tool: "read_file".into(), behavior: ToolPermissionBehavior::Allow, scope: Default::default(), }, ],})?;-
Understand rule evaluation.
Rules are evaluated with firewall-like priority:
-
Deny — highest priority, blocks the tool call immediately
-
Allow — permits the tool call without user interaction
-
Ask — suspends the tool call and waits for human approval
The pattern syntax supports:
| Pattern | Matches |
|---|---|
read_file | Exact tool name |
file_* | Glob on tool name |
mcp__github__* | Glob for MCP-prefixed tools |
Bash(npm *) | Tool name with primary argument glob |
Edit(file_path ~ "src/**") | Named field glob |
Bash(command =~ "(?i)rm") | Named field regex |
/mcp__(gh|gl)__.*/ | Regex tool name |
Verify
Section titled “Verify”- Register a tool that matches a
denyrule and attempt to invoke it. - The tool call should be blocked before execution, with the run terminating for deny rules.
- Register a tool matching an
askrule. The run should suspend, waiting for human approval via the mailbox. - Send approval through the mailbox endpoint and confirm the run resumes.
Common Errors
Section titled “Common Errors”| Symptom | Cause | Fix |
|---|---|---|
| All tools blocked | default_behavior: deny with no allow rules | Add explicit allow rules for safe tools |
| Rules not evaluated | Plugin not loaded or hook filtered out | Register PermissionPlugin, add "permission" to plugin_ids, and include with_hook_filter("permission") when using hook filters |
| Invalid pattern error | Malformed glob or regex | Check syntax against the pattern table above |
| Ask rule never resolves | No mailbox consumer | Wire up a frontend or API client to respond to mailbox items |
Related Example
Section titled “Related Example”crates/awaken-ext-permission/tests/
Key Files
Section titled “Key Files”| Path | Purpose |
|---|---|
crates/awaken-ext-permission/src/lib.rs | Module root and public re-exports |
crates/awaken-ext-permission/src/config.rs | PermissionRulesConfig and YAML/JSON loading |
crates/awaken-ext-permission/src/rules.rs | Pattern syntax, ToolPermissionBehavior, rule evaluation |
crates/awaken-ext-permission/src/plugin/plugin.rs | PermissionPlugin registration |
crates/awaken-ext-permission/src/plugin/checker.rs | PermissionToolGateHook (ToolGate) |
crates/awaken-tool-pattern/ | Shared glob/regex pattern matching library |