Permissions
How Stella's per-tool permission model separates read-only tools from mutating and shell tools, and how PreToolUse hooks can gate or block tool calls.
Every tool the agent can call — built-in, custom, or provided by an MCP server — has a per-tool permission model. This is what lets Stella treat a harmless file read differently from a shell command that can change your system.
Read-only vs. mutating tools
Tools fall into two categories:
- Read-only tools only observe your workspace and environment. They never change files, run commands, or perform side effects. These tools are marked
read_only: true. The authoritative read-only set in the built-in catalog isread_file,grep,glob,gather_context,explorations,ci_status,repo_status,task_list, the read-only issue tools (search_issues,get_issue,list_labels,list_members), the read-only web tools (web_search,web_fetch,web_extract_assets) when the web opt-in is on, and — oncestella inithas built the code graph —graph_query. Read-only tools are also the only ones eligible for speculative execution during the model stream. - Mutating and shell tools can change files, execute commands, or perform other side effects. Examples include
write_file,edit_file,delete_file,bash,build_project,run_tests,save_memory,screenshot,web_download, andverify_done.
The read_only: true marker is the signal that a tool is safe to run without changing anything. Tools without it should be treated as capable of side effects — note that screenshot and verify_done are mutating even though they only write into .stella/ or shell out to a test command.
Read-only vs. mutating is a property of the tool, not of a specific invocation. Some friendly names are actually families whose members split across the boundary — within the issue tools, search_issues / get_issue / list_labels / list_members are read-only while create_issue / update_issue / close_issue / start_work_on_issue are mutating. The same split runs through the repo family (repo_status is read-only; repo_commit / repo_push / repo_pull / repo_rollback are mutating) and the task board (task_list is read-only; the other five task_* tools are mutating).
Gating tool calls with hooks
The permission model is enforced ahead of each tool call, and it can be extended with lifecycle hooks. In particular, a PreToolUse hook runs before a tool executes and can block it: if the hook exits with a non-zero status, the tool does not run and the model receives the hook's message instead.
This gives you a programmable gate in front of any tool. A PreToolUse hook's matcher is a glob over the tool name, so you can target a single tool (for example, bash) or a family of tools.
For blocking by pattern without a hook script, Stella also has native workspace-rule guards enforced at the tool boundary: guard-tool (deny a tool), guard-deny-command (a glob over the bash command), and guard-deny-path (a glob over a file tool's path). A rule such as guard-deny-command: "git push --force*" is hard-enforced per invocation.
{
"hooks": {
"PreToolUse": [
{
"matcher": "bash",
"hooks": [
{
"type": "command",
"command": "./scripts/guard.sh",
"timeoutMs": 5000
}
]
}
]
}
}For the full hook model — events, matchers, timeouts, and the JSON payload delivered on stdin — see Hooks.
Hooks
Run shell commands on Stella's agent lifecycle events — SessionStart, PreToolUse, and PostToolUse — configured under the hooks key in settings.json.
Telemetry & budget
Local-only telemetry in a SQLite store on your disk — tokens, cost, steps, tool calls, files, memory, reflections — and how --budget turns metering into a hard spend cap.