You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Hard execution and budget limits for autonomous agents — enforced locally.**
8
+
Hard execution and budget limits for autonomous agents — enforced locally.
9
9
10
-
AuthorityLayer prevents runaway spend, infinite tool loops, and uncontrolled external calls in agentic systems. It enforces strict boundaries inside your runtime and halts execution safely when limits are breached.
11
-
12
-
- ✅ No telemetry
13
-
- ✅ No cloud dependency
14
-
- ✅ Fail-closed by default
15
-
- ✅ Works fully offline
16
-
- ✅ Zero runtime dependencies
10
+
- No telemetry
11
+
- Works fully offline
12
+
- Fail-closed by default
13
+
- Zero runtime dependencies
17
14
18
15
---
19
16
20
-
## The Problem
21
-
22
-
Autonomous agents introduce a new risk surface that traditional systems don't account for:
23
-
24
-
-**Unbounded token spend** — a looping agent can burn thousands of dollars before you notice
25
-
-**Infinite tool loops** — agents can get stuck calling the same tool repeatedly
26
-
-**Retry storms** — failed API calls retried indefinitely with no ceiling
27
-
-**Cascading API calls** — one agent spawns sub-calls, which spawn more
28
-
-**Silent cost explosions** — spend accumulates across runs with no enforced ceiling
17
+
## Live Enforcement Demo
29
18
30
-
Most systems rely on warnings, dashboard alerts, or provider-level quotas — all of which react *after* the damage is done.
31
-
32
-
**AuthorityLayer enforces hard limits directly in your execution loop.** When a boundary is crossed, execution stops immediately.
AuthorityLayer V1 implements three composable enforcement primitives. Each is independently opt-in — omit a config key to disable that primitive entirely.
99
-
100
-
### 1. Budget Cap
79
+
Three composable primitives. Each is independently opt-in — omit a config key to disable it entirely.
101
80
102
-
Tracks cumulative USD spend across the lifetime of the process. Halts when the total exceeds the configured cap.
81
+
| Primitive | Config key | What it enforces |
82
+
|-----------|------------|------------------|
83
+
|**Budget cap**|`budget.dailyUSD`| Cumulative USD spend across the process lifetime. Halts when spend exceeds the cap. → [docs](./docs/enforcement.md#1-budget-cap)|
84
+
|**Loop guard**|`loopGuard.maxToolCallsPerRun`| Total tool calls per `wrap()` invocation. Counter resets each run. → [docs](./docs/enforcement.md#2-loop-guard)|
85
+
|**Tool throttle**|`toolThrottle.maxCallsPerMinute`| Rate of tool calls using a sliding 60-second window — no fixed buckets. → [docs](./docs/enforcement.md#3-tool-throttle)|
> **Why explicit spend reporting?** Different providers expose token counts and pricing differently. AuthorityLayer doesn't assume your pricing model — you calculate the USD cost and report it. This makes the integration provider-agnostic.
118
-
119
-
**Halt reason:**`"budget_exceeded"`
87
+
When a primitive breaches, AuthorityLayer throws a typed `EnforcementHalt` error with a structured `.enforcement` object. Execution never crashes silently.
120
88
121
89
---
122
90
123
-
### 2. Loop Guard
124
-
125
-
Limits the total number of tool calls within a single `wrap()` invocation. The counter resets at the start of each `wrap()` call.
126
-
127
-
```typescript
128
-
const authority =newAuthorityLayer({
129
-
loopGuard: { maxToolCallsPerRun: 25 },
130
-
});
131
-
```
132
-
133
-
Every call to `authority.tool()` increments the counter *before* the tool function executes. If the limit is already reached, the tool function is never called.
134
-
135
-
**Halt reason:**`"loop_limit_exceeded"`
136
-
137
-
---
138
-
139
-
### 3. Tool Throttle
140
-
141
-
Rate-limits tool calls using a **sliding 60-second window** — not a fixed reset bucket. This accurately reflects the true call density over the last minute, preventing bursts at bucket boundaries.
142
-
143
-
```typescript
144
-
const authority =newAuthorityLayer({
145
-
toolThrottle: { maxCallsPerMinute: 60 },
146
-
});
147
-
```
148
-
149
-
On each `authority.tool()` call:
150
-
1. Timestamps older than 60 seconds are evicted
151
-
2. The current timestamp is added
152
-
3. If the count exceeds `maxCallsPerMinute`, execution halts
153
-
154
-
**Halt reason:**`"tool_throttle_exceeded"`
155
-
156
-
---
157
-
158
-
## API Reference
159
-
160
-
### `new AuthorityLayer(config)`
161
-
162
-
```typescript
163
-
interfaceAuthorityConfig {
164
-
mode?:"strict"|"warn"; // Default: "strict"
165
-
budget?: { dailyUSD:number };
166
-
loopGuard?: { maxToolCallsPerRun:number };
167
-
toolThrottle?: { maxCallsPerMinute:number };
168
-
}
169
-
```
170
-
171
-
| Mode | Behavior |
172
-
|------|----------|
173
-
|`"strict"`| Throws `EnforcementHalt` immediately when a limit is breached (default) |
174
-
|`"warn"`| Emits a `console.warn` — **stubbed in V1**, full implementation coming in a future release |
175
-
176
-
---
177
-
178
-
### `authority.wrap(fn)`
179
-
180
-
Wraps an agent run with enforcement. Resets per-run counters (loop guard) and logs `run.start` / `run.complete` events to the audit chain.
181
-
182
-
```typescript
183
-
awaitauthority.wrap(async () => {
184
-
// your agent loop here
185
-
});
186
-
```
187
-
188
-
---
189
-
190
-
### `authority.tool(name, fn)`
191
-
192
-
The single hook for all external tool calls. Checks loop guard and throttle *before* calling `fn`. If either limit is breached, `fn` is never invoked.
193
-
194
-
```typescript
195
-
const data =awaitauthority.tool("stripe.charge", () =>
|`name`|`string`| Human-readable label logged to the event chain |
203
-
|`fn`|`() => Promise<T>`| The actual tool call |
204
-
205
-
---
206
-
207
-
### `authority.recordSpend(amountUSD)`
208
-
209
-
Report token or API spend in USD. Accumulates across all calls in the current process lifetime (not per-run). Call this after each model or billable API response.
210
-
211
-
```typescript
212
-
authority.recordSpend(0.0042); // $0.0042 spent this call
213
-
```
214
-
215
-
---
216
-
217
-
### `authority.getChain()`
218
-
219
-
Returns a read-only copy of the enforcement event chain. Useful for inspecting what happened during a run or persisting to disk.
// e.g. "Halted: budget_exceeded (52.14 > 50) [evt_3f9a2c1b...]"
268
-
}
269
-
}
270
-
```
271
-
272
-
---
273
-
274
-
## Audit Chain
275
-
276
-
Every significant event in an agent run is logged to a hash-linked in-memory chain:
277
-
278
-
| Event type | When it fires |
279
-
|------------|---------------|
280
-
|`run.start`| At the start of each `wrap()` call |
281
-
|`tool.call`| Each time `authority.tool()` is called |
282
-
|`enforcement.halt`| When a limit is breached |
283
-
|`run.complete`| When `wrap()` completes without error |
284
-
|`run.error`| When an unexpected error is thrown inside `wrap()`|
285
-
286
-
Each event is cryptographically linked to its predecessor via SHA-256. Altering, reordering, or removing any event invalidates all subsequent hashes — detectable instantly with `verifyChain()`.
0 commit comments