Permissions, Execution Modes, & Workspace Confinement Plugin (SuperPi.Extension behaviour).
Enforces execution modes and workspace boundaries:
dangerouslySkipPermissions: true: Auto-approves all tool executions without prompting.executionMode: "plan": Allows read-only inspection tools (read,grep,find,ls,tool_search), tools whose only effect isread, and network reads (see Tool effects). Blocks everything else (write,edit,bash, state changes).executionMode: "accept-edits": Allowsread,write,edit,grep,find,lsinside trusted workspaces and checkspermissions.allow(command(...)entries insettings.json) forbashcommands.allowNonWorkspaceAccess: Defaults tofalse. File tools may only touchworkspacePathsandtrustedWorkspaces. Set it totrueto lift the confinement.
Tool effects
A tool declares what a call does (SuperPi.Tools.effects/2): read, fs_write, exec,
net_read, net_write, state_write. The decision is a pure function of effects, mode and policy
(decide/4):
| Effects | plan | accept-edits | auto |
|---|---|---|---|
only read | allow | allow | allow |
net_read, with read | allow unless network_in_plan_mode: false | allow | allow |
state_write, with read and net_read | block | allow | allow |
fs_write, exec, net_write, or no declared effects | block | block (the write and edit tools and allowed shell commands excepted) | allow |
Network policy keys, orthogonal to the mode: network: "allow" | "allowlist" | "deny" (default
"allow") and network_in_plan_mode (default true). deny refuses every tool with a network
effect. allowlist allows a call whose url argument has a host in networkAllowHosts (an entry
matches the host and its subdomains) and refuses a call without one.
A harness can set maxExecutionMode in pi.agent. A mode above it is lowered to it (clamp_mode/1).
Policy
Policy is data. It merges, lowest priority first, from:
config :super_pi, :permissions, ...(keys such asallowNonWorkspaceAccess,permissions)<app data dir>/settings.json- the session's
settingsOverride
permissions.deny and permissions.allow hold command(...) patterns. A deny match blocks
a shell command in every mode. allow opens commands in accept-edits mode.
Modes for remote callers
A Gateway, RPC or MCP caller can request a mode. remote_mode/1 limits the request:
config :super_pi, :permissions,
remote_default_mode: "accept-edits",
remote_max_mode: "accept-edits"A request may select any mode up to remote_max_mode (plan < accept-edits < auto).
The ceiling defaults to the default mode, so a request can only tighten the mode unless
config raises the ceiling. sanitize_remote_agent/1 also removes keys that would let a
remote caller lift the policy (dangerouslySkipPermissions, settingsOverride, paths).
Summary
Functions
Sets executionMode in agent_cfg for a remote caller.
Lowers agent_cfg["executionMode"] to the harness ceiling agent_cfg["maxExecutionMode"].
A config without a ceiling, or with a mode at or below it, is returned as it is.
Checks whether a shell command matches any command(...) pattern in settings.json's permissions.allow list.
Matches exact commands (command(git status)) or command prefixes (command(head) matching head -n 20 file.txt).
The decision for one call, as a pure function of the tool, its arguments, the agent config and
the effective settings. before_tool/3 loads the last two and decides through evaluate/4.
Like decide/4, with a third answer: {:prompt, reason, risk} for a block that a human may lift.
Exactly two blocks are promptable: a shell command that is not in permissions.allow, and a tool
that accept-edits does not allow by default (every MCP tool among them). A permissions.deny
match, a mode block, a workspace boundary and a network policy block are never promptable.
The effective network policy: "allow" (default), "allowlist" or "deny".
Rank of a mode: plan 0, accept-edits 1, auto 2. An unknown mode ranks as plan, the strictest.
Returns the mode that a remote caller gets for a request.
Removes the keys from a client-supplied agent map that would lift the permission policy,
and limits executionMode with remote_mode/1.
True when path is root or inside it, after following symlinks component by component.
Both must be absolute. A link inside root that points outside it is not inside.
Functions
Sets executionMode in agent_cfg for a remote caller.
With requested of nil, the harness default is limited to the ceiling. A harness default
that is not a permission mode (for example "interactive") is left as it is.
Lowers agent_cfg["executionMode"] to the harness ceiling agent_cfg["maxExecutionMode"].
A config without a ceiling, or with a mode at or below it, is returned as it is.
Checks whether a shell command matches any command(...) pattern in settings.json's permissions.allow list.
Matches exact commands (command(git status)) or command prefixes (command(head) matching head -n 20 file.txt).
The decision for one call, as a pure function of the tool, its arguments, the agent config and
the effective settings. before_tool/3 loads the last two and decides through evaluate/4.
The decision depends on the tool's effects (SuperPi.Tools.effects/2), the mode and the policy,
and it never loosens as the mode rises (plan < accept-edits < auto).
@spec evaluate(String.t(), map(), map(), map()) :: :allow | {:block, String.t()} | {:prompt, String.t(), String.t()}
Like decide/4, with a third answer: {:prompt, reason, risk} for a block that a human may lift.
Exactly two blocks are promptable: a shell command that is not in permissions.allow, and a tool
that accept-edits does not allow by default (every MCP tool among them). A permissions.deny
match, a mode block, a workspace boundary and a network policy block are never promptable.
The effective network policy: "allow" (default), "allowlist" or "deny".
@spec rank_of(term()) :: 0..2
Rank of a mode: plan 0, accept-edits 1, auto 2. An unknown mode ranks as plan, the strictest.
Returns the mode that a remote caller gets for a request.
requested is the mode that the caller asked for, or nil. An unknown or missing value gives
remote_default_mode. A value above remote_max_mode is lowered to it.
Removes the keys from a client-supplied agent map that would lift the permission policy,
and limits executionMode with remote_mode/1.
True when path is root or inside it, after following symlinks component by component.
Both must be absolute. A link inside root that points outside it is not inside.