@arbiterhq/sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +563 -0
- package/dist/approvalMapping.d.ts +26 -0
- package/dist/approvalMapping.d.ts.map +1 -0
- package/dist/approvalMapping.js +27 -0
- package/dist/approvalMapping.js.map +1 -0
- package/dist/arbiter.d.ts +23 -0
- package/dist/arbiter.d.ts.map +1 -0
- package/dist/arbiter.js +58 -0
- package/dist/arbiter.js.map +1 -0
- package/dist/arbiterAdmin.d.ts +32 -0
- package/dist/arbiterAdmin.d.ts.map +1 -0
- package/dist/arbiterAdmin.js +125 -0
- package/dist/arbiterAdmin.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +36 -0
- package/dist/errors.js.map +1 -0
- package/dist/httpClient.d.ts +16 -0
- package/dist/httpClient.d.ts.map +1 -0
- package/dist/httpClient.js +45 -0
- package/dist/httpClient.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +51 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifestValidation.d.ts +9 -0
- package/dist/manifestValidation.d.ts.map +1 -0
- package/dist/manifestValidation.js +456 -0
- package/dist/manifestValidation.js.map +1 -0
- package/dist/resolveBaseUrl.d.ts +3 -0
- package/dist/resolveBaseUrl.d.ts.map +1 -0
- package/dist/resolveBaseUrl.js +12 -0
- package/dist/resolveBaseUrl.js.map +1 -0
- package/dist/runtime/ArbiterRuntime.d.ts +30 -0
- package/dist/runtime/ArbiterRuntime.d.ts.map +1 -0
- package/dist/runtime/ArbiterRuntime.js +163 -0
- package/dist/runtime/ArbiterRuntime.js.map +1 -0
- package/dist/runtime/credentialKind.d.ts +5 -0
- package/dist/runtime/credentialKind.d.ts.map +1 -0
- package/dist/runtime/credentialKind.js +9 -0
- package/dist/runtime/credentialKind.js.map +1 -0
- package/dist/runtime/resolveRuntimeAgentExternalId.d.ts +9 -0
- package/dist/runtime/resolveRuntimeAgentExternalId.d.ts.map +1 -0
- package/dist/runtime/resolveRuntimeAgentExternalId.js +38 -0
- package/dist/runtime/resolveRuntimeAgentExternalId.js.map +1 -0
- package/dist/types.d.ts +382 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +54 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-06-28
|
|
4
|
+
|
|
5
|
+
First public npm release under **`@arbiterhq/sdk`**. The product name remains **Arbiter**; only the npm scope changed from the previously planned `@arbiter` org.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `ArbiterRuntime` — connect, heartbeat, disconnect, evaluate, pollApproval, consumeRelease, getReceipt
|
|
10
|
+
- `ArbiterAdmin` — registry, credentials, manifest plan/sync/drift, human approve/reject
|
|
11
|
+
- **Discovery bootstrap** — workspace API key (`arb_test_*`) + `runtime.connect()` discovers unknown agents
|
|
12
|
+
- **Permission discovery** — `runtime.evaluate()` discovers unknown actions (requires adopted agent)
|
|
13
|
+
- **Runtime dual-auth** — backend accepts workspace API key or agent credential on runtime routes
|
|
14
|
+
- Manifest validation, plan, sync, drift, state signature
|
|
15
|
+
|
|
16
|
+
### Migration
|
|
17
|
+
|
|
18
|
+
- Use `ArbiterAdmin` instead of deprecated `Arbiter` for control-plane operations
|
|
19
|
+
- Use `ArbiterRuntime` with `arb_agent_*` for production runtime loops
|
|
20
|
+
- Discovery-first onboarding: `ArbiterRuntime({ credential: ARBITER_API_KEY })` then adopt in dashboard
|
|
21
|
+
- Set `ARBITER_AGENT` (or `connect({ agent })`) in production for stable identity across redeployments
|
|
22
|
+
|
|
23
|
+
### Breaking changes
|
|
24
|
+
|
|
25
|
+
- None for new installs. Deprecated `Arbiter` class remains exported with runtime warning.
|
package/README.md
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
# @arbiterhq/sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for the Arbiter AI governance API.
|
|
4
|
+
|
|
5
|
+
Two clients align with W7.3 runtime identity:
|
|
6
|
+
|
|
7
|
+
- **`ArbiterRuntime`** — autonomous agent execution. Accepts **`arb_agent_*`** (production) or **`arb_test_*`** (discovery bootstrap).
|
|
8
|
+
- **`ArbiterAdmin`** — control plane (`arb_test_*`) including manifest plan/drift
|
|
9
|
+
|
|
10
|
+
### Runtime authentication model
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Discovery bootstrap (first connect)
|
|
14
|
+
Workspace API Key (arb_test_*)
|
|
15
|
+
↓
|
|
16
|
+
runtime.connect()
|
|
17
|
+
↓
|
|
18
|
+
Automatic Agent Discovery
|
|
19
|
+
↓
|
|
20
|
+
Adopt Agent (dashboard)
|
|
21
|
+
↓
|
|
22
|
+
Issue Agent Credential
|
|
23
|
+
↓
|
|
24
|
+
Production runtime
|
|
25
|
+
arb_agent_* → runtime.connect() / evaluate() / heartbeat()
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For production deployments, set **`ARBITER_AGENT`** (or pass `connect({ agent })`) to guarantee a stable runtime identity across redeployments. Package name and hostname fallbacks exist for local development convenience only — do not rely on them in production.
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- Node.js 18+
|
|
33
|
+
- A running Arbiter backend instance
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @arbiterhq/sdk
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Discovery-first prerequisite:** Enable **Runtime Discovery** in workspace settings before using a workspace API key with `runtime.connect()`.
|
|
42
|
+
|
|
43
|
+
## Base URL
|
|
44
|
+
|
|
45
|
+
The SDK resolves the API base URL in this order:
|
|
46
|
+
|
|
47
|
+
1. `baseUrl` passed to the client constructor
|
|
48
|
+
2. `ARBITER_BASE_URL` environment variable
|
|
49
|
+
3. Production default: `https://api.arbitertrust.com`
|
|
50
|
+
|
|
51
|
+
**Production** — omit `baseUrl` or set `ARBITER_BASE_URL=https://api.arbitertrust.com`.
|
|
52
|
+
|
|
53
|
+
**Local development** — set `ARBITER_BASE_URL=http://localhost:3001` or pass `baseUrl: 'http://localhost:3001'` explicitly.
|
|
54
|
+
|
|
55
|
+
## Credentials
|
|
56
|
+
|
|
57
|
+
| Variable | Used by | Purpose |
|
|
58
|
+
|----------|---------|---------|
|
|
59
|
+
| `ARBITER_AGENT_CREDENTIAL` | `ArbiterRuntime` | Agent runtime auth (`arb_agent_*`) |
|
|
60
|
+
| `ARBITER_AGENT` | `ArbiterRuntime` | Stable agent externalId for discovery bootstrap with workspace API key |
|
|
61
|
+
| `ARBITER_API_KEY` | `ArbiterAdmin`, discovery-first `ArbiterRuntime` | Workspace control plane (`arb_test_*`) |
|
|
62
|
+
| `ARBITER_HUMAN_TOKEN` | `ArbiterAdmin.approve()` / `reject()` | Human session token for authorization actions |
|
|
63
|
+
| `ARBITER_BASE_URL` | Both clients | Override API endpoint |
|
|
64
|
+
|
|
65
|
+
## Onboarding paths
|
|
66
|
+
|
|
67
|
+
There are two onboarding paths:
|
|
68
|
+
|
|
69
|
+
1. **Discovery-first** — use a workspace API key with `ArbiterRuntime`, call `runtime.connect()` to discover the agent, adopt in the Discovery Inbox, then call `runtime.evaluate()` to discover permissions.
|
|
70
|
+
2. **Registry-first** — register agents and permissions with `ArbiterAdmin`, issue an agent credential, then connect with `ARBITER_AGENT_CREDENTIAL`.
|
|
71
|
+
|
|
72
|
+
Both paths converge on the same governed runtime loop after adoption.
|
|
73
|
+
|
|
74
|
+
## Automatic Discovery
|
|
75
|
+
|
|
76
|
+
Discovery is fully automatic — there are no `discoverAgent()` or `discoverPermission()` APIs.
|
|
77
|
+
|
|
78
|
+
### Agent discovery (at connect)
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Workspace API Key
|
|
82
|
+
↓
|
|
83
|
+
runtime.connect()
|
|
84
|
+
↓
|
|
85
|
+
Agent Discovery
|
|
86
|
+
↓
|
|
87
|
+
Discovery Inbox
|
|
88
|
+
↓
|
|
89
|
+
Adopt
|
|
90
|
+
↓
|
|
91
|
+
Registry
|
|
92
|
+
↓
|
|
93
|
+
(Optional) Agent Credential issued for production
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
When `ArbiterRuntime` is constructed with a workspace API key (`arb_test_*`), `runtime.connect()` sends the agent identity to the backend. If the agent is unknown and workspace discovery is enabled, it appears in the Discovery Inbox as `pending_review`.
|
|
97
|
+
|
|
98
|
+
### Permission discovery (at evaluate)
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Known active agent
|
|
102
|
+
↓
|
|
103
|
+
runtime.evaluate()
|
|
104
|
+
↓
|
|
105
|
+
Unknown Permission
|
|
106
|
+
↓
|
|
107
|
+
Automatic Permission Discovery
|
|
108
|
+
↓
|
|
109
|
+
Discovery Inbox
|
|
110
|
+
↓
|
|
111
|
+
Adopt
|
|
112
|
+
↓
|
|
113
|
+
Permission becomes available
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Permission discovery requires an active runtime identity. Unknown actions discovered during `runtime.evaluate()` appear in the Discovery Inbox for operator adoption.
|
|
117
|
+
|
|
118
|
+
### Production identity (`ARBITER_AGENT`)
|
|
119
|
+
|
|
120
|
+
Production deployments should set **`ARBITER_AGENT`** to maintain a stable runtime identity across restarts and replicas.
|
|
121
|
+
|
|
122
|
+
When using a workspace API key, the SDK resolves the agent externalId in this order:
|
|
123
|
+
|
|
124
|
+
1. `connect({ agent })` — explicit argument
|
|
125
|
+
2. `ARBITER_AGENT` environment variable
|
|
126
|
+
3. `ARBITER_AGENT_EXTERNAL_ID` environment variable
|
|
127
|
+
4. `npm_package_name` from `package.json`
|
|
128
|
+
5. Hostname (development convenience only)
|
|
129
|
+
|
|
130
|
+
Fallbacks (package name / hostname) exist for local development convenience. Do not rely on them in production — set `ARBITER_AGENT` explicitly.
|
|
131
|
+
|
|
132
|
+
### Discovery-first quickstart
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { ArbiterRuntime, SDK_VERSION } from '@arbiterhq/sdk';
|
|
136
|
+
|
|
137
|
+
// Workspace API key — discovery bootstrap (not production runtime auth)
|
|
138
|
+
const runtime = new ArbiterRuntime({
|
|
139
|
+
credential: process.env.ARBITER_API_KEY!,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Set ARBITER_AGENT in production for stable identity
|
|
143
|
+
await runtime.connect({
|
|
144
|
+
agent: process.env.ARBITER_AGENT ?? 'my-agent',
|
|
145
|
+
framework: 'custom',
|
|
146
|
+
runtimeType: 'node',
|
|
147
|
+
runtimeVersion: process.version,
|
|
148
|
+
sdkVersion: SDK_VERSION,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Agent appears in Discovery Inbox → adopt in dashboard
|
|
152
|
+
// After adoption, evaluate discovers unknown permissions:
|
|
153
|
+
const decision = await runtime.evaluate({ action: 'send_payment', amount: 500 });
|
|
154
|
+
// Permission appears in Discovery Inbox → adopt → governance applies
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
See [`../../examples/discovery-first/`](../../examples/discovery-first/) for the canonical onboarding script.
|
|
158
|
+
|
|
159
|
+
## Complete runtime flow
|
|
160
|
+
|
|
161
|
+
End-to-end governed execution: connect → heartbeat → evaluate → approval → poll → consume release → receipt.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { ArbiterAdmin, ArbiterRuntime, SDK_VERSION } from '@arbiterhq/sdk';
|
|
165
|
+
|
|
166
|
+
// Production: omit baseUrl. Local dev: baseUrl: 'http://localhost:3001'
|
|
167
|
+
const admin = new ArbiterAdmin({
|
|
168
|
+
apiKey: process.env.ARBITER_API_KEY!,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const runtime = new ArbiterRuntime({
|
|
172
|
+
credential: process.env.ARBITER_AGENT_CREDENTIAL!,
|
|
173
|
+
environment: 'production',
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await runtime.connect({
|
|
177
|
+
framework: 'custom',
|
|
178
|
+
runtimeType: 'node',
|
|
179
|
+
runtimeVersion: process.version,
|
|
180
|
+
sdkVersion: SDK_VERSION,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await runtime.heartbeat();
|
|
184
|
+
|
|
185
|
+
const evaluationPayload = {
|
|
186
|
+
action: 'send_payment',
|
|
187
|
+
amount: 5000,
|
|
188
|
+
} as const;
|
|
189
|
+
|
|
190
|
+
const decision = await runtime.evaluate(evaluationPayload);
|
|
191
|
+
|
|
192
|
+
if (decision.decision === 'hold' && decision.approvalRequestId) {
|
|
193
|
+
// Human operator approves via admin + session token
|
|
194
|
+
await admin.approve(decision.approvalRequestId, {
|
|
195
|
+
humanToken: process.env.ARBITER_HUMAN_TOKEN!,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const approval = await runtime.pollApproval(decision.approvalRequestId);
|
|
199
|
+
|
|
200
|
+
if (approval.status === 'approved') {
|
|
201
|
+
const release = await runtime.consumeRelease({
|
|
202
|
+
approvalRequestId: decision.approvalRequestId,
|
|
203
|
+
originalPayload: evaluationPayload,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const receipt = await runtime.getReceipt(release.evaluationId);
|
|
207
|
+
console.log(receipt.receiptHash);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Obtain `ARBITER_HUMAN_TOKEN` from a workspace session (`POST /auth/session` or dashboard login). Obtain `ARBITER_AGENT_CREDENTIAL` via `createAgentCredentialByExternalId()` or the dashboard.
|
|
213
|
+
|
|
214
|
+
## Runtime Quickstart
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { ArbiterRuntime, SDK_VERSION } from '@arbiterhq/sdk';
|
|
218
|
+
|
|
219
|
+
const runtime = new ArbiterRuntime({
|
|
220
|
+
credential: process.env.ARBITER_AGENT_CREDENTIAL!,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await runtime.connect({
|
|
224
|
+
framework: 'langgraph',
|
|
225
|
+
runtimeType: 'node',
|
|
226
|
+
runtimeVersion: process.version,
|
|
227
|
+
sdkVersion: SDK_VERSION,
|
|
228
|
+
environment: 'production',
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const result = await runtime.evaluate({
|
|
232
|
+
action: 'send_payment',
|
|
233
|
+
amount: 5000,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
console.log(result.decision, result.evaluationId);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Local development
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const runtime = new ArbiterRuntime({
|
|
243
|
+
credential: process.env.ARBITER_AGENT_CREDENTIAL!,
|
|
244
|
+
baseUrl: 'http://localhost:3001',
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Admin Quickstart
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { ArbiterAdmin } from '@arbiterhq/sdk';
|
|
252
|
+
|
|
253
|
+
const admin = new ArbiterAdmin({
|
|
254
|
+
apiKey: process.env.ARBITER_API_KEY!,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await admin.registerAgent({ externalId: 'finance-agent' });
|
|
258
|
+
await admin.registerPermission({ action: 'send_payment' });
|
|
259
|
+
await admin.grantPermission({ agent: 'finance-agent', action: 'send_payment' });
|
|
260
|
+
|
|
261
|
+
const { credential } = await admin.createAgentCredentialByExternalId({
|
|
262
|
+
externalId: 'finance-agent',
|
|
263
|
+
name: 'Runtime credential',
|
|
264
|
+
createdByUserId: 'owner-user-id',
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Local development
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const admin = new ArbiterAdmin({
|
|
272
|
+
apiKey: process.env.ARBITER_API_KEY!,
|
|
273
|
+
baseUrl: 'http://localhost:3001',
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Manifest lifecycle
|
|
278
|
+
|
|
279
|
+
Manifest SDK methods accept a typed **`ManifestWorkspace`** object — not a YAML string, file path, or full `arbiter.yaml` document.
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
arbiter.yaml
|
|
283
|
+
↓
|
|
284
|
+
parse YAML
|
|
285
|
+
↓
|
|
286
|
+
doc.spec
|
|
287
|
+
↓
|
|
288
|
+
ManifestWorkspace
|
|
289
|
+
↓
|
|
290
|
+
plan()
|
|
291
|
+
↓
|
|
292
|
+
syncRegistryManifest()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The SDK does **not** read files. The CLI reads and parses `arbiter.yaml` for you. When using the SDK directly, parse the file, extract `doc.spec`, and optionally validate with `validateManifestWorkspace()`.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
interface ManifestWorkspace {
|
|
299
|
+
agents: ManifestAgent[];
|
|
300
|
+
permissions: ManifestPermission[];
|
|
301
|
+
policies: ManifestPolicy[];
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Use `validateManifestWorkspace(manifest)` before calling SDK methods to get actionable validation errors that mirror the backend contract.
|
|
306
|
+
|
|
307
|
+
## Manifest fields
|
|
308
|
+
|
|
309
|
+
### Agent (`agents[]`)
|
|
310
|
+
|
|
311
|
+
| Field | Required | Description |
|
|
312
|
+
|-------|----------|-------------|
|
|
313
|
+
| `externalId` | For sync/plan | Stable agent identifier used by sync and plan |
|
|
314
|
+
| `name` | Yes | Display name |
|
|
315
|
+
| `description` | No | Human-readable summary |
|
|
316
|
+
| `source` | No | Provenance: `dashboard`, `sdk`, `discovered` |
|
|
317
|
+
| `management` | No | Ownership mode: `dashboard`, `sdk`, `manifest`, `discovered` |
|
|
318
|
+
| `owner` | No | Structured owner `{ slug, type }` where `type` is `user`, `team`, or `service_account` |
|
|
319
|
+
| `ownerSlug` | No | Owner slug when not using the `owner` object |
|
|
320
|
+
| `ownerType` | No | Owner type when using `ownerSlug` |
|
|
321
|
+
| `riskTier` | No | `low`, `medium`, `high`, or `critical` |
|
|
322
|
+
| `status` | No | `pending_review`, `active`, `disabled`, `expired`, or `archived` |
|
|
323
|
+
| `expiresAt` | No | ISO 8601 datetime |
|
|
324
|
+
| `approvedBy` | No | Approver identity for governed onboarding |
|
|
325
|
+
| `grants` | No | Permission grants to apply with the agent (see Grants) |
|
|
326
|
+
|
|
327
|
+
### Permission (`permissions[]`)
|
|
328
|
+
|
|
329
|
+
| Field | Required | Description |
|
|
330
|
+
|-------|----------|-------------|
|
|
331
|
+
| `action` | Yes | Snake_case action identifier |
|
|
332
|
+
| `description` | No | Human-readable summary |
|
|
333
|
+
| `status` | No | `pending_review`, `allowed`, `denied`, or `approval_required` |
|
|
334
|
+
| `source` | No | Provenance: `dashboard`, `sdk`, `discovered` |
|
|
335
|
+
| `management` | No | Ownership mode: `dashboard`, `sdk`, `manifest`, `discovered` |
|
|
336
|
+
| `assignedAgents` | No | Agent `externalId` values assigned to this permission |
|
|
337
|
+
| `displayName` | No | UI-friendly label |
|
|
338
|
+
| `category` | No | Grouping label (for example Financial) |
|
|
339
|
+
| `documentation` | No | Documentation URL |
|
|
340
|
+
| `tags` | No | String tags for discovery and filtering |
|
|
341
|
+
| `metadata` | No | Arbitrary key/value metadata object |
|
|
342
|
+
|
|
343
|
+
### Policy (`policies[]`)
|
|
344
|
+
|
|
345
|
+
| Field | Required | Description |
|
|
346
|
+
|-------|----------|-------------|
|
|
347
|
+
| `id` | For sync/plan | Stable policy slug |
|
|
348
|
+
| `action` | Yes | Snake_case action this policy governs |
|
|
349
|
+
| `field` | Legacy style | Field path for single-condition policies |
|
|
350
|
+
| `operator` | Legacy style | Comparison operator |
|
|
351
|
+
| `value` | Legacy style | Scalar or array value |
|
|
352
|
+
| `conditions` | Modern style | Array of `{ field, operator, value?, type? }` |
|
|
353
|
+
| `conditionOperator` | Modern style | `AND` or `OR` when using multiple `conditions` |
|
|
354
|
+
| `priority` | No | Integer priority (lower runs first) |
|
|
355
|
+
| `displayName` | No | UI-friendly label |
|
|
356
|
+
| `decision` | Yes | `allow`, `deny`, or `hold` |
|
|
357
|
+
| `source` | No | Provenance: `dashboard`, `sdk`, `discovered` |
|
|
358
|
+
| `management` | No | Ownership mode: `dashboard`, `sdk`, `manifest`, `discovered` |
|
|
359
|
+
|
|
360
|
+
Every policy must declare **either** legacy `field` + `operator` + `value` **or** a non-empty `conditions[]` array.
|
|
361
|
+
|
|
362
|
+
#### Legacy policy style
|
|
363
|
+
|
|
364
|
+
Use a single field comparison when the rule is simple:
|
|
365
|
+
|
|
366
|
+
```yaml
|
|
367
|
+
- id: payment-amount-hold
|
|
368
|
+
action: send_payment
|
|
369
|
+
field: amount
|
|
370
|
+
operator: '>'
|
|
371
|
+
value: 1000
|
|
372
|
+
decision: hold
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### Modern policy style
|
|
376
|
+
|
|
377
|
+
Use `conditions[]` when you need multiple predicates or richer operators:
|
|
378
|
+
|
|
379
|
+
```yaml
|
|
380
|
+
- id: payment-region-deny
|
|
381
|
+
action: send_payment
|
|
382
|
+
conditions:
|
|
383
|
+
- field: region
|
|
384
|
+
operator: in
|
|
385
|
+
value: [sanctioned, blocked]
|
|
386
|
+
- field: amount
|
|
387
|
+
operator: '>='
|
|
388
|
+
value: 100
|
|
389
|
+
conditionOperator: AND
|
|
390
|
+
decision: deny
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Prefer legacy style for single comparisons. Prefer modern style for compound rules or operator sets like `in`, `between`, and valueless operators (`exists`, `is_null`, etc.).
|
|
394
|
+
|
|
395
|
+
### Grants
|
|
396
|
+
|
|
397
|
+
`grants` on an agent replace that agent's permission grants during sync:
|
|
398
|
+
|
|
399
|
+
```yaml
|
|
400
|
+
grants:
|
|
401
|
+
- action: send_payment
|
|
402
|
+
grantedBy: platform-ops
|
|
403
|
+
grantedReason: Baseline finance automation access
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Each grant requires `action` (snake_case). Optional `grantedBy` and `grantedReason` document provenance.
|
|
407
|
+
|
|
408
|
+
### Ownership and risk
|
|
409
|
+
|
|
410
|
+
- **`owner`** — structured `{ slug, type }` for accountable ownership
|
|
411
|
+
- **`ownerSlug` / `ownerType`** — flat alternative to the `owner` object
|
|
412
|
+
- **`riskTier`** — classifies agent blast radius: `low`, `medium`, `high`, `critical`
|
|
413
|
+
|
|
414
|
+
### Source and management semantics
|
|
415
|
+
|
|
416
|
+
- **`source`** — where the record originated (`dashboard`, `sdk`, `discovered`)
|
|
417
|
+
- **`management`** — who may mutate the record (`dashboard`, `sdk`, `manifest`, `discovered`)
|
|
418
|
+
|
|
419
|
+
Manifest-managed resources typically use `management: manifest` so the CLI/SDK owns changes and the dashboard stays read-only for those records.
|
|
420
|
+
|
|
421
|
+
## Manifest SDK workflow
|
|
422
|
+
|
|
423
|
+
Install a YAML parser in your project:
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
npm install yaml
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Plan
|
|
430
|
+
|
|
431
|
+
```javascript
|
|
432
|
+
import fs from 'node:fs';
|
|
433
|
+
import { parse as parseYaml } from 'yaml';
|
|
434
|
+
import { ArbiterAdmin, validateManifestWorkspace } from '@arbiterhq/sdk';
|
|
435
|
+
|
|
436
|
+
const admin = new ArbiterAdmin({
|
|
437
|
+
apiKey: process.env.ARBITER_API_KEY,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const doc = parseYaml(fs.readFileSync('./arbiter.yaml', 'utf8'));
|
|
441
|
+
const manifest = validateManifestWorkspace(doc.spec);
|
|
442
|
+
|
|
443
|
+
const plan = await admin.plan(manifest);
|
|
444
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Sync (apply manifest)
|
|
448
|
+
|
|
449
|
+
High-level API — pass a parsed `ManifestWorkspace`:
|
|
450
|
+
|
|
451
|
+
```javascript
|
|
452
|
+
import fs from 'node:fs';
|
|
453
|
+
import { parse as parseYaml } from 'yaml';
|
|
454
|
+
import { ArbiterAdmin, validateManifestWorkspace } from '@arbiterhq/sdk';
|
|
455
|
+
|
|
456
|
+
const admin = new ArbiterAdmin({
|
|
457
|
+
apiKey: process.env.ARBITER_API_KEY,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const doc = parseYaml(fs.readFileSync('./arbiter.yaml', 'utf8'));
|
|
461
|
+
const manifest = validateManifestWorkspace(doc.spec);
|
|
462
|
+
|
|
463
|
+
const applied = await admin.syncRegistryManifest(manifest);
|
|
464
|
+
console.log(JSON.stringify(applied, null, 2));
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Drift
|
|
468
|
+
|
|
469
|
+
Drift is workspace-scoped — no manifest input:
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
import { ArbiterAdmin } from '@arbiterhq/sdk';
|
|
473
|
+
|
|
474
|
+
const admin = new ArbiterAdmin({
|
|
475
|
+
apiKey: process.env.ARBITER_API_KEY,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
const drift = await admin.getDrift();
|
|
479
|
+
console.log(JSON.stringify(drift, null, 2));
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### State signature
|
|
483
|
+
|
|
484
|
+
```javascript
|
|
485
|
+
import { ArbiterAdmin } from '@arbiterhq/sdk';
|
|
486
|
+
|
|
487
|
+
const admin = new ArbiterAdmin({
|
|
488
|
+
apiKey: process.env.ARBITER_API_KEY,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
const signature = await admin.getStateSignature();
|
|
492
|
+
console.log(signature.stateSignature);
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## Manifest API surface
|
|
496
|
+
|
|
497
|
+
| API | Level | Input | Use when |
|
|
498
|
+
|-----|-------|-------|----------|
|
|
499
|
+
| `plan(manifest)` | High-level | `ManifestWorkspace` | Preview changes from parsed manifest |
|
|
500
|
+
| `syncRegistryManifest(manifest)` | High-level | `ManifestWorkspace` | Apply parsed manifest to workspace |
|
|
501
|
+
| `getDrift()` | High-level | none | Compare live workspace drift |
|
|
502
|
+
| `getStateSignature()` | High-level | none | Read workspace governance fingerprint |
|
|
503
|
+
| `syncRegistry(input)` | Low-level transport | `SyncRegistryInput` | You already built the typed sync payload |
|
|
504
|
+
|
|
505
|
+
Prefer **`syncRegistryManifest()`** for manifest-driven workflows. Use **`syncRegistry()`** only when you construct the transport payload yourself.
|
|
506
|
+
|
|
507
|
+
## Runnable examples
|
|
508
|
+
|
|
509
|
+
See [`../../examples/discovery-first/`](../../examples/discovery-first/) for the canonical discovery-first onboarding flow.
|
|
510
|
+
|
|
511
|
+
See [`../../examples/manifest/`](../../examples/manifest/) for complete manifest scripts:
|
|
512
|
+
|
|
513
|
+
- `plan.mjs` — preview manifest changes
|
|
514
|
+
- `sync.mjs` — apply manifest changes
|
|
515
|
+
- `drift.mjs` — inspect workspace drift
|
|
516
|
+
- `state-signature.mjs` — read workspace governance fingerprint
|
|
517
|
+
- `loadManifest.mjs` — shared YAML loader with SDK validation
|
|
518
|
+
- `arbiter.yaml` — canonical manifest demonstrating supported fields
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
cd examples/manifest
|
|
522
|
+
npm install
|
|
523
|
+
export ARBITER_API_KEY=your_key
|
|
524
|
+
npm run plan
|
|
525
|
+
npm run sync
|
|
526
|
+
npm run drift
|
|
527
|
+
npm run state-signature
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## API Reference
|
|
531
|
+
|
|
532
|
+
### `ArbiterRuntime`
|
|
533
|
+
|
|
534
|
+
| Method | Description |
|
|
535
|
+
|--------|-------------|
|
|
536
|
+
| `connect(input?)` | `POST /runtime/connect` — agent discovery with workspace API key |
|
|
537
|
+
| `heartbeat()` | `POST /runtime/heartbeat` |
|
|
538
|
+
| `evaluate(input)` | `POST /evaluate` — permission discovery for unknown actions |
|
|
539
|
+
| `getApproval(id)` | `GET /approvals/:id` |
|
|
540
|
+
| `pollApproval(id, options?)` | Poll until approved/rejected/expired |
|
|
541
|
+
| `consumeRelease(input)` | `POST /releases/consume` |
|
|
542
|
+
| `getReceipt(evaluationId)` | `GET /evaluations/:id/receipt` |
|
|
543
|
+
|
|
544
|
+
### `ArbiterAdmin`
|
|
545
|
+
|
|
546
|
+
| Method | Description |
|
|
547
|
+
|--------|-------------|
|
|
548
|
+
| `registerAgent` | `POST /sdk/register-agent` |
|
|
549
|
+
| `registerPermission` | `POST /sdk/register-permission` |
|
|
550
|
+
| `grantPermission` | `POST /sdk/register-agent-permission` |
|
|
551
|
+
| `registerPolicy` | `POST /sdk/register-policy` |
|
|
552
|
+
| `plan(manifest)` | `POST /sdk/plan` — requires `ManifestWorkspace` |
|
|
553
|
+
| `syncRegistryManifest(manifest)` | `POST /sdk/sync-registry` — requires `ManifestWorkspace` |
|
|
554
|
+
| `syncRegistry(input)` | `POST /sdk/sync-registry` — low-level typed payload |
|
|
555
|
+
| `getDrift()` | `GET /manifest/drift` |
|
|
556
|
+
| `getStateSignature()` | `GET /state-signature` |
|
|
557
|
+
| `createAgentCredential` | `POST /agents/:id/credentials` |
|
|
558
|
+
| `createAgentCredentialByExternalId` | Resolve agent + create credential |
|
|
559
|
+
| `approve` / `reject` | Human session required |
|
|
560
|
+
|
|
561
|
+
## License
|
|
562
|
+
|
|
563
|
+
ISC
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ApprovalResult, EvaluateResult } from './types.js';
|
|
2
|
+
export interface ApprovalApiResponse {
|
|
3
|
+
id: string;
|
|
4
|
+
status: string;
|
|
5
|
+
evaluationId: string;
|
|
6
|
+
requestedAt: string;
|
|
7
|
+
approvedAt: string | null;
|
|
8
|
+
rejectedAt: string | null;
|
|
9
|
+
evaluation: {
|
|
10
|
+
agent: string;
|
|
11
|
+
action: string;
|
|
12
|
+
decision: EvaluateResult['decision'];
|
|
13
|
+
amount: number | null;
|
|
14
|
+
};
|
|
15
|
+
executionRelease: {
|
|
16
|
+
id: string;
|
|
17
|
+
status: string;
|
|
18
|
+
issuedAt: string;
|
|
19
|
+
expiresAt: string;
|
|
20
|
+
consumedAt: string | null;
|
|
21
|
+
} | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function mapApproval(response: ApprovalApiResponse): ApprovalResult;
|
|
24
|
+
export declare function isTerminalApprovalStatus(status: string): boolean;
|
|
25
|
+
export declare function resolveTerminalApprovalStatus(approval: ApprovalResult): string;
|
|
26
|
+
//# sourceMappingURL=approvalMapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalMapping.d.ts","sourceRoot":"","sources":["../src/approvalMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KACvB,CAAC;IACF,gBAAgB,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,GAAG,IAAI,CAAC;CACV;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,mBAAmB,GAAG,cAAc,CAgBzE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAM9E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function mapApproval(response) {
|
|
2
|
+
return {
|
|
3
|
+
id: response.id,
|
|
4
|
+
status: response.status,
|
|
5
|
+
evaluationId: response.evaluationId,
|
|
6
|
+
requestedAt: response.requestedAt,
|
|
7
|
+
approvedAt: response.approvedAt,
|
|
8
|
+
rejectedAt: response.rejectedAt,
|
|
9
|
+
evaluation: {
|
|
10
|
+
agent: response.evaluation.agent,
|
|
11
|
+
action: response.evaluation.action,
|
|
12
|
+
decision: response.evaluation.decision,
|
|
13
|
+
amount: response.evaluation.amount,
|
|
14
|
+
},
|
|
15
|
+
executionRelease: response.executionRelease,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function isTerminalApprovalStatus(status) {
|
|
19
|
+
return status === 'approved' || status === 'rejected' || status === 'expired';
|
|
20
|
+
}
|
|
21
|
+
export function resolveTerminalApprovalStatus(approval) {
|
|
22
|
+
if (approval.executionRelease?.status === 'expired') {
|
|
23
|
+
return 'expired';
|
|
24
|
+
}
|
|
25
|
+
return approval.status;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=approvalMapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalMapping.js","sourceRoot":"","sources":["../src/approvalMapping.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,WAAW,CAAC,QAA6B;IACvD,OAAO;QACL,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,UAAU,EAAE;YACV,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,KAAK;YAChC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,MAAM;YAClC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ;YACtC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,MAAM;SACnC;QACD,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;KAC5C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAO,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,SAAS,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,QAAwB;IACpE,IAAI,QAAQ,CAAC,gBAAgB,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ApprovalResult, ArbiterOptions, ConsumeReleaseInput, ConsumeReleaseResult, DriftResult, EvaluateInput, EvaluateResult, GrantPermissionInput, GrantPermissionResult, HumanActionOptions, ListApprovalsOptions, ManifestWorkspace, PlanResult, RegisterAgentInput, RegisterAgentResult, RegisterPermissionInput, RegisterPermissionResult, RegisterPolicyInput, RegisterPolicyResult, RejectApprovalOptions, StateSignatureResult, SyncRegistryResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Use {@link ArbiterAdmin} for control-plane operations and {@link ArbiterRuntime} for autonomous agent execution.
|
|
4
|
+
*/
|
|
5
|
+
export declare class Arbiter {
|
|
6
|
+
private readonly admin;
|
|
7
|
+
constructor(options: ArbiterOptions);
|
|
8
|
+
registerAgent(input: RegisterAgentInput): Promise<RegisterAgentResult>;
|
|
9
|
+
registerPermission(input: RegisterPermissionInput): Promise<RegisterPermissionResult>;
|
|
10
|
+
grantPermission(input: GrantPermissionInput): Promise<GrantPermissionResult>;
|
|
11
|
+
registerPolicy(input: RegisterPolicyInput): Promise<RegisterPolicyResult>;
|
|
12
|
+
evaluate(input: EvaluateInput): Promise<EvaluateResult>;
|
|
13
|
+
getApproval(id: string): Promise<ApprovalResult>;
|
|
14
|
+
listApprovals(options?: ListApprovalsOptions): Promise<ApprovalResult[]>;
|
|
15
|
+
approve(approvalRequestId: string, options: HumanActionOptions): Promise<ApprovalResult>;
|
|
16
|
+
reject(approvalRequestId: string, options: RejectApprovalOptions): Promise<ApprovalResult>;
|
|
17
|
+
consumeRelease(input: ConsumeReleaseInput): Promise<ConsumeReleaseResult>;
|
|
18
|
+
syncRegistry(manifest: ManifestWorkspace): Promise<SyncRegistryResult>;
|
|
19
|
+
plan(manifest: ManifestWorkspace): Promise<PlanResult>;
|
|
20
|
+
getDrift(): Promise<DriftResult>;
|
|
21
|
+
getStateSignature(): Promise<StateSignatureResult>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=arbiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arbiter.d.ts","sourceRoot":"","sources":["../src/arbiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAIpB;;GAEG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAElB,OAAO,EAAE,cAAc;IAW7B,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAItE,kBAAkB,CAC7B,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,wBAAwB,CAAC;IAIvB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAI5E,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAIzE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAIvD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAIhD,aAAa,CACxB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,cAAc,EAAE,CAAC;IAIf,OAAO,CAClB,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,cAAc,CAAC;IAIb,MAAM,CACjB,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC;IAIb,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAIzE,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAItE,IAAI,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IAItD,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAIhC,iBAAiB,IAAI,OAAO,CAAC,oBAAoB,CAAC;CAGhE"}
|