aira-sdk 3.2.0 → 3.5.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/README.md +136 -582
- package/dist/client.d.ts +67 -0
- package/dist/client.js +108 -0
- package/dist/extras/mcp.js +26 -0
- package/dist/types.d.ts +10 -4
- package/dist/types.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,500 +1,122 @@
|
|
|
1
|
-
# Aira TypeScript SDK
|
|
1
|
+
# Aira TypeScript SDK
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/aira-sdk)
|
|
4
|
-
[](LICENSE)
|
|
5
|
+
[](package.json)
|
|
6
|
+
|
|
7
|
+
Authorize every AI agent action before it runs. Sign every outcome with Ed25519.
|
|
8
|
+
|
|
9
|
+
## Table of contents
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick start](#quick-start)
|
|
13
|
+
- [Core methods](#core-methods)
|
|
14
|
+
- [Gateway](#gateway)
|
|
15
|
+
- [Framework integrations](#framework-integrations)
|
|
16
|
+
- [LangChain.js](#langchainjs)
|
|
17
|
+
- [Vercel AI SDK](#vercel-ai-sdk)
|
|
18
|
+
- [OpenAI Agents SDK](#openai-agents-sdk)
|
|
19
|
+
- [MCP](#mcp)
|
|
20
|
+
- [Content scanning](#content-scanning)
|
|
21
|
+
- [Compliance & DORA](#compliance--dora)
|
|
22
|
+
- [Self-hosted](#self-hosted)
|
|
23
|
+
- [Links](#links)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```sh
|
|
9
28
|
npm install aira-sdk
|
|
10
29
|
```
|
|
11
30
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install aira-sdk langchain # for LangChain.js
|
|
16
|
-
npm install aira-sdk ai # for Vercel AI
|
|
17
|
-
npm install aira-sdk @openai/agents # for OpenAI Agents
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Quick Start
|
|
23
|
-
|
|
24
|
-
Aira uses a **two-step flow**: `authorize()` BEFORE the action runs,
|
|
25
|
-
`notarize()` AFTER. This lets you block disallowed actions before they have
|
|
26
|
-
any real-world effect, and still produce a cryptographic receipt once the
|
|
27
|
-
action has completed.
|
|
31
|
+
## Quick start
|
|
28
32
|
|
|
29
33
|
```typescript
|
|
30
34
|
import { Aira, AiraError } from "aira-sdk";
|
|
31
35
|
|
|
32
36
|
const aira = new Aira({ apiKey: "aira_live_xxx" });
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
// Step 1 — ask Aira whether the action is allowed.
|
|
36
|
-
const auth = await aira.authorize({
|
|
37
|
-
actionType: "wire_transfer",
|
|
38
|
-
details: "Send €75K to vendor X",
|
|
39
|
-
agentId: "payments-agent",
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (auth.status === "authorized") {
|
|
43
|
-
// Step 2a — execute the action, then notarize the outcome.
|
|
44
|
-
const result = await sendWire(75_000, "vendor");
|
|
45
|
-
await aira.notarize({
|
|
46
|
-
actionId: auth.action_id,
|
|
47
|
-
outcome: "completed",
|
|
48
|
-
outcomeDetails: `Sent. ref=${result.id}`,
|
|
49
|
-
});
|
|
50
|
-
} else if (auth.status === "pending_approval") {
|
|
51
|
-
// Step 2b — enqueue for the approver flow; do NOT execute.
|
|
52
|
-
await queue.enqueue(auth.action_id);
|
|
53
|
-
}
|
|
54
|
-
} catch (e) {
|
|
55
|
-
// Step 2c — a policy denied the action. Nothing to execute, nothing to
|
|
56
|
-
// notarize. POLICY_DENIED is thrown, never returned as a status.
|
|
57
|
-
if (e instanceof AiraError && e.code === "POLICY_DENIED") {
|
|
58
|
-
console.log(`Blocked: ${e.message}`);
|
|
59
|
-
} else {
|
|
60
|
-
throw e;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
The returned `ActionReceipt` carries the Ed25519 signature and RFC 3161
|
|
66
|
-
timestamp token. If you pass `outcome: "failed"`, the backend still writes
|
|
67
|
-
an audit entry but leaves `signature` / `receipt_id` null.
|
|
68
|
-
|
|
69
|
-
### Reproducibility metadata (replay context)
|
|
70
|
-
|
|
71
|
-
Pass any of the following optional fields to `authorize()` and they're committed in the signed receipt payload (v1.3) and surfaced via `getReplayContext()` so an external replay tool can confirm it has the same inputs as the original run:
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
38
|
+
// 1. Authorize before the action runs
|
|
74
39
|
const auth = await aira.authorize({
|
|
75
|
-
actionType: "
|
|
76
|
-
details: "
|
|
77
|
-
agentId: "research-agent",
|
|
78
|
-
modelId: "claude-sonnet-4-6",
|
|
79
|
-
// Optional reproducibility metadata
|
|
80
|
-
systemPromptHash: "sha256:a1b2c3...",
|
|
81
|
-
toolInputsHash: "sha256:d4e5f6...",
|
|
82
|
-
modelParams: { temperature: 0.0, top_p: 1.0, seed: 42 },
|
|
83
|
-
executionEnv: {
|
|
84
|
-
sdk_version: "2.0.1",
|
|
85
|
-
framework: "langchain",
|
|
86
|
-
framework_version: "0.3.0",
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## Compliance bundles
|
|
94
|
-
|
|
95
|
-
Seal a regulator-ready evidence bundle for a date range. Every receipt in the period is Merkle-rooted, signed, and the export is JWKS-verifiable offline.
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
// Build a Q1 2026 EU AI Act Article 12 evidence packet
|
|
99
|
-
const bundle = await aira.createComplianceBundle({
|
|
100
|
-
framework: "eu_ai_act_art12", // or iso_42001, aiuc_1, soc_2_cc7, raw
|
|
101
|
-
periodStart: "2026-01-01T00:00:00Z",
|
|
102
|
-
periodEnd: "2026-04-01T00:00:00Z",
|
|
103
|
-
title: "Q1 2026 evidence packet",
|
|
104
|
-
agentFilter: ["payments-agent", "support-agent"],
|
|
105
|
-
});
|
|
106
|
-
console.log(bundle.merkle_root, bundle.receipt_count);
|
|
107
|
-
|
|
108
|
-
// Download the self-contained JSON for an auditor
|
|
109
|
-
const exported = await aira.exportComplianceBundle(bundle.id as string);
|
|
110
|
-
// `exported` includes every receipt, the JWKS URL, and a verification recipe.
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Drift detection
|
|
114
|
-
|
|
115
|
-
Per-agent behavioral baselines + KL divergence scoring + alerts when an agent's behavior shifts away from its expected pattern.
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
// Compute a baseline from the last 7 days of action history
|
|
119
|
-
const end = new Date();
|
|
120
|
-
const start = new Date(end.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
121
|
-
|
|
122
|
-
await aira.computeDriftBaseline({
|
|
40
|
+
actionType: "wire_transfer",
|
|
41
|
+
details: "Send EUR 75K to vendor X",
|
|
123
42
|
agentId: "payments-agent",
|
|
124
|
-
windowStart: start.toISOString(),
|
|
125
|
-
windowEnd: end.toISOString(),
|
|
126
43
|
});
|
|
127
44
|
|
|
128
|
-
//
|
|
129
|
-
await
|
|
130
|
-
agentId: "payments-agent",
|
|
131
|
-
expectedDistribution: { wire_transfer: 0.05, email_sent: 0.40, api_call: 0.55 },
|
|
132
|
-
expectedActionsPerDay: 200,
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Read-only status check for dashboards
|
|
136
|
-
const status = await aira.getDriftStatus("payments-agent", 24);
|
|
137
|
-
console.log(status.kl_divergence, status.severity);
|
|
138
|
-
|
|
139
|
-
// Run a check that records an alert if the threshold is exceeded
|
|
140
|
-
const alert = await aira.runDriftCheck("payments-agent");
|
|
141
|
-
if (alert) console.log(`Drift detected: ${alert.severity}`);
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## Merkle settlement
|
|
45
|
+
// 2. Execute your business logic
|
|
46
|
+
const result = await sendWire(75_000, "vendor");
|
|
145
47
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (settlement) {
|
|
152
|
-
console.log(settlement.merkle_root, settlement.receipt_count);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// An auditor wants to prove a single receipt was in a settlement
|
|
156
|
-
const proof = await aira.getSettlementInclusionProof("rct-abc-123");
|
|
157
|
-
// proof has { merkle_root, leaf_hash, index, leaf_count, siblings }
|
|
158
|
-
// A regulator can verify it offline with a 10-line pure-function walker.
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## Core SDK Methods
|
|
164
|
-
|
|
165
|
-
Every write operation produces a cryptographic receipt.
|
|
166
|
-
|
|
167
|
-
| Category | Method | Description |
|
|
168
|
-
|---|---|---|
|
|
169
|
-
| **Actions** | `authorize()` | Step 1 — authorize an action BEFORE it runs (throws POLICY_DENIED). Accepts optional replay context fields (`systemPromptHash`, `toolInputsHash`, `modelParams`, `executionEnv`). |
|
|
170
|
-
| | `notarize()` | Step 2 — notarize the outcome, returns Ed25519-signed receipt |
|
|
171
|
-
| | `getAction()` | Retrieve action details + receipt |
|
|
172
|
-
| | `listActions()` | List actions with filters (type, agent, status) |
|
|
173
|
-
| | `cosign()` | Human co-signature on a high-stakes action |
|
|
174
|
-
| | `setLegalHold()` | Prevent deletion -- litigation hold |
|
|
175
|
-
| | `releaseLegalHold()` | Release litigation hold |
|
|
176
|
-
| | `getActionChain()` | Chain of custody for an action |
|
|
177
|
-
| | `getReplayContext()` | All reproducibility metadata for an action (system prompt hash, tool inputs hash, model params, execution env) |
|
|
178
|
-
| | `verifyAction()` | Public verification -- no auth required. Returns full evidence (signature, public key, signed payload, RFC 3161 token) plus the second-party `policy_evaluator_attestation` for multi-party signing. |
|
|
179
|
-
| **Compliance** | `createComplianceBundle()` | Seal a regulator-ready evidence bundle for a date range. Frameworks: `eu_ai_act_art12`, `iso_42001`, `aiuc_1`, `soc_2_cc7`, `raw`. Merkle-rooted, signed, JWKS-verifiable offline. |
|
|
180
|
-
| | `listComplianceBundles()` | List bundles for the org |
|
|
181
|
-
| | `getComplianceBundle()` | Get bundle metadata |
|
|
182
|
-
| | `exportComplianceBundle()` | Download the self-contained JSON for offline verification |
|
|
183
|
-
| | `getBundleInclusionProof()` | Merkle inclusion proof for one receipt within a bundle |
|
|
184
|
-
| **Drift** | `getDriftStatus()` | Read-only KL divergence + volume ratio against the active baseline |
|
|
185
|
-
| | `computeDriftBaseline()` | Build a baseline from a window of production action history |
|
|
186
|
-
| | `seedSyntheticBaseline()` | Seed a baseline from a config dict (cold start) |
|
|
187
|
-
| | `runDriftCheck()` | Score the current window and persist a `DriftAlert` if it exceeds the threshold |
|
|
188
|
-
| | `listDriftAlerts()` | List drift alerts for an agent |
|
|
189
|
-
| | `acknowledgeDriftAlert()` | Acknowledge an alert |
|
|
190
|
-
| **Settlement** | `createSettlement()` | Seal every unsettled receipt into a Merkle-rooted, signed batch (admin-only) |
|
|
191
|
-
| | `listSettlements()` | List settlements |
|
|
192
|
-
| | `getSettlement()` | Get settlement metadata |
|
|
193
|
-
| | `getSettlementInclusionProof()` | Get a receipt's Merkle inclusion proof from its settlement |
|
|
194
|
-
| **Agents** | `registerAgent()` | Register verifiable agent identity |
|
|
195
|
-
| | `getAgent()` | Retrieve agent profile |
|
|
196
|
-
| | `listAgents()` | List registered agents |
|
|
197
|
-
| | `updateAgent()` | Update agent metadata |
|
|
198
|
-
| | `publishVersion()` | Publish versioned agent config |
|
|
199
|
-
| | `listVersions()` | List agent versions |
|
|
200
|
-
| | `decommissionAgent()` | Decommission agent |
|
|
201
|
-
| | `transferAgent()` | Transfer ownership to another org |
|
|
202
|
-
| | `getAgentActions()` | List actions by agent |
|
|
203
|
-
| **Trust Layer** | `getAgentDid()` | Retrieve agent's W3C DID (`did:web`) |
|
|
204
|
-
| | `rotateAgentKeys()` | Rotate agent's Ed25519 signing keys |
|
|
205
|
-
| | `getAgentCredential()` | Get agent's W3C Verifiable Credential |
|
|
206
|
-
| | `verifyCredential()` | Verify a Verifiable Credential |
|
|
207
|
-
| | `revokeCredential()` | Revoke agent's Verifiable Credential |
|
|
208
|
-
| | `requestMutualSign()` | Initiate mutual notarization with counterparty |
|
|
209
|
-
| | `completeMutualSign()` | Complete mutual notarization (counterparty signs) |
|
|
210
|
-
| | `getReputation()` | Get agent reputation score and tier |
|
|
211
|
-
| | `listReputationHistory()` | List reputation score history |
|
|
212
|
-
| | `resolveDid()` | Resolve any DID to its DID Document |
|
|
213
|
-
| **Cases** | `runCase()` | Multi-model consensus adjudication |
|
|
214
|
-
| | `getCase()` | Retrieve case result |
|
|
215
|
-
| | `listCases()` | List cases |
|
|
216
|
-
| **Receipts** | `getReceipt()` | Retrieve cryptographic receipt |
|
|
217
|
-
| | `exportReceipt()` | Export receipt as JSON or PDF |
|
|
218
|
-
| **Evidence** | `createEvidencePackage()` | Sealed, tamper-proof evidence bundle |
|
|
219
|
-
| | `listEvidencePackages()` | List evidence packages |
|
|
220
|
-
| | `getEvidencePackage()` | Retrieve evidence package |
|
|
221
|
-
| | `timeTravel()` | Query actions at a point in time |
|
|
222
|
-
| | `liabilityChain()` | Walk full liability chain |
|
|
223
|
-
| **Estate** | `setAgentWill()` | Define succession plan |
|
|
224
|
-
| | `getAgentWill()` | Retrieve agent will |
|
|
225
|
-
| | `issueDeathCertificate()` | Decommission with succession trigger |
|
|
226
|
-
| | `getDeathCertificate()` | Retrieve death certificate |
|
|
227
|
-
| | `createComplianceSnapshot()` | Compliance snapshot (EU AI Act, SR 11-7, GDPR) |
|
|
228
|
-
| | `listComplianceSnapshots()` | List snapshots by framework |
|
|
229
|
-
| **Escrow** | `createEscrowAccount()` | Create liability commitment ledger |
|
|
230
|
-
| | `listEscrowAccounts()` | List escrow accounts |
|
|
231
|
-
| | `getEscrowAccount()` | Retrieve escrow account |
|
|
232
|
-
| | `escrowDeposit()` | Record liability commitment |
|
|
233
|
-
| | `escrowRelease()` | Release commitment after completion |
|
|
234
|
-
| | `escrowDispute()` | Dispute -- flag liability issue |
|
|
235
|
-
| **Chat** | `ask()` | Query your notarized data via AI |
|
|
236
|
-
| **Offline** | `sync()` | Flush offline queue to API |
|
|
237
|
-
| **Session** | `session()` | Scoped session with pre-filled defaults |
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## Trust Layer
|
|
242
|
-
|
|
243
|
-
Standards-based identity and trust for agents: W3C DIDs, Verifiable Credentials, mutual notarization, and reputation scoring. Every agent gets a cryptographically verifiable identity that other agents (and humans) can check before interacting.
|
|
244
|
-
|
|
245
|
-
### DID Identity
|
|
246
|
-
|
|
247
|
-
Every registered agent gets a W3C-compliant DID (`did:web`):
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// Retrieve the agent's DID
|
|
251
|
-
const did = await aira.getAgentDid("my-agent");
|
|
252
|
-
console.log(did); // "did:web:airaproof.com:agents:my-agent"
|
|
253
|
-
|
|
254
|
-
// Rotate signing keys (old keys are revoked, new keys are published)
|
|
255
|
-
await aira.rotateAgentKeys("my-agent");
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Verifiable Credentials
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
// Get the agent's W3C Verifiable Credential
|
|
262
|
-
const vc = await aira.getAgentCredential("my-agent");
|
|
263
|
-
|
|
264
|
-
// Verify any VC (returns validity, issuer, expiry)
|
|
265
|
-
const result = await aira.verifyCredential(vc);
|
|
266
|
-
console.log(result.valid); // true
|
|
267
|
-
|
|
268
|
-
// Revoke a credential
|
|
269
|
-
await aira.revokeCredential("my-agent", { reason: "Agent deprecated" });
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### Mutual Notarization
|
|
273
|
-
|
|
274
|
-
For high-stakes actions, both parties co-sign:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
// Agent A initiates — sends a signing request to the counterparty
|
|
278
|
-
const request = await aira.requestMutualSign({
|
|
279
|
-
actionId: "act-uuid",
|
|
280
|
-
counterpartyDid: "did:web:partner.com:agents:their-agent",
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// Agent B completes — signs the same payload
|
|
284
|
-
const receipt = await aira.completeMutualSign({
|
|
285
|
-
actionId: "act-uuid",
|
|
286
|
-
did: "did:web:partner.com:agents:their-agent",
|
|
287
|
-
signature: "z...",
|
|
288
|
-
signedPayloadHash: "sha256:...",
|
|
48
|
+
// 3. Notarize the outcome -- returns an Ed25519-signed receipt
|
|
49
|
+
const receipt = await aira.notarize({
|
|
50
|
+
actionId: auth.action_id,
|
|
51
|
+
outcome: "completed",
|
|
52
|
+
outcomeDetails: `Sent. ref=${result.id}`,
|
|
289
53
|
});
|
|
290
54
|
```
|
|
291
55
|
|
|
292
|
-
|
|
56
|
+
If the policy denies the action, `authorize()` throws an `AiraError` with code `POLICY_DENIED`. Actions requiring human approval return `status: "pending_approval"` -- listen for the webhook or poll `getAction()`.
|
|
293
57
|
|
|
294
|
-
|
|
295
|
-
const rep = await aira.getReputation("my-agent");
|
|
296
|
-
console.log(rep.score); // 84
|
|
297
|
-
console.log(rep.tier); // "Verified"
|
|
298
|
-
```
|
|
58
|
+
> **Universal receipts** — Every action — authorized, denied, or failed — produces an Ed25519 receipt. The audit trail has zero gaps.
|
|
299
59
|
|
|
300
|
-
|
|
60
|
+
## Core methods
|
|
301
61
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
62
|
+
| Method | Description |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `authorize()` | Gate an action **before** it runs |
|
|
65
|
+
| `notarize()` | Sign the outcome **after** execution (Ed25519 + RFC 3161) |
|
|
66
|
+
| `verifyAction()` | Verify a receipt -- no auth required |
|
|
67
|
+
| `getAction()` / `listActions()` | Retrieve action details and history |
|
|
68
|
+
| `cosign()` | Human co-signature on high-stakes actions |
|
|
69
|
+
| `registerAgent()` / `getAgent()` | Verifiable agent identity (W3C DID) |
|
|
70
|
+
| `createComplianceBundle()` | Regulator-ready evidence for a date range |
|
|
71
|
+
| `computeDriftBaseline()` / `runDriftCheck()` | Behavioral drift detection (KL divergence) |
|
|
72
|
+
| `createSettlement()` | Merkle-anchor a batch of receipts |
|
|
73
|
+
| `runCase()` | Multi-model consensus adjudication |
|
|
74
|
+
| `ask()` | Query your notarized data via AI |
|
|
75
|
+
| `createComplianceReport()` | Generate a compliance report (EU AI Act, ISO 42001, SOC 2) |
|
|
76
|
+
| `createDoraIncident()` | Report a DORA ICT incident |
|
|
77
|
+
| `getOutputPolicy()` / `updateOutputPolicy()` | Content-scan policy for notarized outputs |
|
|
78
|
+
| `getActionExplanation()` | Human-readable explanation of an action decision |
|
|
307
79
|
|
|
308
|
-
|
|
309
|
-
import { Aira, AiraError } from "aira-sdk";
|
|
80
|
+
60+ methods total. See the [API reference](https://docs.airaproof.com/docs/api-reference) for the full list.
|
|
310
81
|
|
|
311
|
-
|
|
312
|
-
const auth = await aira.authorize({
|
|
313
|
-
actionType: "api_call",
|
|
314
|
-
details: "Charged customer $49.99 for subscription renewal",
|
|
315
|
-
agentId: "billing-agent",
|
|
316
|
-
modelId: "claude-sonnet-4-6",
|
|
317
|
-
endpointUrl: "https://api.stripe.com/v1/charges",
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
if (auth.status === "authorized") {
|
|
321
|
-
const result = await stripe.charges.create({ amount: 4999, /* ... */ });
|
|
322
|
-
await aira.notarize({
|
|
323
|
-
actionId: auth.action_id,
|
|
324
|
-
outcome: "completed",
|
|
325
|
-
outcomeDetails: `Charged ${result.id}`,
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
} catch (e) {
|
|
329
|
-
if (e instanceof AiraError && e.code === "ENDPOINT_NOT_WHITELISTED") {
|
|
330
|
-
console.log(`Blocked: ${e.message}`);
|
|
331
|
-
} else {
|
|
332
|
-
throw e;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### Trust Policy in Integrations
|
|
82
|
+
## Gateway
|
|
338
83
|
|
|
339
|
-
|
|
84
|
+
Route existing LLM calls through Aira with zero code changes. Every request is authorized and logged automatically.
|
|
340
85
|
|
|
341
86
|
```typescript
|
|
342
|
-
import
|
|
87
|
+
import OpenAI from "openai";
|
|
88
|
+
import { gatewayOpenAIConfig } from "aira-sdk/gateway";
|
|
343
89
|
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
verifyCounterparty: true, // resolve counterparty DID
|
|
348
|
-
minReputation: 60, // warn if reputation score below 60
|
|
349
|
-
requireValidVc: true, // check Verifiable Credential validity
|
|
350
|
-
blockRevokedVc: true, // block if counterparty VC is revoked
|
|
351
|
-
blockUnregistered: false, // don't block agents without Aira DIDs
|
|
352
|
-
},
|
|
90
|
+
const client = new OpenAI({
|
|
91
|
+
...gatewayOpenAIConfig({ airaApiKey: "aira_live_..." }),
|
|
92
|
+
apiKey: "sk-...",
|
|
353
93
|
});
|
|
94
|
+
// Use `client` exactly as before -- Aira gates every call
|
|
354
95
|
```
|
|
355
96
|
|
|
356
|
-
|
|
97
|
+
Works with OpenAI, Azure OpenAI, and Anthropic:
|
|
357
98
|
|
|
358
|
-
|
|
99
|
+
| Helper | Provider |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `gatewayOpenAIConfig()` | OpenAI, Azure OpenAI, any OpenAI-compatible API |
|
|
102
|
+
| `gatewayAnthropicConfig()` | Anthropic |
|
|
359
103
|
|
|
360
|
-
|
|
361
|
-
within the session inherits the agent identity, producing receipts that
|
|
362
|
-
share a common provenance chain.
|
|
104
|
+
Both helpers accept a `gatewayUrl` option for [self-hosted](#self-hosted) deployments.
|
|
363
105
|
|
|
364
|
-
|
|
365
|
-
const sess = aira.session("onboarding-agent", { modelId: "claude-sonnet-4-6" });
|
|
106
|
+
## Framework integrations
|
|
366
107
|
|
|
367
|
-
|
|
368
|
-
const auth = await sess.authorize({ actionType, details });
|
|
369
|
-
if (auth.status === "authorized") {
|
|
370
|
-
await sess.notarize({ actionId: auth.action_id, outcome: "completed" });
|
|
371
|
-
}
|
|
372
|
-
}
|
|
108
|
+
Each integration is a sub-import path. Install the peer dependency alongside `aira-sdk`.
|
|
373
109
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
## Offline Mode
|
|
382
|
-
|
|
383
|
-
Queue `authorize()` calls locally when connectivity is unavailable. On
|
|
384
|
-
`sync()`, the backend runs the real authorizations and returns their
|
|
385
|
-
results. The agent can then call `notarize()` per action to close the loop.
|
|
386
|
-
|
|
387
|
-
```typescript
|
|
388
|
-
const aira = new Aira({ apiKey: "aira_live_xxx", offline: true });
|
|
389
|
-
|
|
390
|
-
// These queue locally — no network calls
|
|
391
|
-
await aira.authorize({ actionType: "scan_completed", details: "Scanned document batch #77" });
|
|
392
|
-
await aira.authorize({ actionType: "classification_done", details: "Classified 142 documents" });
|
|
393
|
-
|
|
394
|
-
console.log(aira.pendingCount); // 2
|
|
395
|
-
|
|
396
|
-
// Flush to the API when back online — returns the Authorization for each
|
|
397
|
-
// queued request. Use the action_ids to notarize outcomes afterwards.
|
|
398
|
-
const results = await aira.sync();
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
Offline mode is intended for actions the agent will execute regardless of
|
|
402
|
-
Aira's decision (sensor reads, local scans). For actions whose execution
|
|
403
|
-
depends on the authorization result, run the SDK online.
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Human Approval
|
|
408
|
-
|
|
409
|
-
Hold high-stakes actions for human review before they execute. Approvers
|
|
410
|
-
receive an email with Approve/Deny buttons. If the action is held, the SDK
|
|
411
|
-
returns `status: "pending_approval"` from `authorize()` and the agent must
|
|
412
|
-
enqueue the `action_id` instead of executing.
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
const auth = await aira.authorize({
|
|
416
|
-
actionType: "loan_decision",
|
|
417
|
-
details: "Approve €15,000 loan for Maria Schmidt",
|
|
418
|
-
agentId: "lending-agent",
|
|
419
|
-
requireApproval: true,
|
|
420
|
-
approvers: ["compliance@acme.com", "risk@acme.com"],
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
if (auth.status === "pending_approval") {
|
|
424
|
-
// Do NOT execute the action. Store action_id and wait for the
|
|
425
|
-
// action.approved webhook, then execute + notarize.
|
|
426
|
-
await queue.enqueue(auth.action_id);
|
|
427
|
-
}
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
The approver clicks "Approve" in the email → the action transitions to
|
|
431
|
-
`authorized` → `action.approved` webhook fires → your handler executes the
|
|
432
|
-
action and calls `aira.notarize({ actionId, outcome: "completed" })`.
|
|
433
|
-
|
|
434
|
-
Configure default approvers in the [dashboard](https://app.airaproof.com/dashboard/settings/approvers).
|
|
435
|
-
|
|
436
|
-
### Automatic Policy Evaluation
|
|
437
|
-
|
|
438
|
-
Org admins configure policies in the dashboard — your code doesn't change.
|
|
439
|
-
Every `authorize()` call is automatically evaluated against active policies
|
|
440
|
-
before returning. If a policy denies the action, the SDK throws
|
|
441
|
-
`AiraError` with code `POLICY_DENIED` and the action is never persisted as
|
|
442
|
-
authorized. If a policy forces human review, `status` is
|
|
443
|
-
`pending_approval`. Otherwise `status` is `authorized`.
|
|
444
|
-
|
|
445
|
-
Three evaluation modes:
|
|
446
|
-
|
|
447
|
-
- **Rules**: Deterministic conditions — instant, no LLM call
|
|
448
|
-
- **AI**: Single LLM evaluates action against a natural language policy (1-5s)
|
|
449
|
-
- **Consensus**: Multiple LLMs evaluate independently — disagreement triggers human review (3-10s)
|
|
450
|
-
|
|
451
|
-
```typescript
|
|
452
|
-
import { AiraError } from "aira-sdk";
|
|
453
|
-
|
|
454
|
-
try {
|
|
455
|
-
const auth = await aira.authorize({
|
|
456
|
-
actionType: "data_deletion",
|
|
457
|
-
details: "Delete customer records",
|
|
458
|
-
agentId: "support-agent",
|
|
459
|
-
});
|
|
460
|
-
// ... execute + notarize ...
|
|
461
|
-
} catch (e) {
|
|
462
|
-
if (e instanceof AiraError && e.code === "POLICY_DENIED") {
|
|
463
|
-
console.log(e.message); // "Action denied by policy 'Block deletions': ..."
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
Configure policies at [Settings → Policies](https://app.airaproof.com/dashboard/policies).
|
|
469
|
-
|
|
470
|
-
---
|
|
471
|
-
|
|
472
|
-
## Framework Integrations
|
|
473
|
-
|
|
474
|
-
Drop Aira into your existing agent framework with one import. Every integration is honestly labeled as one of three kinds:
|
|
475
|
-
|
|
476
|
-
- **gate** — intercepts before execution and can deny. The action is authorized through Aira's policy engine *before* the framework runs the underlying call. Denied actions never run.
|
|
477
|
-
- **audit** — runs after execution because the host framework does not expose a pre-execution hook that can abort. Aira still records a signed receipt; it just cannot prevent the action.
|
|
478
|
-
- **adapter** — exposes Aira's own API as a tool the host framework can call. Neither a gate nor an audit hook over other tools.
|
|
479
|
-
|
|
480
|
-
We ship fewer integrations than some competitors and label every one of them honestly. The integration matrix is generated from `INTEGRATIONS` in `aira-sdk/extras` — the docs cannot drift from the code.
|
|
481
|
-
|
|
482
|
-
| Integration | Import | Type | Pre-execution gate? | Surface | Notes |
|
|
483
|
-
|---|---|---|---|---|---|
|
|
484
|
-
| **LangChain.js** | `aira-sdk/extras/langchain` | gate | Yes (tools); No (chains/LLMs) | `AiraCallbackHandler` | `handleToolStart` calls `authorize()` and throws on `POLICY_DENIED` so the tool never runs. Chain/LLM hooks are post-hoc because LangChain has no pre-execution chain hook that can abort. |
|
|
485
|
-
| **Vercel AI SDK** | `aira-sdk/extras/vercel-ai` | gate | Yes (`wrapTool`); No (`onFinish`) | `AiraVercelMiddleware` | `wrapTool()` wraps the tool's `execute` so `authorize()` runs before the tool body. `onStepFinish` / `onFinish` callbacks are explicitly labeled audit-only. |
|
|
486
|
-
| **OpenAI Agents** | `aira-sdk/extras/openai-agents` | gate | Yes | `AiraGuardrail.wrapTool()` | Wraps each tool function: `authorize()` runs before the tool body. Denied calls throw; failed calls notarize with `outcome="failed"`. |
|
|
487
|
-
| **MCP** | `aira-sdk/extras/mcp` | adapter | N/A | `createServer()` | MCP is bidirectional: the agent CHOOSES to call `authorize_action` / `notarize_action`. Not a wrapper over other MCP tools — it's a protocol adapter. |
|
|
488
|
-
| **Webhooks** | `aira-sdk/extras/webhooks` | adapter | N/A | `verifySignature()` | Standalone HMAC-SHA256 webhook signature verifier. Not an agent integration. |
|
|
110
|
+
| Framework | Import path | Install |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| LangChain.js | `aira-sdk/extras/langchain` | `npm i aira-sdk @langchain/core` |
|
|
113
|
+
| Vercel AI SDK | `aira-sdk/extras/vercel-ai` | `npm i aira-sdk ai` |
|
|
114
|
+
| OpenAI Agents SDK | `aira-sdk/extras/openai-agents` | `npm i aira-sdk openai` |
|
|
115
|
+
| MCP | `aira-sdk/extras/mcp` | `npm i aira-sdk @modelcontextprotocol/sdk` |
|
|
116
|
+
| Webhooks | `aira-sdk/extras/webhooks` | `npm i aira-sdk` |
|
|
489
117
|
|
|
490
118
|
### LangChain.js
|
|
491
119
|
|
|
492
|
-
`AiraCallbackHandler` runs the two-step flow on every tool, chain, and LLM
|
|
493
|
-
event. The `Start` callbacks call `authorize()` — if a policy denies the
|
|
494
|
-
action or flags it for human review, LangChain aborts the step. The `End`
|
|
495
|
-
and `Error` callbacks call `notarize()` with the appropriate outcome. This
|
|
496
|
-
is a **real authorization gate**, not just post-hoc audit logging.
|
|
497
|
-
|
|
498
120
|
```typescript
|
|
499
121
|
import { Aira } from "aira-sdk";
|
|
500
122
|
import { AiraCallbackHandler } from "aira-sdk/extras/langchain";
|
|
@@ -502,7 +124,6 @@ import { AiraCallbackHandler } from "aira-sdk/extras/langchain";
|
|
|
502
124
|
const aira = new Aira({ apiKey: "aira_live_xxx" });
|
|
503
125
|
const handler = new AiraCallbackHandler(aira, "research-agent", {
|
|
504
126
|
modelId: "gpt-5.2",
|
|
505
|
-
strict: false, // fail-open on network errors; set true to fail-closed
|
|
506
127
|
});
|
|
507
128
|
|
|
508
129
|
const result = await chain.invoke(
|
|
@@ -511,16 +132,7 @@ const result = await chain.invoke(
|
|
|
511
132
|
);
|
|
512
133
|
```
|
|
513
134
|
|
|
514
|
-
### Vercel AI
|
|
515
|
-
|
|
516
|
-
`AiraVercelMiddleware` exposes two integration points:
|
|
517
|
-
|
|
518
|
-
- **`wrapTool()`** — the real authorization gate. Calls `authorize()`
|
|
519
|
-
before the tool runs, notarizes the outcome afterwards. Use this for any
|
|
520
|
-
tool that touches the outside world.
|
|
521
|
-
- **`onStepFinish` / `onFinish`** — post-hoc audit callbacks. These fire
|
|
522
|
-
after the step has run and cannot gate execution. Useful for logging
|
|
523
|
-
generation metadata.
|
|
135
|
+
### Vercel AI SDK
|
|
524
136
|
|
|
525
137
|
```typescript
|
|
526
138
|
import { Aira } from "aira-sdk";
|
|
@@ -528,8 +140,10 @@ import { AiraVercelMiddleware } from "aira-sdk/extras/vercel-ai";
|
|
|
528
140
|
import { tool } from "ai";
|
|
529
141
|
import { z } from "zod";
|
|
530
142
|
|
|
531
|
-
const
|
|
532
|
-
|
|
143
|
+
const middleware = new AiraVercelMiddleware(
|
|
144
|
+
new Aira({ apiKey: "aira_live_xxx" }),
|
|
145
|
+
"assistant-agent",
|
|
146
|
+
);
|
|
533
147
|
|
|
534
148
|
const webSearch = tool({
|
|
535
149
|
description: "Search the web",
|
|
@@ -538,58 +152,23 @@ const webSearch = tool({
|
|
|
538
152
|
return await search(query);
|
|
539
153
|
}, "web_search"),
|
|
540
154
|
});
|
|
541
|
-
|
|
542
|
-
const result = await generateText({
|
|
543
|
-
model: openai("gpt-5.2"),
|
|
544
|
-
prompt: "Find today's EU AI Act news",
|
|
545
|
-
tools: { webSearch },
|
|
546
|
-
...middleware.asCallbacks(),
|
|
547
|
-
});
|
|
548
155
|
```
|
|
549
156
|
|
|
550
157
|
### OpenAI Agents SDK
|
|
551
158
|
|
|
552
|
-
`AiraGuardrail.wrapTool()` gates every tool invocation through Aira's
|
|
553
|
-
two-step flow: `authorize()` runs first and can block the tool on
|
|
554
|
-
`POLICY_DENIED` or `pending_approval`, then `notarize()` closes the loop
|
|
555
|
-
after the tool returns. Only tool names and arg keys are sent — raw user
|
|
556
|
-
input stays in your process.
|
|
557
|
-
|
|
558
159
|
```typescript
|
|
559
160
|
import { Aira } from "aira-sdk";
|
|
560
161
|
import { AiraGuardrail } from "aira-sdk/extras/openai-agents";
|
|
561
162
|
|
|
562
|
-
const
|
|
563
|
-
|
|
163
|
+
const guardrail = new AiraGuardrail(
|
|
164
|
+
new Aira({ apiKey: "aira_live_xxx" }),
|
|
165
|
+
"assistant-agent",
|
|
166
|
+
);
|
|
564
167
|
|
|
565
168
|
const search = guardrail.wrapTool(searchTool, "web_search");
|
|
566
|
-
const execute = guardrail.wrapTool(codeExecutor, "code_exec");
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
---
|
|
570
|
-
|
|
571
|
-
## MCP Server
|
|
572
|
-
|
|
573
|
-
Expose Aira as an MCP tool server. Any MCP-compatible AI agent can run the
|
|
574
|
-
two-step flow and verify receipts without a direct SDK dependency.
|
|
575
|
-
|
|
576
|
-
```typescript
|
|
577
|
-
import { Aira } from "aira-sdk";
|
|
578
|
-
import { createServer } from "aira-sdk/extras/mcp";
|
|
579
|
-
|
|
580
|
-
const aira = new Aira({ apiKey: "aira_live_xxx" });
|
|
581
|
-
const { listTools, callTool } = createServer(aira);
|
|
582
|
-
// Wire into @modelcontextprotocol/sdk's Server.
|
|
583
169
|
```
|
|
584
170
|
|
|
585
|
-
|
|
586
|
-
`authorize_action`, `notarize_action`, `get_action`, `verify_action`,
|
|
587
|
-
`get_receipt`, plus trust-layer helpers. An MCP client is expected to call
|
|
588
|
-
`authorize_action` before performing a side effect and `notarize_action`
|
|
589
|
-
after — the MCP protocol has no hidden hook point, so the authorization
|
|
590
|
-
gate only exists if the agent cooperates with the contract.
|
|
591
|
-
|
|
592
|
-
Add to your MCP client config:
|
|
171
|
+
### MCP
|
|
593
172
|
|
|
594
173
|
```json
|
|
595
174
|
{
|
|
@@ -603,104 +182,79 @@ Add to your MCP client config:
|
|
|
603
182
|
}
|
|
604
183
|
```
|
|
605
184
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
## Webhook Verification
|
|
185
|
+
## Content scanning
|
|
609
186
|
|
|
610
|
-
|
|
187
|
+
Aira can scan content passed to `notarize()` and block or flag policy violations. Configure the org-level policy:
|
|
611
188
|
|
|
612
189
|
```typescript
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const isValid = verifySignature({
|
|
617
|
-
payload: request.body,
|
|
618
|
-
signature: request.headers["x-aira-signature"],
|
|
619
|
-
secret: "whsec_xxx",
|
|
190
|
+
const policy = await aira.updateOutputPolicy({
|
|
191
|
+
mode: "block",
|
|
192
|
+
categories: ["pii", "secrets", "malware"],
|
|
620
193
|
});
|
|
621
194
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
console.log(event.eventType); // "action.notarized"
|
|
625
|
-
console.log(event.data); // Action data with cryptographic receipt
|
|
626
|
-
console.log(event.deliveryId); // Unique delivery ID
|
|
627
|
-
}
|
|
195
|
+
// If outcomeDetails triggers a scan hit, notarize() returns 422
|
|
196
|
+
// with code OUTPUT_SCAN_VIOLATION
|
|
628
197
|
```
|
|
629
198
|
|
|
630
|
-
|
|
199
|
+
Retrieve the current policy at any time with `getOutputPolicy()`.
|
|
631
200
|
|
|
632
|
-
|
|
201
|
+
## Compliance & DORA
|
|
633
202
|
|
|
634
|
-
|
|
203
|
+
Generate regulator-ready evidence packets and manage ICT incidents under DORA:
|
|
635
204
|
|
|
636
205
|
```typescript
|
|
637
|
-
|
|
206
|
+
// Compliance bundle (EU AI Act, ISO 42001, SOC 2)
|
|
207
|
+
const bundle = await aira.createComplianceBundle({
|
|
208
|
+
startDate: "2026-01-01",
|
|
209
|
+
endDate: "2026-03-31",
|
|
210
|
+
framework: "eu_ai_act",
|
|
211
|
+
});
|
|
638
212
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
213
|
+
// DORA incident reporting
|
|
214
|
+
const incident = await aira.createDoraIncident({
|
|
215
|
+
title: "API gateway outage",
|
|
216
|
+
description: "Payment processing unavailable for 45 minutes",
|
|
217
|
+
severity: "major",
|
|
218
|
+
detectedAt: new Date().toISOString(),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Compliance report with PDF download
|
|
222
|
+
const report = await aira.createComplianceReport({
|
|
223
|
+
framework: "eu_ai_act",
|
|
224
|
+
startDate: "2026-01-01",
|
|
225
|
+
endDate: "2026-03-31",
|
|
226
|
+
});
|
|
227
|
+
const pdf = await aira.downloadComplianceReport(report.id);
|
|
649
228
|
```
|
|
650
229
|
|
|
651
|
-
|
|
652
|
-
by default** on transient errors (network, 5xx) — a warning is logged and
|
|
653
|
-
the tool still runs. `POLICY_DENIED` and `pending_approval` always
|
|
654
|
-
propagate as thrown errors so disallowed actions are never executed. Pass
|
|
655
|
-
`strict: true` to the integration constructor to fail closed on transient
|
|
656
|
-
errors too.
|
|
230
|
+
Additional DORA methods: `classifyDoraIncident()`, `resolveDoraIncident()`, `createDoraTest()`, `createIctThirdParty()`, and more.
|
|
657
231
|
|
|
658
|
-
|
|
232
|
+
## Self-hosted
|
|
659
233
|
|
|
660
|
-
|
|
234
|
+
Point the SDK at your own deployment:
|
|
661
235
|
|
|
662
236
|
```typescript
|
|
663
237
|
const aira = new Aira({
|
|
664
|
-
apiKey: "aira_live_xxx",
|
|
665
|
-
baseUrl: "https://your-
|
|
666
|
-
timeout: 60_000, // Request timeout in ms
|
|
667
|
-
offline: true, // Queue locally, sync later
|
|
238
|
+
apiKey: "aira_live_xxx",
|
|
239
|
+
baseUrl: "https://aira.your-company.com",
|
|
668
240
|
});
|
|
669
241
|
```
|
|
670
242
|
|
|
671
|
-
|
|
672
|
-
|---|---|
|
|
673
|
-
| `AIRA_API_KEY` | API key (used by MCP server) |
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
## SDK Parity
|
|
243
|
+
Gateway helpers also accept a `gatewayUrl` parameter:
|
|
678
244
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
| Vercel AI | -- (JS-only) | Yes |
|
|
686
|
-
| OpenAI Agents | Yes | Yes |
|
|
687
|
-
| Google ADK | Yes (Python-only) | -- |
|
|
688
|
-
| AWS Bedrock | Yes (Python-only) | -- |
|
|
689
|
-
| MCP Server | Yes | Yes |
|
|
690
|
-
| Webhooks | Yes | Yes |
|
|
691
|
-
| CLI | Yes | -- (use Python CLI) |
|
|
692
|
-
| Offline Mode | Yes | Yes |
|
|
693
|
-
| Session | Yes | Yes |
|
|
694
|
-
|
|
695
|
-
---
|
|
245
|
+
```typescript
|
|
246
|
+
gatewayOpenAIConfig({
|
|
247
|
+
airaApiKey: "aira_live_...",
|
|
248
|
+
gatewayUrl: "https://aira.your-company.com/gateway",
|
|
249
|
+
});
|
|
250
|
+
```
|
|
696
251
|
|
|
697
252
|
## Links
|
|
698
253
|
|
|
699
|
-
- [Website](https://airaproof.com)
|
|
700
|
-
- [npm Package](https://www.npmjs.com/package/aira-sdk)
|
|
701
|
-
- [Python SDK (PyPI)](https://pypi.org/project/aira-sdk/)
|
|
702
254
|
- [Documentation](https://docs.airaproof.com)
|
|
703
255
|
- [API Reference](https://docs.airaproof.com/docs/api-reference)
|
|
704
|
-
- [
|
|
256
|
+
- [Gateway Guide](https://docs.airaproof.com/docs/gateway)
|
|
705
257
|
- [Dashboard](https://app.airaproof.com)
|
|
258
|
+
- [Interactive Demo](https://app.airaproof.com/demo)
|
|
706
259
|
- [GitHub](https://github.com/aira-proof/typescript-sdk)
|
|
260
|
+
- [Python SDK](https://pypi.org/project/aira-sdk/)
|
package/dist/client.d.ts
CHANGED
|
@@ -19,6 +19,73 @@ export declare class Aira {
|
|
|
19
19
|
private patch;
|
|
20
20
|
private del;
|
|
21
21
|
private paginated;
|
|
22
|
+
/** Create a governance policy. */
|
|
23
|
+
createPolicy(params: {
|
|
24
|
+
name: string;
|
|
25
|
+
mode: string;
|
|
26
|
+
decision?: string;
|
|
27
|
+
priority?: number;
|
|
28
|
+
conditions?: Array<{
|
|
29
|
+
field: string;
|
|
30
|
+
op: string;
|
|
31
|
+
value: any;
|
|
32
|
+
}>;
|
|
33
|
+
aiPrompt?: string;
|
|
34
|
+
aiModels?: string[];
|
|
35
|
+
scanConfig?: Record<string, any>;
|
|
36
|
+
description?: string;
|
|
37
|
+
}): Promise<Record<string, any>>;
|
|
38
|
+
/** List all active policies. */
|
|
39
|
+
listPolicies(): Promise<Array<Record<string, any>>>;
|
|
40
|
+
/** Get a single policy by ID. */
|
|
41
|
+
getPolicy(policyId: string): Promise<Record<string, any>>;
|
|
42
|
+
/** Update a policy. Pass only fields to change. */
|
|
43
|
+
updatePolicy(policyId: string, params: Record<string, any>): Promise<Record<string, any>>;
|
|
44
|
+
/** Delete a policy. */
|
|
45
|
+
deletePolicy(policyId: string): Promise<Record<string, any>>;
|
|
46
|
+
/** Activate a policy. */
|
|
47
|
+
activatePolicy(policyId: string): Promise<Record<string, any>>;
|
|
48
|
+
/** Deactivate a policy. */
|
|
49
|
+
deactivatePolicy(policyId: string): Promise<Record<string, any>>;
|
|
50
|
+
/** Test a policy against an action context without executing it. */
|
|
51
|
+
dryRunPolicy(policyId: string, actionContext: Record<string, any>): Promise<Record<string, any>>;
|
|
52
|
+
/** Scan text for PII, PHI, secrets, and prompt injection. */
|
|
53
|
+
sanitizeText(params: {
|
|
54
|
+
content: string;
|
|
55
|
+
policy?: string;
|
|
56
|
+
mode?: string;
|
|
57
|
+
aiModel?: string;
|
|
58
|
+
}): Promise<Record<string, any>>;
|
|
59
|
+
/** Reverse tokenization — map tokens back to original values. */
|
|
60
|
+
detokenize(params: {
|
|
61
|
+
content: string;
|
|
62
|
+
tokenMapping: Record<string, string>;
|
|
63
|
+
}): Promise<Record<string, any>>;
|
|
64
|
+
/** Create a webhook subscription. */
|
|
65
|
+
createWebhook(params: {
|
|
66
|
+
url: string;
|
|
67
|
+
events?: string[];
|
|
68
|
+
secret?: string;
|
|
69
|
+
}): Promise<Record<string, any>>;
|
|
70
|
+
/** List all webhooks for the organization. */
|
|
71
|
+
listWebhooks(): Promise<Array<Record<string, any>>>;
|
|
72
|
+
/** Delete a webhook. */
|
|
73
|
+
deleteWebhook(webhookId: string): Promise<Record<string, any>>;
|
|
74
|
+
/** Get usage summary for the current billing period. */
|
|
75
|
+
getUsage(): Promise<Record<string, any>>;
|
|
76
|
+
/** List individual usage events. */
|
|
77
|
+
listUsageEvents(params?: {
|
|
78
|
+
page?: number;
|
|
79
|
+
perPage?: number;
|
|
80
|
+
}): Promise<Record<string, any>>;
|
|
81
|
+
/** List available models. */
|
|
82
|
+
listModels(includeDisabled?: boolean): Promise<Array<Record<string, any>>>;
|
|
83
|
+
/** Get model preferences (disabled models list). */
|
|
84
|
+
getModelPreferences(): Promise<Record<string, any>>;
|
|
85
|
+
/** Update which models are disabled. */
|
|
86
|
+
updateModelPreferences(disabledModels: string[]): Promise<Record<string, any>>;
|
|
87
|
+
/** Get Aira's policy violations for a GitHub PR. */
|
|
88
|
+
getPrViolations(owner: string, repo: string, pullNumber: number): Promise<Array<Record<string, any>>>;
|
|
22
89
|
/**
|
|
23
90
|
* Step 1 — Authorize an action BEFORE it executes.
|
|
24
91
|
*
|
package/dist/client.js
CHANGED
|
@@ -132,6 +132,114 @@ class Aira {
|
|
|
132
132
|
const p = data.pagination;
|
|
133
133
|
return { data: data.data, total: p.total, page: p.page, per_page: p.per_page, has_more: p.has_more };
|
|
134
134
|
}
|
|
135
|
+
// ==================== Policies ====================
|
|
136
|
+
/** Create a governance policy. */
|
|
137
|
+
async createPolicy(params) {
|
|
138
|
+
return this.post("/policies", buildBody({
|
|
139
|
+
name: params.name,
|
|
140
|
+
mode: params.mode,
|
|
141
|
+
decision: params.decision ?? "deny",
|
|
142
|
+
priority: params.priority ?? 0,
|
|
143
|
+
conditions: params.conditions,
|
|
144
|
+
ai_prompt: params.aiPrompt,
|
|
145
|
+
ai_models: params.aiModels,
|
|
146
|
+
scan_config: params.scanConfig,
|
|
147
|
+
description: params.description,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
/** List all active policies. */
|
|
151
|
+
async listPolicies() {
|
|
152
|
+
const data = await this.get("/policies");
|
|
153
|
+
return data.data ?? [];
|
|
154
|
+
}
|
|
155
|
+
/** Get a single policy by ID. */
|
|
156
|
+
async getPolicy(policyId) {
|
|
157
|
+
return this.get(`/policies/${policyId}`);
|
|
158
|
+
}
|
|
159
|
+
/** Update a policy. Pass only fields to change. */
|
|
160
|
+
async updatePolicy(policyId, params) {
|
|
161
|
+
return this.put(`/policies/${policyId}`, buildBody(params));
|
|
162
|
+
}
|
|
163
|
+
/** Delete a policy. */
|
|
164
|
+
async deletePolicy(policyId) {
|
|
165
|
+
return this.del(`/policies/${policyId}`);
|
|
166
|
+
}
|
|
167
|
+
/** Activate a policy. */
|
|
168
|
+
async activatePolicy(policyId) {
|
|
169
|
+
return this.post(`/policies/${policyId}/activate`, {});
|
|
170
|
+
}
|
|
171
|
+
/** Deactivate a policy. */
|
|
172
|
+
async deactivatePolicy(policyId) {
|
|
173
|
+
return this.post(`/policies/${policyId}/deactivate`, {});
|
|
174
|
+
}
|
|
175
|
+
/** Test a policy against an action context without executing it. */
|
|
176
|
+
async dryRunPolicy(policyId, actionContext) {
|
|
177
|
+
return this.post(`/policies/${policyId}/dry-run`, { action_context: actionContext });
|
|
178
|
+
}
|
|
179
|
+
// ==================== Sanitize ====================
|
|
180
|
+
/** Scan text for PII, PHI, secrets, and prompt injection. */
|
|
181
|
+
async sanitizeText(params) {
|
|
182
|
+
return this.post("/sanitize", buildBody({
|
|
183
|
+
content: params.content,
|
|
184
|
+
policy: params.policy ?? "standard",
|
|
185
|
+
mode: params.mode ?? "flag",
|
|
186
|
+
ai_model: params.aiModel,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
/** Reverse tokenization — map tokens back to original values. */
|
|
190
|
+
async detokenize(params) {
|
|
191
|
+
return this.post("/sanitize/detokenize", {
|
|
192
|
+
content: params.content,
|
|
193
|
+
token_mapping: params.tokenMapping,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// ==================== Webhooks ====================
|
|
197
|
+
/** Create a webhook subscription. */
|
|
198
|
+
async createWebhook(params) {
|
|
199
|
+
return this.post("/webhooks", buildBody(params));
|
|
200
|
+
}
|
|
201
|
+
/** List all webhooks for the organization. */
|
|
202
|
+
async listWebhooks() {
|
|
203
|
+
return this.get("/webhooks");
|
|
204
|
+
}
|
|
205
|
+
/** Delete a webhook. */
|
|
206
|
+
async deleteWebhook(webhookId) {
|
|
207
|
+
return this.del(`/webhooks/${webhookId}`);
|
|
208
|
+
}
|
|
209
|
+
// ==================== Usage ====================
|
|
210
|
+
/** Get usage summary for the current billing period. */
|
|
211
|
+
async getUsage() {
|
|
212
|
+
return this.get("/usage");
|
|
213
|
+
}
|
|
214
|
+
/** List individual usage events. */
|
|
215
|
+
async listUsageEvents(params) {
|
|
216
|
+
const qs = new URLSearchParams();
|
|
217
|
+
if (params?.page)
|
|
218
|
+
qs.set("page", String(params.page));
|
|
219
|
+
if (params?.perPage)
|
|
220
|
+
qs.set("per_page", String(params.perPage));
|
|
221
|
+
return this.get(`/usage/events?${qs}`);
|
|
222
|
+
}
|
|
223
|
+
// ==================== Models ====================
|
|
224
|
+
/** List available models. */
|
|
225
|
+
async listModels(includeDisabled = false) {
|
|
226
|
+
const suffix = includeDisabled ? "?include_disabled=true" : "";
|
|
227
|
+
return this.get(`/models${suffix}`);
|
|
228
|
+
}
|
|
229
|
+
/** Get model preferences (disabled models list). */
|
|
230
|
+
async getModelPreferences() {
|
|
231
|
+
return this.get("/models/preferences");
|
|
232
|
+
}
|
|
233
|
+
/** Update which models are disabled. */
|
|
234
|
+
async updateModelPreferences(disabledModels) {
|
|
235
|
+
return this.put("/models/preferences", { disabled_models: disabledModels });
|
|
236
|
+
}
|
|
237
|
+
// ==================== Code Governance ====================
|
|
238
|
+
/** Get Aira's policy violations for a GitHub PR. */
|
|
239
|
+
async getPrViolations(owner, repo, pullNumber) {
|
|
240
|
+
const data = await this.get(`/webhooks/github/violations/${owner}/${repo}/${pullNumber}`);
|
|
241
|
+
return data.data ?? [];
|
|
242
|
+
}
|
|
135
243
|
// ==================== Actions (two-step: authorize → notarize) ====================
|
|
136
244
|
/**
|
|
137
245
|
* Step 1 — Authorize an action BEFORE it executes.
|
package/dist/extras/mcp.js
CHANGED
|
@@ -144,6 +144,24 @@ function getTools() {
|
|
|
144
144
|
required: ["action_uuid", "counterparty_did"],
|
|
145
145
|
},
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
name: "get_pr_violations",
|
|
149
|
+
description: "Get Aira's policy violation findings for a GitHub pull request. Returns file, line, finding, description, and suggested fix for each violation.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
type: "object",
|
|
152
|
+
properties: {
|
|
153
|
+
owner: { type: "string", description: "GitHub org or user" },
|
|
154
|
+
repo: { type: "string", description: "Repository name" },
|
|
155
|
+
pull_number: { type: "integer", description: "PR number" },
|
|
156
|
+
},
|
|
157
|
+
required: ["owner", "repo", "pull_number"],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "list_policies",
|
|
162
|
+
description: "List all active governance policies for the organization",
|
|
163
|
+
inputSchema: { type: "object", properties: {} },
|
|
164
|
+
},
|
|
147
165
|
];
|
|
148
166
|
}
|
|
149
167
|
/** Handle an MCP tool call and return text content. */
|
|
@@ -197,6 +215,14 @@ async function handleToolCall(client, name, args) {
|
|
|
197
215
|
const result = await client.requestMutualSign(args.action_uuid, args.counterparty_did);
|
|
198
216
|
return [{ type: "text", text: JSON.stringify(result) }];
|
|
199
217
|
}
|
|
218
|
+
if (name === "get_pr_violations") {
|
|
219
|
+
const result = await client.getPrViolations(args.owner, args.repo, args.pull_number);
|
|
220
|
+
return [{ type: "text", text: JSON.stringify(result) }];
|
|
221
|
+
}
|
|
222
|
+
if (name === "list_policies") {
|
|
223
|
+
const result = await client.listPolicies();
|
|
224
|
+
return [{ type: "text", text: JSON.stringify(result) }];
|
|
225
|
+
}
|
|
200
226
|
return [{ type: "text", text: JSON.stringify({ error: `Unknown tool: ${name}` }) }];
|
|
201
227
|
}
|
|
202
228
|
catch (e) {
|
package/dist/types.d.ts
CHANGED
|
@@ -27,12 +27,17 @@ export interface Authorization {
|
|
|
27
27
|
/**
|
|
28
28
|
* Cryptographic receipt from notarizing an action — Step 2 of the two-step flow.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* Status values:
|
|
31
|
+
* - "notarized" — action completed successfully, receipt minted
|
|
32
|
+
* - "failed" — action failed, Ed25519 receipt still minted for audit
|
|
33
|
+
* - "denied" — policy denied the action, Ed25519 receipt minted for the denial
|
|
34
|
+
* - "denied_by_human" — human reviewer denied the action, Ed25519 receipt minted
|
|
35
|
+
*
|
|
36
|
+
* Every action state produces an Ed25519 receipt — the audit trail has zero gaps.
|
|
32
37
|
*/
|
|
33
38
|
export interface ActionReceipt {
|
|
34
39
|
action_uuid: string;
|
|
35
|
-
status: "notarized" | "failed";
|
|
40
|
+
status: "notarized" | "failed" | "denied" | "denied_by_human";
|
|
36
41
|
created_at: string;
|
|
37
42
|
request_id: string;
|
|
38
43
|
receipt_uuid: string | null;
|
|
@@ -313,7 +318,8 @@ export declare class AiraError extends Error {
|
|
|
313
318
|
statusCode: number;
|
|
314
319
|
/** Error code string (e.g. "POLICY_DENIED", "INVALID_STATE"). */
|
|
315
320
|
code: string;
|
|
316
|
-
/** Optional backend-supplied context (policy_uuid, action_uuid, etc.).
|
|
321
|
+
/** Optional backend-supplied context (policy_uuid, action_uuid, receipt_uuid, etc.).
|
|
322
|
+
* For POLICY_DENIED errors, `receipt_uuid` is the Ed25519 receipt minted for the denial. */
|
|
317
323
|
details: Record<string, unknown>;
|
|
318
324
|
constructor(statusCode: number, code: string, message: string, details?: Record<string, unknown>);
|
|
319
325
|
}
|
package/dist/types.js
CHANGED
|
@@ -23,7 +23,8 @@ class AiraError extends Error {
|
|
|
23
23
|
statusCode;
|
|
24
24
|
/** Error code string (e.g. "POLICY_DENIED", "INVALID_STATE"). */
|
|
25
25
|
code;
|
|
26
|
-
/** Optional backend-supplied context (policy_uuid, action_uuid, etc.).
|
|
26
|
+
/** Optional backend-supplied context (policy_uuid, action_uuid, receipt_uuid, etc.).
|
|
27
|
+
* For POLICY_DENIED errors, `receipt_uuid` is the Ed25519 receipt minted for the denial. */
|
|
27
28
|
details;
|
|
28
29
|
constructor(statusCode, code, message, details = {}) {
|
|
29
30
|
super(`[${code}] ${message}`);
|