Autonomy Levels
An agent's autonomy level is the answer to one question: what is this agent allowed to do?
Behavry exposes exactly two axes for an agent, and nothing may add a third:
| Axis | Field | Meaning |
|---|---|---|
| Granted | autonomy_level (A0–A3) | The authority an operator has deliberately given the agent |
| Observed | BRF Decision Scope → risk_tier | What the agent's behaviour actually looks like |
Before this existed, autonomy was smeared across four places at once — agents.agent_type, the Restricted Mode profile catalog, BRF Decision Scope, and the maturity narrative — with no precedence between them. Nobody could answer the question without reading code.
agent_type is now a display hintagent_type (autonomous | semi-autonomous | human-in-the-loop) is derived for display and is no longer enforced. The level is authoritative.
The four levels
A level does not carry its own rules. It binds a seeded restriction profile, and the restriction gate that already exists in the policy engine does the evaluating. That is why adding autonomy added no new gate to the hot path.
| Level | Binds | In practice |
|---|---|---|
| A0 | Read-only profile | The agent may read. Any write escalates. |
| A1 | Read plus ordinary writes | Approved write categories are simply the bound profile's allowed_actions. |
| A2 | Guardrails only | Broad authority, still fenced by the guardrail checks. |
| A3 | Nothing | Policy-only: OPA plus circuit breakers, no action allowlist. |
What changed about enforcement
The restriction gate has only ever denied. Under an autonomy binding, an ungranted action escalates to a human instead of being denied — reusing the existing blast-radius escalation plumbing rather than inventing a second path.
Guardrail violations — sensitive resources, network reach, payload size — still hard-deny even at A3. A human approving "this agent may write" must not implicitly approve reading ~/.aws/credentials.
Precedence is explicit, and tighter always wins
live incident restriction > OPA-named profile > autonomy binding
A demotion applied during an incident can never be loosened by whatever level the agent nominally holds. Without that ordering, incident response would be advisory.
Nothing tightens when you upgrade
Two independent mechanisms guarantee this, because either alone would be a bad bet:
- Levels are derived from behaviour you already had, not assigned in bulk.
human-in-the-loop→ A0;semi-autonomouswith a live read-only restriction → A1;semi-autonomous→ A2;autonomousor anything ambiguous → A3, biasing permissive. - The whole disposition sits behind a tenant switch,
autonomy_enforcement_enabled, which defaults to false.
With the flag off, levels are recorded but change no runtime decision. This is intended: the flag gates whether a level affects enforcement, not whether the record is accurate. Turning it on is a deliberate act.
A blanket A0 would have pushed every existing agent's writes into a human-approval queue nobody was watching. A blanket A2 or A3 would have thrown away the information that some agents are deliberately supervised. Deriving per-agent avoids both.
Promotion is earned
Evidence thresholds are tenant-configurable (autonomy_promotion_config) and cover:
- Clean sessions within a window
- An approved behavioural baseline
- Zero unresolved escalations
An admin can still promote without the evidence. The resulting event records that no evidence backed it, so an auditor can tell an earned promotion from a waived one. The dashboard's promotion-evidence panel says why an agent is not yet eligible rather than simply refusing.
Demotion, and what demotes automatically
Demotion is immediate and ungated — losing authority should never wait on a threshold.
These paths demote automatically:
| Trigger | Why it acts by itself |
|---|---|
| Fleet-control quarantine | Containment already happened; the agent should not return at the authority it held going in. |
| Kill-switch quarantine | A genuinely separate code path from the above, and a real trip. |
| Emergency credential revocation | Reaching for this means an operator believes the credential is in the wrong hands, so everything it did is suspect. |
| Confirmed baseline drift | Set membership against a manifest the agent's own baseline declared and an admin approved: deterministic, agent-attributable, no threshold to be wrong about. Deduplicated over an hour, so one agent looping on one unregistered tool is a single incident rather than a ride from A3 to A0. |
Behavioural drift recommends — it does not act
BEHAVIORAL_DRIFT_DETECTED is a slope on a risk score at medium severity. A team legitimately widening an agent's workload produces the same shape as an attacker walking it up the ladder. Acting inline would quietly strip authority from agents that did nothing wrong, and the operator would find out from a broken workflow.
So it records an autonomy_demotion_recommended alert instead. GET /agents/{id}/autonomy returns it as demotion_recommendation, and an admin applies it through the ordinary PATCH, so the resulting change is attested exactly like every other level change rather than arriving from a background task.
The dashboard renders it as an amber block with an Apply demotion to An button, and the copy says "Not applied automatically" out loud. A panel implying the level had already dropped would undo the entire reason this signal does not act on its own.
One open recommendation per agent — an alert storm is how a real recommendation gets missed. Any actual level change resolves the open row in either direction, since an admin who promoted instead has still answered the question.
Not yet wired: policy-exception abuse. policy_exceptions has no per-use counter today, so abuse is not measurable. It is tracked on the roadmap rather than half-implemented.
Attested, not just enforced
Every promotion, demotion, and admin override emits a signed AUTONOMY_LEVEL_CHANGED event carrying the prior level, the new level, the justifying evidence, and the actor.
These are new event types on the existing hash-chained envelope, written decision-class, so they land on the agent's decision chain and verify through verify.behavry.ai with no verifier change. The autonomy_events table is a queryable index into them; the audit chain remains authoritative.
This is the part a self-attested competitor log structurally cannot produce: the privilege ladder itself is independently verifiable.
Where the level travels
- Agent JWT — carries
autonomy_level, defaulting closed to A0 - OPA envelope —
input.agent.autonomy_level, also defaulting closed
Deliberate: a token minted before a demotion must not grant authority the agent no longer holds.
Drift against the grant
The granted level became the Decision Scope prior (replacing the deprecated agent_type), and observed uplift measures how far actual behaviour has moved above it — using the share of recent decisions that had to be escalated because the grant did not cover them.
Observed scope above the level's ceiling raises AUTONOMY_DRIFT and feeds Intent Drift. It is record-only: a ratio over a rolling window is exactly the kind of imprecise signal that should inform an operator rather than deny a call inline.
API
| Endpoint | Purpose |
|---|---|
GET /api/v1/agents/{id}/autonomy | Current level, promotion eligibility, any open demotion recommendation |
PATCH /api/v1/agents/{id}/autonomy | Change the level (admin, attested) |
GET /api/v1/agents/{id}/autonomy/history | Every level change with actor and evidence |
New agents land at the tenant's default_autonomy_level. See Onboarding an agent.
Related
- Security Zones — the
infrastructurezone requires A2 or above, and inherits this opt-in rather than adding a second switch - Restricted Mode — the profiles a level binds
- Risk Scoring — the observed axis
- Decision Trace — where level changes are recorded