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
- A working awaken agent runtime (see First Agent)
- Feature
permissionenabled on theawakencrate (enabled by default)
[dependencies]
awaken = { package = "awaken-agent", version = "0.1", features = ["permission"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
Steps
- Register the permission plugin.
use std::sync::Arc;
use awaken::engine::GenaiExecutor;
use awaken::ext_permission::PermissionPlugin;
use awaken::registry_spec::{AgentSpec, ModelSpec};
use awaken::{AgentRuntimeBuilder, Plugin};
let agent_spec = AgentSpec::new("my-agent")
.with_model("gpt-4o-mini")
.with_system_prompt("You are a helpful assistant.")
.with_hook_filter("permission");
let runtime = AgentRuntimeBuilder::new()
.with_provider("openai", Arc::new(GenaiExecutor::new()))
.with_model(
"gpt-4o-mini",
ModelSpec {
id: "gpt-4o-mini".into(),
provider: "openai".into(),
model: "gpt-4o-mini".into(),
},
)
.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 BeforeToolExecute phase hook that evaluates permission rules before every tool call.
- 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: ask
rules:
- tool: "read_file"
behavior: allow
- tool: "Bash(npm *)"
behavior: allow
- tool: "file_*"
behavior: ask
- tool: "delete_*"
behavior: deny
- tool: "mcp__*"
behavior: ask
Load 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;
let agent_spec = AgentSpec::new("my-agent")
.with_model("gpt-4o-mini")
.with_system_prompt("You are a helpful assistant.")
.with_hook_filter("permission");
-
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
- Register a tool that matches a
denyrule and attempt to invoke it. - The tool call should be blocked and a
ToolInterceptActionevent emitted. - 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
| 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 registered | Register PermissionPlugin and add "permission" to plugin_ids in agent spec |
| 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
crates/awaken-ext-permission/tests/
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 | PermissionInterceptHook (BeforeToolExecute) |
crates/awaken-tool-pattern/ | Shared glob/regex pattern matching library |