Docs / Developer mode
Editing and approvals
Every call that changes something stops at one dialog first. This page is that dialog in full, with the permission modes, how an unattended run may edit, and the cases where ivralo asks even when you said not to.
What asks and what does not
A call that only reads never asks: reading a file, searching, listing, git's reads, loading a skill, delegating to a specialist. These stop at the dialog and wait for you:
- an edit or a new file (
edit_file,write_file, a rename through the language server, the symbol edits); run_python, the Python runtime;- a shell command the policy did not clear on its own (Shell commands);
- a git write (staging, a commit, a branch, a revert) and the one push (Git);
- a send, such as an e-mail or a chat message.
The run is paused underneath. Nothing the dialog shows is cut: an edit is shown as a diff of what is being replaced, a new file in full, a command highlighted the way the shell that runs it reads it. PageUp, PageDown and the arrow keys scroll it, and the answers stay on screen at any terminal size.
The dialog
The title says what ivralo wants to do, who asks, and the shape of the change (new file, 90 lines, overwrites, +12 −3). Under it are the call's facts:
| Row | What it says |
|---|---|
| effects | What the call does, read off the tool's contract (writes files, runs a command) |
| risk | Low, medium or high, and what raised it |
| reason | Why it asks although edits are accepted (a test the request did not name) |
| where | For a coder in a worktree: which worktree the edit lands in |
| runs | For a verification: the declared commands it would run |
<agent> says | The asking agent's own words before the call, under its name, so they never read as ivralo's |
Then the answers. Only the ones that apply to this call are shown:
| Key | Answer | Offered on |
|---|---|---|
y | This call | every call |
| Enter | This call | every call except a high-risk one |
a | This call and everything else this turn wants to run | every call that does not always ask |
s | This edit and every edit for the rest of the session | edits |
p | Run it and always allow its prefix, or the host it reaches, in this repository | shell commands and git writes, in a trusted folder |
n, Esc | Deny, with a note for the model | every call |
d | Open the change in the diff viewer, read-only; q comes back | edits |
a dies with the turn. In a plan's wave it covers only that step's run, so a step running beside it still asks.
n does not dismiss at once. It opens a line where you say what to do instead, such as "keep the old return value, add a test instead", and Enter sends it to the model with the refusal. A second Esc refuses without a note.
Some calls always ask. For a tool whose contract says it always asks (a send, the push), the broader answers are not disabled: they are absent, so no key can lift that call.
High risk. On a high-risk call (a send, a change somewhere else such as GitHub through gh, or a tool no contract covers) Enter is not bound at all and the focus sits on deny. It takes one deliberate y.
Permission modes
| Mode | How to turn it on | What happens |
|---|---|---|
| Normal | the default | Every call above asks |
| Accept edits | s in an edit's dialog, or /accept-edits | Edits land without a dialog for the rest of the session; shell commands, Python and sends still ask |
| Auto-approve | /confirm | No call asks, except those that always ask |
/accept-edits toggles accept edits on and off. /confirm toggles auto-approve; turning it off again turns accept edits off too, so all the asking comes back at once.
A parked job's question opens in the same dialog with three answers only: y, s for every edit of the rest of that job, and n. There is no turn for a to cover and no prefix for p to store.
Unattended runs and allow_tools
A run nobody is watching, such as ivralo ask in a pipe, a schedule or a job, has nobody to show a dialog to. Its Python runs inside the worker's limits (The Python worker), but an edit, a git write and a shell command that would ask are refused instead of run unseen, and so is a send.
The one way to let an unattended run edit is allow_tools in a permissions.json, the home's or the repository's .ivralo/permissions.json:
{
"allow_tools": ["edit_file", "write_file"]
}
| Name | Lets an unattended run |
|---|---|
edit_file | Edit files, including the six symbol edits, which land through the same write |
write_file | Create files, or replace one whole; never lifted by edit_file |
replace_symbol, replace_range, insert_before_symbol, insert_after_symbol, delete_symbol, add_import | Each symbol edit on its own |
git_create_branch, git_stage_files, git_unstage_files, git_commit, git_revert_commit, git_restore_file | Each git write on its own |
Any other name is reported by ivralo doctor and ignored. In a session, a tool named there runs with an allowed line instead of a dialog. The repository's file is usually the right place: "this repository may be edited unattended" is rarely true of every repository you work in. In a folder you have not trusted, the repository's allow_tools does not apply.
A test the request did not name
The shortcut that turns a red suite green while the bug stays is editing the test. So an edit of an existing test file asks even under accept edits, with the reason edits a test the task did not name among its facts, and an unattended run refuses it even with edit_file in allow_tools.
A file counts as a test by the repository's own naming: under a directory called tests, test, __tests__ or spec, or named test_*, *_test.*, *.test.*, *.spec.*, FooTest or FooTests. The rule lifts when your request names the file, its path or a test inside it. What the main agent writes when it hands work to a specialist names nothing, so only your own words count. Creating a new test file is not editing one.
A rename or organize-imports through the language server is judged on every file it would change: one unnamed test among them makes the whole call ask, or be refused unattended.
When a test really is wrong, the agent is told to say so (which test, what it expects, what the code does instead) rather than change it. To let it edit, name the file in your request.
Integrating a coder's worktree
A Code Engineer working in its own git worktree, as a parallel coder or an instance, changes nothing in your workspace while it works. When it finishes, its whole change comes back as one patch, and that patch asks once more in the integration dialog: the step, the coder, and every file with its lines added and removed.
| Key | Answer |
|---|---|
y, Enter | Apply the patch to your workspace |
s | Apply it, and accept edits for the rest of the session |
n, Esc | Refuse it; the worktree is kept |
d | Open the whole patch in the diff viewer, with the same three answers |
Each edit inside the worktree already asked as it was made. Accept edits for the session, or edit_file in allow_tools, lifts the integration dialog too. How the patch is applied, and what happens on a conflict, is on Parallel coders and worktrees.