You are reading Nightly documentation for 0.12.6.dev0+g563af09.

This documentation may describe behavior that differs from Stable.

Open Stable documentation

Autonomy and approvals

Autonomy and approvals

Holaryn Agent never assumes it may act on your behalf. Every tool call is classified by its consequence, resolved against your chosen autonomy posture, and — when it matters — held for your explicit yes. This page explains the model, the postures, unattended behavior, policy overrides, and dry-run mode.

The two axes

Autonomy is not one dial. Holaryn separates two independent questions:

  • Control — does the action wait for your yes before it runs?
  • Awareness — are you told that it happened?

Crossing the two gives the four autonomy levels a tool call can resolve to:

Level Waits for yes? Tells you?
Autonomous (silent) No No — trivial, reversible work only
Autonomous + notify No Yes — do it, then report
Ask first Yes — the run suspends until you answer Yes
Never unattended Refused in a scheduled run; held for a foreground session Yes

An unanswered ask is never auto-approved and never auto-failed — the run simply waits.

Consequences

Every tool carries a declared consequence: whether the action is reversible, and a category (fs.read, fs.write, shell, script, memory.write, mcp.*, skill.*, command.script, command.app, and so on). The approval policy resolves a consequence plus the call's arguments to one of the four levels. The default rule is simple:

  • Reversible → act (autonomous + notify).
  • Irreversible → ask first.

Postures

The posture is the run-wide stance that decides how consequences resolve. Four postures exist:

Posture Behavior
ask Ask before each tool action.
selective File and memory reads proceed; other actions ask unless a permission rule allows them.
allow-all Acts on everything except a residual of hard-floor categories (shell, script, coding.delegate, command.script, command.app by default — configurable via HOLARYN_ALLOW_ALL_RESIDUAL), which still ask. Security reviews can also ask.
unrestricted Run all enabled tools without permission prompts, including commands, app launches, and local files outside the workspace.

Both allow-all and unrestricted are gated behind explicit confirmation ceremonies in the UI — you cannot drift into them.

Unrestricted is the session's full-access (YOLO) option. Local security findings remain in the
activity/audit record but do not stop actions or open an approval dialog. This includes reading
an operator-requested SSH configuration after another tool returns data. OS permissions,
selected execution profiles, organization policies, inherited limits, and resource limits still
apply. Questions asking for missing information remain possible. Switching back restores the
selected mode's approvals and workspace restrictions; it does not undo actions already performed.

Set the posture:

  • CLI: holaryn run --autonomy ask|selective|allow-all|unrestricted, or the shorthand flags --allow-all yes / --unrestricted yes.
  • Web chat: the mode picker in the composer, or type /mode ask|selective|allow-all|unrestricted in the chat. The mode change applies to the live session immediately. See slash-commands.md.

/mode is a control-plane command: only surfaces that grant the posture capability offer it (the web chat does; messaging channels and the scheduler refuse it by construction, and the CLI REPL bakes its posture in at start).

The approval dialog

When a live Chat or Code run asks first, a focus-trapped dialog moves focus to its title so you
review context before acting. It names the tool, affected resource, one-call scope, consequence,
reversibility, request time, provenance, trust/sensitivity, request identity, summarized fields,
and exact arguments (file edits show the exact diff), then offers the choices below. An approval
covers exactly those arguments: if anything changes them while the decision is pending, the call
is refused, and the tool runs a private copy of what you reviewed.

  • Allow once — permits only this named call.
  • Deny once — refuses only this call; the run continues and the agent is told. Pressing Esc is the same explicit deny — it is announced, never a silent dismiss.
  • More options, a menu with the scoped decisions:
  • Approve and don't ask again for {tool} (this session) — further calls to that tool run with notification instead of asking, until the chat session ends. The grant is never persisted, and the hard-floor categories (shell, scripts) still outrank it.
  • Approve all actions for this session — approves and switches the session to allow-all in the same gesture (the same switch as the arming ceremony; shell and scripts still require approval).
  • Deny and terminate the run — refuses the call and stops the in-flight run cleanly. The transcript notes "run terminated by the operator" and the chat is immediately ready for your next message.

Every decision is announced to screen readers the moment it lands ("Approved {tool}", "Denied {tool}", "Session switched to allow-all", "Run terminated").

Unattended and scheduled runs

