13. Tool permissions and approval
AVL Code applies fine-grained policy to every tool call.
13.1 Three-state permission
Each tool:
- ✓ Allow: pass through.
- ◇ Ask: pop an approval modal on every call.
- ✗ Deny: reject directly.
Configurable per work mode; common patterns: enable write tools under execute, disable all writes under plan. The three states apply to sub-agents as well (a denied tool is unavailable on the sub-agent side too), and leftover tool config is cleaned up automatically after a plugin / external service is uninstalled.
13.2 Approve before use
When a tool marked "Ask" is about to run:
- The desktop pops an approval dialog showing the tool name, parameters and impact.
- You pick:
- ✓ Allow (once)
- ✓ Always allow: adds tool+parameter pattern to the allow list.
- ✗ Deny
- The dialog shows a 30-second countdown; no response by the end counts as Deny.
13.2.1 Unrestricted mode (advanced)
To the right of the mode slider in the input area is a shield switch, for advanced users who know what they're doing. Turning it on goes through a confirmation dialog (with a 5-second cooling-off — the confirm button starts disabled and counts down "Please read the risk note 5s…"; it can't be clicked until it reaches 0, and Enter also doesn't activate it). Once on, a red banner stays put as a reminder; toggling off takes effect immediately.
Scope: per-session, in-memory only; cleared on restart.
On removes three frictions:
| Friction | When unrestricted |
|---|---|
| Tool approval | Skip the approval modal (no per-call ask / deny / defer); tools execute directly |
| Command allow-list | fs.exec is no longer restricted by the command allow-list |
| Network filter | web.fetch / web.bing no longer filter internal / loopback / link-local addresses |
Hard guards still apply (unaffected by the switch):
samples/sandbox isolation.rm -rfargument validation.- The injection-denylist (fork bombs / writing
/etc/curl|sh/eval). - The
http(s)protocol allow-list.
Slash-command shortcut — type directly in the input box:
/unrestricted(or Chinese alias/无限) — toggle the current session's unrestricted mode. Turning on still goes through the 5-second risk-confirmation dialog; turning off is immediate. The Chinese alias is hidden from the command palette and/help(avoids a duplicate row) but still executes when typed.
Message marking for the no-guardrails span: the consecutive messages produced while unrestricted mode is on are enclosed as a whole by a light border — start and end at a glance, so a later review shows immediately which content was generated without guardrails. Toggling off closes the border on the spot; while the mode is still on, no premature "end" boundary is drawn. Sub-agents can recursively inherit the mode on demand, and messages produced under inheritance carry the same marking.
13.3 Always-allow granularity
| Granularity | Coverage | When to use |
|---|---|---|
| Once only | Single call | Cautious evaluation |
| Same tool + same args | One specific call shape | Recommended default |
| Same tool + any args | Any call to this tool | Internal scripts |
| Entire workspace | Every tool in this workspace | Isolated environments only |
13.4 Multi-device approval
The same approval request shows up on the desktop dialog and every bound remote channel (e.g. WeChat); the first response wins, and later replies from other devices are reported as already answered. A commonly-confused rule: the deny > defer > ask > allow priority order belongs to the hook layer — it merges permission decisions returned by multiple hooks within one evaluation, and has nothing to do with multi-device responses.
13.5 Remote approval
If WeChat is bound, every "Ask" approval mirrors to the bound chat. Reply with:
| Command | Equivalent desktop action |
|---|---|
/approve <id> |
✓ Allow once |
/always <id> |
✓ Always allow |
/deny <id> |
✗ Deny |
| No reply | Times out as deny when the 30 s countdown ends |
13.6 Persistence
Approval decisions are not stored in a separate audit log: only "Always allow" is persisted — written to the workspace-level authorization file hook-approvals.yaml (authorization fingerprint + decision time), viewable and revocable under Settings → Hooks. "Allow once" and "Deny" affect only the current call and leave no record.
