Releases: clawnify/agent-permissions
v0.4.0 — operator-scoped allow-always
What's new
When the user clicks 'always' on an approval prompt, the persisted rule is now the pattern of the rule that triggered the ask (not the exact call). Operators control the breadth via their ask rule patterns; one click grants the breadth the operator already declared.
Each prompt description names the rule that will be persisted:
Run clawnify_action (GMAIL_SEND_EMAIL)?
Matched: rule 'clawnify_action(*_SEND*)' from config settings
'Always' will allow: `clawnify_action(*_SEND*)`
The dangerousPatterns check now runs against the rule that would be persisted, so allow-always is refused for rules with dangerous prefixes regardless of which specific call triggered the prompt.
Falls back to the exact call content when no rule matched (only happens under strict mode where everything asks by default).
Forward-compat note
This is the v0.4.0 bridge while OpenClaw's requireApproval protocol only supports a fixed allow-once | allow-always | deny enum. A v0.5.0 release will add a sideband-encoded scope ladder for Clawnify-style dashboards that want to render multi-button 'Always for X / Always for Y / ...' prompts (the Claude Code pattern). When OpenClaw adds allowedDecisions upstream, the sideband becomes redundant and the matched-rule semantic from this release stays as the default button.
Tests
54/54 passing.
— Initiated and maintained by the Clawnify team.
v0.3.0 — paramKeys config
What's new
Operator-configurable per-tool content extraction via the new paramKeys config.
"agent-permissions": {
"config": {
"paramKeys": {
"clawnify_action": "slug",
"clawnify_call_app_api": "method"
},
"ask": ["clawnify_action(*_DELETE*)", "clawnify_action(*_SEND*)"],
"allow": ["clawnify_action(GMAIL_EMAIL_LIST)"]
}
}When a tool call fires, the engine reads params[paramKeys[toolName]] as the rule content. Wildcard rules like clawnify_action(GMAIL_EMAIL_*) now match without consumer plugins needing to register resolvers. Zero coupling — entirely operator-driven.
Falls back to existing behavior (shell-command extraction for bash/exec, tool-wide matching otherwise) when no paramKeys entry exists.
Tests
54/54 still passing.
— Initiated and maintained by the Clawnify team.
v0.2.0 — resolver-less gating + operator-opt-in default
Breaking semantic changes
Resolverless mode is now the primary path
The before_tool_call hook no longer bails when no resolver is registered for a tool. It falls back to a generic GateRequest:
- For
bash/exectools: pullsparams.commandintoruleContentso wildcard rules likeBash(curl *)match the actual shell command - For all other tools: tool-wide matching (only
Tool/Tool(*)rules apply) with a JSON-preview prompt
This means agent-permissions can now gate ANY tool by name from openclaw.json rules, with no consumer-plugin awareness required. No inter-plugin coupling. Each plugin ships independently.
Consumer plugins can still call registerResolver({ toolName, resolve }) for tool-specific richer prompts (titles/descriptions). Strictly opt-in.
Default mode 'default' now means operator-opt-in
| Mode | Behavior |
|---|---|
default (new) |
Operator-opt-in: tools pass through unless an ask or deny rule explicitly matches |
strict (NEW) |
Previous default behavior — ask on anything not explicitly allowed (Claude-Code style) |
bypassPermissions / dontAsk |
Allow everything except matching deny rules |
acceptEdits |
Currently same as default. Reserved for future tool-category-aware behavior. |
Why: the previous design required every consumer plugin to call registerResolver to participate. That's inter-plugin coupling that scales badly — every Clawnify plugin plus any third-party plugin would need to know about us. The new resolverless path inverts that.
Migration
If you were previously calling registerResolver for each tool you wanted gated, you can:
- Keep doing that — resolvers still work, give you richer prompts
- OR drop the calls — add rules to
openclaw.jsontargeting the tool names directly. Generic prompts will be used.
If you relied on default mode asking on every tool call, switch to strict mode explicitly.
Tests
54/54 passing.
— Initiated and maintained by the Clawnify team.
v0.1.0 — initial publish
Initial release of @clawnify/agent-permissions.
OpenClaw plugin — permission and approval engine for any OpenClaw agent.
What it does
- Gates built-in tool calls (bash, file edit, etc.) and any plugin-registered tool
- Three-bucket policy model:
allow/deny/ask - Rule sources walked in priority order: session → workspace → user → config
- In-chat approval via OpenClaw's native
requireApprovalmechanism - Learning into
allow-alwaysrules persisted to a chosen destination - Wildcard rule format (
Tool(foo)exact,Tool(foo:*)legacy prefix,Tool(foo *)new wildcard) - Dangerous-pattern denylist (
python:*,node:*,eval, …) that cannot be allow-always-persisted - Fail-closed: any internal error returns
{ block: true }(OpenClaw's hook runner is fail-open by default)
Install
openclaw plugins install @clawnify/agent-permissions --pinThen in openclaw.json:
{
"plugins": {
"allow": ["agent-permissions", "your-consumer-plugin"],
"entries": {
"agent-permissions": {
"enabled": true,
"config": {
"defaultMode": "default",
"ask": ["Bash(*)"]
}
}
}
}
}See README for full inter-plugin API + rule format.
— Initiated and maintained by the Clawnify team.