Nothing auto-proceeds in an unattended run:

  • A job scheduled with allow-all or unrestricted is downgraded to ask-first at dispatch (with a visible warning), so consequential actions suspend instead of running unwatched. The one exception is dry-run jobs — staging is already the safe mode.
  • When a gated action fires unattended, the job's --on-gated setting decides: hold parks the approval and suspends the run; cancel denies it. Either way the decision is yours — a suspended run never times out into approval and never silently fails.
  • Parked approvals, operator questions, and post-restart recovery decisions all land in the
    Approvals workspace (Ctrl+0 by default), which shows a pending count from anywhere. Answer
    from there, or from the CLI with holaryn question and holaryn resume.

See scheduler-and-automation.md for how jobs are defined.

Policy profiles: per-category and per-tool overrides

For routine automations, a hand-editable TOML policy profile pre-authorizes (or tightens) specific categories and tools instead of flipping one big switch. The file uses four level aliases: silent, notify, ask, never.

[selective.categories]
"fs.write" = "notify"     # let the selective posture write files without asking
"team.message" = "ask"

[selective.tools]
"run_shell" = "never"     # this tool never runs unattended

[allow-all.tools]
"generate_image" = "silent"
  • [selective.categories] and [selective.tools] refine the selective posture.
  • [allow-all.tools] refines allow-all per tool. There is deliberately no allow-all.categories table — the allow-all residual is controlled only by HOLARYN_ALLOW_ALL_RESIDUAL, and the residual is a hard floor a profile cannot weaken.
  • [[selective.paths]] rules refine selective by where a tool call's path points. Each rule names one boundary, within or outside: an absolute path, or "workspace" for the root of a code session. tools and categories narrow which calls the rule considers, and the first matching rule wins.
[[selective.paths]]
categories = ["fs.write"]
within = "workspace"
level = "ask"             # ask before every file edit inside the code workspace

Code sessions keep their own rules

A code session adds rules of its own to the selected mode and profile:

  • the workspace root, which every "workspace" path rule resolves against, including your profile's rules;
  • file reads inside the workspace run silently, and edits inside it run with notification (IDE sessions ask before edits instead);
  • writes outside the workspace are never (never unattended);
  • shell commands ask, and code validation runs with notification, unless the mode or profile sets those categories.

Changing the mode, choosing Approve all actions for this session, saving or removing a policy rule, and reloading the profile all rebuild only the mode and profile rules. The session's own rules are applied again, unchanged. When the new profile has [[selective.paths]] rules, they replace the previous profile's path rules and are checked before the session's rules, so a rule you removed stops applying and a rule you added takes precedence. The unrestricted mode skips these rules while it is on; switching back applies them again.

The active profile is resolved in this order: the --policy <file.toml> flag (on holaryn run and holaryn schedule) > the HOLARYN_POLICY_FILE environment variable > ~/.holaryn/policy.toml if it exists. Inspect the active profile with holaryn policy show, or edit rules graphically in Settings → Autonomy & Permissions.

Threshold rules ("purchases under $20 allowed, ask above"; "internal recipients silent, external ask") are also supported by the policy engine for categories that expose a numeric or recipient field.

Dry-run: propose a plan, approve in batch

Dry-run mode is how a new automation earns trust. With dry-run on, irreversible tool calls are staged into a reviewable plan instead of executing; reversible actions (reads, recalls) still run so the agent can actually plan.

Turn it on:

  • CLI: holaryn run --dry-run "reorganize my downloads" — orthogonal to the posture flags.
  • Web: the Dry run checkbox in the composer tray, or /dryrun on|off in the chat.
  • Scheduled jobs: a dry-run flag on the job stages the run's consequential actions for later review. In this one case the unattended allow-all downgrade is skipped — previewing what allow-all would do unattended is exactly the point.

Review and execute:

  • Web: when a dry-run task finishes with staged actions, a plan card appears above the composer listing every action with its consequence category. Approve & execute runs exactly the recorded calls, in order — the task is never re-run. Discard drops the plan.
  • CLI (works for web- and scheduler-staged plans too):
holaryn plan list                 # reviewable plans
holaryn plan show <plan_id>       # per-action tool, args, category, state
holaryn plan approve <plan_id>    # execute the remaining staged actions, in order
holaryn plan discard <plan_id>

Plans are durable (staged-plans.sqlite3 in the state directory). Execution stops at the first failure; the remaining actions stay staged, so re-approving retries only what never ran. Two honest caveats shown in the review surfaces: arguments were computed against world state at dry-run time and may be stale, and batch approval deliberately replaces the per-call gates — which is why every call's category (including shell) is shown before you approve.