01What it is
A control plane for domain services: decorate with @Autonomous, @Observe, and @Capability (metadata in @hazeljs/core). AutonomousRuntime records runs, evaluates a decision engine (rules by default; Decision Plane optional), parks for HITL when required, then executes only granted capabilities that Gatekeeper allows.
02Problem it solves
- Stop bolting a custom agent loop onto every ops service
- Keep Observation ≠ Decision ≠ Authorization ≠ Execution as separate layers
- Reuse Gatekeeper, durable runs, and HITL instead of a second orchestration stack
- Default-deny grants so “autonomous” never means unrestricted
03How you use it today
Install @hazeljs/autonomous, import AutonomousModule.forRoot(), emit events with runtime.emit, resume approvals with runtime.resume. Inspect timelines, dry-run replay, and simulate without live side effects. Full walkthrough: Autonomous Runtime guide.
04Where it sits in the stack
Beside Agent Runtime for services that are not @Agent classes: Observe → Decide → Gatekeeper → Capability. Optional Decision Runtime can propose; AAR still enforces grants and Gatekeeper.
05Boundaries
- Not a replacement for @Agent / @Tool loops
- No permission-bypassing mode — governed-auto still needs grants + Gatekeeper
- Dry-run replay and simulate() never repeat live side effects
- Not roadmap @AutonomousOptimization (HTTP self-tuning)
06FAQ
- Do I need an LLM?
- No. The default decision engine is deterministic rules. Decision Plane / LLMs are optional proposers.
- Can a capability run without a grant?
- No. Default deny — the service must list the capability id in @Autonomous({ capabilities }).
- How does this relate to Gatekeeper?
- AAR generates policies from granted capabilities and executes through AgentGatekeeper. Approvals bind argument digests.
07Governed service autonomy
import { Injectable, Autonomous, Observe, Capability } from '@hazeljs/core';
import { AutonomousModule } from '@hazeljs/autonomous';
@Injectable()
@Autonomous({
id: 'order-ops',
mode: 'governed-auto',
capabilities: ['orders.retry'],
})
export class OrderOps {
@Observe('orders.failed')
onFailure(e: { orderId: string }) { return e; }
@Capability({ id: 'orders.retry', effect: 'idempotent', approval: 'auto', idempotencyKey: true })
retry(input: { orderId: string }) { return this.orders.retry(input); }
}
// AutonomousModule.forRoot() — emit → decide → Gatekeeper → execute