Per-tool admission control: :allow, :ask or :deny, matched against tool
names with glob patterns — the same model opencode uses for tool safety.
Rules are evaluated in order and the last matching rule wins (so put a
catch-all * first, then more specific rules after it). Anything that matches
no rule falls back to :default (:allow by default).
:allow— run the tool.:deny— never run it; the model receives a "permission denied" tool return so it can adapt.:ask— require human approval. In a one-shotExAgent.run/3you pass an:approvecallback(tool_call -> :approve | :deny); the run calls it (it may block on a PubSub round-trip in a LiveView). With no callback,:askis treated as:deny(fail closed).
Example
perms =
ExAgent.Permissions.new!(
rules: [{"*", :deny}, {"read", :allow}, {"search_*", :allow}, {"bash", :ask}],
default: :deny
)
ExAgent.Permissions.decide(perms, "bash") #=> :ask
ExAgent.Permissions.decide(perms, "search_web") #=> :allow
ExAgent.Permissions.decide(perms, "write") #=> :deny
Summary
Functions
Decide the action for a tool name (last matching rule wins, else default).
Build a permissions set from rules ([{glob_string, action}]) and a
:default action. Globs support * (any run of chars) and ? (single char).
Resolve an :ask decision through an optional approve callback.
Types
Functions
Decide the action for a tool name (last matching rule wins, else default).
Build a permissions set from rules ([{glob_string, action}]) and a
:default action. Globs support * (any run of chars) and ? (single char).
Resolve an :ask decision through an optional approve callback.
:allow→:allow.:deny→:deny.:askwith a callback → calls it withtool_calland maps:approveto:allow, anything else to:deny.:askwithout a callback →:deny(fail closed).